diff --git a/.bun-version b/.bun-version index 9728bd69ac8..3a3cd8cc8b0 100644 --- a/.bun-version +++ b/.bun-version @@ -1 +1 @@ -1.2.21 +1.3.1 diff --git a/.claude/hooks/skill-activation-prompt.sh b/.claude/hooks/skill-activation-prompt.sh new file mode 100755 index 00000000000..27fb24d0584 --- /dev/null +++ b/.claude/hooks/skill-activation-prompt.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Copied from https://github.com/diet103/claude-code-infrastructure-showcase/blob/c586f9d8854989abbe9040cde61527888ded3904/.claude/hooks/skill-activation-prompt.sh +set -e + +cd "$CLAUDE_PROJECT_DIR/.claude/hooks" +cat | bun run skill-activation-prompt.ts diff --git a/.claude/hooks/skill-activation-prompt.ts b/.claude/hooks/skill-activation-prompt.ts new file mode 100644 index 00000000000..f8feef6343d --- /dev/null +++ b/.claude/hooks/skill-activation-prompt.ts @@ -0,0 +1,132 @@ +#!/usr/bin/env node +/** biome-ignore-all lint/suspicious/noConsole: script output */ +// Copied from https://github.com/diet103/claude-code-infrastructure-showcase/blob/c586f9d8854989abbe9040cde61527888ded3904/.claude/hooks/skill-activation-prompt.ts +import { readFileSync } from 'fs' +import { join } from 'path' + +interface HookInput { + session_id: string + transcript_path: string + cwd: string + permission_mode: string + prompt: string +} + +interface PromptTriggers { + keywords?: string[] + intentPatterns?: string[] +} + +interface SkillRule { + type: 'guardrail' | 'domain' + enforcement: 'block' | 'suggest' | 'warn' + priority: 'critical' | 'high' | 'medium' | 'low' + promptTriggers?: PromptTriggers +} + +interface SkillRules { + version: string + skills: Record +} + +interface MatchedSkill { + name: string + matchType: 'keyword' | 'intent' + config: SkillRule +} + +async function main() { + try { + // Read input from stdin + const input = readFileSync(0, 'utf-8') + const data: HookInput = JSON.parse(input) + const prompt = data.prompt.toLowerCase() + + // Load skill rules + const projectDir = process.env.CLAUDE_PROJECT_DIR || '$HOME/project' + const rulesPath = join(projectDir, '.claude', 'skills', 'skill-rules.json') + const rules: SkillRules = JSON.parse(readFileSync(rulesPath, 'utf-8')) + + const matchedSkills: MatchedSkill[] = [] + + // Check each skill for matches + for (const [skillName, config] of Object.entries(rules.skills)) { + const triggers = config.promptTriggers + if (!triggers) { + continue + } + + // Keyword matching + if (triggers.keywords) { + const keywordMatch = triggers.keywords.some((kw) => prompt.includes(kw.toLowerCase())) + if (keywordMatch) { + matchedSkills.push({ name: skillName, matchType: 'keyword', config }) + continue + } + } + + // Intent pattern matching + if (triggers.intentPatterns) { + const intentMatch = triggers.intentPatterns.some((pattern) => { + const regex = new RegExp(pattern, 'i') + return regex.test(prompt) + }) + if (intentMatch) { + matchedSkills.push({ name: skillName, matchType: 'intent', config }) + } + } + } + + // Generate output if matches found + if (matchedSkills.length > 0) { + let output = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' + output += '🎯 SKILL ACTIVATION CHECK\n' + output += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' + + // Group by priority + const critical = matchedSkills.filter((s) => s.config.priority === 'critical') + const high = matchedSkills.filter((s) => s.config.priority === 'high') + const medium = matchedSkills.filter((s) => s.config.priority === 'medium') + const low = matchedSkills.filter((s) => s.config.priority === 'low') + + if (critical.length > 0) { + output += '⚠️ CRITICAL SKILLS (REQUIRED):\n' + critical.forEach((s) => (output += ` → ${s.name}\n`)) + output += '\n' + } + + if (high.length > 0) { + output += '📚 RECOMMENDED SKILLS:\n' + high.forEach((s) => (output += ` → ${s.name}\n`)) + output += '\n' + } + + if (medium.length > 0) { + output += '💡 SUGGESTED SKILLS:\n' + medium.forEach((s) => (output += ` → ${s.name}\n`)) + output += '\n' + } + + if (low.length > 0) { + output += '📌 OPTIONAL SKILLS:\n' + low.forEach((s) => (output += ` → ${s.name}\n`)) + output += '\n' + } + + output += 'ACTION: Use Skill tool BEFORE responding\n' + output += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' + + console.log(output) + } + + process.exit(0) + } catch (err) { + console.error('Error in skill-activation-prompt hook:', err) + process.exit(1) + } +} + +main().catch((err) => { + console.error('Uncaught error:', err) + process.exit(1) +}) diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000000..98c9f2f7c89 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,32 @@ +{ + "permissions": { + "deny": [ + "Read(**/.env)", + "Edit(**/.env)", + "Read(~/.aws/**)", + "Edit(~/.aws/**)", + "Read(~/.ssh/**)", + "Edit(~/.ssh/**)", + "Read(~/.gnupg/**)", + "Edit(~/.gnupg/**)", + "Read(~/.git-credentials)", + "Edit(~/.git-credentials)", + "Read($HOME/Library/Keychains/**)", + "Edit($HOME/Library/Keychains/**)", + "Read(/private/etc/**)", + "Edit(/private/etc/**)" + ] + }, + "hooks": { + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-activation-prompt.sh" + } + ] + } + ] + } +} diff --git a/.claude/skills/skill-rules.json b/.claude/skills/skill-rules.json new file mode 100644 index 00000000000..83577579083 --- /dev/null +++ b/.claude/skills/skill-rules.json @@ -0,0 +1,29 @@ +{ + "version": "1.0", + "description": "Skill activation triggers for Claude Code. Controls when skills automatically suggest or block actions.", + "skills": { + "web-e2e": { + "type": "domain", + "enforcement": "block", + "priority": "critical", + "description": "Run, debug, and create Playwright e2e tests for the web app.", + "promptTriggers": { + "keywords": ["e2e", "end-to-end", "playwright"], + "intentPatterns": ["(run|start|debug|create|explain).*?e2e"] + } + } + }, + "notes": { + "enforcement_types": { + "suggest": "Skill suggestion appears but doesn't block execution", + "block": "Requires skill to be used before proceeding (guardrail)", + "warn": "Shows warning but allows proceeding" + }, + "priority_levels": { + "critical": "Highest - Always trigger when matched", + "high": "Important - Trigger for most matches", + "medium": "Moderate - Trigger for clear matches", + "low": "Optional - Trigger only for explicit matches" + } + } +} diff --git a/.claude/skills/web-e2e/SKILL.md b/.claude/skills/web-e2e/SKILL.md new file mode 100644 index 00000000000..aad00d76d9b --- /dev/null +++ b/.claude/skills/web-e2e/SKILL.md @@ -0,0 +1,350 @@ +--- +name: web-e2e +description: Run, create, and debug Playwright e2e tests for the web app. ALWAYS invoke this skill using the SlashCommand tool (i.e., `/web-e2e`) BEFORE attempting to run any e2e tests, playwright tests, anvil tests, or debug test failures. DO NOT run `bun playwright test` or other e2e commands directly - you must invoke this skill first to learn the correct commands and test architecture. +allowed-tools: [Read, Write, Edit, Bash, Glob, Grep, mcp__playwright__browser_navigate, mcp__playwright__browser_snapshot, mcp__playwright__browser_click, mcp__playwright__browser_type, mcp__playwright__browser_take_screenshot, mcp__playwright__browser_console_messages, mcp__playwright__browser_network_requests, mcp__playwright__browser_evaluate] +--- + +# Web E2E Testing Skill + +This skill helps you create and run end-to-end (e2e) Playwright tests for the Uniswap web application. + +## Test Architecture + +### Test Location +- All e2e tests live in `apps/web/src/` directory structure +- Test files use the naming convention: `*.e2e.test.ts` +- Anvil-specific tests (requiring local blockchain): `*.anvil.e2e.test.ts` + +### Automatic Wallet Connection + +**Important**: When running Playwright tests, the app automatically connects to a test wallet: +- **Address**: `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` (constant: `TEST_WALLET_ADDRESS`) +- **Display name**: `test0` (the Unitag associated with this address) +- **Connection**: Happens automatically via `wagmiAutoConnect.ts` when in Playwright environment + +This means: +- Tests start with a wallet already connected +- You can immediately test wallet-dependent features +- The wallet button will show "test0" instead of "Connect wallet" + +**When using Playwright MCP**: To enable automatic wallet connection when browsing via MCP tools, set the environment variable `REACT_APP_IS_PLAYWRIGHT_ENV=true` before starting the dev server. This makes the app behave identically to how it does in automated tests, with the test wallet auto-connected. + +### Custom Fixtures + +The web app uses custom Playwright fixtures and mocks that extend base Playwright functionality. +They are located in `apps/web/src/playwright/fixtures/*` and `apps/web/src/playwright/mocks/*`. + +#### Import Pattern +```typescript +import { expect, getTest } from 'playwright/fixtures' + +// For regular tests (no blockchain) +const test = getTest() + +// For anvil tests (with blockchain) +const test = getTest({ withAnvil: true }) +``` + +#### Available Fixtures + +1. **graphql** - Mock GraphQL responses + ```typescript + await graphql.intercept('OperationName', Mocks.Path.to_mock) + await graphql.waitForResponse('OperationName') + ``` + +2. **anvil** - Local blockchain client (only in anvil tests) + ```typescript + // Set token balances + await anvil.setErc20Balance({ address, balance }) + + // Check balances + await anvil.getBalance({ address }) + await anvil.getErc20Balance(tokenAddress, ownerAddress) + + // Manage allowances + await anvil.setErc20Allowance({ address, spender, amount }) + await anvil.setPermit2Allowance({ token, spender, amount }) + + // Mining blocks + await anvil.mine({ blocks: 1 }) + + // Snapshots for isolation + const snapshotId = await anvil.takeSnapshot() + await anvil.revertToSnapshot(snapshotId) + ``` + +3. **tradingApi** - Mock Trading API responses + ```typescript + await stubTradingApiEndpoint({ + page, + endpoint: uniswapUrls.tradingApiPaths.swap + }) + ``` + +4. **amplitude** - Analytics mocking (automatic) + +### Test Structure + +```typescript +import { expect, getTest } from 'playwright/fixtures' +import { TestID } from 'uniswap/src/test/fixtures/testIDs' + +const test = getTest({ withAnvil: true }) // or getTest() for non-anvil + +test.describe('Feature Name', () => { + test.beforeEach(async ({ page }) => { + // Setup before each test + }) + + test('should do something', async ({ page, anvil, graphql }) => { + // Setup mocks + await graphql.intercept('Operation', Mocks.Path.mock) + + // Setup blockchain state (if anvil test) + await anvil.setErc20Balance({ address, balance }) + + // Navigate to page + await page.goto('/path') + + // Interact with UI using TestIDs + await page.getByTestId(TestID.SomeButton).click() + + // Make assertions + await expect(page.getByText('Expected Text')).toBeVisible() + }) +}) +``` + +### Best Practices + +1. **Use TestIDs** - Always use the TestID enum for selectors (not string literals) + ```typescript + // Good + await page.getByTestId(TestID.ReviewSwap) + + // Bad + await page.getByTestId('review-swap') + ``` + +2. **Mock External Services** - Use fixtures to mock GraphQL, Trading API, REST API etc. + ```typescript + await graphql.intercept('PortfolioBalances', Mocks.PortfolioBalances.test_wallet) + await stubTradingApiEndpoint({ page, endpoint: uniswapUrls.tradingApiPaths.quote }) + ``` + +3. **Use Mocks Helper** - Import mock paths from `playwright/mocks/mocks.ts` + ```typescript + import { Mocks } from 'playwright/mocks/mocks' + await graphql.intercept('Token', Mocks.Token.uni_token) + ``` + +4. **Test Constants** - Use constants from the codebase + ```typescript + import { USDT, DAI } from 'uniswap/src/constants/tokens' + import { TEST_WALLET_ADDRESS } from 'playwright/fixtures/wallets' + + // TEST_WALLET_ADDRESS is the automatically connected wallet + // It displays as "test0" in the UI + ``` + +5. **Anvil State Management** - Set up blockchain state properly + ```typescript + // Always set token balances before testing swaps + await anvil.setErc20Balance({ + address: assume0xAddress(USDT.address), + balance: 100_000_000n + }) + ``` + +## Running Tests + +The following commands must be run from the `apps/web/` folder. + +**⚠️ PREREQUISITE**: Playwright tests require the Vite preview server to be running at `http://localhost:3000` BEFORE tests start. The `bun e2e` commands handle this automatically, but if running tests directly you must start the server first. + +### Development Commands + +The `e2e` commands handle all requisite setup tasks for the playwright tests. These include building the app for production and running the Vite preview server. + +```bash +# Run all e2e tests (starts anvil, builds, and runs tests) +bun e2e + +# Run only non-anvil tests (faster, no blockchain required) +bun e2e:no-anvil + +# Run only anvil tests (blockchain tests only) +bun e2e:anvil + +# Run specific test file +bun e2e TokenSelector.e2e.test +``` + +### Direct Playwright Commands + +In some cases it may be helpful to run the commands more directly with the different tasks in different terminals. + +```bash +# Step 1: Build the web app for e2e +bun build:e2e + +# Step 2: Start the Vite preview server (REQUIRED - must be running before tests) +bun preview:e2e +# Wait for "Local: http://localhost:3000" message + +# (Optional) Step 3: Start Anvil (note, Anvil tests can start this themselves) +bun anvil:mainnet +# Wait for "Listening on 127.0.0.1:8545" message + +# Step 4: Run the playwright tests (only after servers are ready) +bun playwright:test +``` + +### Test Modes + +```bash +# Headed mode (see browser) +bun playwright test --headed + +# Debug mode with Playwright Inspector +bun playwright test --debug + +# UI mode (interactive) +bun playwright test --ui +``` + +## Configuration + +### Playwright Config (`playwright.config.ts`) + +Key settings: +- `testDir`: `./src` +- `testMatch`: `**/*.e2e.test.ts` +- `workers`: 1 (configured in CI) +- `fullyParallel`: false +- `baseURL`: `http://localhost:3000` + +## Common Patterns + +### Navigation and URL Testing +```typescript +await page.goto('/swap?inputCurrency=ETH&outputCurrency=USDT') +await expect(page.getByTestId(TestID.ChooseInputToken + '-label')).toHaveText('ETH') +``` + +### Form Interactions +```typescript +await page.getByTestId(TestID.AmountInputIn).fill('0.01') +await page.getByTestId(TestID.AmountInputIn).clear() +``` + +### Token Selection +```typescript +await page.getByTestId(TestID.ChooseOutputToken).click() +await page.getByTestId('token-option-1-USDT').first().click() +``` + +### Waiting for Transaction Completion +```typescript +await page.getByTestId(TestID.Swap).click() +await expect(page.getByText('Swapped')).toBeVisible() +``` + +### Blockchain Verification +```typescript +const balance = await anvil.getBalance({ address: TEST_WALLET_ADDRESS }) +await expect(balance).toBeLessThan(parseEther('10000')) +``` + +## Troubleshooting + +### Tests Timeout +- Check if Anvil is running: `bun anvil:mainnet` +- Ensure preview server is running: `bun preview:e2e` + +### Anvil Issues +- Tests automatically manage Anvil snapshots for isolation +- Anvil restarts automatically if unhealthy +- For manual restart: stop the e2e command and run again + +### Mock Not Working +- Ensure mock path is correct in `Mocks` object +- Check GraphQL operation name matches exactly +- Verify timing - intercept before the request is made + +### Test Flakiness +- Use proper waiting: `await expect(element).toBeVisible()` +- Don't use fixed `setTimeout` - use Playwright's auto-waiting +- Check for race conditions with network requests + +### Debugging + +- Run tests with `--headed` flag to watch the browser +- Use `--debug` flag to step through with Playwright Inspector +- Add `await page.pause()` in your test to stop at a specific point +- Check test output and error messages carefully +- Review screenshots/videos in `test-results/` directory after failures + +## Playwright Documentation References + +For more details on Playwright features, refer to: + +- **[Writing Tests](https://playwright.dev/docs/writing-tests)** - Test structure, actions, assertions +- **[Test Fixtures](https://playwright.dev/docs/test-fixtures)** - Creating custom fixtures (like our anvil/graphql fixtures) +- **[Running Tests](https://playwright.dev/docs/running-tests)** - Command line options, filtering, debugging +- **[API Testing](https://playwright.dev/docs/api-testing)** - Mocking and intercepting network requests +- **[Locators](https://playwright.dev/docs/locators)** - Finding elements (we use `getByTestId` primarily) +- **[Assertions](https://playwright.dev/docs/test-assertions)** - Available expect matchers +- **[Test Hooks](https://playwright.dev/docs/api/class-test#test-before-each)** - beforeEach, afterEach, beforeAll, afterAll +- **[Test Configuration](https://playwright.dev/docs/test-configuration)** - playwright.config.ts options +- **[Debugging Tests](https://playwright.dev/docs/debug)** - UI mode, inspector, trace viewer + +## Playwright MCP Integration (Optional but Recommended) + +The Playwright MCP (Model Context Protocol) provides browser automation capabilities that make test development and debugging easier: +- **Interactive debugging** - Navigate the app in a real browser to understand behavior +- **Creating tests** - Explore the UI to identify selectors and interactions +- **Debugging failures** - Inspect page state when tests fail + +### Installing Playwright MCP + +If you don't have the Playwright MCP installed, you can add it to your Claude Code configuration: + +1. Open Claude Code settings (Command/Ctrl + Shift + P → "Claude Code: Open Settings") +2. Add the Playwright MCP to your `mcpServers` configuration: + +```json +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["-y", "@executeautomation/playwright-mcp-server"] + } + } +} +``` + +3. Restart Claude Code + +Alternatively, follow the installation guide at: https://github.com/executeautomation/playwright-mcp + +### Using Playwright MCP for Test Development (Optional) + +**If you have the MCP installed**, you can use these tools during development: + +1. **Navigate and explore** - Use `mcp__playwright__browser_navigate` to visit pages +2. **Take snapshots** - Use `mcp__playwright__browser_snapshot` to see the page structure and find TestIDs +3. **Interact with elements** - Use `mcp__playwright__browser_click` and `mcp__playwright__browser_type` to test interactions +4. **Inspect state** - Use `mcp__playwright__browser_console_messages` and `mcp__playwright__browser_network_requests` to debug +5. **Take screenshots** - Use `mcp__playwright__browser_take_screenshot` to visualize issues + +## When to Use This Skill + +Use this skill when you need to: +- Create new end-to-end tests for web features +- Debug or fix failing e2e tests +- Run e2e tests during development +- Understand the e2e testing architecture +- Set up test fixtures or mocks +- Work with Anvil blockchain state in tests diff --git a/.cursor/cli.json b/.cursor/cli.json new file mode 100644 index 00000000000..b50608cd785 --- /dev/null +++ b/.cursor/cli.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "permissions": { + "deny": [ + "Read(**/.env)", + "Write(**/.env)", + "Read(~/.aws/**)", + "Write(~/.aws/**)", + "Read(~/.ssh/**)", + "Write(~/.ssh/**)", + "Read(~/.gnupg/**)", + "Write(~/.gnupg/**)", + "Read(~/.git-credentials)", + "Write(~/.git-credentials)", + "Read($HOME/Library/Keychains/**)", + "Write($HOME/Library/Keychains/**)", + "Read(/private/etc/**)", + "Write(/private/etc/**)" + ] + } + } diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 00000000000..70179d19f2e --- /dev/null +++ b/.cursorignore @@ -0,0 +1,7 @@ +**/.env +**/.aws/** +**/.ssh/** +**/.gnupg/** +**/.git-credentials +**/Library/Keychains/** +**/private/etc/** diff --git a/.env.defaults b/.env.defaults index af00c667e56..d46cd294f96 100644 --- a/.env.defaults +++ b/.env.defaults @@ -24,6 +24,9 @@ TRADING_API_KEY=stored-in-.env.local FIREBASE_APP_CHECK_DEBUG_TOKEN=stored-in-.env.local INCLUDE_PROTOTYPE_FEATURES=stored-in-.env.local IS_E2E_TEST=false +ENABLE_SESSION_SERVICE=false +ENABLE_SESSION_UPGRADE_AUTO=false +ENABLE_ENTRY_GATEWAY_PROXY=false # URL overrides (keep empty in this file) AMPLITUDE_PROXY_URL_OVERRIDE= API_BASE_URL_OVERRIDE= @@ -34,5 +37,9 @@ SCANTASTIC_API_URL_OVERRIDE= STATSIG_PROXY_URL_OVERRIDE= TRADING_API_URL_OVERRIDE= UNITAGS_API_URL_OVERRIDE= +UNISWAP_NOTIF_API_BASE_URL_OVERRIDE= +ENTRY_GATEWAY_API_URL_OVERRIDE= +LIQUIDITY_SERVICE_URL_OVERRIDE= GH_TOKEN_RN_CLI= JUPITER_PROXY_URL= +BLOCKAID_PROXY_URL= diff --git a/.gitignore b/.gitignore index 3b3a172871a..d284a0e6cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ yarn-error.log* # local env files .env +.env.local .env.defaults.local # turbo @@ -50,6 +51,9 @@ packages/uniswap/src/i18n/locales/source/*_old.json # ci .ci-cache/ +# cli gh analysis artifacts +.analysis/ + # JetBrains .idea/ @@ -59,8 +63,11 @@ packages/uniswap/src/i18n/locales/source/*_old.json # CodeTours Extension .tours/* -# RNEF -.rnef/ +# Expo +.expo/ + +# auto-generated test ids +apps/mobile/.maestro/scripts/testIds.js # claude claude.local.md @@ -76,9 +83,10 @@ CLAUDE.local.md # lefthook .lefthook/ - - +# Nx .nx/cache .nx/workspace-data -.cursor/rules/nx-rules.mdc .github/instructions/nx.instructions.md + +# Spec Workflow MCP +.spec-workflow/ diff --git a/.nxignore b/.nxignore index 3e785b63ed7..52d9b176674 100644 --- a/.nxignore +++ b/.nxignore @@ -10,6 +10,7 @@ apps/extension/dev apps/extension/build packages/*/dist packages/*/types +dist/out-tsc # Ignore Generator Templates tools/**/generators/**/files diff --git a/CLAUDE.md b/CLAUDE.md index 00a8280afa0..7224bbdb032 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,6 +44,7 @@ bun extension build:production # Extension production ```bash bun g:test # Run all tests +bun notifications test # Run tests for a specific package (e.g. notifications) bun g:test:coverage # With coverage bun web playwright:test # Web E2E tests bun mobile e2e # Mobile E2E tests @@ -158,4 +159,4 @@ Be cognizant of the app or package within which a given change is being made. Be - If the user needs help with an Nx configuration or project graph error, use the `nx_workspace` tool to get any errors - \ No newline at end of file + diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index f70773659eb..00000000000 --- a/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @uniswap/web-admins diff --git a/RELEASE b/RELEASE index 80f8e3ca3e2..af0456c226c 100644 --- a/RELEASE +++ b/RELEASE @@ -1,23 +1 @@ -IPFS hash of the deployment: -- CIDv0: `QmZmRjJXcRL1HVbuFBLX23qqmQydzmqGsLmb94qrqTU9A7` -- CIDv1: `bafybeifjzfti2rq27be42zfwqnturszxqyecs4zxklxxr4pejgcgi72eja` - -The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). - -You can also access the Uniswap Interface from an IPFS gateway. -**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported. -**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org). -Your Uniswap settings are never remembered across different URLs. - -IPFS gateways: -- https://bafybeifjzfti2rq27be42zfwqnturszxqyecs4zxklxxr4pejgcgi72eja.ipfs.dweb.link/ -- [ipfs://QmZmRjJXcRL1HVbuFBLX23qqmQydzmqGsLmb94qrqTU9A7/](ipfs://QmZmRjJXcRL1HVbuFBLX23qqmQydzmqGsLmb94qrqTU9A7/) - -## 5.115.0 (2025-10-24) - - -### Features - -* **web:** special case metamask dual vm connection flow (#24756) (#24789) b21eafd - - +Various bug fixes and performance improvements diff --git a/VERSION b/VERSION index 885f8abd813..d82ad472983 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -web/5.115.0 \ No newline at end of file +mobile/1.64.1 \ No newline at end of file diff --git a/apps/api-self-serve/.eslintrc.js b/apps/api-self-serve/.eslintrc.js new file mode 100644 index 00000000000..bc045be8575 --- /dev/null +++ b/apps/api-self-serve/.eslintrc.js @@ -0,0 +1,44 @@ +const restrictedGlobals = require('confusing-browser-globals') +const rulesDirPlugin = require('eslint-plugin-rulesdir') +rulesDirPlugin.RULES_DIR = '../../packages/uniswap/eslint_rules' + +module.exports = { + root: true, + extends: ['@uniswap/eslint-config/extension'], + plugins: ['rulesdir'], + ignorePatterns: [ + 'node_modules', + '.react-router', + 'dist', + 'build', + '.eslintrc.js', + 'manifest.json', + '.nx', + 'vite.config.ts', + ], + parserOptions: { + project: 'tsconfig.eslint.json', + tsconfigRootDir: __dirname, + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 2018, + sourceType: 'module', + }, + rules: { + 'rulesdir/i18n': 'error', + }, + overrides: [ + { + files: ['*.ts', '*.tsx'], + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: false, + }, + ], + }, + }, + ], +} diff --git a/apps/api-self-serve/.gitignore b/apps/api-self-serve/.gitignore new file mode 100644 index 00000000000..039ee62d21a --- /dev/null +++ b/apps/api-self-serve/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.env +/node_modules/ + +# React Router +/.react-router/ +/build/ diff --git a/apps/api-self-serve/README.md b/apps/api-self-serve/README.md new file mode 100644 index 00000000000..25d1b31c061 --- /dev/null +++ b/apps/api-self-serve/README.md @@ -0,0 +1 @@ +# API Self Serve Portal diff --git a/apps/api-self-serve/app/app.css b/apps/api-self-serve/app/app.css new file mode 100644 index 00000000000..abd90f244db --- /dev/null +++ b/apps/api-self-serve/app/app.css @@ -0,0 +1,170 @@ +@import "tailwindcss/preflight"; +@import "tailwindcss"; +@plugin "tailwindcss-animate"; +@tailwind utilities; +@config "../tailwind.config.ts"; + +@custom-variant dark (&:is(.dark *)); + +@font-face { + font-family: "Basel Grotesk"; + src: url("https://app.uniswap.org/fonts/Basel-Grotesk-Book.woff2") + format("woff2"); + font-weight: 485; + font-style: normal; + font-display: swap; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +@font-face { + font-family: "Basel Grotesk"; + src: url("https://app.uniswap.org/fonts/Basel-Grotesk-Medium.woff2") + format("woff2"); + font-weight: 535; + font-style: normal; + font-display: swap; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +@layer base { + html, body { + @apply bg-background text-foreground font-basel; + } + * { + @apply border-border outline-ring/50; + } +} + +/* Light mode is the default */ +:root { + color-scheme: light; + --font-basel: "Basel Grotesk"; + /* Light mode shadows */ + --shadow-short: + 0px 1px 6px 2px rgba(0, 0, 0, 0.03), 0px 1px 2px 0px rgba(0, 0, 0, 0.02); + --shadow-medium: + 0px 6px 12px -3px rgba(19, 19, 19, 0.04), + 0px 2px 5px -2px rgba(19, 19, 19, 0.03); + --shadow-large: + 0px 10px 20px -5px rgba(19, 19, 19, 0.05), + 0px 4px 12px -3px rgba(19, 19, 19, 0.04); + /*shadcn*/ + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +/* Dark mode applies when .dark class is present */ +.dark { + color-scheme: dark; + /* Dark mode shadows */ + --shadow-short: + 0px 1px 3px 0px rgba(0, 0, 0, 0.12), 0px 1px 2px 0px rgba(0, 0, 0, 0.24); + --shadow-medium: + 0px 10px 15px -3px rgba(19, 19, 19, 0.54), + 0px 4px 6px -2px rgba(19, 19, 19, 0.4); + --shadow-large: + 0px 16px 24px -6px rgba(0, 0, 0, 0.6), 0px 8px 12px -4px rgba(0, 0, 0, 0.48); + /*shadcn*/ + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +/*shadcn*/ +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} diff --git a/apps/api-self-serve/app/lib/utils.ts b/apps/api-self-serve/app/lib/utils.ts new file mode 100644 index 00000000000..d32b0fe652e --- /dev/null +++ b/apps/api-self-serve/app/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/apps/api-self-serve/app/root.tsx b/apps/api-self-serve/app/root.tsx new file mode 100644 index 00000000000..9b3208dc36b --- /dev/null +++ b/apps/api-self-serve/app/root.tsx @@ -0,0 +1,53 @@ +import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' +import type { Route } from './+types/root' +import './app.css' + +export const links: Route.LinksFunction = () => [] + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ) +} + +export default function App() { + return +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = 'Oops!' + let details = 'An unexpected error occurred.' + let stack: string | undefined + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? '404' : 'Error' + details = error.status === 404 ? 'The requested page could not be found.' : error.statusText || details + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message + stack = error.stack + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ) +} diff --git a/apps/api-self-serve/app/routes.ts b/apps/api-self-serve/app/routes.ts new file mode 100644 index 00000000000..10d7044bf99 --- /dev/null +++ b/apps/api-self-serve/app/routes.ts @@ -0,0 +1,3 @@ +import { index, type RouteConfig } from '@react-router/dev/routes' + +export default [index('routes/home.tsx')] satisfies RouteConfig diff --git a/apps/api-self-serve/app/routes/home.tsx b/apps/api-self-serve/app/routes/home.tsx new file mode 100644 index 00000000000..c6316aae628 --- /dev/null +++ b/apps/api-self-serve/app/routes/home.tsx @@ -0,0 +1,11 @@ +import { Welcome } from '~/welcome/welcome' +import type { Route } from './+types/home' + +// biome-ignore lint/correctness/noEmptyPattern: this will likely be updated. this is ootb from the create react router app tool. +export function meta({}: Route.MetaArgs) { + return [{ title: 'New React Router App' }, { name: 'description', content: 'Welcome to React Router!' }] +} + +export default function Home() { + return +} diff --git a/apps/api-self-serve/app/welcome/logo-dark.svg b/apps/api-self-serve/app/welcome/logo-dark.svg new file mode 100644 index 00000000000..dd820289447 --- /dev/null +++ b/apps/api-self-serve/app/welcome/logo-dark.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/api-self-serve/app/welcome/logo-light.svg b/apps/api-self-serve/app/welcome/logo-light.svg new file mode 100644 index 00000000000..73284929d36 --- /dev/null +++ b/apps/api-self-serve/app/welcome/logo-light.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/api-self-serve/app/welcome/welcome.tsx b/apps/api-self-serve/app/welcome/welcome.tsx new file mode 100644 index 00000000000..92555f1574a --- /dev/null +++ b/apps/api-self-serve/app/welcome/welcome.tsx @@ -0,0 +1,80 @@ +/** biome-ignore-all lint/correctness/noRestrictedElements: this will be removed, it's default template */ +import logoDark from './logo-dark.svg' +import logoLight from './logo-light.svg' + +export function Welcome() { + return ( +
+
+
+
+ React Router + React Router +
+
+
+ +
+
+
+ ) +} + +const resources = [ + { + href: 'https://reactrouter.com/docs', + text: 'React Router Docs', + icon: ( + + + + ), + }, + { + href: 'https://rmx.as/discord', + text: 'Join Discord', + icon: ( + + + + ), + }, +] diff --git a/apps/api-self-serve/components.json b/apps/api-self-serve/components.json new file mode 100644 index 00000000000..d0a566ccc19 --- /dev/null +++ b/apps/api-self-serve/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/app.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "~/components", + "utils": "~/lib/utils", + "ui": "~/components/ui", + "lib": "~/lib", + "hooks": "~/hooks" + }, + "registries": {} +} diff --git a/apps/api-self-serve/package.json b/apps/api-self-serve/package.json new file mode 100644 index 00000000000..17c95e0eca3 --- /dev/null +++ b/apps/api-self-serve/package.json @@ -0,0 +1,36 @@ +{ + "name": "api-self-serve", + "private": true, + "type": "module", + "scripts": { + "build": "react-router build", + "dev": "react-router dev", + "start": "react-router-serve ./build/server/index.js", + "typecheck": "react-router typegen && tsc" + }, + "dependencies": { + "@react-router/node": "7.6.3", + "@react-router/serve": "7.6.3", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "isbot": "5.1.31", + "lucide-react": "0.548.0", + "react": "19.0.0", + "react-dom": "19.0.0", + "react-router": "7.6.3", + "tailwind-merge": "3.3.1", + "tailwindcss-animate": "1.0.7" + }, + "devDependencies": { + "@react-router/dev": "7.6.3", + "@tailwindcss/vite": "4.1.13", + "@types/node": "22.13.1", + "@types/react": "19.0.10", + "@uniswap/eslint-config": "workspace:^", + "eslint": "8.57.1", + "tailwindcss": "4.1.16", + "typescript": "5.8.3", + "vite": "npm:rolldown-vite@7.0.10", + "vite-tsconfig-paths": "5.1.4" + } +} diff --git a/apps/api-self-serve/react-router.config.ts b/apps/api-self-serve/react-router.config.ts new file mode 100644 index 00000000000..6ff16f91779 --- /dev/null +++ b/apps/api-self-serve/react-router.config.ts @@ -0,0 +1,7 @@ +import type { Config } from "@react-router/dev/config"; + +export default { + // Config options... + // Server-side render by default, to enable SPA mode set this to `false` + ssr: true, +} satisfies Config; diff --git a/apps/api-self-serve/tailwind.config.ts b/apps/api-self-serve/tailwind.config.ts new file mode 100644 index 00000000000..8135de497d5 --- /dev/null +++ b/apps/api-self-serve/tailwind.config.ts @@ -0,0 +1,430 @@ +import type { Config } from 'tailwindcss' +import tailwindAnimate from 'tailwindcss-animate' + +export default { + darkMode: 'class', + content: [ + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + './registry/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + fontFamily: { + basel: ['var(--font-basel)', 'sans-serif'], + baselBook: [ + 'Basel Grotesk Book', + '-apple-system', + 'system-ui', + 'BlinkMacSystemFont', + 'Segoe UI', + 'Roboto', + 'Helvetica', + 'Arial', + 'sans-serif', + ], + baselMedium: [ + 'Basel Grotesk Medium', + '-apple-system', + 'system-ui', + 'BlinkMacSystemFont', + 'Segoe UI', + 'Roboto', + 'Helvetica', + 'Arial', + 'sans-serif', + ], + mono: ['InputMono-Regular', 'monospace'], + }, + fontWeight: { + book: '485', + medium: '535', + }, + screens: { + xxs: '360px', + xs: '380px', + sm: '450px', + md: '640px', + lg: '768px', + xl: '1024px', + xxl: '1280px', + xxxl: '1536px', + 'h-short': { raw: '(max-height: 736px)' }, + 'h-mid': { raw: '(max-height: 800px)' }, + }, + fontSize: { + // Headings + 'heading-1': [ + '52px', + { + lineHeight: '60px', + letterSpacing: '-0.02em', + fontWeight: '485', + }, + ], + 'heading-2': [ + '36px', + { + lineHeight: '44px', + letterSpacing: '-0.01em', + fontWeight: '485', + }, + ], + 'heading-3': [ + '24px', + { + lineHeight: '32px', + letterSpacing: '-0.005em', + fontWeight: '485', + }, + ], + // Subheadings + 'subheading-1': [ + '18px', + { + lineHeight: '24px', + fontWeight: '485', + }, + ], + 'subheading-2': [ + '16px', + { + lineHeight: '24px', + fontWeight: '485', + }, + ], + // Body + 'body-1': [ + '18px', + { + lineHeight: '24px', + fontWeight: '485', + }, + ], + 'body-2': [ + '16px', + { + lineHeight: '24px', + fontWeight: '485', + }, + ], + 'body-3': [ + '14px', + { + lineHeight: '20px', + fontWeight: '485', + }, + ], + 'body-4': [ + '12px', + { + lineHeight: '16px', + fontWeight: '485', + }, + ], + // Button Labels + 'button-1': [ + '18px', + { + lineHeight: '24px', + fontWeight: '535', + }, + ], + 'button-2': [ + '16px', + { + lineHeight: '24px', + fontWeight: '535', + }, + ], + 'button-3': [ + '14px', + { + lineHeight: '20px', + fontWeight: '535', + }, + ], + 'button-4': [ + '12px', + { + lineHeight: '16px', + fontWeight: '535', + }, + ], + }, + colors: { + // Base colors + white: '#FFFFFF', + black: '#000000', + + // Semantic colors for light theme + background: { + DEFAULT: '#FFFFFF', // colors.white + dark: '#000000', // colors.black + }, + + // Neutral colors with semantic naming + neutral1: { + DEFAULT: '#222222', // neutral1_light + dark: '#FFFFFF', // neutral1_dark + }, + neutral2: { + DEFAULT: '#7D7D7D', // neutral2_light + dark: '#9B9B9B', // neutral2_dark + }, + neutral3: { + DEFAULT: '#CECECE', // neutral3_light + dark: '#5E5E5E', // neutral3_dark + }, + + // Surface colors with semantic naming + surface1: { + DEFAULT: '#FFFFFF', // surface1_light + dark: '#131313', // surface1_dark + hovered: { + DEFAULT: '#F5F5F5', // surface1_hovered_light + dark: '#181818', // surface1_hovered_dark + }, + }, + surface2: { + DEFAULT: '#F9F9F9', // surface2_light + dark: '#1B1B1B', // surface2_dark + hovered: { + DEFAULT: '#F2F2F2', // surface2_hovered_light + dark: '#242424', // surface2_hovered_dark + }, + }, + surface3: { + DEFAULT: '#22222212', // surface3_light + dark: '#FFFFFF12', // surface3_dark + hovered: { + DEFAULT: 'rgba(34, 34, 34, 0.12)', // surface3_hovered_light + dark: 'rgba(255, 255, 255, 0.16)', // surface3_hovered_dark + }, + }, + surface4: { + DEFAULT: '#FFFFFF64', // surface4_light + dark: '#FFFFFF20', // surface4_dark + }, + surface5: { + DEFAULT: '#00000004', // surface5_light + dark: '#00000004', // surface5_dark + }, + + // Accent colors with semantic naming + accent1: { + DEFAULT: '#FC72FF', // accent1_light + dark: '#FC72FF', // accent1_dark + }, + accent2: { + DEFAULT: '#FFEFFF', // accent2_light + dark: '#311C31', // accent2_dark + }, + accent3: { + DEFAULT: '#4C82FB', // accent3_light + dark: '#4C82FB', // accent3_dark + }, + + // Token colors + token0: { + DEFAULT: '#FC72FF', // token0 in light theme + dark: '#FC72FF', // token0 in dark theme + }, + token1: { + DEFAULT: '#4C82FB', // token1 in light theme + dark: '#4C82FB', // token1 in dark theme + }, + + // Status colors + success: { + DEFAULT: '#40B66B', // success + }, + critical: { + DEFAULT: '#FF5F52', // critical + secondary: { + DEFAULT: '#FFF2F1', // critical2_light + dark: '#2E0805', // critical2_dark + }, + }, + warning: { + DEFAULT: '#EEB317', // gold200 + }, + + // Network colors + network: { + ethereum: '#627EEA', + optimism: '#FF0420', + polygon: '#A457FF', + arbitrum: '#28A0F0', + bsc: '#F0B90B', + base: '#0052FF', + blast: '#FCFC03', + }, + + // Gray palette + gray: { + 50: '#F5F6FC', + 100: '#E8ECFB', + 150: '#D2D9EE', + 200: '#B8C0DC', + 250: '#A6AFCA', + 300: '#98A1C0', + 350: '#888FAB', + 400: '#7780A0', + 450: '#6B7594', + 500: '#5D6785', + 550: '#505A78', + 600: '#404A67', + 650: '#333D59', + 700: '#293249', + 750: '#1B2236', + 800: '#131A2A', + 850: '#0E1524', + 900: '#0D111C', + 950: '#080B11', + }, + + // Pink palette + pink: { + 50: '#F9ECF1', + 100: '#FFD9E4', + 200: '#FBA4C0', + 300: '#FF6FA3', + 400: '#FB118E', + 500: '#C41969', + 600: '#8C0F49', + 700: '#55072A', + 800: '#350318', + 900: '#2B000B', + vibrant: '#F50DB4', + base: '#FC74FE', + }, + + // Red palette + red: { + 50: '#FAECEA', + 100: '#FED5CF', + 200: '#FEA79B', + 300: '#FD766B', + 400: '#FA2B39', + 500: '#C4292F', + 600: '#891E20', + 700: '#530F0F', + 800: '#380A03', + 900: '#240800', + vibrant: '#F14544', + }, + + // Additional color palettes + yellow: { + 50: '#F6F2D5', + 100: '#DBBC19', + 200: '#DBBC19', + 300: '#BB9F13', + 400: '#A08116', + 500: '#866311', + 600: '#5D4204', + 700: '#3E2B04', + 800: '#231902', + 900: '#180F02', + vibrant: '#FAF40A', + }, + + green: { + 50: '#E3F3E6', + 100: '#BFEECA', + 200: '#76D191', + 300: '#40B66B', + 400: '#209853', + 500: '#0B783E', + 600: '#0C522A', + 700: '#053117', + 800: '#091F10', + 900: '#09130B', + vibrant: '#5CFE9D', + }, + + blue: { + 50: '#EDEFF8', + 100: '#DEE1FF', + 200: '#ADBCFF', + 300: '#869EFF', + 400: '#4C82FB', + 500: '#1267D6', + 600: '#1D4294', + 700: '#09265E', + 800: '#0B193F', + 900: '#040E34', + vibrant: '#587BFF', + }, + + gold: { + 200: '#EEB317', + 400: '#B17900', + vibrant: '#FEB239', + }, + + magenta: { + 300: '#FD82FF', + vibrant: '#FC72FF', + }, + + purple: { + 300: '#8440F2', + 900: '#1C0337', + vibrant: '#6100FF', + }, + + // Legacy colors mapping (for compatibility) + border: '#F9F9F9', + input: '#F9F9F9', + ring: '#222222', + foreground: '#222222', + card: { + DEFAULT: '#FFFFFF', + foreground: '#222222', + }, + popover: { + DEFAULT: '#FFFFFF', + foreground: '#222222', + }, + primary: { + DEFAULT: '#222222', + foreground: '#F9F9F9', + }, + secondary: { + DEFAULT: '#F9F9F9', + foreground: '#222222', + }, + muted: { + DEFAULT: '#F9F9F9', + foreground: '#7D7D7D', + }, + destructive: { + DEFAULT: '#FF5F52', + foreground: '#F9F9F9', + }, + scrim: 'rgba(0, 0, 0, 0.60)', + }, + borderRadius: { + none: '0px', + rounded4: '4px', + rounded6: '6px', + rounded8: '8px', + rounded12: '12px', + rounded16: '16px', + rounded20: '20px', + rounded24: '24px', + rounded32: '32px', + roundedFull: '999999px', + }, + boxShadow: { + short: 'var(--shadow-short)', + medium: 'var(--shadow-medium)', + large: 'var(--shadow-large)', + }, + }, + }, + plugins: [tailwindAnimate], +} satisfies Config diff --git a/apps/api-self-serve/tsconfig.eslint.json b/apps/api-self-serve/tsconfig.eslint.json new file mode 100644 index 00000000000..0af7bb26f3f --- /dev/null +++ b/apps/api-self-serve/tsconfig.eslint.json @@ -0,0 +1,5 @@ +// same as tsconfig.json but without references which caused performance issues with typescript-eslint +{ + "extends": "./tsconfig.json", + "references": [] +} diff --git a/apps/api-self-serve/tsconfig.json b/apps/api-self-serve/tsconfig.json new file mode 100644 index 00000000000..a0d80e99890 --- /dev/null +++ b/apps/api-self-serve/tsconfig.json @@ -0,0 +1,23 @@ +{ + "include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"], + "exclude": ["tools/**/*"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["node", "vite/client"], + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "rootDirs": [".", "./.react-router/types"], + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + "esModuleInterop": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true + } +} diff --git a/apps/api-self-serve/vite.config.ts b/apps/api-self-serve/vite.config.ts new file mode 100644 index 00000000000..e0925ec7ad1 --- /dev/null +++ b/apps/api-self-serve/vite.config.ts @@ -0,0 +1,15 @@ +import { reactRouter } from '@react-router/dev/vite' +import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from 'vite' +import tsconfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + plugins: [ + tailwindcss(), + reactRouter(), + tsconfigPaths({ + // ignores tsconfig files in Nx generator template directories + skip: (dir) => dir.includes('files'), + }), + ], +}) diff --git a/apps/cli/.eslintrc.cjs b/apps/cli/.eslintrc.cjs new file mode 100644 index 00000000000..e2b34a6c0b6 --- /dev/null +++ b/apps/cli/.eslintrc.cjs @@ -0,0 +1,45 @@ +module.exports = { + root: true, + extends: ['@uniswap/eslint-config/native', '@uniswap/eslint-config/webPlatform'], + ignorePatterns: [ + 'node_modules', + '.turbo', + '.eslintrc.js', + 'vitest.config.ts', + 'codegen.ts', + '.nx', + 'scripts', + 'dist', + 'src/**/__generated__', + ], + parserOptions: { + project: 'tsconfig.lint.json', + tsconfigRootDir: __dirname, + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 2018, + sourceType: 'module', + }, + overrides: [ + { + files: ['src/index.ts'], + rules: { + 'check-file/no-index': 'off', + }, + }, + { + files: ['*.ts', '*.tsx'], + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: false, + prefix: '@universe/cli', + }, + ], + }, + }, + ], + rules: {}, +} diff --git a/apps/cli/README.md b/apps/cli/README.md new file mode 100644 index 00000000000..ce9112633d7 --- /dev/null +++ b/apps/cli/README.md @@ -0,0 +1,2 @@ +# @universe/cli + diff --git a/apps/cli/package.json b/apps/cli/package.json new file mode 100644 index 00000000000..86b22c11c5c --- /dev/null +++ b/apps/cli/package.json @@ -0,0 +1,38 @@ +{ + "name": "@universe/cli", + "version": "0.0.0", + "type": "module", + "dependencies": { + "@ai-sdk/anthropic": "2.0.41", + "ai": "5.0.87", + "ink": "5.2.1", + "ink-box": "2.0.0", + "ink-select-input": "6.2.0", + "ink-spinner": "5.0.0", + "ink-text-input": "6.0.0", + "react": "19.0.0" + }, + "devDependencies": { + "@types/bun": "1.3.1", + "@types/node": "22.13.1", + "@types/react": "19.0.10", + "@uniswap/eslint-config": "workspace:^", + "depcheck": "1.4.7", + "eslint": "8.57.1", + "ink-gradient": "3.0.0", + "typescript": "5.8.3" + }, + "scripts": { + "dev": "bun run --watch src/cli-ui.tsx", + "lint:biome": "nx lint:biome cli", + "lint:biome:fix": "nx lint:biome:fix cli", + "lint": "nx lint cli", + "lint:fix": "nx lint:fix cli" + }, + "nx": { + "includedScripts": [] + }, + "main": "src/index.ts", + "private": true, + "sideEffects": false +} diff --git a/apps/cli/project.json b/apps/cli/project.json new file mode 100644 index 00000000000..a9dc79ffbd2 --- /dev/null +++ b/apps/cli/project.json @@ -0,0 +1,19 @@ +{ + "name": "@universe/cli", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/cli/src", + "projectType": "application", + "tags": [], + "targets": { + "build": { + "executor": "nx:noop" + }, + "typecheck": {}, + "lint:biome": {}, + "lint:biome:fix": {}, + "lint:eslint": {}, + "lint:eslint:fix": {}, + "lint": {}, + "lint:fix": {} + } +} diff --git a/apps/cli/src/cli-ui.tsx b/apps/cli/src/cli-ui.tsx new file mode 100644 index 00000000000..731955fdc03 --- /dev/null +++ b/apps/cli/src/cli-ui.tsx @@ -0,0 +1,27 @@ +#!/usr/bin/env bun +import { App } from '@universe/cli/src/ui/App' +import { render } from 'ink' +// Ensure React is loaded before ink +import React from 'react' + +export async function runUI(): Promise { + // Check for API key + if (!process.env.ANTHROPIC_API_KEY) { + // eslint-disable-next-line no-console + console.error('Error: ANTHROPIC_API_KEY environment variable is required') + // eslint-disable-next-line no-console + console.error('Get your API key from: https://console.anthropic.com/') + process.exit(1) + } + + render(React.createElement(App)) +} + +// Run if executed directly +if (import.meta.main) { + runUI().catch((error) => { + // eslint-disable-next-line no-console + console.error('Unhandled error:', error) + process.exit(1) + }) +} diff --git a/apps/cli/src/cli.ts b/apps/cli/src/cli.ts new file mode 100644 index 00000000000..3ac8f3939b9 --- /dev/null +++ b/apps/cli/src/cli.ts @@ -0,0 +1,429 @@ +#!/usr/bin/env bun +/* eslint-disable complexity */ +import { type CollectOptions } from '@universe/cli/src/core/data-collector' +import { Orchestrator, type OrchestratorConfig, type OutputConfig } from '@universe/cli/src/core/orchestrator' +import { createVercelAIProvider } from '@universe/cli/src/lib/ai-provider-vercel' +import { SqliteCacheProvider } from '@universe/cli/src/lib/cache-provider-sqlite' +import { ConsoleLogger, type Logger } from '@universe/cli/src/lib/logger' +import { parseReleaseIdentifier, ReleaseScanner } from '@universe/cli/src/lib/release-scanner' +import { detectRepository, resolveTeam } from '@universe/cli/src/lib/team-resolver' +import { parseArgs } from 'util' + +/* eslint-disable no-console */ + +// ============================================================================ +// CLI Configuration +// ============================================================================ + +async function main(): Promise { + const { values, positionals } = parseArgs({ + args: Bun.argv, + options: { + // UI options + interactive: { type: 'boolean', default: false, description: 'Force interactive UI mode' }, + ui: { type: 'boolean', default: false, description: 'Force interactive UI mode (alias for --interactive)' }, + + // Analysis options + mode: { + type: 'string', + description: 'Analysis mode (team-digest, changelog, release-changelog, bug-bisect, etc.)', + }, + prompt: { type: 'string', description: 'Custom prompt (file path or inline text)' }, + bug: { type: 'string', description: 'Bug description for bug-bisect mode (requires --release)' }, + + // Data filtering + team: { type: 'string', description: 'Team filter (@org/team or user1,user2)' }, + since: { type: 'string', default: '30 days ago', description: 'Time period to analyze' }, + repo: { type: 'string', description: 'Repository (owner/name)' }, + 'include-open-prs': { type: 'boolean', default: false, description: 'Include open PRs' }, + + // Release options + release: { type: 'string', description: 'Release to analyze (e.g., mobile/1.60 or extension/1.30.0)' }, + 'compare-with': { type: 'string', description: 'Specific version to compare with' }, + 'list-releases': { type: 'boolean', default: false, description: 'List available releases' }, + platform: { type: 'string', description: 'Platform filter (mobile or extension)' }, + + // Commit data options + 'include-diffs': { type: 'boolean', default: false, description: 'Include actual diff content in commits' }, + 'max-diff-size': { type: 'string', default: '100', description: 'Max lines changed per file to include diff' }, + 'max-diff-files': { type: 'string', default: '20', description: 'Max number of files to include diffs for' }, + 'diff-pattern': { type: 'string', description: 'Regex pattern for files to include diffs' }, + 'exclude-test-diffs': { type: 'boolean', default: true, description: 'Exclude test files from diffs' }, + 'token-budget': { type: 'string', default: '50000', description: 'Approximate token budget for commit data' }, + 'pr-body-limit': { type: 'string', default: '2000', description: 'Max characters for PR body' }, + 'save-artifacts': { type: 'boolean', default: false, description: 'Save analysis artifacts for debugging' }, + + // Output options + output: { type: 'string', multiple: true, description: 'Output targets (can specify multiple)' }, + + // Other options + verbose: { type: 'boolean', default: false, description: 'Verbose logging' }, + 'dry-run': { type: 'boolean', default: false, description: 'Test mode without publishing' }, + 'no-cache': { type: 'boolean', default: false, description: 'Bypass cache and fetch fresh data' }, + 'force-refresh': { + type: 'boolean', + default: false, + description: 'Bypass cache and fetch fresh data (alias for --no-cache)', + }, + help: { type: 'boolean', default: false, description: 'Show help' }, + }, + strict: true, + allowPositionals: true, + }) + + if (values.help) { + showHelp() + process.exit(0) + } + + // Detect if we should use UI mode + const shouldUseUI = + values.interactive || + values.ui || + (!values.release && + !values.mode && + !values.prompt && + !values.team && + !values['list-releases'] && + !values.output && + positionals.length === 0) + + if (shouldUseUI) { + throw new Error('UI mode is not supported') + } + + try { + // Create logger early for consistent logging + const logger = new ConsoleLogger(values.verbose || false) + + // Handle --list-releases + if (values['list-releases']) { + const scanner = new ReleaseScanner(process.cwd(), logger) + const platform = values.platform as 'mobile' | 'extension' | undefined + await scanner.listReleases(platform) + process.exit(0) + } + + // Check for API key + if (!process.env.ANTHROPIC_API_KEY) { + logger.error('Error: ANTHROPIC_API_KEY environment variable is required') + logger.error('Get your API key from: https://console.anthropic.com/') + process.exit(1) + } + + // Build configuration + const config = await buildConfig(values, logger) + + if (values.verbose) { + logger.debug(`Configuration: ${JSON.stringify(config, null, 2)}`) + } + + // Create cache provider (unless bypassing cache) + const bypassCache = values['no-cache'] || values['force-refresh'] + const cacheProvider = bypassCache ? undefined : new SqliteCacheProvider() + + // Run orchestrator + const aiProvider = createVercelAIProvider(process.env.ANTHROPIC_API_KEY) + const orchestrator = new Orchestrator({ + config, + aiProvider, + cacheProvider, + logger, + }) + await orchestrator.execute() + + // Close cache connection if used + if (cacheProvider) { + cacheProvider.close() + } + + logger.info('✨ Analysis complete!') + } catch (error) { + // Create a minimal logger if we don't have one yet + const errorLogger = new ConsoleLogger(false) + errorLogger.error(`Fatal error: ${error}`) + process.exit(1) + } +} + +interface BuildConfigArgs { + repo?: string + team?: string + since?: string + 'include-open-prs'?: boolean + release?: string + 'compare-with'?: string + mode?: string + prompt?: string + bug?: string + 'include-diffs'?: boolean + 'max-diff-size'?: string + 'max-diff-files'?: string + 'diff-pattern'?: string + 'exclude-test-diffs'?: boolean + 'token-budget'?: string + 'pr-body-limit'?: string + output?: string[] + verbose?: boolean + 'dry-run'?: boolean + 'save-artifacts'?: boolean + 'no-cache'?: boolean + 'force-refresh'?: boolean +} + +async function buildConfig(args: BuildConfigArgs, logger: Logger): Promise { + // Parse repository + let repository: { owner?: string; name?: string } | undefined + + if (args.repo) { + const match = args.repo.match(/^([^/]+)\/([^/]+)$/) + if (!match) { + throw new Error(`Invalid repository format: "${args.repo}". Expected: owner/repo`) + } + repository = { owner: match[1], name: match[2] } + } else { + // Try to detect from git + repository = (await detectRepository()) || undefined + } + + if (repository) { + logger.info(`Repository: ${repository.owner}/${repository.name}`) + } + + // Resolve team if specified + let teamFilter: string[] | undefined + let teamUsernames: string[] | undefined + + if (args.team) { + logger.info(`Resolving team: ${args.team}`) + const resolution = await resolveTeam(args.team) + teamFilter = resolution.emails + teamUsernames = resolution.usernames + + if (teamFilter.length === 0) { + throw new Error('Failed to resolve team filter') + } + logger.info(`Team resolved to ${teamFilter.length} email(s)`) + if (args.verbose) { + logger.debug(`Emails: ${teamFilter}`) + logger.debug(`Usernames: ${teamUsernames}`) + } + } + + // Parse outputs + const outputs = parseOutputs(args.output || ['stdout']) + + // Handle release mode + let releaseOptions + if (args.release) { + const releaseId = parseReleaseIdentifier(args.release) + if (!releaseId) { + throw new Error(`Invalid release format: "${args.release}". Expected: mobile/1.60 or extension/1.30.0`) + } + + let version = releaseId.version + + // Handle "latest" keyword + if (version === 'latest') { + const scanner = new ReleaseScanner(process.cwd(), logger) + const latestRelease = await scanner.getLatestRelease(releaseId.platform) + + if (!latestRelease) { + throw new Error(`No releases found for platform: ${releaseId.platform}`) + } + + version = latestRelease.version + logger.info(`Using latest ${releaseId.platform} release: ${version}`) + } + + releaseOptions = { + platform: releaseId.platform, + version, + compareWith: args['compare-with'], + } + + // Auto-set mode to release-changelog if not explicitly set + if (!args.mode) { + args.mode = 'release-changelog' + } + } + + // Build commit data config + const commitDataConfig = { + includeFilePaths: true, // Always include file paths + includeDiffs: args['include-diffs'], + maxDiffSize: parseInt(args['max-diff-size'] || '100', 10), + maxDiffFiles: parseInt(args['max-diff-files'] || '20', 10), + diffFilePattern: args['diff-pattern'], + excludeTestFiles: args['exclude-test-diffs'] !== false, + tokenBudget: parseInt(args['token-budget'] || '50000', 10), + prBodyLimit: parseInt(args['pr-body-limit'] || '2000', 10), + } + + // Build collection options + const collectOptions: CollectOptions = { + since: args.since ?? '30 days ago', + repository, + teamFilter, + teamUsernames, + includeOpenPrs: args['include-open-prs'], + commitDataConfig, + } + + // Handle bug-bisect mode + if (args.bug) { + if (!args.release) { + throw new Error('--bug requires --release to be specified') + } + // Auto-set mode to bug-bisect if not explicitly set + if (!args.mode) { + args.mode = 'bug-bisect' + } else if (args.mode !== 'bug-bisect') { + throw new Error('--bug can only be used with --mode bug-bisect') + } + } + + // Build analysis config + const analysisConfig = { + mode: args.mode, + prompt: args.prompt, + releaseOptions, + variables: args.bug + ? { + BUG_DESCRIPTION: args.bug, + } + : undefined, + } + + return { + analysis: analysisConfig, + outputs, + collect: collectOptions, + verbose: args.verbose, + dryRun: args['dry-run'], + saveArtifacts: args['save-artifacts'], + bypassCache: args['no-cache'] || args['force-refresh'] || false, + } +} + +function parseOutputs(outputs: string[]): OutputConfig[] { + return outputs.map((output) => { + // Parse format: type:target + // Examples: + // stdout + // file:report.md + // slack:#channel + // github-release + + const parts = output.split(':') + const type = parts[0] + const target = parts.slice(1).join(':') // Handle colons in target + + if (!type) { + throw new Error(`Invalid output format: "${output}"`) + } + + return { + type, + target: target || undefined, + } + }) +} + +function showHelp(): void { + console.log(` +Repository Intelligence System - Analyze git history with AI + +Usage: bun scripts/gh-agent-refactored.ts [options] + +ANALYSIS OPTIONS: + --mode Predefined analysis mode (team-digest, changelog, release-changelog, bug-bisect) + --prompt Custom prompt (file path or inline text) + Examples: + --prompt ./my-analysis.md + --prompt "Analyze for security issues" + --bug Bug description for bug-bisect mode (requires --release) + Example: + --bug "Users can't connect wallet on mobile app" + --release mobile/1.60 --bug "Crash on launch" + +DATA FILTERING: + --team Team filter (@org/team or user1,user2) + Examples: + --team @Uniswap/apps-swap + --team alice,bob + --team @Uniswap/backend,external-contributor + --since Time period to analyze (default: "30 days ago") + --repo Repository to analyze (auto-detected if not specified) + --include-open-prs Include open/in-review PRs in analysis + +RELEASE OPTIONS: + --release Release to analyze (e.g., mobile/1.60, extension/1.30.0, or mobile/latest) + --compare-with Specific version to compare with (auto-detects if not specified) + --list-releases List available releases + --platform Platform filter for --list-releases (mobile or extension) + +OUTPUT OPTIONS: + --output Output target (can specify multiple) + Examples: + --output stdout (default) + --output file:report.md + --output slack:#channel + Multiple outputs: + --output file:report.md --output slack:#updates + +OTHER OPTIONS: + --verbose Enable verbose logging + --dry-run Test mode without publishing + --no-cache Bypass cache and fetch fresh data + --force-refresh Bypass cache and fetch fresh data (alias for --no-cache) + --help Show this help message + +EXAMPLES: + # Team digest with default settings + bun scripts/gh-agent-refactored.ts --mode team-digest --team @Uniswap/apps-swap + + # Weekly changelog + bun scripts/gh-agent-refactored.ts --mode changelog --since "1 week ago" + + # Release changelog for mobile + bun scripts/gh-agent-refactored.ts --release mobile/1.60 + + # Release changelog for latest mobile release + bun scripts/gh-agent-refactored.ts --release mobile/latest + + # Release changelog with specific comparison + bun scripts/gh-agent-refactored.ts --release mobile/1.60 --compare-with mobile/1.58 + + # Bug bisect - find which commit introduced a bug + bun scripts/gh-agent-refactored.ts --release mobile/1.60 --bug "Users can't connect wallet" + + # List all releases + bun scripts/gh-agent-refactored.ts --list-releases + + # List mobile releases only + bun scripts/gh-agent-refactored.ts --list-releases --platform mobile + + # Custom analysis with multiple outputs + bun scripts/gh-agent-refactored.ts \\ + --prompt "Analyze for performance improvements" \\ + --team alice,bob \\ + --output file:performance.md \\ + --output slack:#perf-updates + +ENVIRONMENT VARIABLES: + ANTHROPIC_API_KEY Required - Your Anthropic API key + SLACK_WEBHOOK Optional - Webhook URL for Slack integration + +For more information, see the documentation at: +https://github.com/Uniswap/universe/scripts/gh-agent/README.md +`) +} + +// Run if executed directly +if (import.meta.main) { + main().catch((error) => { + console.error('Unhandled error:', error) + process.exit(1) + }) +} diff --git a/apps/cli/src/core/data-collector.ts b/apps/cli/src/core/data-collector.ts new file mode 100644 index 00000000000..fd9979b9cc6 --- /dev/null +++ b/apps/cli/src/core/data-collector.ts @@ -0,0 +1,1096 @@ +/* eslint-disable max-lines */ +/* eslint-disable max-depth */ +/* eslint-disable complexity */ +/* eslint-disable max-params */ + +/** biome-ignore-all lint/suspicious/noConsole: CLI tool requires console output */ + +import { getCommitsCacheKey, getPullRequestsCacheKey, getStatsCacheKey } from '@universe/cli/src/lib/cache-keys' +import { type CacheProvider } from '@universe/cli/src/lib/cache-provider' +import type { Logger } from '@universe/cli/src/lib/logger' +import { cleanPRBody } from '@universe/cli/src/lib/pr-body-cleaner' +import { type ReleaseComparison } from '@universe/cli/src/lib/release-scanner' +import { isTrivialFile } from '@universe/cli/src/lib/trivial-files' +import { $ } from 'bun' + +// ============================================================================ +// Data Collection Types +// ============================================================================ + +export interface CommitDataConfig { + includeFilePaths?: boolean // Include file paths in commit data (default: true) + includeDiffs?: boolean // Include actual diff content (default: false) + maxDiffSize?: number // Max lines changed per file to include diff (default: 100) + maxDiffFiles?: number // Max number of files to include diffs for (default: 20) + diffFilePattern?: string // Regex pattern for files to include diffs (default: \.(ts|tsx|js|jsx)$) + excludeTestFiles?: boolean // Exclude test files from diffs (default: true) + tokenBudget?: number // Approximate token budget for all commit data (default: 50000) + prBodyLimit?: number // Max characters for PR body (default: 2000) + cleanPRBodies?: boolean // Enable PR body cleaning (default: true) +} + +export interface CollectOptions { + since: string + branch?: string + author?: string + repoPath?: string + includeOpenPrs?: boolean + teamFilter?: string[] + teamUsernames?: string[] + repository?: { owner?: string; name?: string } + releaseComparison?: ReleaseComparison + excludeTrivialCommits?: boolean // Filter out commits with only lockfile/snapshot changes + commitDataConfig?: CommitDataConfig // Configuration for commit data collection +} + +export interface RepositoryData { + commits: Commit[] + pullRequests: PullRequest[] + stats: StatsOutput + metadata: { + repository: string + period: string + collectedAt: Date + commitCount: number + prCount: number + releaseInfo?: { + from: string + to: string + platform: 'mobile' | 'extension' + } + filtering?: { + totalCommitsFound: number + trivialCommitsFiltered: number + filesProcessed: number + trivialFilesSkipped: number + } + } +} + +export interface Commit { + sha: string + author: { name: string; email: string } + timestamp: Date + message: string + stats: { filesChanged: number; insertions: number; deletions: number } + files?: { + path: string + status: 'added' | 'modified' | 'deleted' | 'renamed' + additions: number + deletions: number + diff?: string // Optional: actual diff content + diffTruncated?: boolean // Optional: indicates if diff was truncated + }[] +} + +export interface PullRequest { + number: number + title: string + body: string + author: string + state: 'open' | 'closed' + mergedAt: string + mergeCommitSha?: string +} + +export interface StatsOutput { + totalCommits: number + totalAuthors: number + filesChanged: number + linesAdded: number + linesDeleted: number +} + +// ============================================================================ +// Helper Functions for Git Output Parsing +// ============================================================================ + +/** + * Validates and parses a git log line in the format: sha|email|name|timestamp|message + * Returns undefined if the line is malformed + */ +function parseGitLogLine( + line: string, +): { sha: string; email: string; name: string; timestamp: string; message: string } | undefined { + const parts = line.split('|') + if (parts.length < 5) { + return undefined + } + + const sha = parts[0]?.trim() + const email = parts[1]?.trim() + const name = parts[2]?.trim() + const timestamp = parts[3]?.trim() + const messageParts = parts.slice(4) + const message = messageParts.join('|') + + if (!sha || !email || !name || !timestamp) { + return undefined + } + + return { sha, email, name, timestamp, message } +} + +/** + * Validates and parses a git numstat line in the format: additions\tdeletions\tfilepath + * Returns undefined if the line is malformed + */ +function parseNumstatLine(line: string): { additions: string; deletions: string; filepath: string } | undefined { + const parts = line.split('\t') + if (parts.length < 3) { + return undefined + } + + const additions = parts[0]?.trim() + const deletions = parts[1]?.trim() + const filepath = parts.slice(2).join('\t') // Handle filepaths with tabs + + if (additions === undefined || deletions === undefined || !filepath) { + return undefined + } + + return { additions, deletions, filepath } +} + +// ============================================================================ +// Data Collector +// ============================================================================ + +export class DataCollector { + private filteringStats = { + totalCommitsFound: 0, + trivialCommitsFiltered: 0, + filesProcessed: 0, + trivialFilesSkipped: 0, + } + private tokenUsage = 0 + private diffsCollected = 0 + + constructor( + private repoPath: string = process.cwd(), + private cacheProvider?: CacheProvider, + private bypassCache: boolean = false, + private logger?: Logger, + ) {} + + /** + * Estimate tokens for a given text (rough approximation: 1 token ≈ 4 characters) + */ + private estimateTokens(text: string): number { + return Math.ceil(text.length / 4) + } + + /** + * Check if a file should have its diff included based on config + */ + private shouldIncludeDiff(filePath: string, additions: number, deletions: number, config: CommitDataConfig): boolean { + // Check if diffs are enabled + if (!config.includeDiffs) { + return false + } + + // Check if we've hit the diff file limit + if (this.diffsCollected >= (config.maxDiffFiles || 20)) { + return false + } + + // Check if we're within token budget + const budget = config.tokenBudget || 50000 // Can be higher with compact format + if (this.tokenUsage >= budget) { + this.logger?.info(`Token budget reached (${this.tokenUsage}/${budget}), skipping remaining diffs`) + return false + } + + // Check file size limit + const totalChanges = additions + deletions + if (totalChanges > (config.maxDiffSize || 100)) { + return false + } + + // Check if it's a test file and we're excluding them + if (config.excludeTestFiles !== false) { + const testPatterns = [ + /\.test\.(ts|tsx|js|jsx)$/, + /\.spec\.(ts|tsx|js|jsx)$/, + /__tests__\//, + /test\//i, + /tests\//i, + /e2e\//, + ] + if (testPatterns.some((p) => p.test(filePath))) { + return false + } + } + + // Check file pattern if provided + if (config.diffFilePattern) { + // eslint-disable-next-line security/detect-non-literal-regexp -- User-provided pattern from config, used for file filtering + const pattern = new RegExp(config.diffFilePattern) + if (!pattern.test(filePath)) { + return false + } + } else { + // Default pattern: TypeScript and JavaScript files + const defaultPattern = /\.(ts|tsx|js|jsx)$/ + if (!defaultPattern.test(filePath)) { + return false + } + } + + return true + } + + /** + * Collect diff for a specific file in a commit + */ + private async collectFileDiff(sha: string, filePath: string, config: CommitDataConfig): Promise { + try { + // Get just the unified diff for this file (more compact than full git show) + const diff = await $`git -C ${this.repoPath} diff ${sha}^..${sha} -- ${filePath}`.text() + + if (!diff || diff.trim().length === 0) { + // File might be new, try different approach + const showDiff = await $`git -C ${this.repoPath} show ${sha} --format= -- ${filePath}`.text() + if (!showDiff) { + return undefined + } + + // For new files, just get the content preview + const lines = showDiff.split('\n') + const maxSize = Math.min(config.maxDiffSize || 100, 30) // Smaller preview for new files + if (lines.length > maxSize) { + return lines.slice(0, maxSize).join('\n') + `\n... [+${lines.length - maxSize} more lines]` + } + return showDiff + } + + // Extract just the hunks (skip file headers for compactness) + const lines = diff.split('\n') + const hunkLines = lines.filter( + (line: string) => line.startsWith('@@') || line.startsWith('+') || line.startsWith('-') || line.startsWith(' '), + ) + + // Check size and truncate if needed + const maxSize = config.maxDiffSize || 100 + if (hunkLines.length > maxSize) { + const truncated = hunkLines.slice(0, maxSize) + truncated.push(`... [${hunkLines.length - maxSize} more lines]`) + return truncated.join('\n') + } + + return hunkLines.join('\n') + } catch (_error) { + // Might fail for new files or first commit, that's ok + return undefined + } + } + + /** + * Restore Date objects from cached JSON (JSON.parse converts dates to strings) + */ + private restoreDatesFromCache(items: T[]): T[] { + return items.map((item) => { + if (item.timestamp && typeof item.timestamp === 'string') { + return { ...item, timestamp: new Date(item.timestamp) } + } + return item + }) + } + + async collect(options: CollectOptions): Promise { + this.logger?.info('Collecting repository data...') + + // Reset filtering stats + this.filteringStats = { + totalCommitsFound: 0, + trivialCommitsFiltered: 0, + filesProcessed: 0, + trivialFilesSkipped: 0, + } + this.tokenUsage = 0 + this.diffsCollected = 0 + + // Try to get commits from cache + let allCommits: Commit[] | null = null + if (!this.bypassCache && this.cacheProvider) { + const cacheKey = getCommitsCacheKey(options) + const cached = await this.cacheProvider.get(cacheKey) + if (cached) { + allCommits = this.restoreDatesFromCache(cached) + this.logger?.info(`Cache hit: Found ${allCommits.length} commits in cache`) + } + } + + // Fetch commits if not cached + if (!allCommits) { + if (options.releaseComparison) { + this.logger?.info(`Fetching commits for release: ${options.releaseComparison.commitRange}`) + allCommits = await this.getReleaseCommits(options.releaseComparison, options) + } else { + this.logger?.info('Fetching commits from git log...') + allCommits = await this.getCommits(options) + } + + // Store in cache (release comparisons are deterministic, cache indefinitely) + if (this.cacheProvider) { + const cacheKey = getCommitsCacheKey(options) + const ttl = options.releaseComparison ? undefined : 3600 // 1 hour for time-based queries + await this.cacheProvider.set(cacheKey, allCommits, ttl) + this.logger?.info(`Cached ${allCommits.length} commits`) + } + } + + // Filter by team if specified + const commits = options.teamFilter?.length ? this.filterCommitsByTeam(allCommits, options.teamFilter) : allCommits + + this.logger?.info(`Found ${commits.length} commits (${allCommits.length} total)`) + + // Try to get PRs from cache + let pullRequests: PullRequest[] | null = null + if (!this.bypassCache && this.cacheProvider) { + const cacheKey = getPullRequestsCacheKey(options) + const cached = await this.cacheProvider.get(cacheKey) + if (cached) { + pullRequests = cached + this.logger?.info(`Cache hit: Found ${pullRequests.length} PRs in cache`) + } + } + + // Fetch PRs if not cached + if (!pullRequests) { + this.logger?.info('Fetching pull requests from GitHub...') + pullRequests = await this.getPullRequests(options) + + // Store in cache + if (this.cacheProvider) { + const cacheKey = getPullRequestsCacheKey(options) + const ttl = options.releaseComparison ? undefined : 3600 // 1 hour for time-based queries + await this.cacheProvider.set(cacheKey, pullRequests, ttl) + this.logger?.info(`Cached ${pullRequests.length} PRs`) + } + } + + this.logger?.info(`Found ${pullRequests.length} pull requests`) + + // Try to get stats from cache + let stats: StatsOutput | null = null + if (!this.bypassCache && this.cacheProvider) { + const cacheKey = getStatsCacheKey(options) + const cached = await this.cacheProvider.get(cacheKey) + if (cached) { + stats = cached + this.logger?.info('Cache hit: Found stats in cache') + } + } + + // Calculate stats if not cached + if (!stats) { + stats = await this.getStats(options) + + // Store in cache + if (this.cacheProvider) { + const cacheKey = getStatsCacheKey(options) + const ttl = options.releaseComparison ? undefined : 3600 // 1 hour for time-based queries + await this.cacheProvider.set(cacheKey, stats, ttl) + this.logger?.info('Cached stats') + } + } + + // Build metadata + const repoName = + options.repository?.owner && options.repository.name + ? `${options.repository.owner}/${options.repository.name}` + : this.repoPath + + const metadata: RepositoryData['metadata'] = { + repository: repoName, + period: options.since, + collectedAt: new Date(), + commitCount: commits.length, + prCount: pullRequests.length, + } + + // Add release info if available + if (options.releaseComparison) { + metadata.releaseInfo = { + from: options.releaseComparison.from.version, + to: options.releaseComparison.to.version, + platform: options.releaseComparison.to.platform, + } + } + + // Add filtering stats if we have them + if (this.filteringStats.totalCommitsFound > 0) { + metadata.filtering = { + totalCommitsFound: this.filteringStats.totalCommitsFound, + trivialCommitsFiltered: this.filteringStats.trivialCommitsFiltered, + filesProcessed: this.filteringStats.filesProcessed, + trivialFilesSkipped: this.filteringStats.trivialFilesSkipped, + } + } + + return { + commits, + pullRequests, + stats, + metadata, + } + } + + private async getCommits(options: CollectOptions): Promise { + const format = '%H|%ae|%an|%at|%s' + const result = await $`git -C ${this.repoPath} log --since="${options.since}" --format="${format}" --numstat`.text() + + const commits: Commit[] = [] + const lines = result.split('\n') + let skippedTrivialCommits = 0 + let totalCommitsProcessed = 0 + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (!line || !line.includes('|')) { + continue + } + + // Parse the commit line + const parsed = parseGitLogLine(line) + if (!parsed) { + this.logger?.warn(`Skipping malformed git log line: ${line}`) + continue + } + + totalCommitsProcessed++ + const { sha, email, name, timestamp, message } = parsed + const stats = { filesChanged: 0, insertions: 0, deletions: 0 } + const fileChanges: Commit['files'] = [] + + // Parse numstat + i++ + while (i < lines.length) { + const currentLine = lines[i] + if (!currentLine || currentLine.includes('|')) { + break + } + const statLine = currentLine.trim() + if (statLine) { + const numstat = parseNumstatLine(statLine) + if (numstat) { + const { additions, deletions, filepath } = numstat + + // Skip trivial files when in release mode + if (options.releaseComparison && isTrivialFile(filepath)) { + i++ + continue + } + + const adds = additions === '-' ? 0 : parseInt(additions, 10) || 0 + const dels = deletions === '-' ? 0 : parseInt(deletions, 10) || 0 + + // Determine file status + let status: 'added' | 'modified' | 'deleted' | 'renamed' = 'modified' + if (adds > 0 && dels === 0) { + status = 'added' + } else if (adds === 0 && dels > 0) { + status = 'deleted' + } else if (additions === '-' || deletions === '-') { + status = 'modified' + } + + fileChanges.push({ + path: filepath, + status, + additions: adds, + deletions: dels, + }) + + stats.insertions += adds + stats.deletions += dels + stats.filesChanged++ + } + } + i++ + } + i-- // Back up one since the outer loop will increment + + // Skip commits that only touch trivial files (only in release mode) + if (options.releaseComparison && stats.filesChanged === 0) { + skippedTrivialCommits++ + continue + } + + const commit = { + sha, + author: { name, email }, + timestamp: new Date(parseInt(timestamp, 10) * 1000), + message, + stats, + files: fileChanges, + } + + // Collect diffs if configured + if (options.commitDataConfig?.includeDiffs && fileChanges.length > 0) { + for (const file of fileChanges) { + if (this.shouldIncludeDiff(file.path, file.additions, file.deletions, options.commitDataConfig)) { + const diff = await this.collectFileDiff(sha, file.path, options.commitDataConfig) + if (diff) { + file.diff = diff + file.diffTruncated = diff.includes('[diff truncated') + this.diffsCollected++ + this.tokenUsage += this.estimateTokens(diff) + + // Log progress + if (this.diffsCollected % 5 === 0) { + this.logger?.info(`Collected ${this.diffsCollected} diffs (${this.tokenUsage} tokens used)`) + } + } + } + } + } + + commits.push(commit) + } + + // Store stats for reporting (if in release mode) + if (totalCommitsProcessed > 0) { + this.filteringStats.totalCommitsFound += totalCommitsProcessed + this.filteringStats.trivialCommitsFiltered += skippedTrivialCommits + } + + if (skippedTrivialCommits > 0) { + this.logger?.info(`Filtered out ${skippedTrivialCommits} commits with only trivial file changes`) + } + + return commits + } + + private filterCommitsByTeam(commits: Commit[], teamFilter: string[]): Commit[] { + return commits.filter((c) => { + const matches = teamFilter.includes(c.author.email) + if (!matches) { + this.logger?.debug(`Filtering out commit ${c.sha.slice(0, 7)} by ${c.author.email}`) + } + return matches + }) + } + + private async getPullRequests(options: CollectOptions): Promise { + if (!options.repository?.owner || !options.repository.name) { + this.logger?.debug('No repository configured, skipping PR fetch') + return [] + } + + const repository = `${options.repository.owner}/${options.repository.name}` + + try { + // For release comparisons, get PR numbers from the commit range + if (options.releaseComparison) { + return await this.getReleasePullRequests(options, repository) + } + + // Parse the 'since' date for time-based analysis + const sinceDate = this.parseSinceDate(options.since) + const sinceISO = sinceDate.toISOString().split('T')[0] ?? sinceDate.toISOString() + + // Build search query + const authorFilter = options.teamUsernames?.length + ? options.teamUsernames.map((author) => `author:${author}`).join(' ') + : '' + + const query = options.includeOpenPrs + ? `repo:${repository} is:pr created:>=${sinceISO} ${authorFilter}` + : `repo:${repository} is:pr is:closed closed:>=${sinceISO} ${authorFilter}` + + this.logger?.debug(`GitHub Search Query: ${query}`) + + // Get PR numbers from main branch commits for filtering + const prNumbersInMain = await this.getPRNumbersFromMainBranch(sinceISO) + + // Fetch PRs from GitHub + const allPRs: PullRequest[] = [] + let page = 1 + const perPage = 100 + const maxPages = 10 + + while (page <= maxPages) { + const apiPath = `/search/issues?q=${encodeURIComponent(query)}&per_page=${perPage}&page=${page}` + const searchResult = await $`gh api ${apiPath}`.text() + const searchData = JSON.parse(searchResult) + + if (!searchData.items || searchData.items.length === 0) { + break + } + + for (const pr of searchData.items) { + // Filter to only include PRs whose commits are in main (unless open) + if (pr.state === 'open' || prNumbersInMain.has(pr.number)) { + const limit = options.commitDataConfig?.prBodyLimit || 2000 + const shouldClean = options.commitDataConfig?.cleanPRBodies !== false // Default to true + let body = pr.body ? pr.body : '' + + // Clean PR body if enabled (default: true) + if (body && shouldClean) { + body = cleanPRBody(body) + } + + // Apply character limit after cleaning + body = body.slice(0, limit) + + allPRs.push({ + number: pr.number, + title: pr.title, + body, + author: pr.user?.login || 'unknown', + state: pr.state as 'open' | 'closed', + mergedAt: pr.closed_at, + mergeCommitSha: pr.pull_request?.merge_commit_sha, + }) + } + } + + if (searchData.items.length < perPage) { + break + } + page++ + } + + return allPRs + } catch (error) { + this.logger?.error(`GitHub PR fetch failed: ${error}`) + return [] + } + } + + private parseSinceDate(since: string): Date { + const sinceDate = new Date() + const sinceMatch = since.match(/(\d+)\s+(day|week|month|year)s?\s+ago/) + + if (sinceMatch?.[1] && sinceMatch[2]) { + const amount = sinceMatch[1] + const unit = sinceMatch[2] + const num = parseInt(amount, 10) + + switch (unit) { + case 'day': + sinceDate.setDate(sinceDate.getDate() - num) + break + case 'week': + sinceDate.setDate(sinceDate.getDate() - num * 7) + break + case 'month': + sinceDate.setMonth(sinceDate.getMonth() - num) + break + case 'year': + sinceDate.setFullYear(sinceDate.getFullYear() - num) + break + } + } + + return sinceDate + } + + private async getPRNumbersFromMainBranch(sinceISO: string): Promise> { + const allCommitMessages = await $`git log main --since="${sinceISO}" --format="%s"`.text() + const prNumbersInMain = new Set() + const prRegex = /#(\d+)/g + + for (const match of allCommitMessages.matchAll(prRegex)) { + if (match[1]) { + const prNum = parseInt(match[1], 10) + if (prNum) { + prNumbersInMain.add(prNum) + } + } + } + + this.logger?.debug(`Found ${prNumbersInMain.size} unique PR numbers in main branch commits`) + return prNumbersInMain + } + + private async getReleasePullRequests(options: CollectOptions, repository: string): Promise { + if (!options.releaseComparison) { + return [] + } + + const range = options.releaseComparison.commitRange + this.logger?.info('Extracting PR information from commits...') + + // Extract PR numbers and titles from commit messages + const commits = await $`git -C ${this.repoPath} log ${range} --format="%H|%s|%ae|%an"`.text() + const pullRequests: PullRequest[] = [] + const seenPRs = new Set() + + for (const line of commits.split('\n')) { + if (!line) { + continue + } + const parts = line.split('|') + const sha = parts[0] + const message = parts[1] + const author = parts[3] + + if (!sha || !message || !author) { + continue + } + + // Look for PR number in commit message (e.g., "(#1234)" or "PR #1234") + const prMatch = message.match(/#(\d+)/) + if (prMatch?.[1]) { + const prNumber = parseInt(prMatch[1], 10) + + // Skip if we've already seen this PR + if (seenPRs.has(prNumber)) { + continue + } + seenPRs.add(prNumber) + + // Extract PR title from commit message (usually after the PR number) + let title = message + // Remove PR number patterns + title = title + .replace(/\(#\d+\)/, '') + .replace(/#\d+/, '') + .trim() + + // Create a minimal PR object from commit data + pullRequests.push({ + number: prNumber, + title: title || `PR #${prNumber}`, + body: '', // We don't have the body without API call + author: author || 'unknown', + state: 'closed', // Assume closed if in release + mergedAt: '', // We don't have exact merge time + mergeCommitSha: sha, + }) + } + } + + this.logger?.info(`Found ${pullRequests.length} PRs from commit messages`) + + // Fetch detailed PR info for all PRs using gh api with parallel batching + if (pullRequests.length > 0) { + this.logger?.info(`Fetching detailed info for ${pullRequests.length} PRs...`) + + const limit = options.commitDataConfig?.prBodyLimit || 2000 + const CONCURRENCY_LIMIT = 15 // Fetch 15 PRs in parallel at a time + let fetchedCount = 0 + + // Helper function to fetch a single PR + const fetchPR = async (pr: PullRequest): Promise => { + try { + // Use jq to output a JSON object to properly handle multi-line PR bodies + const prResultJson = + await $`gh api repos/${repository}/pulls/${pr.number} --jq '{title: .title, body: .body, author: .user.login, mergedAt: .merged_at}'`.text() + const prData = JSON.parse(prResultJson) + + // Update with real data + if (prData.title) { + pr.title = prData.title + } + if (prData.body && prData.body !== 'null') { + const shouldClean = options.commitDataConfig?.cleanPRBodies !== false // Default to true + let body = prData.body + + // Clean PR body if enabled (default: true) + if (shouldClean) { + body = cleanPRBody(body) + } + + // Apply character limit after cleaning + pr.body = body.slice(0, limit) + } + if (prData.author) { + pr.author = prData.author + } + if (prData.mergedAt && prData.mergedAt !== 'null') { + pr.mergedAt = prData.mergedAt + } + } catch (_error) { + // Keep the minimal data we already have + this.logger?.warn(`Failed to fetch PR #${pr.number}, continuing with minimal data`) + } + } + + // Process PRs in batches for parallel fetching + for (let i = 0; i < pullRequests.length; i += CONCURRENCY_LIMIT) { + const batch = pullRequests.slice(i, i + CONCURRENCY_LIMIT) + const batchNumber = Math.floor(i / CONCURRENCY_LIMIT) + 1 + const totalBatches = Math.ceil(pullRequests.length / CONCURRENCY_LIMIT) + + // Fetch all PRs in this batch in parallel + await Promise.all(batch.map((pr) => fetchPR(pr))) + + fetchedCount += batch.length + + // Log progress after each batch + this.logger?.info( + `Fetched ${fetchedCount}/${pullRequests.length} PRs (batch ${batchNumber}/${totalBatches})...`, + ) + + // Add small delay between batches to respect rate limits (except for last batch) + if (i + CONCURRENCY_LIMIT < pullRequests.length) { + await new Promise((resolve) => setTimeout(resolve, 200)) + } + } + + this.logger?.info(`Successfully fetched detailed info for ${fetchedCount}/${pullRequests.length} PRs`) + } + + return pullRequests + } + + private async getReleaseCommits(comparison: ReleaseComparison, options: CollectOptions): Promise { + const format = '%H|%ae|%an|%at|%s' + const range = comparison.commitRange + + this.logger?.info(`Getting commits for release: ${range}`) + + const result = await $`git -C ${this.repoPath} log ${range} --format="${format}" --numstat`.text() + + const commits: Commit[] = [] + const lines = result.split('\n') + let skippedTrivialCommits = 0 + let totalCommitsProcessed = 0 + let totalFilesProcessed = 0 + let trivialFilesSkipped = 0 + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (!line || !line.includes('|')) { + continue + } + + // Parse the commit line + const parsed = parseGitLogLine(line) + if (!parsed) { + this.logger?.warn(`Skipping malformed git log line: ${line}`) + continue + } + + totalCommitsProcessed++ + const { sha, email, name, timestamp, message } = parsed + const stats = { filesChanged: 0, insertions: 0, deletions: 0 } + const fileChanges: Commit['files'] = [] + const trivialFiles: string[] = [] + + // Parse numstat + i++ + // Skip empty line after commit header + if (i < lines.length && lines[i] === '') { + i++ + } + + while (i < lines.length) { + const statLine = lines[i] + + // Stop if this is the start of a new commit (contains |) or we hit another empty line followed by a commit + if (!statLine || statLine.includes('|')) { + if (!statLine) { + // Check if next line is a commit + const nextLine = lines[i + 1] + if (nextLine?.includes('|')) { + break + } + // Otherwise it's just an empty line in the numstat, skip it + i++ + continue + } else { + // New commit line, back up for outer loop + i-- + break + } + } + + if (statLine.trim()) { + // Git numstat format is: additions\tdeletions\tfilepath + const tabIndex1 = statLine.indexOf('\t') + const tabIndex2 = statLine.indexOf('\t', tabIndex1 + 1) + + if (tabIndex1 > -1 && tabIndex2 > -1) { + const additions = statLine.substring(0, tabIndex1) + const deletions = statLine.substring(tabIndex1 + 1, tabIndex2) + const filepath = statLine.substring(tabIndex2 + 1) + + // Skip empty filepaths + if (!filepath) { + i++ + continue + } + + totalFilesProcessed++ + + // Skip trivial files + if (!isTrivialFile(filepath)) { + const adds = additions === '-' ? 0 : parseInt(additions, 10) || 0 + const dels = deletions === '-' ? 0 : parseInt(deletions, 10) || 0 + + // Determine file status based on additions/deletions + let status: 'added' | 'modified' | 'deleted' | 'renamed' = 'modified' + if (adds > 0 && dels === 0) { + status = 'added' + } else if (adds === 0 && dels > 0) { + status = 'deleted' + } else if (additions === '-' || deletions === '-') { + // Binary files or renames show '-' for stats + status = 'modified' + } + + fileChanges.push({ + path: filepath, + status, + additions: adds, + deletions: dels, + }) + + stats.insertions += adds + stats.deletions += dels + stats.filesChanged++ + } else { + trivialFiles.push(filepath) + trivialFilesSkipped++ + } + } + } + i++ + } + i-- // Back up one since the outer loop will increment + + // Skip commits that only touch trivial files + if (stats.filesChanged === 0 && trivialFiles.length > 0) { + skippedTrivialCommits++ + continue + } else if (stats.filesChanged === 0 && trivialFiles.length === 0) { + // Empty commit or merge commit, skip + continue + } + + const commit = { + sha, + author: { name, email }, + timestamp: new Date(parseInt(timestamp, 10) * 1000), + message, + stats, + files: fileChanges, + } + + // Collect diffs if configured + if (options.commitDataConfig?.includeDiffs && fileChanges.length > 0) { + for (const file of fileChanges) { + if (this.shouldIncludeDiff(file.path, file.additions, file.deletions, options.commitDataConfig)) { + const diff = await this.collectFileDiff(sha, file.path, options.commitDataConfig) + if (diff) { + file.diff = diff + file.diffTruncated = diff.includes('[diff truncated') + this.diffsCollected++ + this.tokenUsage += this.estimateTokens(diff) + + // Log progress + if (this.diffsCollected % 5 === 0) { + this.logger?.info(`Collected ${this.diffsCollected} diffs (${this.tokenUsage} tokens used)`) + } + } + } + } + } + + commits.push(commit) + } + + // Store stats for reporting (if in release mode) + if (totalCommitsProcessed > 0) { + this.filteringStats.totalCommitsFound = totalCommitsProcessed + this.filteringStats.trivialCommitsFiltered = skippedTrivialCommits + this.filteringStats.filesProcessed = totalFilesProcessed + this.filteringStats.trivialFilesSkipped = trivialFilesSkipped + } + + if (skippedTrivialCommits > 0) { + this.logger?.info(`Filtered out ${skippedTrivialCommits} commits with only trivial file changes`) + } + + this.logger?.info(`Analyzed ${commits.length} meaningful commits from ${totalCommitsProcessed} total`) + + if (options.commitDataConfig?.includeDiffs && this.diffsCollected > 0) { + this.logger?.info(`Collected ${this.diffsCollected} diffs using ~${this.tokenUsage.toLocaleString()} tokens`) + } + + return commits + } + + private async getStats(options: CollectOptions): Promise { + // For release comparisons, use the commit range + if (options.releaseComparison) { + const range = options.releaseComparison.commitRange + const shortstat = await $`git -C ${this.repoPath} log ${range} --shortstat --format=""`.text() + const authors = await $`git -C ${this.repoPath} log ${range} --format="%ae" | sort -u | wc -l`.text() + + let filesChanged = 0, + linesAdded = 0, + linesDeleted = 0, + totalCommits = 0 + + for (const line of shortstat.split('\n')) { + if (line.includes('changed')) { + totalCommits++ + const fileMatch = line.match(/(\d+) file/) + const insertMatch = line.match(/(\d+) insertion/) + const deleteMatch = line.match(/(\d+) deletion/) + + if (fileMatch?.[1]) { + filesChanged += parseInt(fileMatch[1], 10) + } + if (insertMatch?.[1]) { + linesAdded += parseInt(insertMatch[1], 10) + } + if (deleteMatch?.[1]) { + linesDeleted += parseInt(deleteMatch[1], 10) + } + } + } + + return { + totalCommits, + totalAuthors: parseInt(authors.trim(), 10), + filesChanged, + linesAdded, + linesDeleted, + } + } + + // Original implementation for time-based analysis + const shortstat = await $`git -C ${this.repoPath} log --since="${options.since}" --shortstat --format=""`.text() + const authors = + await $`git -C ${this.repoPath} log --since="${options.since}" --format="%ae" | sort -u | wc -l`.text() + + let filesChanged = 0, + linesAdded = 0, + linesDeleted = 0, + totalCommits = 0 + + for (const line of shortstat.split('\n')) { + if (line.includes('changed')) { + totalCommits++ + const fileMatch = line.match(/(\d+) file/) + const insertMatch = line.match(/(\d+) insertion/) + const deleteMatch = line.match(/(\d+) deletion/) + + if (fileMatch?.[1]) { + filesChanged += parseInt(fileMatch[1], 10) + } + if (insertMatch?.[1]) { + linesAdded += parseInt(insertMatch[1], 10) + } + if (deleteMatch?.[1]) { + linesDeleted += parseInt(deleteMatch[1], 10) + } + } + } + + return { + totalCommits, + totalAuthors: parseInt(authors.trim(), 10), + filesChanged, + linesAdded, + linesDeleted, + } + } +} diff --git a/apps/cli/src/core/orchestrator.ts b/apps/cli/src/core/orchestrator.ts new file mode 100644 index 00000000000..a72effbcf42 --- /dev/null +++ b/apps/cli/src/core/orchestrator.ts @@ -0,0 +1,810 @@ +/* eslint-disable max-depth */ +/* eslint-disable security/detect-non-literal-regexp */ +/* eslint-disable complexity */ +/* eslint-disable max-lines */ +/* eslint-disable no-console */ +import { join } from 'node:path' +import { + type CollectOptions, + DataCollector, + type PullRequest, + type RepositoryData, +} from '@universe/cli/src/core/data-collector' +import type { AIProvider } from '@universe/cli/src/lib/ai-provider' +import { AnalysisWriter } from '@universe/cli/src/lib/analysis-writer' +import type { CacheProvider } from '@universe/cli/src/lib/cache-provider' +import type { Logger } from '@universe/cli/src/lib/logger' +import { ReleaseScanner } from '@universe/cli/src/lib/release-scanner' + +// ============================================================================ +// Types +// ============================================================================ + +export interface AnalysisConfig { + mode?: string // Predefined mode (team-digest, changelog, release-changelog, etc.) + prompt?: string // Custom prompt (file path or inline text) + promptFile?: string // Explicit prompt file path + variables?: Record // Variable substitution for prompts + releaseOptions?: { + // Release-specific options + platform: 'mobile' | 'extension' + version: string + compareWith?: string + } +} + +export interface OutputConfig { + type: string // Output type (slack, markdown, file, etc.) + target?: string // Target destination (file path, channel, etc.) + options?: Record // Type-specific options +} + +export interface OrchestratorConfig { + analysis: AnalysisConfig + outputs: OutputConfig[] + collect: CollectOptions + verbose?: boolean + dryRun?: boolean + saveArtifacts?: boolean + model?: string // AI model to use (defaults to claude-opus-4-1-20250805) + bypassCache?: boolean // Bypass cache for this run +} + +// ============================================================================ +// Prompt Management +// ============================================================================ + +class PromptResolver { + private builtInPromptsPath = join((import.meta.dir as string | undefined) ?? process.cwd(), 'src', 'prompts') + private projectPromptsPath = '.claude/prompts' + + async resolve(promptRef: string): Promise { + // If it's a multiline string or looks like instructions, use as-is + if (promptRef.includes('\n') || promptRef.length > 100) { + return promptRef + } + + // If it ends with .md, treat as file path + if (promptRef.endsWith('.md')) { + return await this.loadFromFile(promptRef) + } + + // Check for built-in prompts + const builtInPath = join(this.builtInPromptsPath, `${promptRef}.md`) + if (await this.fileExists(builtInPath)) { + return await this.loadFromFile(builtInPath) + } + + // Check for project prompts + const projectPath = join(this.projectPromptsPath, `${promptRef}.md`) + if (await this.fileExists(projectPath)) { + return await this.loadFromFile(projectPath) + } + + // Treat as inline prompt if not found as file + return promptRef + } + + private async fileExists(path: string): Promise { + try { + await Bun.file(path).text() + return true + } catch { + return false + } + } + + private async loadFromFile(path: string): Promise { + try { + return await Bun.file(path).text() + } catch (error) { + throw new Error(`Failed to load prompt from ${path}: ${error}`) + } + } + + substituteVariables(prompt: string, variables: Record): string { + let result = prompt + for (const [key, value] of Object.entries(variables)) { + const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + // Use string replace with regex pattern - escapedKey is safe as it's escaped + const dollarPattern = new RegExp(`\\$${escapedKey}`, 'g') + const bracePattern = new RegExp(`{{${escapedKey}}}`, 'g') + result = result.replace(dollarPattern, value) + result = result.replace(bracePattern, value) + } + return result + } +} + +// ============================================================================ +// Analysis Orchestrator +// ============================================================================ + +export interface OrchestratorDependencies { + config: OrchestratorConfig + aiProvider: AIProvider + cacheProvider?: CacheProvider + logger: Logger +} + +export class Orchestrator { + private promptResolver = new PromptResolver() + private dataCollector: DataCollector + private analysisWriter: AnalysisWriter + private startTime: number = 0 + private timings: { dataCollection: number; claudeAnalysis: number } = { dataCollection: 0, claudeAnalysis: 0 } + private config: OrchestratorConfig + private aiProvider: AIProvider + private logger: Logger + + constructor(deps: OrchestratorDependencies) { + this.config = deps.config + this.aiProvider = deps.aiProvider + this.logger = deps.logger + this.dataCollector = new DataCollector( + deps.config.collect.repoPath, + deps.cacheProvider, + deps.config.bypassCache || false, + deps.logger, + ) + this.analysisWriter = new AnalysisWriter() + } + + async execute(): Promise> { + this.startTime = Date.now() + const runId = this.analysisWriter.getRunId() + this.logger.info(`Starting repository analysis${this.config.saveArtifacts ? ` (run: ${runId})` : ''}`) + + // Initialize analysis directory if saving artifacts + if (this.config.saveArtifacts) { + await this.analysisWriter.initialize() + // Save configuration + await this.analysisWriter.saveConfig(this.config) + } + + // Step 1: Collect repository data + const dataStartTime = Date.now() + const data = await this.collectData() + this.timings.dataCollection = Date.now() - dataStartTime + + // Step 2: Run analysis with universal analyzer + const analysisStartTime = Date.now() + const insights = await this.analyze(data) + this.timings.claudeAnalysis = Date.now() - analysisStartTime + + // Step 3: Deliver to outputs + await this.deliver(insights, data) + + // Generate summary + await this.generateSummary(data) + + const totalTime = Date.now() - this.startTime + this.logger.info(`Analysis complete! (${(totalTime / 1000).toFixed(1)}s)`) + this.logger.info(`View artifacts: ${this.analysisWriter.getRunPath()}/`) + + // Return the analysis results for UI consumption + return insights + } + + private async collectData(): Promise { + this.logger.info('Collecting repository data...') + + let data: RepositoryData + + // If in release mode (release-changelog or bug-bisect), augment collect options with release comparison + if ( + (this.config.analysis.mode === 'release-changelog' || this.config.analysis.mode === 'bug-bisect') && + this.config.analysis.releaseOptions + ) { + const scanner = new ReleaseScanner(this.config.collect.repoPath, this.logger) + const { platform, version, compareWith } = this.config.analysis.releaseOptions + + const comparison = await scanner.getReleaseComparison({ + platform, + version, + compareWith, + }) + if (!comparison) { + throw new Error(`Could not find release comparison for ${platform}/${version}`) + } + + this.logger.info(`Analyzing release: ${platform}/${version} (comparing with ${comparison.from.version})`) + + // Add release comparison to collect options + const collectOptions: CollectOptions = { + ...this.config.collect, + releaseComparison: comparison, + } + + data = await this.dataCollector.collect(collectOptions) + } else { + data = await this.dataCollector.collect(this.config.collect) + } + + // Save collected data if saving artifacts + if (this.config.saveArtifacts) { + await this.analysisWriter.saveCommits(data.commits, data.metadata) + await this.analysisWriter.savePullRequests(data.pullRequests) + await this.analysisWriter.saveStats(data.stats) + } + + return data + } + + private async analyze(data: RepositoryData): Promise> { + this.logger.info('Running analysis...') + + // Build the analysis prompt + const prompt = await this.buildPrompt(data) + if (this.config.saveArtifacts) { + await this.analysisWriter.savePrompt(prompt) + } + + // Prepare data context + const context = this.prepareContext(data) + if (this.config.saveArtifacts) { + await this.analysisWriter.saveContext(context) + } + + // Smart injection: replace if template has placeholder, otherwise append + let analysisPrompt: string + if (prompt.includes('{{COMMIT_DATA}}')) { + // New style: Replace the variable + analysisPrompt = prompt.replace(/{{COMMIT_DATA}}/g, context) + } else { + // Legacy style: Append to end + analysisPrompt = `${prompt}\n\n## Repository Data\n\n${context}` + } + + // Estimate tokens (rough approximation: 1 token ≈ 4 characters) + let estimatedTokens = Math.round(analysisPrompt.length / 4) + this.logger.info(`Prepared Claude context (estimated ~${estimatedTokens.toLocaleString()} tokens)...`) + + // Check if we're over Claude's limit (roughly 200k tokens for Claude 3) + const MAX_TOKENS = 150000 // Conservative limit to leave room for response + + if (estimatedTokens > MAX_TOKENS) { + this.logger.warn(`Context too large (${estimatedTokens} tokens), reducing data...`) + + // Step 1: Try again without diffs (prefer PR bodies over diffs) + const reducedContext = this.prepareContext(data, true) // skipDiffs flag + if (prompt.includes('{{COMMIT_DATA}}')) { + analysisPrompt = prompt.replace(/{{COMMIT_DATA}}/g, reducedContext) + } else { + analysisPrompt = `${prompt}\n\n## Repository Data\n\n${reducedContext}` + } + estimatedTokens = Math.round(analysisPrompt.length / 4) + this.logger.info(`Reduced context to ~${estimatedTokens.toLocaleString()} tokens (removed diffs)`) + + // Step 2: If still too large, truncate PR bodies proportionally + if (estimatedTokens > MAX_TOKENS) { + this.logger.warn('Still too large, truncating PR bodies...') + const dataWithTruncatedPRs = this.truncatePRBodies(data, 0.5) // Reduce to 50% of original length + const contextWithTruncatedPRs = this.prepareContext(dataWithTruncatedPRs, true) + if (prompt.includes('{{COMMIT_DATA}}')) { + analysisPrompt = prompt.replace(/{{COMMIT_DATA}}/g, contextWithTruncatedPRs) + } else { + analysisPrompt = `${prompt}\n\n## Repository Data\n\n${contextWithTruncatedPRs}` + } + estimatedTokens = Math.round(analysisPrompt.length / 4) + this.logger.info(`Reduced context to ~${estimatedTokens.toLocaleString()} tokens (truncated PR bodies to 50%)`) + + // Step 3: If still too large, truncate commits + if (estimatedTokens > MAX_TOKENS) { + this.logger.warn('Still too large, truncating commit list...') + const truncatedData = { ...dataWithTruncatedPRs, commits: dataWithTruncatedPRs.commits.slice(0, 100) } + const minimalContext = this.prepareContext(truncatedData, true) + if (prompt.includes('{{COMMIT_DATA}}')) { + analysisPrompt = prompt.replace(/{{COMMIT_DATA}}/g, minimalContext) + } else { + analysisPrompt = `${prompt}\n\n## Repository Data\n\n${minimalContext}` + } + estimatedTokens = Math.round(analysisPrompt.length / 4) + this.logger.info(`Final context: ~${estimatedTokens.toLocaleString()} tokens (kept first 100 commits)`) + } + } + } + + if (this.config.saveArtifacts) { + await this.analysisWriter.saveClaudeInput(analysisPrompt) + } + + if (this.config.verbose) { + this.logger.debug(`Analysis prompt: ${analysisPrompt.slice(0, 500)}...`) + } + + // For now, directly use Claude API since Task is not available in this context + // In production, this would use the Task API + const result = await this.analyzeWithClaude(analysisPrompt) + + // Save Claude's output + if (this.config.saveArtifacts) { + await this.analysisWriter.saveClaudeOutput(result) + } + + return result + } + + private async buildPrompt(data: RepositoryData): Promise { + let promptText = '' + + // Load base prompt + if (this.config.analysis.mode) { + promptText = await this.promptResolver.resolve(this.config.analysis.mode) + } else if (this.config.analysis.promptFile) { + promptText = await this.promptResolver.resolve(this.config.analysis.promptFile) + } else if (this.config.analysis.prompt) { + promptText = await this.promptResolver.resolve(this.config.analysis.prompt) + } else { + // Default to team-digest + promptText = await this.promptResolver.resolve('team-digest') + } + + // Build variables for substitution + const variables: Record = { + ...this.config.analysis.variables, + } + + // Add release context variables for bug-bisect mode + if ( + this.config.analysis.mode === 'bug-bisect' && + this.config.analysis.releaseOptions && + data.metadata.releaseInfo + ) { + variables.PLATFORM = data.metadata.releaseInfo.platform + variables.RELEASE_TO = data.metadata.releaseInfo.to + variables.RELEASE_FROM = data.metadata.releaseInfo.from + } + + // Substitute variables if provided + if (Object.keys(variables).length > 0) { + promptText = this.promptResolver.substituteVariables(promptText, variables) + } + + return promptText + } + + /** + * Truncate PR bodies proportionally to reduce context size + * @param data Original repository data + * @param ratio Ratio to keep (0.5 = keep 50% of each PR body) + */ + private truncatePRBodies(data: RepositoryData, ratio: number): RepositoryData { + const truncatedPRs = data.pullRequests.map((pr: PullRequest) => { + if (pr.body && pr.body.length > 0) { + const targetLength = Math.max(100, Math.floor(pr.body.length * ratio)) // Keep at least 100 chars + const truncatedBody = pr.body.slice(0, targetLength) + (pr.body.length > targetLength ? '... [truncated]' : '') + return { + number: pr.number, + title: pr.title, + body: truncatedBody, + author: pr.author, + state: pr.state, + mergedAt: pr.mergedAt, + mergeCommitSha: pr.mergeCommitSha, + } satisfies PullRequest + } + return { + number: pr.number, + title: pr.title, + body: pr.body, + author: pr.author, + state: pr.state, + mergedAt: pr.mergedAt, + mergeCommitSha: pr.mergeCommitSha, + } satisfies PullRequest + }) as PullRequest[] + + return { + ...data, + pullRequests: truncatedPRs, + } + } + + private prepareContext(data: RepositoryData, skipDiffs: boolean = false): string { + const lines: string[] = [] + let prBodyTokens = 0 + + // Metadata section (compact format) + lines.push('=== REPOSITORY METADATA ===') + lines.push(`Repository: ${data.metadata.repository}`) + lines.push(`Period: ${data.metadata.period}`) + + if (data.metadata.releaseInfo) { + lines.push( + `Release: ${data.metadata.releaseInfo.platform} ${data.metadata.releaseInfo.from} → ${data.metadata.releaseInfo.to}`, + ) + } + + lines.push(`Total commits: ${data.metadata.commitCount}`) + lines.push(`Pull requests: ${data.metadata.prCount}`) + + if (data.metadata.filtering) { + lines.push(`Filtered: ${data.metadata.filtering.trivialCommitsFiltered} trivial commits removed`) + } + lines.push('') + + // Stats section (compact) + lines.push('=== STATISTICS ===') + lines.push(`Authors: ${data.stats.totalAuthors}`) + lines.push(`Files changed: ${data.stats.filesChanged}`) + lines.push(`Lines: +${data.stats.linesAdded} -${data.stats.linesDeleted}`) + lines.push('') + + // Pull requests section with full bodies (up to prBodyLimit) + if (data.pullRequests.length > 0) { + lines.push('=== PULL REQUESTS ===') + for (const pr of data.pullRequests) { + lines.push(`PR #${pr.number}: ${pr.title} [${pr.state}] @${pr.author}`) + if (pr.body) { + // Include full PR body (already truncated to prBodyLimit during collection) + // Preserve markdown formatting with proper newlines + const bodyLines = pr.body.split('\n') + for (const line of bodyLines) { + lines.push(` ${line}`) + } + // Track token usage for PR bodies (rough approximation: 1 token ≈ 4 characters) + prBodyTokens += Math.ceil(pr.body.length / 4) + } + } + lines.push('') + } + + // Log PR body token contribution if significant + if (prBodyTokens > 0) { + this.logger.info(`PR bodies contribute ~${prBodyTokens.toLocaleString()} tokens to context`) + } + + // Commits section (ultra-compact format) + lines.push('=== COMMITS ===') + for (const commit of data.commits) { + // Format: sha | author | message | stats + const date = new Date(commit.timestamp).toISOString().split('T')[0] + lines.push(`${commit.sha.slice(0, 7)} | ${date} | ${commit.author.email.split('@')[0]} | ${commit.message}`) + + // If we have file information, show it compactly + if (commit.files && commit.files.length > 0) { + // Group files by directory for even more compact display + const fileGroups = new Map() + + for (const file of commit.files) { + const parts = file.path.split('/') + const dir = parts.length > 1 ? parts.slice(0, -1).join('/') : '.' + const filename = parts[parts.length - 1] + + if (!fileGroups.has(dir)) { + fileGroups.set(dir, []) + } + + // Status indicators: M=modified, A=added, D=deleted, R=renamed + const statusChar = file.status[0]?.toUpperCase() ?? 'M' + const changes = `${statusChar}:+${file.additions}-${file.deletions}` + const files = fileGroups.get(dir) + if (files) { + files.push(`${filename}(${changes})`) + } + } + + // Output grouped files + for (const [dir, files] of fileGroups) { + if (files.length <= 3) { + lines.push(` ${dir}/: ${files.join(' ')}`) + } else { + // If many files in same dir, summarize + lines.push(` ${dir}/: ${files.slice(0, 2).join(' ')} +${files.length - 2} more`) + } + } + + // Include diffs if available (already in compact diff format) + if (!skipDiffs) { + for (const file of commit.files) { + if (file.diff) { + lines.push(` --- ${file.path} ---`) + // Add indent to diff lines for readability + const diffLines = file.diff.split('\n').map((line: string) => ` ${line}`) + lines.push(...diffLines.slice(0, 10)) // Limit diff preview + if (diffLines.length > 10) { + lines.push(` ... (${diffLines.length - 10} more lines)`) + } + } + } + } + } + } + + return lines.join('\n') + } + + private async analyzeWithClaude(prompt: string): Promise> { + this.logger.info('Analyzing with Claude...') + + const model = this.config.model || 'claude-sonnet-4-5-20250929' + const stream = this.aiProvider.streamText({ + prompt, + model, + maxTokens: 64000, + temperature: 1, + }) + + // Stream and accumulate full response, emitting excerpts for UI + let fullText = '' + + // Buffers for accumulating deltas into meaningful chunks + let textBuffer = '' + let reasoningBuffer = '' + const MIN_EXCERPT_LENGTH = 150 // Minimum chars before emitting (higher threshold to reduce frequency) + const MAX_EXCERPT_LENGTH = 160 // Maximum chars per excerpt (very compact) + const MAX_BUFFER_LENGTH = 220 // Force emit if buffer gets too large + let lastEmittedTime = 0 + const MIN_EMIT_INTERVAL_MS = 2000 // Only emit excerpts every 2 seconds max + + for await (const chunk of stream) { + // Accumulate text + if (chunk.text) { + fullText += chunk.text + textBuffer += chunk.text + + // Check if we should emit a text excerpt + if (this.logger.emitStreamingExcerpt) { + const now = Date.now() + // Throttle excerpt emissions + if (now - lastEmittedTime >= MIN_EMIT_INTERVAL_MS) { + // Find sentence boundaries (including newlines for changelog-style content) + const lastSentenceEnd = Math.max( + textBuffer.lastIndexOf('.\n'), + textBuffer.lastIndexOf('.\n\n'), + textBuffer.lastIndexOf('. '), + textBuffer.lastIndexOf('!\n'), + textBuffer.lastIndexOf('?\n'), + textBuffer.lastIndexOf('\n\n'), // Double newline (paragraph break) + ) + + // Only emit if we have a good boundary and enough content + if ( + (lastSentenceEnd >= MIN_EXCERPT_LENGTH && lastSentenceEnd <= MAX_EXCERPT_LENGTH) || + (textBuffer.length >= MAX_BUFFER_LENGTH && lastSentenceEnd > MIN_EXCERPT_LENGTH) + ) { + const excerpt = textBuffer.slice(0, lastSentenceEnd > 0 ? lastSentenceEnd + 1 : MAX_EXCERPT_LENGTH).trim() + // Filter out markdown headers and very short content + if (excerpt.length >= MIN_EXCERPT_LENGTH && !excerpt.startsWith('##')) { + this.logger.emitStreamingExcerpt(excerpt, false) + textBuffer = textBuffer.slice(lastSentenceEnd > 0 ? lastSentenceEnd + 1 : MAX_EXCERPT_LENGTH) + lastEmittedTime = now + } + } + } + } else { + // CLI mode: write directly to console + process.stdout.write(chunk.text) + } + } + + // Accumulate reasoning + if (chunk.reasoning) { + reasoningBuffer += chunk.reasoning + + // Check if we should emit a reasoning excerpt (prefer reasoning over text) + if (this.logger.emitStreamingExcerpt) { + const now = Date.now() + // Throttle excerpt emissions + if (now - lastEmittedTime >= MIN_EMIT_INTERVAL_MS) { + // Find sentence boundaries for reasoning + const lastSentenceEnd = Math.max( + reasoningBuffer.lastIndexOf('.\n'), + reasoningBuffer.lastIndexOf('.\n\n'), + reasoningBuffer.lastIndexOf('. '), + reasoningBuffer.lastIndexOf('!\n'), + reasoningBuffer.lastIndexOf('?\n'), + reasoningBuffer.lastIndexOf('\n\n'), + ) + + // Only emit if we have a good boundary and enough content + if ( + (lastSentenceEnd >= MIN_EXCERPT_LENGTH && lastSentenceEnd <= MAX_EXCERPT_LENGTH) || + (reasoningBuffer.length >= MAX_BUFFER_LENGTH && lastSentenceEnd > MIN_EXCERPT_LENGTH) + ) { + const excerpt = reasoningBuffer + .slice(0, lastSentenceEnd > 0 ? lastSentenceEnd + 1 : MAX_EXCERPT_LENGTH) + .trim() + // Filter out markdown headers and very short content + if (excerpt.length >= MIN_EXCERPT_LENGTH && !excerpt.startsWith('##')) { + this.logger.emitStreamingExcerpt(excerpt, true) + reasoningBuffer = reasoningBuffer.slice(lastSentenceEnd > 0 ? lastSentenceEnd + 1 : MAX_EXCERPT_LENGTH) + lastEmittedTime = now + } + } + } + } + } + + if (chunk.isComplete) { + // Emit any remaining buffered content + if (this.logger.emitStreamingExcerpt) { + if (textBuffer.trim().length > 0) { + this.logger.emitStreamingExcerpt(textBuffer.trim(), false) + } + if (reasoningBuffer.trim().length > 0) { + this.logger.emitStreamingExcerpt(reasoningBuffer.trim(), true) + } + } else { + } + break + } + } + + // Try to parse as JSON if possible (especially for bug-bisect mode) + if (this.config.analysis.mode === 'bug-bisect') { + try { + // Extract JSON from markdown code blocks if present + const jsonMatch = fullText.match(/```(?:json)?\s*(\{[\s\S]*\})\s*```/) || fullText.match(/(\{[\s\S]*\})/) + const jsonText = jsonMatch && jsonMatch[1] ? jsonMatch[1] : fullText + const parsed = JSON.parse(jsonText) as Record + // Ensure it has the expected structure + if (parsed.suspiciousCommits && Array.isArray(parsed.suspiciousCommits)) { + return parsed + } + // If structure is wrong, wrap it + return { analysis: fullText, parsed } + } catch (error) { + this.logger.warn(`Failed to parse JSON response: ${error}`) + // Return as analysis text but try to extract any JSON-like content + return { analysis: fullText, error: 'Failed to parse JSON response' } + } + } + + // For other modes, try to parse as JSON but fallback to text + try { + return JSON.parse(fullText) as Record + } catch { + return { analysis: fullText } + } + } + + private async deliver(insights: Record, data: RepositoryData): Promise { + this.logger.info('Delivering results...') + + for (const output of this.config.outputs) { + await this.deliverToOutput({ insights, data, output }) + } + } + + private async deliverToOutput(args: { + insights: Record + data: RepositoryData + output: OutputConfig + }): Promise { + const { insights, data, output } = args + this.logger.info(`Delivering to ${output.type}...`) + + switch (output.type) { + case 'stdout': { + console.log('\n=== Analysis Results ===\n') + console.log(JSON.stringify(insights, null, 2)) + break + } + + case 'file': + case 'markdown': { + const path = output.target || 'analysis-output.md' + if (this.config.dryRun) { + this.logger.info(`[DRY RUN] Would save to file: ${path}`) + this.logger.info('[DRY RUN] Preview of content:') + console.log(this.formatAsMarkdown(insights, data).slice(0, 500) + '...') + } else { + // In production, this would use markdown-formatter subagent + await this.saveToFile({ insights, data, path }) + this.logger.info(`Saved to ${path}`) + } + break + } + + case 'slack': { + if (this.config.dryRun) { + this.logger.info(`[DRY RUN] Would publish to Slack channel: ${output.target}`) + this.logger.info(`[DRY RUN] Message preview: ${JSON.stringify(insights).slice(0, 200)}...`) + } else { + // In production, this would use slack-publisher subagent + this.logger.info(`Would publish to Slack channel: ${output.target}`) + this.logger.info('Note: Slack integration requires subagent implementation') + } + break + } + + default: + this.logger.warn(`Unknown output type: ${output.type}`) + } + } + + private async saveToFile(args: { + insights: Record + data: RepositoryData + path: string + }): Promise { + const { insights, data, path } = args + const content = this.formatAsMarkdown(insights, data) + await Bun.write(path, content) + + // Also save to analysis folder + await this.analysisWriter.saveReport(content) + } + + private formatAsMarkdown(insights: Record, data: RepositoryData): string { + const lines: string[] = [] + + lines.push(`# Repository Analysis: ${data.metadata.repository}`) + lines.push(`*Period: ${data.metadata.period}*`) + lines.push(`*Generated: ${data.metadata.collectedAt}*`) + lines.push('') + + if (insights.themes && Array.isArray(insights.themes)) { + lines.push('## Themes') + for (const theme of insights.themes) { + if (theme && typeof theme === 'object' && 'title' in theme && 'description' in theme) { + lines.push(`### ${String(theme.title)}`) + lines.push(String(theme.description)) + lines.push('') + } + } + } + + if (insights.highlights && Array.isArray(insights.highlights)) { + lines.push('## Highlights') + for (const highlight of insights.highlights) { + lines.push(`- ${String(highlight)}`) + } + lines.push('') + } + + if (insights.metrics && typeof insights.metrics === 'object') { + const metrics = insights.metrics as Record + lines.push('## Metrics') + if (typeof metrics.total_commits === 'number') { + lines.push(`- Commits: ${metrics.total_commits}`) + } + if (typeof metrics.total_prs === 'number') { + lines.push(`- Pull Requests: ${metrics.total_prs}`) + } + if (typeof metrics.active_contributors === 'number') { + lines.push(`- Contributors: ${metrics.active_contributors}`) + } + lines.push('') + } + + if (typeof insights === 'string') { + lines.push('## Analysis') + lines.push(insights) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } else if (insights && typeof insights === 'object' && 'analysis' in insights && insights.analysis) { + lines.push('## Analysis') + lines.push(String(insights.analysis)) + } + + return lines.join('\n') + } + + private async generateSummary(data: RepositoryData): Promise { + if (!this.config.saveArtifacts) { + return + } + + // Get stats from data collector if available + const totalCommits = data.metadata.filtering?.totalCommitsFound ?? data.metadata.commitCount + const trivialFiltered = data.metadata.filtering?.trivialCommitsFiltered ?? 0 + + await this.analysisWriter.saveSummary({ + config: this.config, + dataCollection: { + totalCommits, + trivialCommitsFiltered: trivialFiltered, + commitsAnalyzed: data.commits.length, + prsExtracted: data.pullRequests.length, + tokensEstimated: Math.round((data.commits.length * 100 + data.pullRequests.length * 50) / 4), + }, + timing: { + dataCollection: this.timings.dataCollection, + claudeAnalysis: this.timings.claudeAnalysis, + total: Date.now() - this.startTime, + }, + }) + } +} diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts new file mode 100644 index 00000000000..d6a57aae330 --- /dev/null +++ b/apps/cli/src/index.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// diff --git a/apps/cli/src/lib/ai-provider-vercel.ts b/apps/cli/src/lib/ai-provider-vercel.ts new file mode 100644 index 00000000000..d4aeb3d148a --- /dev/null +++ b/apps/cli/src/lib/ai-provider-vercel.ts @@ -0,0 +1,143 @@ +import { AnthropicProviderOptions, anthropic } from '@ai-sdk/anthropic' +import type { AIProvider, GenerateTextInput, StreamChunk, StreamTextInput } from '@universe/cli/src/lib/ai-provider' +import { generateText, streamText } from 'ai' + +/** + * Vercel AI SDK implementation of AIProvider + * + * Maps Vercel AI SDK responses to our contract interface. + */ +export class VercelAIProvider implements AIProvider { + constructor(private apiKey?: string) { + // API key can be provided via constructor or ANTHROPIC_API_KEY env var + // If provided, set it as environment variable for Vercel AI SDK to use + if (apiKey) { + process.env.ANTHROPIC_API_KEY = apiKey + } + } + + private processFullStreamChunk( + chunk: unknown, + accumulators: { fullText: { value: string }; fullReasoning: { value: string } }, + ): StreamChunk | null { + const chunkObj = typeof chunk === 'object' && chunk !== null && 'type' in chunk ? chunk : null + + if (!chunkObj) { + // Fallback: treat as text chunk + const textChunk = String(chunk) + accumulators.fullText.value += textChunk + return { + text: textChunk, + isComplete: false, + } + } + + const chunkType = chunkObj.type as string + + // Reasoning delta chunks (per Vercel AI SDK docs) + if (chunkType === 'reasoning-delta') { + const reasoningContent = String((chunkObj as { text?: string }).text || '') + if (reasoningContent) { + accumulators.fullReasoning.value += reasoningContent + return { + text: '', + reasoning: reasoningContent, + isComplete: false, + } + } + return null + } + + // Text delta chunks (per Vercel AI SDK docs) + if (chunkType === 'text-delta') { + const textContent = String((chunkObj as { text?: string }).text || '') + if (textContent) { + accumulators.fullText.value += textContent + return { + text: textContent, + reasoning: undefined, + isComplete: false, + } + } + } + + return null + } + + async *streamText(input: StreamTextInput): AsyncGenerator { + const model = anthropic(input.model) + + const result = streamText({ + model, + prompt: input.prompt, + system: input.systemPrompt, + temperature: input.temperature, + ...(input.maxTokens && { maxTokens: input.maxTokens }), + providerOptions: { + anthropic: { + thinking: { type: 'enabled', budgetTokens: 63999 }, + sendReasoning: true, + } satisfies AnthropicProviderOptions, + }, + }) + + const accumulators = { + fullText: { value: '' }, + fullReasoning: { value: '' }, + } + + // Check if fullStream is available (contains reasoning chunks when sendReasoning is true) + // Process fullStream if available to access reasoning, otherwise use textStream + const textStream = result.textStream + const fullStream = 'fullStream' in result ? (result as { fullStream?: AsyncIterable }).fullStream : null + + if (fullStream) { + for await (const chunk of fullStream) { + const processedChunk = this.processFullStreamChunk(chunk, accumulators) + if (processedChunk) { + yield processedChunk + } + } + } else { + // Fallback: process text stream normally + for await (const chunk of textStream) { + const textChunk = String(chunk) + accumulators.fullText.value += textChunk + yield { + text: textChunk, + isComplete: false, + } + } + } + + // Yield final complete chunk (signal only, no duplicate content) + // The orchestrator already accumulated all delta chunks during streaming, + // so we only need to signal completion without re-sending the entire text + yield { + text: '', + reasoning: undefined, + isComplete: true, + } + } + + async generateText(input: GenerateTextInput): Promise { + const model = anthropic(input.model) + + const result = await generateText({ + model, + prompt: input.prompt, + system: input.systemPrompt, + temperature: input.temperature, + ...(input.maxTokens && { maxTokens: input.maxTokens }), + }) + + return result.text + } +} + +/** + * Factory function to create a VercelAIProvider instance + */ +export function createVercelAIProvider(apiKey?: string): VercelAIProvider { + return new VercelAIProvider(apiKey || process.env.ANTHROPIC_API_KEY) +} diff --git a/apps/cli/src/lib/ai-provider.ts b/apps/cli/src/lib/ai-provider.ts new file mode 100644 index 00000000000..24c37fbd150 --- /dev/null +++ b/apps/cli/src/lib/ai-provider.ts @@ -0,0 +1,50 @@ +/** + * AI Provider Contract + * + * Defines the interface for AI text generation providers. + * Allows swapping implementations (e.g., Claude SDK, Vercel AI SDK, etc.) + */ + +export interface StreamTextInput { + prompt: string + systemPrompt?: string + model: string + temperature?: number + maxTokens?: number +} + +export interface StreamChunk { + text: string + isComplete: boolean + reasoning?: string +} + +export interface GenerateTextInput { + prompt: string + systemPrompt?: string + model: string + temperature?: number + maxTokens?: number +} + +/** + * AI Provider interface contract + * + * Provides methods for streaming and non-streaming text generation. + * Implementations should map to their respective SDKs while maintaining this interface. + */ +export interface AIProvider { + /** + * Stream text generation with incremental chunks + * @param input - Configuration for text generation + * @returns Async generator yielding text chunks + */ + streamText(input: StreamTextInput): AsyncGenerator + + /** + * Generate complete text without streaming + * @param input - Configuration for text generation + * @returns Complete generated text + */ + generateText(input: GenerateTextInput): Promise +} diff --git a/apps/cli/src/lib/analysis-writer.ts b/apps/cli/src/lib/analysis-writer.ts new file mode 100644 index 00000000000..e62a3606af9 --- /dev/null +++ b/apps/cli/src/lib/analysis-writer.ts @@ -0,0 +1,241 @@ +import { mkdir } from 'node:fs/promises' +import { join } from 'node:path' + +/** + * Utility for writing analysis artifacts to disk for debugging and audit + */ +export class AnalysisWriter { + private runId: string + private basePath: string + private runPath: string + + constructor(basePath: string = '.analysis') { + this.basePath = basePath + this.runId = this.generateRunId() + this.runPath = join(this.basePath, this.runId) + } + + /** + * Generate a unique run ID based on timestamp + */ + private generateRunId(): string { + const now = new Date() + const year = now.getFullYear() + const month = String(now.getMonth() + 1).padStart(2, '0') + const day = String(now.getDate()).padStart(2, '0') + const hours = String(now.getHours()).padStart(2, '0') + const minutes = String(now.getMinutes()).padStart(2, '0') + const seconds = String(now.getSeconds()).padStart(2, '0') + + return `analysis-${year}${month}${day}-${hours}${minutes}${seconds}` + } + + /** + * Get the run ID for this analysis + */ + getRunId(): string { + return this.runId + } + + /** + * Get the full path to the run directory + */ + getRunPath(): string { + return this.runPath + } + + /** + * Initialize the run directory + */ + async initialize(): Promise { + await mkdir(this.runPath, { recursive: true }) + } + + /** + * Save JSON data to a file + */ + async saveJson(filename: string, data: unknown): Promise { + const filepath = join(this.runPath, filename) + await Bun.write(filepath, JSON.stringify(data, null, 2)) + } + + /** + * Save text content to a file + */ + async saveText(filename: string, content: string): Promise { + const filepath = join(this.runPath, filename) + await Bun.write(filepath, content) + } + + /** + * Save the configuration used for this run + */ + async saveConfig(config: unknown): Promise { + await this.saveJson('config.json', config) + } + + /** + * Save commit data + */ + async saveCommits(commits: unknown[], metadata?: unknown): Promise { + await this.saveJson('commits.json', { + count: commits.length, + metadata, + commits, + }) + } + + /** + * Save pull request data + */ + async savePullRequests(prs: unknown[]): Promise { + await this.saveJson('pull-requests.json', { + count: prs.length, + pullRequests: prs, + }) + } + + /** + * Save repository statistics + */ + async saveStats(stats: unknown): Promise { + await this.saveJson('stats.json', stats) + } + + /** + * Save the context sent to Claude + */ + async saveContext(context: string): Promise { + await this.saveText('context.json', context) + } + + /** + * Save the prompt used + */ + async savePrompt(prompt: string): Promise { + await this.saveText('prompt.md', prompt) + } + + /** + * Save the complete input to Claude + */ + async saveClaudeInput(input: string): Promise { + await this.saveText('claude-input.md', input) + } + + /** + * Save Claude's response + */ + async saveClaudeOutput(output: unknown): Promise { + if (typeof output === 'string') { + await this.saveText('claude-output.md', output) + } else { + await this.saveJson('claude-output.json', output) + } + } + + /** + * Save the final report + */ + async saveReport(report: string): Promise { + await this.saveText('report.md', report) + } + + /** + * Save a debug summary + */ + async saveSummary(data: { + config: unknown + dataCollection: { + totalCommits: number + trivialCommitsFiltered: number + commitsAnalyzed: number + prsExtracted: number + tokensEstimated?: number + } + filesFiltered?: { + snapshots: number + lockfiles: number + generated: number + other: number + } + timing?: { + dataCollection: number + claudeAnalysis: number + total: number + } + }): Promise { + const { config, dataCollection, filesFiltered, timing } = data + + // Type assertion for config structure + const typedConfig = config as { + analysis?: { + mode?: string + releaseOptions?: { + platform?: string + version?: string + compareWith?: string + } + } + collect?: { + since?: string + } + } + + const summary = `# Analysis Run: ${this.runId} + +## Configuration +- Mode: ${typedConfig.analysis?.mode || 'unknown'} +${ + typedConfig.analysis?.releaseOptions + ? `- Platform: ${typedConfig.analysis.releaseOptions.platform} +- Version: ${typedConfig.analysis.releaseOptions.version} +- Comparing with: ${typedConfig.analysis.releaseOptions.compareWith || 'previous'}` + : '' +} +- Since: ${typedConfig.collect?.since || 'unknown'} + +## Data Collection +- Total commits found: ${dataCollection.totalCommits} +- Trivial commits filtered: ${dataCollection.trivialCommitsFiltered} +- Commits analyzed: ${dataCollection.commitsAnalyzed} +- PRs extracted: ${dataCollection.prsExtracted} +${dataCollection.tokensEstimated ? `- Tokens estimated: ~${dataCollection.tokensEstimated.toLocaleString()}` : ''} + +${ + filesFiltered + ? `## Files Filtered +- Snapshots: ${filesFiltered.snapshots} files +- Lockfiles: ${filesFiltered.lockfiles} files +- Generated: ${filesFiltered.generated} files +- Other: ${filesFiltered.other} files` + : '' +} + +${ + timing + ? `## Timing +- Data collection: ${(timing.dataCollection / 1000).toFixed(1)}s +- Claude analysis: ${(timing.claudeAnalysis / 1000).toFixed(1)}s +- Total: ${(timing.total / 1000).toFixed(1)}s` + : '' +} + +## Output Location +- Run ID: ${this.runId} +- Path: ${this.runPath}/ +` + + await this.saveText('summary.md', summary) + } + + /** + * Save list of filtered files + */ + async saveFilteredFiles(files: { path: string; reason: string }[]): Promise { + await this.saveJson('filtered-files.json', { + count: files.length, + files, + }) + } +} diff --git a/apps/cli/src/lib/cache-keys.ts b/apps/cli/src/lib/cache-keys.ts new file mode 100644 index 00000000000..dffacb069c7 --- /dev/null +++ b/apps/cli/src/lib/cache-keys.ts @@ -0,0 +1,106 @@ +import { createHash } from 'node:crypto' +import { type CollectOptions } from '@universe/cli/src/core/data-collector' + +/** + * Generate deterministic cache keys from CollectOptions + * Excludes non-deterministic fields like commitDataConfig that affect output format + */ + +/** + * Create a hash from a string + */ +function hash(input: string): string { + return createHash('sha256').update(input).digest('hex').slice(0, 16) +} + +/** + * Generate cache key components from CollectOptions (excluding output formatting options) + */ +function getCacheKeyComponents(options: CollectOptions): string { + const parts: string[] = [] + + // Repository identifier + if (options.repository?.owner && options.repository.name) { + parts.push(`repo:${options.repository.owner}/${options.repository.name}`) + } else if (options.repoPath) { + parts.push(`repopath:${options.repoPath}`) + } + + // Time-based query + parts.push(`since:${options.since}`) + + // Branch filter + if (options.branch) { + parts.push(`branch:${options.branch}`) + } + + // Author filter + if (options.author) { + parts.push(`author:${options.author}`) + } + + // Team filters + if (options.teamFilter?.length) { + const sortedEmails = [...options.teamFilter].sort().join(',') + parts.push(`team:${sortedEmails}`) + } + + if (options.teamUsernames?.length) { + const sortedUsernames = [...options.teamUsernames].sort().join(',') + parts.push(`usernames:${sortedUsernames}`) + } + + // Include open PRs flag + if (options.includeOpenPrs) { + parts.push('includeOpenPrs:true') + } + + // Release comparison (deterministic by version range) + if (options.releaseComparison) { + parts.push(`release:${options.releaseComparison.from.version}-${options.releaseComparison.to.version}`) + parts.push(`platform:${options.releaseComparison.to.platform}`) + parts.push(`range:${options.releaseComparison.commitRange}`) + } + + // Exclude trivial commits flag + if (options.excludeTrivialCommits) { + parts.push('excludeTrivial:true') + } + + return parts.join('|') +} + +/** + * Generate cache key for commits + */ +export function getCommitsCacheKey(options: CollectOptions): string { + const components = getCacheKeyComponents(options) + return `commits:${hash(components)}` +} + +/** + * Generate cache key for pull requests + */ +export function getPullRequestsCacheKey(options: CollectOptions): string { + const components = getCacheKeyComponents(options) + return `prs:${hash(components)}` +} + +/** + * Generate cache key for stats + */ +export function getStatsCacheKey(options: CollectOptions): string { + const components = getCacheKeyComponents(options) + return `stats:${hash(components)}` +} + +/** + * Generate pattern to invalidate all cache entries for a repository + */ +export function getRepositoryCachePattern(repository?: { owner?: string; name?: string }): string { + if (repository?.owner && repository.name) { + const repoHash = hash(`repo:${repository.owner}/${repository.name}`) + return `%:${repoHash}%` + } + return '%' +} diff --git a/apps/cli/src/lib/cache-provider-sqlite.ts b/apps/cli/src/lib/cache-provider-sqlite.ts new file mode 100644 index 00000000000..4b7e2119fd1 --- /dev/null +++ b/apps/cli/src/lib/cache-provider-sqlite.ts @@ -0,0 +1,130 @@ +import { Database } from 'bun:sqlite' +import { existsSync } from 'node:fs' +import { mkdir } from 'node:fs/promises' +import { join } from 'node:path' +import { type CacheProvider } from '@universe/cli/src/lib/cache-provider' + +/** + * SQLite implementation of CacheProvider using Bun's built-in SQLite + */ +export class SqliteCacheProvider implements CacheProvider { + private db: Database + private readonly dbPath: string + + constructor(dbPath?: string) { + // Default to ~/.gh-agent/cache.db + this.dbPath = dbPath || join(process.env.HOME || process.env.USERPROFILE || '.', '.gh-agent', 'cache.db') + this.ensureCacheDirectorySync() + this.db = new Database(this.dbPath) + this.initializeSchema() + this.cleanupExpired() + } + + private ensureCacheDirectorySync(): void { + const dir = join(this.dbPath, '..') + if (!existsSync(dir)) { + try { + // Use sync version for constructor - Bun will create parent directories + mkdir(dir, { recursive: true }).catch((error) => { + // eslint-disable-next-line no-console + console.error(`[WARN] Failed to create cache directory: ${error}`) + }) + } catch { + // Ignore - will fail gracefully on database creation + } + } + } + + private initializeSchema(): void { + this.db.exec(` + CREATE TABLE IF NOT EXISTS cache_entries ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + expires_at INTEGER, + created_at INTEGER NOT NULL + ); + + CREATE INDEX IF NOT EXISTS idx_expires_at ON cache_entries(expires_at); + `) + } + + /** + * Remove expired entries (called on initialization and periodically) + */ + private cleanupExpired(): void { + const now = Date.now() + this.db.exec(`DELETE FROM cache_entries WHERE expires_at IS NOT NULL AND expires_at < ${now}`) + } + + async get(key: string): Promise { + try { + const now = Date.now() + const stmt = this.db.prepare(` + SELECT value, expires_at + FROM cache_entries + WHERE key = ? AND (expires_at IS NULL OR expires_at >= ?) + `) + + const result = stmt.get(key, now) as { value: string; expires_at: number | null } | undefined + + if (!result) { + return null + } + + // Clean up expired entries periodically (every 100 reads) + if (Math.random() < 0.01) { + this.cleanupExpired() + } + + return JSON.parse(result.value) as T + } catch (_error) { + return null + } + } + + // eslint-disable-next-line max-params -- Required to match CacheProvider interface + async set(key: string, value: T, ttlSeconds?: number): Promise { + try { + const serialized = JSON.stringify(value) + const now = Date.now() + const expiresAt = ttlSeconds ? now + ttlSeconds * 1000 : null + + const stmt = this.db.prepare(` + INSERT OR REPLACE INTO cache_entries (key, value, expires_at, created_at) + VALUES (?, ?, ?, ?) + `) + + stmt.run(key, serialized, expiresAt, now) + } catch (_error) { + // Don't throw - graceful degradation + } + } + + async invalidate(key: string): Promise { + try { + const stmt = this.db.prepare('DELETE FROM cache_entries WHERE key = ?') + stmt.run(key) + } catch (_error) {} + } + + async invalidatePattern(pattern: string): Promise { + try { + // SQLite uses LIKE for pattern matching + const stmt = this.db.prepare('DELETE FROM cache_entries WHERE key LIKE ?') + stmt.run(pattern) + } catch (_error) {} + } + + async clear(): Promise { + try { + this.db.exec('DELETE FROM cache_entries') + } catch (_error) {} + } + + /** + * Close the database connection (call when done) + */ + close(): void { + this.db.close() + } +} diff --git a/apps/cli/src/lib/cache-provider.ts b/apps/cli/src/lib/cache-provider.ts new file mode 100644 index 00000000000..e6126843860 --- /dev/null +++ b/apps/cli/src/lib/cache-provider.ts @@ -0,0 +1,41 @@ +// ============================================================================ +// Cache Provider Interface +// ============================================================================ + +/** + * Contract for cache providers - allows swapping implementations + * (e.g., SQLite, Redis, in-memory) without changing consuming code + */ +export interface CacheProvider { + /** + * Retrieve a value from cache by key + * @param key Cache key + * @returns Cached value or null if not found/expired + */ + get(key: string): Promise + + /** + * Store a value in cache with optional TTL + * @param key Cache key + * @param value Value to cache (will be serialized as JSON) + * @param ttlSeconds Optional time-to-live in seconds (default: no expiration) + */ + set(key: string, value: T, ttlSeconds?: number): Promise + + /** + * Remove a specific key from cache + * @param key Cache key to invalidate + */ + invalidate(key: string): Promise + + /** + * Remove all keys matching a pattern (supports SQL LIKE patterns) + * @param pattern Pattern to match (e.g., "commits:%" or "prs:abc%") + */ + invalidatePattern(pattern: string): Promise + + /** + * Clear all entries from cache + */ + clear(): Promise +} diff --git a/apps/cli/src/lib/logger.ts b/apps/cli/src/lib/logger.ts new file mode 100644 index 00000000000..5608423ca3d --- /dev/null +++ b/apps/cli/src/lib/logger.ts @@ -0,0 +1,221 @@ +/** + * Logger interface for dependency injection + * Allows different logging strategies for interactive vs non-interactive modes + */ +export interface Logger { + info(message: string): void + warn(message: string): void + error(message: string): void + debug(message: string): void + /** + * Emit a streaming excerpt from agent thinking/output (optional, for UI progress updates) + */ + emitStreamingExcerpt?(excerpt: string, isReasoning?: boolean): void +} + +/** + * Progress stage type for UI progress events + */ +export type ProgressStage = 'idle' | 'collecting' | 'analyzing' | 'delivering' | 'complete' | 'error' + +/** + * Progress event type for categorizing messages + */ +export type ProgressEventType = 'reasoning' | 'output' | 'info' + +/** + * Progress event interface for UI updates + */ +export interface ProgressEvent { + stage: ProgressStage + message?: string + progress?: number // 0-100 + cacheInfo?: { + type: 'commits' | 'prs' | 'stats' + count: number + } + /** + * Whether this is reasoning (thinking) content from AI + */ + isReasoning?: boolean + /** + * Type of event for visual distinction in UI + */ + eventType?: ProgressEventType +} + +/** + * ConsoleLogger - Direct console output for non-interactive CLI mode + */ +export class ConsoleLogger implements Logger { + constructor(private readonly verbose: boolean = false) {} + + info(message: string): void { + // eslint-disable-next-line no-console + console.log(`[INFO] ${message}`) + } + + warn(message: string): void { + // eslint-disable-next-line no-console + console.warn(`[WARN] ${message}`) + } + + error(message: string): void { + // eslint-disable-next-line no-console + console.error(`[ERROR] ${message}`) + } + + debug(message: string): void { + if (this.verbose) { + // eslint-disable-next-line no-console + console.log(`[DEBUG] ${message}`) + } + } +} + +/** + * ProgressLogger - Emits progress events for interactive Ink UI mode + * Suppresses stdout to avoid interfering with Ink rendering + */ +export class ProgressLogger implements Logger { + constructor( + private readonly onProgress: (event: ProgressEvent) => void, + private readonly verbose: boolean = false, + ) {} + + info(message: string): void { + // Suppress redundant messages that are already handled by stage transitions + if (this.shouldSuppress(message)) { + return + } + + // Parse message to determine stage and emit appropriate progress event + const stage = this.determineStage(message) + const cleanMessage = message.replace(/^\[INFO\]\s*/, '').trim() + + // Detect cache hit messages and extract cache info + const cacheInfo = this.parseCacheInfo(message) + + if (cleanMessage) { + this.onProgress({ + stage, + message: cleanMessage, + eventType: 'info', + ...(cacheInfo && { cacheInfo }), + }) + } + } + + warn(message: string): void { + const cleanMessage = message.replace(/^\[WARN\]\s*/, '').trim() + // Warnings don't change stage, but emit as progress updates + if (cleanMessage) { + this.onProgress({ stage: 'collecting', message: cleanMessage, eventType: 'info' }) + } + } + + error(message: string): void { + const cleanMessage = message.replace(/^\[ERROR\]\s*/, '').trim() + this.onProgress({ stage: 'error', message: cleanMessage, eventType: 'info' }) + } + + debug(message: string): void { + if (this.verbose) { + const cleanMessage = message.replace(/^\[DEBUG\]\s*/, '').trim() + // Debug messages are emitted as progress updates during current stage + if (cleanMessage) { + this.onProgress({ stage: 'collecting', message: cleanMessage, eventType: 'info' }) + } + } + } + + emitStreamingExcerpt(excerpt: string, isReasoning = false): void { + // Excerpts are already trimmed to meaningful chunks (complete sentences or size limits) + // Just ensure they're not too long for display (safety check) + const maxDisplayLength = 250 + const displayExcerpt = excerpt.length > maxDisplayLength ? `${excerpt.slice(0, maxDisplayLength)}...` : excerpt + + // Emit as progress event during analyzing stage with reasoning flag and event type + this.onProgress({ + stage: 'analyzing', + message: displayExcerpt, + isReasoning, + eventType: isReasoning ? 'reasoning' : 'output', + }) + } + + /** + * Check if a message should be suppressed (not emitted as progress event) + */ + private shouldSuppress(message: string): boolean { + return message.includes('Scanning for') || message.includes('Starting repository analysis') + } + + /** + * Determine the progress stage based on message content + */ + private determineStage(message: string): ProgressEvent['stage'] { + // Stage transitions - check these first + if (message.includes('Collecting repository data')) { + return 'collecting' + } + if (message.includes('Running analysis') || message.includes('Analyzing with Claude')) { + return 'analyzing' + } + if (message.includes('Delivering to') || message.includes('Delivering results')) { + return 'delivering' + } + if (message.includes('Analysis complete')) { + return 'complete' + } + + // Batch progress updates + if ( + (message.includes('Fetched') && message.includes('PRs') && message.includes('batch')) || + message.includes('Successfully fetched') || + (message.includes('Found') && (message.includes('commits') || message.includes('pull requests'))) || + message.includes('Extracting PR information') || + message.includes('Getting commits for release') + ) { + return 'collecting' + } + + // Default to collecting stage for other INFO messages + return 'collecting' + } + + /** + * Parse cache hit information from log messages + */ + private parseCacheInfo(message: string): ProgressEvent['cacheInfo'] | undefined { + // Pattern: "Cache hit: Found X commits in cache" + // Pattern: "Cache hit: Found X PRs in cache" + const cacheHitMatch = message.match(/Cache hit: Found (\d+) (commits|PRs|pull requests) in cache/i) + if (cacheHitMatch) { + const count = parseInt(cacheHitMatch[1] || '0', 10) + const typeStr = cacheHitMatch[2]?.toLowerCase() || '' + + let type: 'commits' | 'prs' | 'stats' + if (typeStr.includes('commit')) { + type = 'commits' + } else if (typeStr.includes('pr') || typeStr.includes('pull request')) { + type = 'prs' + } else { + return undefined + } + + return { type, count } + } + + // Pattern: "Cache hit: Found X stats in cache" (if stats caching exists) + const statsCacheMatch = message.match(/Cache hit: Found (\d+) stats? in cache/i) + if (statsCacheMatch) { + return { + type: 'stats', + count: parseInt(statsCacheMatch[1] || '0', 10), + } + } + + return undefined + } +} diff --git a/apps/cli/src/lib/pr-body-cleaner.ts b/apps/cli/src/lib/pr-body-cleaner.ts new file mode 100644 index 00000000000..5213447eeb4 --- /dev/null +++ b/apps/cli/src/lib/pr-body-cleaner.ts @@ -0,0 +1,492 @@ +/** + * PR Body Cleaner + * + * Intelligently removes unnecessary content from PR bodies while preserving + * important technical details, code blocks, and CURSOR_SUMMARY blocks. + */ + +/** + * Cleans a PR body by removing unnecessary content while preserving valuable information + * @param body The raw PR body text + * @returns Cleaned PR body with unnecessary content removed + */ +export function cleanPRBody(body: string): string { + if (!body || body.trim().length === 0) { + return body + } + + let cleaned = body + + // Step 1: Extract CURSOR_SUMMARY content and remove all HTML comment markers + cleaned = removeHTMLCommentsExceptCursorSummary(cleaned) + + // Step 1.5: Remove Cursor Bugbot footer notes (may appear outside CURSOR_SUMMARY blocks) + cleaned = removeCursorBugbotFooters(cleaned) + + // Step 2: Remove image/video markdown + cleaned = removeImageVideoMarkdown(cleaned) + + // Step 3: Clean tables (remove if only images/videos) + cleaned = cleanTables(cleaned) + + // Step 4: Clean external links (keep text, remove long URLs) + cleaned = cleanExternalLinks(cleaned) + + // Step 5: Remove empty sections + cleaned = removeEmptySections(cleaned) + + // Step 6: Remove minimal value sections + cleaned = removeMinimalValueSections(cleaned) + + // Step 7: Remove redundant sections (screen captures, testing) + cleaned = removeRedundantSections(cleaned) + + // Step 8: Remove redundant headers + cleaned = removeRedundantHeaders(cleaned) + + // Step 9: Aggressive whitespace normalization (max 1 blank line) + cleaned = normalizeWhitespace(cleaned) + + return cleaned +} + +/** + * Extract CURSOR_SUMMARY content and remove all HTML comment markers + */ +function removeHTMLCommentsExceptCursorSummary(text: string): string { + // Extract CURSOR_SUMMARY content (without the comment markers) + const cursorSummaryPlaceholder = '___CURSOR_SUMMARY_PLACEHOLDER___' + const cursorSummaryRegex = /([\s\S]*?)/gi + const summaries: string[] = [] + let matchIndex = 0 + + // Extract just the content between the markers + let textWithProtection = text.replace(cursorSummaryRegex, (match, content) => { + // Clean the content before storing: remove Cursor Bugbot footer notes + let cleanedContent = content.trim() + + // Remove Cursor Bugbot footer notes (metadata lines) + cleanedContent = cleanedContent.replace( + />\s*\[!NOTE\]\s*\n\s*>\s*\[Cursor Bugbot\].*?Configure \[here\].*?<\/sup>/gi, + '', + ) + cleanedContent = cleanedContent.replace( + />\s*Written by \[Cursor Bugbot\].*?Configure \[here\].*?<\/sup>/gi, + '', + ) + cleanedContent = cleanedContent.replace( + />\s*\[Cursor Bugbot\].*?is generating a summary.*?Configure \[here\].*?<\/sup>/gi, + '', + ) + + // Clean up any leftover "> " prefixes from blockquotes + cleanedContent = cleanedContent.replace(/^>\s*/gm, '') + + summaries.push(cleanedContent.trim()) // Store cleaned content + return `${cursorSummaryPlaceholder}${matchIndex++}` + }) + + // Remove all other HTML comments + textWithProtection = textWithProtection.replace(//g, '') + + // Restore CURSOR_SUMMARY content (without comment markers and footers) + summaries.forEach((summary, index) => { + textWithProtection = textWithProtection.replace(`${cursorSummaryPlaceholder}${index}`, summary) + }) + + return textWithProtection +} + +/** + * Remove Cursor Bugbot footer notes (metadata lines) + */ +function removeCursorBugbotFooters(text: string): string { + let cleaned = text + + // Remove standalone footer notes (outside CURSOR_SUMMARY blocks) + // Pattern 1: > [!NOTE]\n> [Cursor Bugbot]...Configure [here]... + cleaned = cleaned.replace( + />\s*\[!NOTE\]\s*\n\s*>\s*\[Cursor Bugbot\][\s\S]*?Configure \[here\][\s\S]*?<\/sup>\s*/gi, + '', + ) + + // Pattern 2: > Written by [Cursor Bugbot]...Configure [here]... + cleaned = cleaned.replace(/>\s*Written by \[Cursor Bugbot\][\s\S]*?Configure \[here\][\s\S]*?<\/sup>\s*/gi, '') + + // Pattern 3: > [Cursor Bugbot]...is generating a summary...Configure [here]... + cleaned = cleaned.replace( + />\s*\[Cursor Bugbot\][\s\S]*?is generating a summary[\s\S]*?Configure \[here\][\s\S]*?<\/sup>\s*/gi, + '', + ) + + // Pattern 4: Standalone note blocks with Cursor Bugbot (any variant) + cleaned = cleaned.replace(/>\s*\[!NOTE\]\s*\n\s*>\s*\[Cursor Bugbot\][\s\S]*?<\/sup>\s*/gi, '') + + // Pattern 5: Any blockquote line containing Cursor Bugbot footer + cleaned = cleaned.replace(/>\s*\[Cursor Bugbot\][\s\S]*?<\/sup>\s*/gi, '') + + return cleaned +} + +/** + * Remove image and video markdown links + */ +function removeImageVideoMarkdown(text: string): string { + // Remove image markdown: ![alt](url) or ![alt](url "title") + let cleaned = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, '') + + // Remove video markdown patterns like [Screen Recording ...](url) + cleaned = cleaned.replace(/\[Screen Recording[^\]]*\]\([^)]+\)/gi, '') + cleaned = cleaned.replace(/\[Screen Recording[^\]]*\]\([^)]+\)/gi, '') + + // Remove tags + cleaned = cleaned.replace(/]*>/gi, '') + + // Remove video links that look like markdown + cleaned = cleaned.replace(/\[.*\.mov.*\]\([^)]+\)/gi, '') + cleaned = cleaned.replace(/\[.*\.mp4.*\]\([^)]+\)/gi, '') + + return cleaned +} + +/** + * Remove tables that only contain images/videos + */ +function cleanTables(text: string): string { + // Match table blocks - split into parts to avoid unsafe regex + const lines = text.split('\n') + const result: string[] = [] + let inTable = false + let tableLines: string[] = [] + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (!line) { + result.push('') + continue + } + + const isTableRow = line.trim().startsWith('|') && line.trim().endsWith('|') + + if (isTableRow) { + if (!inTable) { + inTable = true + tableLines = [] + } + tableLines.push(line) + } else { + if (inTable) { + // Process accumulated table + const tableMatch = tableLines.join('\n') + if (shouldRemoveTable(tableMatch)) { + // Skip this table + } else { + result.push(...tableLines) + } + inTable = false + tableLines = [] + } + result.push(line) + } + } + + // Handle table at end of text + if (inTable && tableLines.length > 0) { + const tableMatch = tableLines.join('\n') + if (!shouldRemoveTable(tableMatch)) { + result.push(...tableLines) + } + } + + return result.join('\n') +} + +function shouldRemoveTable(tableMatch: string): boolean { + // Remove empty tables (only separators like | --- | --- |) + const cleanedForEmpty = tableMatch.replace(/[\s|:-]/g, '') + if (cleanedForEmpty.length === 0) { + return true + } + + // Check if table only contains image/video links or empty cells + const imageVideoPattern = /!\[|\]\([^)]+\)|Screen Recording|\.mov|\.mp4|\.png|\.jpg|\.jpeg|\.gif|\.webp/gi + const hasOnlyImagesOrVideos = imageVideoPattern.test(tableMatch) + const textPattern = /[a-zA-Z]{3,}/ + const cleanedTable = tableMatch.replace(/!\[|\]\([^)]+\)|Screen Recording/gi, '') + const hasTextContent = textPattern.test(cleanedTable) + + // If table only has images/videos and no substantial text, remove it + return hasOnlyImagesOrVideos && !hasTextContent +} + +/** + * Clean external links - keep text but remove long URLs + */ +function cleanExternalLinks(text: string): string { + // Match markdown links: [text](url) + const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g + return text.replace(linkRegex, (match) => { + const linkMatch = match.match(/\[([^\]]+)\]\(([^)]+)\)/) + if (!linkMatch || !linkMatch[1] || !linkMatch[2]) { + return match + } + + const linkText = linkMatch[1] + const url = linkMatch[2] + + // Keep GitHub links (they're short and useful) + if (url.startsWith('https://github.com/') || url.startsWith('http://github.com/')) { + return match + } + + // Keep relative links + if (url.startsWith('/') || url.startsWith('./') || url.startsWith('../')) { + return match + } + + // Keep short URLs (< 50 chars) + if (url.length <= 50) { + return match + } + + // For long URLs, keep only the text + return linkText + }) +} + +/** + * Remove empty sections (headers with no content or only whitespace) + */ +function removeEmptySections(text: string): string { + // Match sections: ## Header followed by content until next ## or end + const sectionRegex = /(##+\s+[^\n]+)\n([\s\S]*?)(?=\n##+\s+|$)/g + let cleaned = text + + cleaned = cleaned.replace(sectionRegex, (match) => { + const sectionMatch = match.match(/(##+\s+[^\n]+)\n([\s\S]*?)(?=\n##+\s+|$)/) + if (!sectionMatch) { + return match + } + + const content = sectionMatch[2] || '' + // Check if content is empty or only whitespace + const trimmedContent = content.trim() + if (trimmedContent.length === 0) { + return '' // Remove empty section + } + return match // Keep section with content + }) + + return cleaned +} + +/** + * Remove minimal value sections (very short descriptions, redundant changes lists) + */ +function removeMinimalValueSections(text: string): string { + let cleaned = text + + // Remove "## Description" sections that are very short and redundant + const descriptionRegex = /##\s+Description\s*\n([\s\S]*?)(?=\n## |$)/gi + cleaned = cleaned.replace(descriptionRegex, (match) => { + const sectionMatch = match.match(/##\s+Description\s*\n([\s\S]*?)(?=\n## |$)/i) + if (!sectionMatch) { + return match + } + + const content = sectionMatch[1] || '' + const trimmedContent = content.trim() + + // Remove if very short (< 50 chars) and doesn't add value + if (trimmedContent.length < 50) { + return '' + } + + return match + }) + + // Remove "## Changes" sections that are just bullet lists without detail + const changesRegex = /##\s+Changes\s*\n([\s\S]*?)(?=\n## |$)/gi + cleaned = cleaned.replace(changesRegex, (match) => { + const sectionMatch = match.match(/##\s+Changes\s*\n([\s\S]*?)(?=\n## |$)/i) + if (!sectionMatch) { + return match + } + + const content = sectionMatch[1] || '' + const trimmedContent = content.trim() + + // Check if it's just a list of very short bullets + const lines = trimmedContent.split('\n').filter((line) => line.trim().length > 0) + const allShortBullets = lines.every((line) => { + const trimmed = line.trim() + return trimmed.startsWith('-') && trimmed.length < 80 + }) + + // Remove if all bullets are very short (likely redundant with CURSOR_SUMMARY) + if (allShortBullets && lines.length < 5) { + return '' + } + + return match + }) + + // Remove "## Implementation Details" that are redundant with CURSOR_SUMMARY + const implDetailsRegex = /##\s+Implementation Details\s*\n([\s\S]*?)(?=\n## |$)/gi + cleaned = cleaned.replace(implDetailsRegex, (match) => { + const sectionMatch = match.match(/##\s+Implementation Details\s*\n([\s\S]*?)(?=\n## |$)/i) + if (!sectionMatch) { + return match + } + + const content = sectionMatch[1] || '' + const trimmedContent = content.trim() + + // Remove if very short (< 100 chars) - likely redundant + if (trimmedContent.length < 100) { + return '' + } + + return match + }) + + return cleaned +} + +/** + * Remove redundant headers (headers with no meaningful content after them) + */ +function removeRedundantHeaders(text: string): string { + let cleaned = text + + // Match sections and check if header should be removed + const sectionRegex = /(##+\s+[^\n]+)\n([\s\S]*?)(?=\n##+\s+|$)/g + + cleaned = cleaned.replace(sectionRegex, (match) => { + const sectionMatch = match.match(/(##+\s+[^\n]+)\n([\s\S]*?)(?=\n##+\s+|$)/) + if (!sectionMatch) { + return match + } + + const header = sectionMatch[1] || '' + const content = sectionMatch[2] || '' + const trimmedContent = content.trim() + + // If header is followed immediately by CURSOR_SUMMARY-like content and nothing else + // Check if content starts with --- (CURSOR_SUMMARY marker pattern) + if (trimmedContent.startsWith('---') && trimmedContent.length < 200) { + // This might be redundant, but let's be conservative and keep it + return match + } + + // Remove headers that are redundant (e.g., "## Description" when content is minimal) + const headerLower = header.toLowerCase() + if (headerLower.includes('description') && trimmedContent.length < 30) { + // Keep the content, remove the header + return trimmedContent + } + + return match + }) + + return cleaned +} + +/** + * Remove redundant sections + */ +function removeRedundantSections(text: string): string { + let cleaned = text + + // Remove "Screen Captures" or "Screenshots" sections that only contain images + // Match: ## Screen Captures / ## Screenshots followed by content ending before next ## + const screenshotSectionRegex = /##\s+(Screen Captures|Screenshots|Screenshots?)\s*\n([\s\S]*?)(?=\n## |$)/gi + cleaned = cleaned.replace(screenshotSectionRegex, (match) => { + const sectionMatch = match.match(/##\s+(Screen Captures|Screenshots|Screenshots?)\s*\n([\s\S]*?)(?=\n## |$)/i) + if (!sectionMatch) { + return match + } + + const content = sectionMatch[2] || '' + // If content only has images/videos/tables with images, remove it + const mediaPattern = /!\[|\]\([^)]+\)|Screen Recording|\.mov|\.mp4| { + const sectionMatch = match.match(/##\s+(Testing|How Has This Been Tested\?)\s*\n([\s\S]*?)(?=\n## |$)/i) + if (!sectionMatch) { + return match + } + + const content = sectionMatch[2] || '' + const trimmedContent = content.trim() + + // Remove if empty + if (trimmedContent.length === 0) { + return '' + } + + // Remove if only checkboxes with no descriptions + const checkboxOnly = /^[\s-]*\[[ xX]\]/m.test(trimmedContent) && trimmedContent.length < 100 + if (checkboxOnly) { + return '' + } + + // Remove if content is just minimal phrases like "locally", "manually", "on simulator" + const minimalPhrases = + /^(locally|manually|on simulator|tested locally|tested manually|local|ios simulator|android sim)[\s.]*$/i + if (minimalPhrases.test(trimmedContent)) { + return '' + } + + // Remove if content is very short (< 50 chars) and doesn't contain meaningful text + const meaningfulTextPattern = /[a-zA-Z]{10,}/ + if (trimmedContent.length < 50 && !meaningfulTextPattern.test(trimmedContent)) { + return '' + } + + // Keep if it has meaningful content (> 100 chars of actual text) + const textOnly = trimmedContent.replace(/[\s\-[\]xX]/g, '') + if (textOnly.length < 100) { + return '' + } + + return match + }) + + return cleaned +} + +/** + * Normalize whitespace - aggressive cleanup + */ +function normalizeWhitespace(text: string): string { + // Collapse multiple blank lines to max 1 consecutive blank line + let cleaned = text.replace(/\n{3,}/g, '\n\n') + + // Trim trailing whitespace from lines + cleaned = cleaned + .split('\n') + .map((line) => line.trimEnd()) + .join('\n') + + // Remove leading/trailing blank lines + cleaned = cleaned.replace(/^\n+|\n+$/g, '') + + // Remove blank lines immediately after headers + cleaned = cleaned.replace(/(##+\s+[^\n]+)\n\n+/g, '$1\n') + + return cleaned +} diff --git a/apps/cli/src/lib/release-scanner.ts b/apps/cli/src/lib/release-scanner.ts new file mode 100644 index 00000000000..3a268135da2 --- /dev/null +++ b/apps/cli/src/lib/release-scanner.ts @@ -0,0 +1,283 @@ +/* eslint-disable no-console */ + +import type { Logger } from '@universe/cli/src/lib/logger' +import { $ } from 'bun' + +// ============================================================================ +// Types +// ============================================================================ + +export interface Release { + platform: 'mobile' | 'extension' + version: string + branch: string + major: number + minor: number + patch: number + prerelease?: string +} + +export interface ReleaseComparison { + from: Release + to: Release + commitRange: string +} + +// ============================================================================ +// Release Scanner +// ============================================================================ + +export class ReleaseScanner { + constructor( + private repoPath: string = process.cwd(), + private logger?: Logger, + ) {} + + /** + * Scan all release branches for a given platform + */ + async scanReleases(platform?: 'mobile' | 'extension'): Promise { + this.logger?.info(`Scanning for ${platform || 'all'} release branches...`) + + // Get all remote branches + const result = await $`git -C ${this.repoPath} branch -r`.text() + const branches = result + .split('\n') + .map((b: string) => b.trim()) + .filter(Boolean) + + // Filter for release branches + const releasePattern = platform ? `origin/releases/${platform}/` : 'origin/releases/' + + const releaseBranches = branches + .filter((b: string) => b.includes(releasePattern)) + .filter((b: string) => !b.includes('->')) // Exclude symbolic refs + .filter((b: string) => !b.includes('/dev')) // Exclude dev branches + .filter((b: string) => !b.match(/cherry|kickstart|mirror|temp|mp\//)) // Exclude special branches + + // Parse into Release objects + const releases: Release[] = [] + + for (const branch of releaseBranches) { + const release = this.parseReleaseBranch(branch) + if (release) { + releases.push(release) + } + } + + // Sort by version (newest first) + return releases.sort((a, b) => this.compareVersions(b, a)) + } + + /** + * Get the latest release for a platform + */ + async getLatestRelease(platform: 'mobile' | 'extension'): Promise { + const releases = await this.scanReleases(platform) + return releases[0] || null + } + + /** + * Get the previous release before a given version + */ + async getPreviousRelease(release: Release): Promise { + const releases = await this.scanReleases(release.platform) + + // Find the current release index + const currentIndex = releases.findIndex((r) => r.version === release.version && r.platform === release.platform) + + if (currentIndex === -1 || currentIndex === releases.length - 1) { + return null + } + + // Return the next one (which is older since we sorted newest first) + const nextRelease = releases[currentIndex + 1] + return nextRelease ?? null + } + + /** + * Find a specific release by platform and version + */ + async findRelease(platform: 'mobile' | 'extension', version: string): Promise { + const releases = await this.scanReleases(platform) + return releases.find((r) => r.version === version) || null + } + + async getReleaseComparison(args: { + platform: 'mobile' | 'extension' + version: string + compareWith?: string + }): Promise { + const { platform, version, compareWith } = args + const toRelease = await this.findRelease(platform, version) + if (!toRelease) { + throw new Error(`Release ${platform}/${version} not found`) + } + + let fromRelease: Release | null = null + + if (compareWith) { + fromRelease = await this.findRelease(platform, compareWith) + if (!fromRelease) { + throw new Error(`Release ${platform}/${compareWith} not found`) + } + } else { + // Auto-detect previous release + fromRelease = await this.getPreviousRelease(toRelease) + if (!fromRelease) { + this.logger?.warn(`No previous release found for ${platform}/${version}`) + return null + } + } + + // Use origin/ prefix for git commands + return { + from: fromRelease, + to: toRelease, + commitRange: `origin/${fromRelease.branch}..origin/${toRelease.branch}`, + } + } + + /** + * List releases in a formatted way + */ + async listReleases(platform?: 'mobile' | 'extension'): Promise { + const releases = await this.scanReleases(platform) + + if (releases.length === 0) { + console.log('No releases found') + return + } + + // Group by platform if showing all + const grouped = releases.reduce( + (acc, release) => { + if (!acc[release.platform]) { + acc[release.platform] = [] + } + const platformReleases = acc[release.platform] + if (platformReleases) { + platformReleases.push(release) + } + return acc + }, + {} as Record, + ) + + for (const [plat, rels] of Object.entries(grouped)) { + console.log(`\n${plat.toUpperCase()} RELEASES:`) + + console.log('─'.repeat(40)) + + for (const rel of rels.slice(0, 10)) { + // Show only latest 10 + + console.log(` ${rel.version.padEnd(10)} → ${rel.branch}`) + } + + if (rels.length > 10) { + console.log(` ... and ${rels.length - 10} more`) + } + } + } + + /** + * Get commits between two releases + */ + async getReleaseCommits(comparison: ReleaseComparison): Promise { + const result = await $`git -C ${this.repoPath} log ${comparison.commitRange} --oneline`.text() + return result + } + + /** + * Parse a release branch name into a Release object + */ + private parseReleaseBranch(branch: string): Release | null { + // Match patterns like: + // origin/releases/mobile/1.60 + // origin/releases/extension/1.30.0 + // Safe regex pattern - matches controlled git branch names only + // eslint-disable-next-line security/detect-unsafe-regex -- Controlled pattern matching git branch names with bounded quantifiers + const match = branch.match(/^origin\/releases\/(mobile|extension)\/(\d+)\.(\d+)(?:\.(\d+))?(?:\.(.+))?$/) + + if (!match) { + return null + } + + const [, platform, major, minor, patch, prerelease] = match + if (!platform || !major || !minor) { + return null + } + const version = patch + ? `${major}.${minor}.${patch}${prerelease ? `.${prerelease}` : ''}` + : `${major}.${minor}${prerelease ? `.${prerelease}` : ''}` + + return { + platform: platform as 'mobile' | 'extension', + version, + branch: branch.replace('origin/', ''), + major: parseInt(major, 10), + minor: parseInt(minor, 10), + patch: parseInt(patch ?? '0', 10), + prerelease: prerelease ?? undefined, + } + } + + /** + * Compare two versions (returns positive if a > b, negative if a < b, 0 if equal) + */ + private compareVersions(a: Release, b: Release): number { + // Compare major + if (a.major !== b.major) { + return a.major - b.major + } + + // Compare minor + if (a.minor !== b.minor) { + return a.minor - b.minor + } + + // Compare patch + if (a.patch !== b.patch) { + return a.patch - b.patch + } + + // Compare prerelease (if both have it) + if (a.prerelease && b.prerelease) { + return a.prerelease.localeCompare(b.prerelease) + } + + // Version without prerelease is greater than with prerelease + if (a.prerelease && !b.prerelease) { + return -1 + } + if (!a.prerelease && b.prerelease) { + return 1 + } + + return 0 + } +} + +/** + * Parse a release identifier like "mobile/1.60" or "extension/1.30.0" + */ +export function parseReleaseIdentifier( + identifier: string, +): { platform: 'mobile' | 'extension'; version: string } | null { + const match = identifier.match(/^(mobile|extension)\/(.+)$/) + if (!match) { + return null + } + + const platform = match[1] as 'mobile' | 'extension' + const version = match[2] + if (!version) { + return null + } + + return { + platform, + version, + } +} diff --git a/apps/cli/src/lib/stream-handler.ts b/apps/cli/src/lib/stream-handler.ts new file mode 100644 index 00000000000..9a262b12e25 --- /dev/null +++ b/apps/cli/src/lib/stream-handler.ts @@ -0,0 +1,35 @@ +/** biome-ignore-all lint/suspicious/noConsole: cli tool */ +import type { StreamChunk } from '@universe/cli/src/lib/ai-provider' + +/** + * Stream Handler for CLI Output + * + * Separates the concern of handling streaming output to console. + * Accumulates full response while streaming to stdout for user feedback. + */ + +/** + * Writes stream chunks to console and accumulates full response + * @param stream - Async generator of stream chunks + * @returns Complete accumulated text + */ +export async function writeStreamToConsole(stream: AsyncGenerator): Promise { + let fullText = '' + + for await (const chunk of stream) { + if (chunk.text) { + fullText += chunk.text + // Write to stdout for real-time feedback + process.stdout.write(chunk.text) + } + + if (chunk.isComplete) { + // New line after streaming completes + // eslint-disable-next-line no-console + console.log() + break + } + } + + return fullText +} diff --git a/apps/cli/src/lib/team-members.ts b/apps/cli/src/lib/team-members.ts new file mode 100644 index 00000000000..91a7fb39f04 --- /dev/null +++ b/apps/cli/src/lib/team-members.ts @@ -0,0 +1,37 @@ +import { $ } from 'bun' + +export interface TeamMember { + login: string + name: string | null +} + +/** + * Fetches team members from a GitHub organization team + * Returns members with their login and display name + */ +export async function fetchTeamMembers(org: string, teamSlug: string): Promise { + try { + // Get team members (just logins) + const membersResult = await $`gh api /orgs/${org}/teams/${teamSlug}/members --jq '.[].login'`.text() + const logins = membersResult.split('\n').filter(Boolean) + + // Fetch detailed user info for each member + const members: TeamMember[] = [] + for (const login of logins) { + try { + const userResult = await $`gh api /users/${login} --jq '{login: .login, name: .name}'`.text() + const userData = JSON.parse(userResult) as TeamMember + members.push(userData) + } catch { + // If fetching user details fails, just use the login + members.push({ login, name: null }) + } + } + + return members + } catch (error) { + throw new Error(`Failed to fetch members for team ${teamSlug}. Ensure gh CLI is authenticated and team exists.`, { + cause: error, + }) + } +} diff --git a/apps/cli/src/lib/team-resolver.ts b/apps/cli/src/lib/team-resolver.ts new file mode 100644 index 00000000000..b765f94c146 --- /dev/null +++ b/apps/cli/src/lib/team-resolver.ts @@ -0,0 +1,156 @@ +import { $ } from 'bun' + +interface UserResolution { + username?: string + emails: string[] +} + +interface GitHubCommitSearchItem { + commit?: { + author?: { + email?: string + } + } +} + +interface GitHubCommitSearchResult { + items?: GitHubCommitSearchItem[] +} + +interface GitHubUserData { + id?: number + email?: string + login?: string +} + +/** + * Resolves a team reference or username to email addresses + * Supports: + * - GitHub teams: @org/team + * - GitHub usernames: alice, bob + * - Email addresses: alice@example.com + * - Mixed: @org/team,alice,bob@example.com + */ +export async function resolveTeam(teamRef: string): Promise<{ emails: string[]; usernames: string[] }> { + const parts = teamRef + .split(',') + .map((p) => p.trim()) + .filter(Boolean) + const allEmails: string[] = [] + const allUsernames: string[] = [] + + for (const part of parts) { + if (part.startsWith('@')) { + // GitHub team reference + const { emails, usernames } = await resolveGitHubTeam(part) + allEmails.push(...emails) + allUsernames.push(...usernames) + } else if (part.includes('@')) { + // Already an email + allEmails.push(part) + } else { + // GitHub username + const resolution = await resolveUserToEmail(part) + allEmails.push(...resolution.emails) + if (resolution.username) { + allUsernames.push(resolution.username) + } + } + } + + return { + emails: [...new Set(allEmails)], // Remove duplicates + usernames: [...new Set(allUsernames)], + } +} + +async function resolveGitHubTeam(teamRef: string): Promise<{ emails: string[]; usernames: string[] }> { + // Parse @org/team format + const [org, team] = teamRef.slice(1).split('/') + + if (!org || !team) { + throw new Error(`Invalid team reference: ${teamRef}. Expected format: @org/team`) + } + + try { + // Get team members + const membersResult = await $`gh api /orgs/${org}/teams/${team}/members --jq '.[].login'`.text() + const members = membersResult.split('\n').filter(Boolean) + + // Resolve each member to emails + const emails: string[] = [] + const usernames: string[] = [] + + for (const member of members) { + const resolution = await resolveUserToEmail(member) + emails.push(...resolution.emails) + if (resolution.username) { + usernames.push(resolution.username) + } + } + + return { emails, usernames } + } catch (_error) { + throw new Error(`Failed to resolve team ${teamRef}. Ensure gh CLI is authenticated and team exists.`) + } +} + +async function resolveUserToEmail(user: string): Promise { + // If it contains @, it's already an email + if (user.includes('@')) { + return { emails: [user] } + } + + // Otherwise, treat it as a GitHub username + try { + const userDataResult = await $`gh api /users/${user}`.text() + const userData = JSON.parse(userDataResult) as GitHubUserData + + // Get user's email from their profile (if public) + const emails: string[] = [] + if (userData.email) { + emails.push(userData.email) + } + + // Also try to get commit email by searching for their commits + try { + const searchResult = await $`gh api /search/commits?q=author:${user}&per_page=5`.text() + const searchData = JSON.parse(searchResult) as GitHubCommitSearchResult + + if (searchData.items && searchData.items.length > 0) { + const commitEmails = searchData.items + .map((item) => item.commit?.author?.email) + .filter((email): email is string => typeof email === 'string' && !emails.includes(email)) + + emails.push(...commitEmails) + } + } catch { + // Commit search failed, continue with what we have + } + + if (emails.length === 0 && userData.id) { + // Fallback: use GitHub's noreply email format + emails.push(`${userData.id}+${user}@users.noreply.github.com`) + } + + return { username: user, emails } + } catch (_error) { + throw new Error(`Failed to resolve GitHub username "${user}". User may not exist or gh CLI is not configured.`) + } +} + +/** + * Detects repository from git remote + */ +export async function detectRepository(): Promise<{ owner: string; name: string } | null> { + try { + const remote = await $`git config --get remote.origin.url`.text() + const match = remote.trim().match(/github\.com[:/]([^/]+)\/([^/.]+)(\.git)?$/) + if (match?.[1] && match[2]) { + return { owner: match[1], name: match[2] } + } + } catch { + // Not a git repo or no remote configured + } + return null +} diff --git a/apps/cli/src/lib/trivial-files.ts b/apps/cli/src/lib/trivial-files.ts new file mode 100644 index 00000000000..cd4b0b814d9 --- /dev/null +++ b/apps/cli/src/lib/trivial-files.ts @@ -0,0 +1,74 @@ +/** + * Utility for identifying trivial files that should be filtered from changelog analysis + */ + +const TRIVIAL_PATTERNS = [ + // Lockfiles + /package-lock\.json$/, + /yarn\.lock$/, + /pnpm-lock\.yaml$/, + /bun\.lockb$/, + /Cargo\.lock$/, + /Gemfile\.lock$/, + /composer\.lock$/, + /poetry\.lock$/, + + // Snapshots + /\.snap$/, + /\.snap\.\w+$/, + /\/__snapshots__\//, + /\.snapshot$/, + /\.snapshot\.json$/, + + // Generated files + /\.generated\./, + /\/__generated__\//, + /\/generated\//, + /codegen\//, + + // Build artifacts + /^dist\//, + /^build\//, + /\.next\//, + /\.turbo\//, + /^out\//, + + // Test artifacts + /^coverage\//, + /\.lcov$/, + /\.nyc_output\//, + /test-results\//, + + // Large data files + /fixtures\//, + /\/__fixtures__\//, + /testdata\//, + + // Binary and media files + /\.(png|jpg|jpeg|gif|ico|svg|webp|pdf|zip|tar|gz)$/i, + + // IDE and OS files + /\.DS_Store$/, + /Thumbs\.db$/, + /\.swp$/, + /\.swo$/, + + // Other + /node_modules\//, + /vendor\//, + /\.pnp\./, +] + +/** + * Check if a file path represents a trivial file that should be filtered + */ +export function isTrivialFile(path: string): boolean { + return TRIVIAL_PATTERNS.some((pattern) => pattern.test(path)) +} + +/** + * Filter an array of file paths to exclude trivial files + */ +export function filterTrivialFiles(paths: string[]): string[] { + return paths.filter((path) => !isTrivialFile(path)) +} diff --git a/apps/cli/src/prompts/bug-bisect.md b/apps/cli/src/prompts/bug-bisect.md new file mode 100644 index 00000000000..fbec2db123e --- /dev/null +++ b/apps/cli/src/prompts/bug-bisect.md @@ -0,0 +1,83 @@ +You are analyzing commits in a release to identify which commit likely introduced a bug. Your goal is to carefully examine all commits and PRs in the release range and rank them by likelihood of introducing the bug described. + +## Bug Description + +{{BUG_DESCRIPTION}} + +## Release Context + +- **Platform:** {{PLATFORM}} +- **Release:** {{RELEASE_TO}} +- **Comparing with:** {{RELEASE_FROM}} + +## Your Task + +Analyze ALL commits and pull requests in the release range. For each commit/PR, determine how likely it is that it introduced the bug described above. Consider: + +1. **Direct relevance**: Does the commit modify code that directly relates to the bug description? +2. **Indirect impact**: Could changes in this commit cause side effects that lead to the bug? +3. **Pattern matching**: Do file paths, function names, or component names match keywords in the bug description? +4. **Timing**: If the bug appeared in this release, commits in this range are prime suspects +5. **Related PRs**: Multiple commits from the same PR may be related and should be considered together + +## Output Format + +You MUST return a valid JSON object with the following structure: + +```json +{ + "suspiciousCommits": [ + { + "sha": "full commit SHA", + "confidence": 0.85, + "reasoning": "Brief explanation of why this commit is suspicious, mentioning specific files/functions/modules changed that relate to the bug", + "relatedPR": 1234 + } + ], + "summary": "Brief summary of findings: how many commits analyzed, how many suspicious commits found, and overall assessment", + "totalCommitsAnalyzed": 247, + "releaseContext": { + "from": "{{RELEASE_FROM}}", + "to": "{{RELEASE_TO}}", + "platform": "{{PLATFORM}}" + } +} +``` + +## Requirements + +1. **Rank commits by confidence**: Order `suspiciousCommits` array from highest to lowest confidence +2. **Confidence scores**: Use 0.0-1.0 scale where: + - 0.9-1.0: Very likely culprit (direct match, clear causation) + - 0.7-0.9: Likely related (strong indirect connection) + - 0.5-0.7: Possibly related (weak connection, worth investigating) + - < 0.5: Unlikely (exclude from results) +3. **Return top 10-20 commits**: Focus on the most suspicious commits, not all commits +4. **Include reasoning**: Each commit must have a clear explanation of why it's suspicious +5. **Match PRs**: If a commit is part of a PR, include the PR number in `relatedPR` +6. **Be specific**: Reference specific files, functions, or components in your reasoning + +## Analysis Process + +Before generating your output, analyze the commit data systematically: + +1. **Scan for keywords**: Look for file paths, function names, or component names that match keywords in the bug description +2. **Review PR descriptions**: PR bodies often contain context about what changed and why +3. **Check related commits**: Commits that touch similar files or components may be related +4. **Consider the full context**: Sometimes the bug is caused by an interaction between multiple changes + +## Important Notes + +- Analyze ALL commits provided, even if the context is truncated due to token limits +- If multiple commits from the same PR are suspicious, include them all but note they're related +- Be thorough but focused - prioritize commits with the strongest connection to the bug +- Your reasoning should help developers quickly understand why each commit is suspicious + +Here is the commit data you need to analyze: + + +{{COMMIT_DATA}} + + +Now analyze the commits and return your JSON response with ranked suspicious commits. + diff --git a/apps/cli/src/prompts/release-changelog.md b/apps/cli/src/prompts/release-changelog.md new file mode 100644 index 00000000000..d1306c92c52 --- /dev/null +++ b/apps/cli/src/prompts/release-changelog.md @@ -0,0 +1,74 @@ +You are creating a technical changelog for engineers at Uniswap Labs. Your goal is to analyze commit data and write a changelog that explains what shipped to colleagues in a direct, conversational, and factual manner - similar to Linear's changelog style. + +**CRITICAL: Focus on what actually changed, not editorial judgments** +- Describe the specific changes that were made +- Avoid inferential language like "finally," "now works properly," or "is finally real" +- Don't make assumptions about timeline, quality, or significance +- State what changed factually without commentary + +Before writing the changelog, do your analysis and planning work in tags inside your thinking block. It's OK for this section to be quite long. Include: + +1. **Commit Extraction**: First, go through the commit data and list out all the key commits with their PR numbers and descriptions to keep them top of mind. + +2. **Pattern Identification**: Read through all the commits and identify major themes or areas of work (aim for 4-7 themes). Look for related PRs that address similar functionality, components, or types of changes. + +3. **Grouping Strategy**: For each theme you identify, list which specific PRs belong to it and what the common thread is. + +4. **Factual Focus**: For each group, identify the specific technical changes made without making inferences about their importance or timeline. + +5. **Structure Planning**: Plan how you'll organize each theme section and what specific language you'll use - some may need more technical detail, others may be straightforward. + +After your planning, write the changelog using this exact structure: + +## Release Overview +- [X] PRs, [Y] contributors +- [One paragraph listing main work areas. Keep it factual and brief.] + +## Major Themes + +For each major pattern (4-7 themes total): + +### [Direct, Clear Theme Name] + +[2-3 paragraphs explaining what changed. Focus on the actual modifications made to the codebase. Vary your structure - not every section needs the same format. Mix technical details with broader changes as appropriate.] + +
+All related PRs (X total) + +- #123: Brief description +- #124: What changed +- #125: Technical detail +[... all PRs for this theme ...] +
+ +**Contributors:** @name1, @name2, @name3 + +**Writing Guidelines:** +- Write like a human, not a content generator +- Vary sentence starters - don't always begin with "The team" or "This release" +- Use present tense for current state: "The extension locks automatically after..." +- Be conversational but professional: "The old `isAddress()` function is gone - replaced with explicit validators" +- Include technical specifics when developers would care about them (function names, specific fixes, workarounds) +- Natural transitions or none at all - sometimes jump between topics +- Some features get detailed explanations, others just need a line or two + +**Avoid:** +- Editorial language: "smart decisions," "clever solutions," "finally," "now works properly" +- Justification: "This is important because..." +- Hedging: "presumably," "apparently" +- Obvious transitions: "Worth noting," "It's important to mention," "The interesting part" + +**Include When Relevant:** +- Specific function names, APIs, or technical components that changed +- Workarounds or gotchas developers should know about +- Patterns that are being reused across the codebase +- Platform-specific considerations +- Breaking changes or migrations + +Your final output should consist only of the changelog in the specified format and should not duplicate or rehash any of the analysis and planning work you did in the thinking block. + +Here is the commit data you need to analyze: + + +{{COMMIT_DATA}} + diff --git a/apps/cli/src/prompts/team-digest.md b/apps/cli/src/prompts/team-digest.md new file mode 100644 index 00000000000..d3271fa0c53 --- /dev/null +++ b/apps/cli/src/prompts/team-digest.md @@ -0,0 +1,102 @@ +You will analyze repository commit data to create a team digest for engineers at Uniswap Labs. Your goal is to transform raw development activity into a readable summary that explains what the team accomplished during a specific time period. + +## Your Task + +Create a structured team digest that describes what was built, added, or modified based on the commit data. Write factually about the work completed without making assumptions about completion status or quality. + +## Critical Requirements + +Follow these requirements strictly: + +1. **Use GitHub usernames only** - Format as @username, never use or invent human names +2. **Describe changes factually** - State what was built/added/modified without making quality judgments +3. **Avoid completion language** - Do not use "complete," "finished," "fully implemented," or "comprehensive" since you cannot determine completion status from commit messages alone +4. **Avoid embellished language** - Do not use "significantly improved," "enhanced," or "optimized" unless these are objectively measurable facts explicitly stated in the commit messages +5. **Right-size technical detail** - Include key components and patterns, skip implementation minutiae +6. **Focus on outcomes first** - Lead with what changed for users/developers, then explain how +7. **Minimize code content** - Be very selective about including code snippets or technical implementation details. Focus on impact and readability. Only include essential code-related information when truly necessary for understanding the work's significance +8. **Improve readability** - Limit to 4-6 themes maximum to avoid monotony. Vary your sentence structure and integrate contributor mentions naturally rather than starting every paragraph with "@person did xyz" + +## Analysis Process + +Before writing your digest, complete a thorough analysis inside tags within your thinking block. It's OK for this section to be quite long. Include: + +1. **Extract All Commit Messages**: Quote every single commit message verbatim from the data, one by one +2. **Extract Contributors**: Systematically go through the data and list all GitHub usernames (format as @username), counting them as you go +3. **Group into 4-6 Themes**: Organize commits into coherent themes based on functionality or area of work. For each theme: + - Theme name + - Specific commits that belong (quote relevant messages verbatim) + - Contributors who worked on it +4. **Plan Technical Details**: For each theme, identify: + - Main outcome or change (focus on impact, not implementation) + - 1-2 key technical details worth mentioning only if they help explain impact + - Avoid code snippets unless absolutely essential +5. **Structure Planning**: Write your planned theme names and brief descriptions +6. **Readability Check**: Review your planned descriptions to ensure you: + - Have 4-6 themes maximum for better flow + - Vary sentence structure (don't start every paragraph with contributor names) + - Integrate contributor mentions naturally throughout the narrative + - State facts from commits without making quality assessments + - Avoid completion language + - Focus on outcomes over technical implementation +7. **Requirements Verification**: Systematically check each planned theme description against all 8 critical requirements listed above, going through them one by one to ensure compliance + +## Output Structure + +Write your digest following this exact structure: + +### Team Digest: [Date Range] + +[Opening paragraph: Main areas of work. Be specific but concise - 2-3 sentences maximum.] + +### What Was Shipped + +For each theme (4-6 total): + +#### [Clear Theme Name] + +[2-3 paragraphs explaining: +- Paragraph 1: What was built and why it matters (outcome and impact) +- Paragraph 2: Key approach - mention important components only when relevant for understanding impact +- Paragraph 3 (if needed): Integration points or collaboration details + +Mention contributors naturally throughout, varying sentence structure] + +### Technical Highlights + +[3-5 bullet points of technically interesting work that other engineers would want to know about. Focus on reusable patterns, architectural decisions, or important changes. Only include items that add significant value and aren't duplicative of what was stated above.] + +## Example Output Structure + +### Team Digest: March 1-15, 2024 + +The team focused on expanding notification capabilities, refactoring data layer components, and improving test infrastructure. + +### What Was Shipped + +#### Notification System Development + +A new notification system now polls the backend and manages dismissal state locally, allowing users to receive timely updates about important events. The system provides options for different notification types and integrates with existing user preferences through the settings panel. + +Working primarily on the core functionality, @alice built the polling mechanism and state management, while @bob handled the integration work that allows users to configure which notifications they receive and how they're delivered. + +#### Data Layer Refactoring + +The data access layer underwent restructuring with multiple API clients consolidated into a unified service. This change provides more consistent error handling and simplifies how the application manages data across different features. + +@charlie led this refactoring effort, which resulted in a unified error handling pattern that reduces code duplication across components. + +### Technical Highlights + +• New notification provider can be easily integrated into other applications +• Unified error handling pattern reduces code duplication across components +• Test suite now runs 40% faster due to infrastructure improvements +• New data fetching approach simplifies component logic and improves testability + +Your final output should consist only of the structured team digest following the format above, without duplicating or rehashing any of the analysis work you completed in your thinking block. + +Here is the commit data you need to analyze: + + +{{COMMIT_DATA}} + diff --git a/apps/cli/src/ui/App.tsx b/apps/cli/src/ui/App.tsx new file mode 100644 index 00000000000..e0ddb98230c --- /dev/null +++ b/apps/cli/src/ui/App.tsx @@ -0,0 +1,175 @@ +import type { OrchestratorConfig } from '@universe/cli/src/core/orchestrator' +import type { Release } from '@universe/cli/src/lib/release-scanner' +import { AppStateProvider, type TeamFilter, useAppState } from '@universe/cli/src/ui/hooks/useAppState' +import { BugBisectResultsScreen } from '@universe/cli/src/ui/screens/BugBisectResultsScreen' +import { BugInputScreen } from '@universe/cli/src/ui/screens/BugInputScreen' +import { ConfigReview } from '@universe/cli/src/ui/screens/ConfigReview' +import { ExecutionScreen } from '@universe/cli/src/ui/screens/ExecutionScreen' +import { ReleaseSelector } from '@universe/cli/src/ui/screens/ReleaseSelector' +import { ResultsScreen } from '@universe/cli/src/ui/screens/ResultsScreen' +import { TeamSelectorScreen } from '@universe/cli/src/ui/screens/TeamSelectorScreen' +import { WelcomeScreen } from '@universe/cli/src/ui/screens/WelcomeScreen' +import { useCallback, useEffect } from 'react' + +function AppContent(): JSX.Element { + const { state, dispatch } = useAppState() + + // Clear terminal on initial mount + useEffect(() => { + process.stdout.write('\x1Bc') // VT100 clear screen and scrollback + }, []) + const handleWelcomeContinue = (mode: 'release-changelog' | 'team-digest' | 'changelog' | 'bug-bisect'): void => { + // Route based on analysis mode + switch (mode) { + case 'release-changelog': + dispatch({ type: 'SET_SCREEN', screen: 'release-select' }) + break + case 'bug-bisect': + dispatch({ type: 'SET_SCREEN', screen: 'release-select' }) + break + case 'team-digest': + dispatch({ type: 'SET_SCREEN', screen: 'team-select' }) + break + case 'changelog': + // Skip to config review for custom analysis + dispatch({ type: 'SET_SCREEN', screen: 'config-review' }) + break + } + } + + const handleReleaseSelect = (_release: Release, _comparison: Release | null): void => { + // If bug-bisect mode, go to bug input screen; otherwise go to config review + if (state.analysisMode === 'bug-bisect') { + dispatch({ type: 'SET_SCREEN', screen: 'bug-input' }) + } else { + dispatch({ type: 'SET_SCREEN', screen: 'config-review' }) + } + } + + const handleBugInputContinue = (): void => { + dispatch({ type: 'SET_SCREEN', screen: 'config-review' }) + } + + const handleBugInputBack = (): void => { + dispatch({ type: 'SET_SCREEN', screen: 'release-select' }) + } + + const handleTeamSelect = (_teamFilter: TeamFilter | null): void => { + dispatch({ type: 'SET_SCREEN', screen: 'config-review' }) + } + + const handleConfigConfirm = (config: OrchestratorConfig): void => { + dispatch({ type: 'UPDATE_CONFIG', config }) + dispatch({ type: 'SET_SCREEN', screen: 'execution' }) + } + + const handleExecutionComplete = useCallback( + (results: Record): void => { + // Transform orchestrator results to app state format + // Orchestrator returns { analysis: "markdown content", ... } or JSON for bug-bisect + const changelog = typeof results.analysis === 'string' ? results.analysis : JSON.stringify(results, null, 2) + + dispatch({ type: 'SET_RESULTS', results: { changelog, metadata: results } }) + // Route to bug-bisect results screen if in bug-bisect mode + if (state.analysisMode === 'bug-bisect') { + dispatch({ type: 'SET_SCREEN', screen: 'results' }) + } else { + dispatch({ type: 'SET_SCREEN', screen: 'results' }) + } + }, + [dispatch, state.analysisMode], + ) + + const handleExecutionError = useCallback( + (_error: Error): void => { + dispatch({ type: 'SET_EXECUTION_STATE', state: 'error' }) + // Could navigate to error screen or show error in execution screen + }, + [dispatch], + ) + + const handleBack = (): void => { + // Determine previous screen based on current screen and mode + if (state.screen === 'config-review') { + // Go back to appropriate selector based on mode + switch (state.analysisMode) { + case 'release-changelog': + dispatch({ type: 'SET_SCREEN', screen: 'release-select' }) + break + case 'bug-bisect': + dispatch({ type: 'SET_SCREEN', screen: 'bug-input' }) + break + case 'team-digest': + dispatch({ type: 'SET_SCREEN', screen: 'team-select' }) + break + case 'changelog': + dispatch({ type: 'SET_SCREEN', screen: 'welcome' }) + break + } + } else if (state.screen === 'bug-input') { + dispatch({ type: 'SET_SCREEN', screen: 'release-select' }) + } else if (state.screen === 'release-select' || state.screen === 'team-select') { + dispatch({ type: 'SET_SCREEN', screen: 'welcome' }) + } else if (state.screen === 'execution' || state.screen === 'results') { + dispatch({ type: 'SET_SCREEN', screen: 'config-review' }) + } + } + + const handleRestart = (): void => { + dispatch({ type: 'SET_SCREEN', screen: 'welcome' }) + dispatch({ type: 'SELECT_RELEASE', release: null }) + dispatch({ type: 'SET_COMPARISON_RELEASE', release: null }) + dispatch({ type: 'SET_RESULTS', results: null }) + dispatch({ type: 'SET_EXECUTION_STATE', state: 'idle' }) + } + + switch (state.screen) { + case 'welcome': + return + case 'release-select': + return + case 'bug-input': + return + case 'team-select': + return + case 'config-review': + return + case 'execution': + return ( + + ) + case 'results': + // Use BugBisectResultsScreen for bug-bisect mode, otherwise ResultsScreen + if (state.analysisMode === 'bug-bisect') { + return ( + + ) + } + return ( + + ) + default: + return + } +} + +export function App(): JSX.Element { + return ( + + + + ) +} diff --git a/apps/cli/src/ui/components/Banner.tsx b/apps/cli/src/ui/components/Banner.tsx new file mode 100644 index 00000000000..09c3d17375a --- /dev/null +++ b/apps/cli/src/ui/components/Banner.tsx @@ -0,0 +1,30 @@ +import { Box, Text } from 'ink' +import Gradient from 'ink-gradient' + +const BANNER_ART = ` + ██╗ ██╗███╗ ██╗██╗██╗ ██╗███████╗██████╗ ███████╗███████╗ + ██║ ██║████╗ ██║██║██║ ██║██╔════╝██╔══██╗██╔════╝██╔════╝ + ██║ ██║██╔██╗ ██║██║██║ ██║█████╗ ██████╔╝███████╗█████╗ + ██║ ██║██║╚██╗██║██║╚██╗ ██╔╝██╔══╝ ██╔══██╗╚════██║██╔══╝ + ╚██████╔╝██║ ╚████║██║ ╚████╔╝ ███████╗██║ ██║███████║███████╗ (cli) + ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝ +` + +interface BannerProps { + subtitle?: string +} + +export function Banner({ subtitle }: BannerProps): JSX.Element { + return ( + + {BANNER_ART} + {subtitle && ( + + + {subtitle} + + + )} + + ) +} diff --git a/apps/cli/src/ui/components/Box.tsx b/apps/cli/src/ui/components/Box.tsx new file mode 100644 index 00000000000..e988e17715f --- /dev/null +++ b/apps/cli/src/ui/components/Box.tsx @@ -0,0 +1,18 @@ +import { Box as InkBox, Text } from 'ink' +import type { ReactNode } from 'react' + +interface BoxProps { + children: ReactNode + title?: string + borderColor?: string + padding?: number +} + +export function Box({ children, title, borderColor, padding = 1 }: BoxProps): JSX.Element { + return ( + + {title && {title}} + {children} + + ) +} diff --git a/apps/cli/src/ui/components/ChangelogPreview.tsx b/apps/cli/src/ui/components/ChangelogPreview.tsx new file mode 100644 index 00000000000..48315cd9a02 --- /dev/null +++ b/apps/cli/src/ui/components/ChangelogPreview.tsx @@ -0,0 +1,20 @@ +import { Box as InkBox, Text } from 'ink' + +interface ChangelogPreviewProps { + changelog: string +} + +export function ChangelogPreview({ changelog }: ChangelogPreviewProps): JSX.Element { + // Split into lines and render + const lines = changelog.split('\n') + + return ( + + Changelog Preview + + {lines.map((line, index) => ( + {line} + ))} + + ) +} diff --git a/apps/cli/src/ui/components/FormField.tsx b/apps/cli/src/ui/components/FormField.tsx new file mode 100644 index 00000000000..68f2aaaca78 --- /dev/null +++ b/apps/cli/src/ui/components/FormField.tsx @@ -0,0 +1,26 @@ +import { Box, Text } from 'ink' +import type { ReactNode } from 'react' + +interface FormFieldProps { + children: ReactNode + focused?: boolean + helpText?: string + marginLeft?: number +} + +/** + * Wrapper component for form fields that provides consistent styling + * and focus handling. Composable with Toggle, TextInput, NumberInput, etc. + */ +export function FormField({ children, focused, helpText, marginLeft = 0 }: FormFieldProps): JSX.Element { + return ( + + {children} + {helpText && focused && ( + + {helpText} + + )} + + ) +} diff --git a/apps/cli/src/ui/components/NumberInput.tsx b/apps/cli/src/ui/components/NumberInput.tsx new file mode 100644 index 00000000000..9e327e9ad9d --- /dev/null +++ b/apps/cli/src/ui/components/NumberInput.tsx @@ -0,0 +1,39 @@ +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Box, Text } from 'ink' + +interface NumberInputProps { + label: string + value: number + onChange: (value: number) => void + focused?: boolean + isEditing?: boolean + editValue?: string + min?: number + max?: number + step?: number +} + +export function NumberInput({ + label, + value, + onChange: _onChange, + focused = false, + isEditing = false, + editValue, + min: _min, + max: _max, + step: _step = 1, +}: NumberInputProps): JSX.Element { + const displayValue = isEditing && editValue !== undefined ? editValue : value + + return ( + + + {focused ? '❯ ' : ' '} + {label}: {displayValue} + {isEditing && (Enter to save, Esc to cancel, type digits)} + {focused && !isEditing && (↑↓←→ to adjust, Enter to edit)} + + + ) +} diff --git a/apps/cli/src/ui/components/ProgressIndicator.tsx b/apps/cli/src/ui/components/ProgressIndicator.tsx new file mode 100644 index 00000000000..4b7a3f75344 --- /dev/null +++ b/apps/cli/src/ui/components/ProgressIndicator.tsx @@ -0,0 +1,75 @@ +import type { ProgressEvent, ProgressStage } from '@universe/cli/src/ui/services/orchestrator-service' +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Box, Text } from 'ink' + +interface Stage { + key: ProgressStage + label: string +} + +const stages: Stage[] = [ + { key: 'collecting', label: 'Collecting data' }, + { key: 'analyzing', label: 'Analyzing with AI' }, + { key: 'delivering', label: 'Delivering results' }, +] + +interface ProgressIndicatorProps { + currentStage: ProgressStage + message?: string + cacheInfo?: ProgressEvent['cacheInfo'] +} + +export function ProgressIndicator({ currentStage, message, cacheInfo }: ProgressIndicatorProps): JSX.Element { + const currentIndex = stages.findIndex((s) => s.key === currentStage) + + const getCacheLabel = (cacheInfoItem: ProgressEvent['cacheInfo']): string => { + if (!cacheInfoItem) { + return '' + } + const typeLabel = cacheInfoItem.type === 'commits' ? 'commits' : cacheInfoItem.type === 'prs' ? 'PRs' : 'stats' + return `(cached: ${cacheInfoItem.count} ${typeLabel})` + } + + return ( + + {stages.map((stage, index) => { + const isComplete = index < currentIndex + const isCurrent = index === currentIndex && currentStage !== 'idle' && currentStage !== 'error' + + let icon = '○' + let color = 'gray' + + if (isComplete) { + icon = '●' + color = 'green' + } else if (isCurrent) { + icon = '◉' + color = colors.primary + } + + // Show cache info for collecting stage when it's current or complete + const showCacheInfo = cacheInfo && stage.key === 'collecting' && (isCurrent || isComplete) + + return ( + + {icon} {stage.label} + {showCacheInfo && ( + + {' '} + {getCacheLabel(cacheInfo)} + + )} + {isCurrent && !showCacheInfo && '...'} + + ) + })} + {message && ( + + + {message} + + + )} + + ) +} diff --git a/apps/cli/src/ui/components/ReleaseList.tsx b/apps/cli/src/ui/components/ReleaseList.tsx new file mode 100644 index 00000000000..04911f578fa --- /dev/null +++ b/apps/cli/src/ui/components/ReleaseList.tsx @@ -0,0 +1,34 @@ +import type { Release } from '@universe/cli/src/lib/release-scanner' +import { colors } from '@universe/cli/src/ui/utils/colors' +import { formatBranch } from '@universe/cli/src/ui/utils/format' +import { Text } from 'ink' + +interface ReleaseListProps { + releases: Release[] + selectedIndex: number | null + platform?: 'mobile' | 'extension' +} + +export function ReleaseList({ releases, selectedIndex, platform }: ReleaseListProps): JSX.Element { + const filtered = platform ? releases.filter((r) => r.platform === platform) : releases + + if (filtered.length === 0) { + return No releases found + } + + return ( + <> + {filtered.map((release, index) => { + const isSelected = selectedIndex === index + const prefix = isSelected ? '→ ' : ' ' + + return ( + + {prefix} + {release.platform}/{release.version} ({formatBranch(release.branch)}) + + ) + })} + + ) +} diff --git a/apps/cli/src/ui/components/Select.tsx b/apps/cli/src/ui/components/Select.tsx new file mode 100644 index 00000000000..18b43a29332 --- /dev/null +++ b/apps/cli/src/ui/components/Select.tsx @@ -0,0 +1,38 @@ +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Text } from 'ink' +import SelectInput, { type IndicatorProps, type ItemProps } from 'ink-select-input' + +interface SelectItem { + label: string + value: string +} + +interface SelectProps { + items: SelectItem[] + onSelect: (item: SelectItem) => void +} + +/** + * Themed SelectInput wrapper with Uniswap pink highlighting + */ +export function Select({ items, onSelect }: SelectProps): JSX.Element { + // Custom item component with pink color + const itemComponent = ({ isSelected, label }: ItemProps): JSX.Element => ( + + {isSelected ? '❯ ' : ' '} + {label} + + ) + + // Empty indicator component to disable default blue chevron + const indicatorComponent = (_props: IndicatorProps): JSX.Element => <> + + return ( + + ) +} diff --git a/apps/cli/src/ui/components/StatusBadge.tsx b/apps/cli/src/ui/components/StatusBadge.tsx new file mode 100644 index 00000000000..16e501d1b2c --- /dev/null +++ b/apps/cli/src/ui/components/StatusBadge.tsx @@ -0,0 +1,20 @@ +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Text } from 'ink' + +type StatusType = 'success' | 'warning' | 'error' | 'info' + +interface StatusBadgeProps { + type: StatusType + children: React.ReactNode +} + +const statusColors: Record = { + success: colors.success, + warning: colors.warning, + error: colors.error, + info: colors.primary, +} + +export function StatusBadge({ type, children }: StatusBadgeProps): JSX.Element { + return {children} +} diff --git a/apps/cli/src/ui/components/TextInput.tsx b/apps/cli/src/ui/components/TextInput.tsx new file mode 100644 index 00000000000..c26dd66cdab --- /dev/null +++ b/apps/cli/src/ui/components/TextInput.tsx @@ -0,0 +1,38 @@ +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Box, Text } from 'ink' + +interface TextInputProps { + label: string + value: string + onChange: (value: string) => void + focused?: boolean + isEditing?: boolean + editValue?: string + placeholder?: string +} + +export function TextInput({ + label, + value, + onChange: _onChange, + focused = false, + isEditing = false, + editValue, + placeholder = '', +}: TextInputProps): JSX.Element { + const displayValue = isEditing && editValue !== undefined ? editValue : value || placeholder + + return ( + + + {focused ? '❯ ' : ' '} + {label}:{' '} + + {displayValue} + + {isEditing && (Enter to save, Esc to cancel, type text)} + {focused && !isEditing && (Enter to edit)} + + + ) +} diff --git a/apps/cli/src/ui/components/Toggle.tsx b/apps/cli/src/ui/components/Toggle.tsx new file mode 100644 index 00000000000..38b1e6cf106 --- /dev/null +++ b/apps/cli/src/ui/components/Toggle.tsx @@ -0,0 +1,22 @@ +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Text } from 'ink' + +interface ToggleProps { + label: string + checked: boolean + onToggle: () => void + focused?: boolean +} + +/** + * Toggle component - does not handle its own input + * Parent component should handle Enter/Space when this is focused + */ +export function Toggle({ label, checked, onToggle: _onToggle, focused = false }: ToggleProps): JSX.Element { + return ( + + {focused ? '❯ ' : ' '} + {checked ? '◉' : '○'} {label} + + ) +} diff --git a/apps/cli/src/ui/components/WindowedSelect.tsx b/apps/cli/src/ui/components/WindowedSelect.tsx new file mode 100644 index 00000000000..13d514398cc --- /dev/null +++ b/apps/cli/src/ui/components/WindowedSelect.tsx @@ -0,0 +1,101 @@ +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Box, Text, useInput } from 'ink' +import { useCallback, useEffect, useState } from 'react' + +interface WindowedSelectItem { + label: string + value: string + data?: T +} + +interface WindowedSelectProps { + items: WindowedSelectItem[] + onSelect: (item: WindowedSelectItem) => void + onFocusChange?: (item: WindowedSelectItem | null) => void + limit?: number // Number of visible items (default: 10) +} + +const DEFAULT_LIMIT = 10 + +export function WindowedSelect({ + items, + onSelect, + onFocusChange, + limit = DEFAULT_LIMIT, +}: WindowedSelectProps): JSX.Element { + const [selectedIndex, setSelectedIndex] = useState(0) + const [startIndex, setStartIndex] = useState(0) + + // Notify parent when focused item changes + useEffect(() => { + if (onFocusChange) { + const focusedItem = items[selectedIndex] ?? null + onFocusChange(focusedItem) + } + }, [selectedIndex, items, onFocusChange]) + + // Calculate visible window + const endIndex = Math.min(startIndex + limit, items.length) + const visibleItems = items.slice(startIndex, endIndex) + const relativeSelectedIndex = selectedIndex - startIndex + + // Keep selected item in view when it moves outside the window + useEffect(() => { + if (selectedIndex < startIndex) { + // Selected item moved above visible window + setStartIndex(Math.max(0, selectedIndex)) + } else if (selectedIndex >= endIndex) { + // Selected item moved below visible window + setStartIndex(Math.max(0, selectedIndex - limit + 1)) + } + }, [selectedIndex, startIndex, endIndex, limit]) + + // Reset to top when items change + useEffect(() => { + setSelectedIndex(0) + setStartIndex(0) + }, []) + + // Handle keyboard input + useInput( + useCallback( + (input: string, key: { upArrow?: boolean; downArrow?: boolean; return?: boolean }) => { + if (key.upArrow && selectedIndex > 0) { + setSelectedIndex(selectedIndex - 1) + } else if (key.downArrow && selectedIndex < items.length - 1) { + setSelectedIndex(selectedIndex + 1) + } else if (key.return) { + const selectedItem = items[selectedIndex] + if (selectedItem) { + onSelect(selectedItem) + } + } + }, + [selectedIndex, items, onSelect], + ), + ) + + const hasMoreAbove = startIndex > 0 + const hasMoreBelow = endIndex < items.length + + return ( + + {hasMoreAbove && ... {startIndex} more above (use ↑ to scroll) ...} + {visibleItems.map((item, index) => { + const isSelected = index === relativeSelectedIndex + return ( + + {isSelected ? '❯ ' : ' '} + {item.label} + + ) + })} + {hasMoreBelow && ... {items.length - endIndex} more below (use ↓ to scroll) ...} + + + Selected: {selectedIndex + 1} of {items.length} (Enter to select) + + + + ) +} diff --git a/apps/cli/src/ui/hooks/useAnalysis.ts b/apps/cli/src/ui/hooks/useAnalysis.ts new file mode 100644 index 00000000000..e66406d0991 --- /dev/null +++ b/apps/cli/src/ui/hooks/useAnalysis.ts @@ -0,0 +1,53 @@ +import type { OrchestratorConfig } from '@universe/cli/src/core/orchestrator' +import { OrchestratorService, type ProgressEvent } from '@universe/cli/src/ui/services/orchestrator-service' +import { useCallback, useState } from 'react' + +interface UseAnalysisResult { + execute: (config: OrchestratorConfig) => Promise | null> + results: Record | null + progress: ProgressEvent | null + error: Error | null + isRunning: boolean +} + +export function useAnalysis(): UseAnalysisResult { + const [results, setResults] = useState | null>(null) + const [progress, setProgress] = useState(null) + const [error, setError] = useState(null) + const [isRunning, setIsRunning] = useState(false) + const [service] = useState(() => new OrchestratorService()) + + const execute = useCallback( + async (config: OrchestratorConfig): Promise | null> => { + try { + setIsRunning(true) + setError(null) + setProgress({ stage: 'idle' }) + setResults(null) + + const result = await service.execute(config, (event: ProgressEvent) => { + setProgress(event) + }) + + setResults(result) + setIsRunning(false) + return result + } catch (err) { + const errorObj = err instanceof Error ? err : new Error(String(err)) + setError(errorObj) + setIsRunning(false) + setProgress({ stage: 'error', message: errorObj.message }) + return null + } + }, + [service], + ) + + return { + execute, + results, + progress, + error, + isRunning, + } +} diff --git a/apps/cli/src/ui/hooks/useAppState.tsx b/apps/cli/src/ui/hooks/useAppState.tsx new file mode 100644 index 00000000000..f786dc9faed --- /dev/null +++ b/apps/cli/src/ui/hooks/useAppState.tsx @@ -0,0 +1,132 @@ +import type { OrchestratorConfig } from '@universe/cli/src/core/orchestrator' +import type { Release } from '@universe/cli/src/lib/release-scanner' +import { createContext, type ReactNode, useContext, useReducer } from 'react' + +export type Screen = + | 'welcome' + | 'release-select' + | 'team-select' + | 'config-review' + | 'execution' + | 'results' + | 'bug-input' + +export type AnalysisMode = 'release-changelog' | 'team-digest' | 'changelog' | 'bug-bisect' + +export interface TeamFilter { + teams?: string[] + usernames?: string[] + emails?: string[] +} + +export interface TeamMembersCache { + emails: string[] + usernames: string[] +} + +interface AppState { + screen: Screen + repository: { owner: string; name: string } | null + releases: Release[] + selectedRelease: Release | null + comparisonRelease: Release | null + analysisMode: AnalysisMode + bugDescription: string | null + teamFilter: TeamFilter | null + teamMembersCache: Record + timePeriod: string + config: Partial + executionState: 'idle' | 'running' | 'complete' | 'error' + results: { changelog: string; metadata: unknown } | null +} + +type AppAction = + | { type: 'SET_SCREEN'; screen: Screen } + | { type: 'SET_REPOSITORY'; repository: { owner: string; name: string } | null } + | { type: 'SET_RELEASES'; releases: Release[] } + | { type: 'SELECT_RELEASE'; release: Release | null } + | { type: 'SET_COMPARISON_RELEASE'; release: Release | null } + | { type: 'SET_ANALYSIS_MODE'; mode: AnalysisMode } + | { type: 'SET_BUG_DESCRIPTION'; description: string | null } + | { type: 'SET_TEAM_FILTER'; filter: TeamFilter | null } + | { type: 'CACHE_TEAM_MEMBERS'; teamSlug: string; members: TeamMembersCache } + | { type: 'SET_TIME_PERIOD'; period: string } + | { type: 'UPDATE_CONFIG'; config: Partial } + | { type: 'SET_EXECUTION_STATE'; state: 'idle' | 'running' | 'complete' | 'error' } + | { type: 'SET_RESULTS'; results: { changelog: string; metadata: unknown } | null } + +const initialState: AppState = { + screen: 'welcome', + repository: null, + releases: [], + selectedRelease: null, + comparisonRelease: null, + analysisMode: 'release-changelog', + bugDescription: null, + teamFilter: null, + teamMembersCache: {}, + timePeriod: '30 days ago', + config: {}, + executionState: 'idle', + results: null, +} + +function appReducer(state: AppState, action: AppAction): AppState { + switch (action.type) { + case 'SET_SCREEN': + return { ...state, screen: action.screen } + case 'SET_REPOSITORY': + return { ...state, repository: action.repository } + case 'SET_RELEASES': + return { ...state, releases: action.releases } + case 'SELECT_RELEASE': + return { ...state, selectedRelease: action.release } + case 'SET_COMPARISON_RELEASE': + return { ...state, comparisonRelease: action.release } + case 'SET_ANALYSIS_MODE': + return { ...state, analysisMode: action.mode } + case 'SET_BUG_DESCRIPTION': + return { ...state, bugDescription: action.description } + case 'SET_TEAM_FILTER': + return { ...state, teamFilter: action.filter } + case 'CACHE_TEAM_MEMBERS': + return { + ...state, + teamMembersCache: { + ...state.teamMembersCache, + [action.teamSlug]: action.members, + }, + } + case 'SET_TIME_PERIOD': + return { ...state, timePeriod: action.period } + case 'UPDATE_CONFIG': + return { ...state, config: { ...state.config, ...action.config } } + case 'SET_EXECUTION_STATE': + return { ...state, executionState: action.state } + case 'SET_RESULTS': + return { ...state, results: action.results } + default: + return state + } +} + +interface AppContextValue { + state: AppState + dispatch: React.Dispatch +} + +const AppContext = createContext(null) + +export function AppStateProvider({ children }: { children: ReactNode }): JSX.Element { + const [state, dispatch] = useReducer(appReducer, initialState) + + return {children} +} + +export function useAppState(): AppContextValue { + const context = useContext(AppContext) + if (!context) { + throw new Error('useAppState must be used within AppStateProvider') + } + return context +} diff --git a/apps/cli/src/ui/hooks/useEditableField.ts b/apps/cli/src/ui/hooks/useEditableField.ts new file mode 100644 index 00000000000..4f407921586 --- /dev/null +++ b/apps/cli/src/ui/hooks/useEditableField.ts @@ -0,0 +1,107 @@ +import { useCallback, useEffect, useState } from 'react' + +interface UseEditableFieldOptions { + value: T + onChange: (value: T) => void + focused: boolean + type?: 'text' | 'number' + min?: number + max?: number + step?: number + onEditStart?: () => void + onEditEnd?: () => void +} + +interface UseEditableFieldReturn { + isEditing: boolean + editValue: string + startEdit: () => void + saveEdit: () => void + cancelEdit: () => void + handleInput: (input: string, key: { backspace?: boolean; delete?: boolean }) => void +} + +/** + * Hook for managing editable field state and keyboard input + * Handles edit mode (Enter to edit, Esc to cancel, typing) + */ +export function useEditableField({ + value, + onChange, + focused, + type = 'text', + min, + max, + step: _step = 1, + onEditStart, + onEditEnd, +}: UseEditableFieldOptions): UseEditableFieldReturn { + const [isEditing, setIsEditing] = useState(false) + const [editValue, setEditValue] = useState('') + + const startEdit = useCallback(() => { + setIsEditing(true) + setEditValue(String(value)) + onEditStart?.() + }, [value, onEditStart]) + + const saveEdit = useCallback(() => { + if (type === 'number') { + const numValue = Number.parseInt(editValue, 10) + if (!Number.isNaN(numValue)) { + const clampedValue = Math.max(min ?? 0, Math.min(max ?? Number.MAX_SAFE_INTEGER, numValue)) + onChange(clampedValue as T) + } + } else { + onChange(editValue as T) + } + setIsEditing(false) + setEditValue('') + onEditEnd?.() + }, [editValue, type, min, max, onChange, onEditEnd]) + + const cancelEdit = useCallback(() => { + setIsEditing(false) + setEditValue('') + onEditEnd?.() + }, [onEditEnd]) + + // Reset editing state when focus is lost + useEffect(() => { + if (!focused && isEditing) { + cancelEdit() + } + }, [focused, isEditing, cancelEdit]) + + const handleInput = useCallback( + (input: string, key: { backspace?: boolean; delete?: boolean; return?: boolean; escape?: boolean }) => { + if (key.return) { + if (isEditing) { + saveEdit() + } else { + startEdit() + } + } else if (key.escape && isEditing) { + cancelEdit() + } else if (key.backspace || key.delete) { + setEditValue((prev) => prev.slice(0, -1)) + } else if (input && input.length === 1) { + if (type === 'number' && /^\d$/.test(input)) { + setEditValue((prev) => prev + input) + } else if (type === 'text') { + setEditValue((prev) => prev + input) + } + } + }, + [type, isEditing, saveEdit, cancelEdit, startEdit], + ) + + return { + isEditing, + editValue, + startEdit, + saveEdit, + cancelEdit, + handleInput, + } +} diff --git a/apps/cli/src/ui/hooks/useFormNavigation.ts b/apps/cli/src/ui/hooks/useFormNavigation.ts new file mode 100644 index 00000000000..125cd62cb9c --- /dev/null +++ b/apps/cli/src/ui/hooks/useFormNavigation.ts @@ -0,0 +1,67 @@ +import { useInput } from 'ink' +import { useCallback, useState } from 'react' + +interface UseFormNavigationOptions { + itemCount: number + onEscape?: () => void + enabled?: boolean + // Optional: block navigation when true (e.g., when editing a field) + blockNavigation?: boolean +} + +interface UseFormNavigationReturn { + focusedIndex: number + setFocusedIndex: (index: number) => void +} + +/** + * Hook for managing keyboard navigation in forms + * Handles up/down arrow navigation only - selection logic handled by parent + */ +export function useFormNavigation({ + itemCount, + onEscape, + enabled = true, + blockNavigation = false, +}: UseFormNavigationOptions): UseFormNavigationReturn { + const [focusedIndex, setFocusedIndex] = useState(0) + + const arrowUp = useCallback(() => { + setFocusedIndex((prev) => Math.max(0, prev - 1)) + }, []) + + const arrowDown = useCallback(() => { + setFocusedIndex((prev) => Math.min(itemCount - 1, prev + 1)) + }, [itemCount]) + + const handleEscape = useCallback(() => { + if (onEscape) { + onEscape() + } + }, [onEscape]) + + // Register keyboard handlers - only handles navigation arrows + useInput( + useCallback( + (_input: string, key: { upArrow?: boolean; downArrow?: boolean; escape?: boolean }) => { + if (!enabled || blockNavigation) { + return + } + + if (key.upArrow) { + arrowUp() + } else if (key.downArrow) { + arrowDown() + } else if (key.escape && onEscape) { + handleEscape() + } + }, + [enabled, blockNavigation, arrowUp, arrowDown, handleEscape, onEscape], + ), + ) + + return { + focusedIndex, + setFocusedIndex, + } +} diff --git a/apps/cli/src/ui/hooks/useReleases.ts b/apps/cli/src/ui/hooks/useReleases.ts new file mode 100644 index 00000000000..55eb85a0484 --- /dev/null +++ b/apps/cli/src/ui/hooks/useReleases.ts @@ -0,0 +1,106 @@ +import { type Release, ReleaseScanner } from '@universe/cli/src/lib/release-scanner' +import { useCallback, useEffect, useRef, useState } from 'react' + +interface UseReleasesResult { + releases: Release[] + loading: boolean + error: Error | null + getLatest: (platform: 'mobile' | 'extension') => Promise + getPrevious: (release: Release) => Promise + findRelease: (platform: 'mobile' | 'extension', version: string) => Promise + refresh: (platform?: 'mobile' | 'extension') => Promise +} + +export function useReleases(platform?: 'mobile' | 'extension'): UseReleasesResult { + const [releases, setReleases] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [scanner] = useState(() => new ReleaseScanner()) + const isScanningRef = useRef(false) + const lastScannedPlatformRef = useRef(undefined) + + const refresh = useCallback( + async (filterPlatform?: 'mobile' | 'extension') => { + // Guard: Prevent multiple simultaneous scans + if (isScanningRef.current) { + return + } + + const targetPlatform = filterPlatform || platform + const platformKey = targetPlatform || 'all' + + // Guard: Don't re-scan if we already scanned for this platform + if (lastScannedPlatformRef.current === platformKey) { + return + } + + try { + isScanningRef.current = true + setLoading(true) + setError(null) + const fetched = await scanner.scanReleases(targetPlatform) + setReleases(fetched) + lastScannedPlatformRef.current = platformKey + setLoading(false) + } catch (err) { + setError(err instanceof Error ? err : new Error(String(err))) + setLoading(false) + } finally { + isScanningRef.current = false + } + }, + [scanner, platform], + ) + + useEffect(() => { + // Only scan on initial mount or when platform actually changes + const platformKey = platform || 'all' + if (lastScannedPlatformRef.current !== platformKey) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises -- Intentionally fire-and-forget promise + refresh() + } + }, [platform, refresh]) + + const getLatest = useCallback( + async (targetPlatform: 'mobile' | 'extension'): Promise => { + try { + return await scanner.getLatestRelease(targetPlatform) + } catch (_err) { + return null + } + }, + [scanner], + ) + + const getPrevious = useCallback( + async (release: Release): Promise => { + try { + return await scanner.getPreviousRelease(release) + } catch (_err) { + return null + } + }, + [scanner], + ) + + const findRelease = useCallback( + async (targetPlatform: 'mobile' | 'extension', version: string): Promise => { + try { + return await scanner.findRelease(targetPlatform, version) + } catch (_err) { + return null + } + }, + [scanner], + ) + + return { + releases, + loading, + error, + getLatest, + getPrevious, + findRelease, + refresh, + } +} diff --git a/apps/cli/src/ui/hooks/useRepository.ts b/apps/cli/src/ui/hooks/useRepository.ts new file mode 100644 index 00000000000..ca3ed53b81a --- /dev/null +++ b/apps/cli/src/ui/hooks/useRepository.ts @@ -0,0 +1,53 @@ +import { detectRepository } from '@universe/cli/src/lib/team-resolver' +import { useEffect, useState } from 'react' + +interface Repository { + owner: string + name: string +} + +interface UseRepositoryResult { + repository: Repository | null + loading: boolean + error: Error | null +} + +export function useRepository(): UseRepositoryResult { + const [repository, setRepository] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + let cancelled = false + + async function detect(): Promise { + try { + setLoading(true) + setError(null) + const detected = await detectRepository() + if (!cancelled) { + if (detected && detected.owner && detected.name) { + setRepository({ owner: detected.owner, name: detected.name }) + } else { + setRepository(null) + } + setLoading(false) + } + } catch (err) { + if (!cancelled) { + setError(err instanceof Error ? err : new Error(String(err))) + setLoading(false) + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-floating-promises -- Intentionally fire-and-forget promise + detect() + + return () => { + cancelled = true + } + }, []) + + return { repository, loading, error } +} diff --git a/apps/cli/src/ui/hooks/useTeams.ts b/apps/cli/src/ui/hooks/useTeams.ts new file mode 100644 index 00000000000..71c7e59bd82 --- /dev/null +++ b/apps/cli/src/ui/hooks/useTeams.ts @@ -0,0 +1,94 @@ +import { $ } from 'bun' +import { useCallback, useEffect, useRef, useState } from 'react' + +export interface GitHubTeam { + name: string + slug: string + description: string | null + membersCount?: number +} + +interface UseTeamsResult { + teams: GitHubTeam[] + loading: boolean + error: Error | null + refresh: () => Promise +} + +/** + * Hook to fetch teams from a GitHub organization + */ +export function useTeams(org: string | null): UseTeamsResult { + const [teams, setTeams] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const isFetchingRef = useRef(false) + const lastFetchedOrgRef = useRef(null) + + const refresh = useCallback(async () => { + if (!org) { + setLoading(false) + return + } + + // Guard: Prevent multiple simultaneous fetches + if (isFetchingRef.current) { + return + } + + // Guard: Don't re-fetch if we already fetched for this org + if (lastFetchedOrgRef.current === org) { + return + } + + try { + isFetchingRef.current = true + setLoading(true) + setError(null) + + // Fetch teams from GitHub API + const teamsResult = + await $`gh api /orgs/${org}/teams --jq '.[] | {name: .name, slug: .slug, description: .description}'`.text() + + const teamLines = teamsResult.trim().split('\n').filter(Boolean) + const parsedTeams: GitHubTeam[] = teamLines + .map((line: string) => { + try { + const parsed = JSON.parse(line) as GitHubTeam + return parsed + } catch { + return null + } + }) + .filter((team: GitHubTeam | null): team is GitHubTeam => team !== null) + + setTeams(parsedTeams) + lastFetchedOrgRef.current = org + setLoading(false) + } catch (err) { + setError( + err instanceof Error + ? err + : new Error(`Failed to fetch teams from org "${org}". Ensure gh CLI is authenticated.`), + ) + setLoading(false) + } finally { + isFetchingRef.current = false + } + }, [org]) + + useEffect(() => { + // Only fetch on initial mount or when org changes + if (org && lastFetchedOrgRef.current !== org) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises -- Intentionally fire-and-forget promise + refresh() + } + }, [org, refresh]) + + return { + teams, + loading, + error, + refresh, + } +} diff --git a/apps/cli/src/ui/hooks/useToggleGroup.ts b/apps/cli/src/ui/hooks/useToggleGroup.ts new file mode 100644 index 00000000000..5be9a703328 --- /dev/null +++ b/apps/cli/src/ui/hooks/useToggleGroup.ts @@ -0,0 +1,70 @@ +import { useCallback, useState } from 'react' + +interface UseToggleGroupOptions { + items: Array<{ key: T; label: string }> + initialSelected?: Set + minSelection?: number // Minimum number of items that must be selected +} + +interface UseToggleGroupReturn { + selected: Set + toggle: (key: T) => void + isSelected: (key: T) => boolean + selectAll: () => void + deselectAll: () => void +} + +/** + * Hook for managing a group of toggles/checkboxes + * Useful for multiple selection scenarios like output options + */ +export function useToggleGroup({ + items, + initialSelected = new Set(), + minSelection = 0, +}: UseToggleGroupOptions): UseToggleGroupReturn { + const [selected, setSelected] = useState>(initialSelected) + + const toggle = useCallback( + (key: T) => { + setSelected((prev) => { + const next = new Set(prev) + if (next.has(key)) { + // Don't allow unchecking if it would violate minSelection + if (next.size > minSelection) { + next.delete(key) + } + } else { + next.add(key) + } + return next + }) + }, + [minSelection], + ) + + const isSelected = useCallback( + (key: T) => { + return selected.has(key) + }, + [selected], + ) + + const selectAll = useCallback(() => { + setSelected(new Set(items.map((item) => item.key))) + }, [items]) + + const deselectAll = useCallback(() => { + if (items.length >= minSelection) { + setSelected(new Set(items.slice(0, minSelection).map((item) => item.key))) + } + }, [items, minSelection]) + + return { + selected, + toggle, + isSelected, + selectAll, + deselectAll, + } +} diff --git a/apps/cli/src/ui/screens/BugBisectResultsScreen.tsx b/apps/cli/src/ui/screens/BugBisectResultsScreen.tsx new file mode 100644 index 00000000000..69b065a0e62 --- /dev/null +++ b/apps/cli/src/ui/screens/BugBisectResultsScreen.tsx @@ -0,0 +1,464 @@ +import { join } from 'node:path' +import { Select } from '@universe/cli/src/ui/components/Select' +import { TextInput } from '@universe/cli/src/ui/components/TextInput' +import { useAppState } from '@universe/cli/src/ui/hooks/useAppState' +import { useRepository } from '@universe/cli/src/ui/hooks/useRepository' +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Box as InkBox, Text, useInput } from 'ink' +import { useCallback, useMemo, useState } from 'react' + +interface BugBisectResultsScreenProps { + results: { changelog: string; metadata: unknown } + onRestart: () => void +} + +interface SuspiciousCommit { + sha: string + confidence: number + reasoning: string + relatedPR?: number +} + +interface BugBisectResults { + suspiciousCommits?: SuspiciousCommit[] + summary?: string + totalCommitsAnalyzed?: number + releaseContext?: { + from: string + to: string + platform: string + } +} + +type ViewMode = 'menu' | 'save-file' | 'saved' + +function isValidBugBisectResults(value: unknown): value is BugBisectResults { + return ( + typeof value === 'object' && + value !== null && + 'suspiciousCommits' in value && + Array.isArray((value as BugBisectResults).suspiciousCommits) + ) +} + +function tryParseFromString(jsonString: string): BugBisectResults | null { + try { + const parsed = JSON.parse(jsonString) as BugBisectResults + if (isValidBugBisectResults(parsed)) { + return parsed + } + } catch { + // Not valid JSON or not valid BugBisectResults + } + return null +} + +function getConfidenceColor(confidence: number): string { + if (confidence >= 0.9) { + return 'red' + } + if (confidence >= 0.7) { + return '#ff8c00' + } // orange + if (confidence >= 0.5) { + return 'yellow' + } + return 'gray' +} + +function getConfidenceLabel(confidence: number): string { + if (confidence >= 0.9) { + return 'Very Likely' + } + if (confidence >= 0.7) { + return 'Likely' + } + if (confidence >= 0.5) { + return 'Possible' + } + return 'Unlikely' +} + +export function BugBisectResultsScreen({ results, onRestart }: BugBisectResultsScreenProps): JSX.Element { + const { state } = useAppState() + const { repository } = useRepository() + const [viewMode, setViewMode] = useState('menu') + const [filename, setFilename] = useState('bug-bisect-results.json') + const [filepath, setFilepath] = useState(process.cwd()) + const [savedPath, setSavedPath] = useState('') + const [focusedIndex, setFocusedIndex] = useState(0) + const [editingIndex, setEditingIndex] = useState(null) + const [editValue, setEditValue] = useState('') + const [saveError, setSaveError] = useState(null) + + // Parse results + const parsedResults = useMemo((): BugBisectResults | null => { + try { + // Try to parse from metadata if it's already parsed + if (results.metadata && typeof results.metadata === 'object') { + const metadata = results.metadata as Record + if (isValidBugBisectResults(metadata)) { + return metadata + } + } + + // Try to parse from changelog string (might be JSON) + if (typeof results.changelog === 'string') { + const parsed = tryParseFromString(results.changelog) + if (parsed) { + return parsed + } + } + + // Try to parse from metadata.analysis if it's a string + if (results.metadata && typeof results.metadata === 'object') { + const metadata = results.metadata as Record + const analysisString = metadata.analysis + if (typeof analysisString === 'string') { + const parsed = tryParseFromString(analysisString) + if (parsed) { + return parsed + } + } + } + + return null + } catch { + return null + } + }, [results]) + + const bugResults = useMemo((): BugBisectResults => { + if (parsedResults) { + return parsedResults + } + return { + suspiciousCommits: [], + summary: 'Failed to parse results', + totalCommitsAnalyzed: 0, + releaseContext: state.selectedRelease + ? { + from: state.comparisonRelease?.version || 'unknown', + to: state.selectedRelease.version, + platform: state.selectedRelease.platform, + } + : undefined, + } + }, [parsedResults, state.selectedRelease, state.comparisonRelease]) + + const githubBaseUrl = repository ? `https://github.com/${repository.owner}/${repository.name}` : '' + + const options = [ + { label: 'Save to File', value: 'save' }, + { label: 'Start Over', value: 'restart' }, + { label: 'Quit', value: 'quit' }, + ] + + const handleSelect = (option: { label: string; value: string }): void => { + if (option.value === 'quit') { + process.exit(0) + } else if (option.value === 'restart') { + onRestart() + } else if (option.value === 'save') { + setViewMode('save-file') + } + } + + const saveFile = useCallback(async () => { + try { + const fullPath = join(filepath, filename) + const content = JSON.stringify(bugResults, null, 2) + await Bun.write(fullPath, content) + setSavedPath(fullPath) + setViewMode('saved') + setSaveError(null) + } catch (error) { + setSaveError(error instanceof Error ? error.message : 'Failed to save file') + } + }, [filepath, filename, bugResults]) + + // Handle input for save-file mode (similar to ResultsScreen) + useInput( + useCallback( + (input, key) => { + if (viewMode !== 'save-file') { + return + } + + const isEditing = editingIndex !== null + + if (key.escape) { + if (isEditing) { + setEditingIndex(null) + setEditValue('') + } else { + setViewMode('menu') + setFocusedIndex(0) + setSaveError(null) + } + return + } + + if (!isEditing) { + if (key.upArrow) { + setFocusedIndex((prev) => Math.max(0, prev - 1)) + return + } + if (key.downArrow) { + setFocusedIndex((prev) => Math.min(2, prev + 1)) + return + } + if (key.return) { + if (focusedIndex === 0 || focusedIndex === 1) { + setEditingIndex(focusedIndex) + setEditValue(focusedIndex === 0 ? filename : filepath) + } else if (focusedIndex === 2) { + saveFile().catch(() => { + // Error already handled in saveFile + }) + } + return + } + } + + if (isEditing) { + if (key.return) { + if (editingIndex === 0) { + setFilename(editValue) + } else if (editingIndex === 1) { + setFilepath(editValue) + } + setEditingIndex(null) + setEditValue('') + return + } + + if (key.backspace || key.delete) { + setEditValue((prev) => prev.slice(0, -1)) + return + } + + if (input && input.length === 1) { + setEditValue((prev) => prev + input) + return + } + } + }, + [viewMode, focusedIndex, editingIndex, editValue, filename, filepath, saveFile], + ), + ) + + // Handle input for saved mode + useInput( + useCallback( + (_input, key) => { + if (viewMode === 'saved' && (key.return || key.escape)) { + setViewMode('menu') + setFocusedIndex(0) + } + }, + [viewMode], + ), + ) + + if (viewMode === 'save-file') { + return ( + + + + Save Results to File + + + + + + + + + + {focusedIndex === 2 ? '❯ ' : ' '} + Save File + + + + {saveError && ( + + Error: {saveError} + + )} + + + + Use ↑↓ to navigate, Enter to edit/save, Esc to {editingIndex !== null ? 'cancel' : 'go back'} + + + + + ) + } + + if (viewMode === 'saved') { + return ( + + + + ✓ File Saved Successfully + + + + + + Saved to:{' '} + + {savedPath} + + + + + Press Enter or Esc to return to menu + + + + ) + } + + const suspiciousCommits = bugResults.suspiciousCommits || [] + + return ( + + + + ✓ Bug Analysis Complete + + + + + {/* Bug Description */} + {state.bugDescription && ( + + Bug Description + {state.bugDescription} + + )} + + {/* Release Context */} + {bugResults.releaseContext && ( + + + Platform: {bugResults.releaseContext.platform} + + + Release: {bugResults.releaseContext.from} → {bugResults.releaseContext.to} + + {bugResults.totalCommitsAnalyzed !== undefined && ( + + Commits Analyzed: {bugResults.totalCommitsAnalyzed} + + )} + + )} + + {/* Summary */} + {bugResults.summary && ( + + {bugResults.summary} + + )} + + {/* Suspicious Commits */} + {suspiciousCommits.length > 0 ? ( + + + + Suspicious Commits ({suspiciousCommits.length}) + + + + {suspiciousCommits.slice(0, 20).map((commit, index) => { + const confidenceColor = getConfidenceColor(commit.confidence) + const confidenceLabel = getConfidenceLabel(commit.confidence) + const shortSha = commit.sha.slice(0, 7) + const commitUrl = githubBaseUrl ? `${githubBaseUrl}/commit/${commit.sha}` : '' + const prUrl = commit.relatedPR && githubBaseUrl ? `${githubBaseUrl}/pull/${commit.relatedPR}` : '' + + return ( + + + + #{index + 1}. {shortSha} + + + + {confidenceLabel} ({Math.round(commit.confidence * 100)}%) + + + + + + {commitUrl ? ( + + {commitUrl} + + ) : ( + SHA: {commit.sha} + )} + + {commit.relatedPR && ( + + PR #{commit.relatedPR} + {prUrl && ( + + {' '} + - {prUrl} + + )} + + )} + + + + {commit.reasoning} + + + ) + })} + + {suspiciousCommits.length > 20 && ( + + ... and {suspiciousCommits.length - 20} more commits + + )} + + ) : ( + + ⚠ No suspicious commits found + + )} + + + + What would you like to do next? + + + + { + if (item.value === 'back') { + setMode('browse') + } else { + setPlatformFilter(item.value as 'mobile' | 'extension' | 'all') + setMode('browse') + } + }} + /> + + + ) + } + + // Browse mode - show releases + if (mode === 'browse') { + // Create options with navigation and filter controls at the top + const browseOptions = [ + { label: '← Back to Quick Actions', value: 'back' }, + { label: '🔍 Filter by Platform', value: 'filter' }, + ...filteredReleases.map((release: Release, index: number) => ({ + label: `${release.platform}/${release.version} (${formatBranch(release.branch)})`, + value: String(index + 2), // Offset by 2 for back and filter options + release, + })), + ] + + return ( + + + + Select Release + + + + {loading && ( + + Loading releases... + + )} + {error && ( + + Error: {error.message} + + )} + + {!loading && !error && ( + <> + + Showing {filteredReleases.length} release{filteredReleases.length !== 1 ? 's' : ''}{' '} + {platformFilter !== 'all' ? `(${platformFilter})` : ''} + + { + if (item.value === 'back') { + setMode('quick') + } else if (item.value === 'filter') { + setMode('filter-platform') + } else if (item.release) { + const release = item.release + const index = filteredReleases.findIndex( + (r: Release) => r.platform === release.platform && r.version === release.version, + ) + if (index >= 0) { + handleBrowseSelect(index) + } + } + }} + /> + + )} + + + ) + } + + return ( + + + + Release Selection + + + + + Choose a quick action or browse releases: + + + + + ) +} diff --git a/apps/cli/src/ui/screens/TeamDetailsScreen.tsx b/apps/cli/src/ui/screens/TeamDetailsScreen.tsx new file mode 100644 index 00000000000..55086f74b85 --- /dev/null +++ b/apps/cli/src/ui/screens/TeamDetailsScreen.tsx @@ -0,0 +1,152 @@ +import { fetchTeamMembers, type TeamMember } from '@universe/cli/src/lib/team-members' +import { resolveTeam } from '@universe/cli/src/lib/team-resolver' +import { Select } from '@universe/cli/src/ui/components/Select' +import { StatusBadge } from '@universe/cli/src/ui/components/StatusBadge' +import { type TeamFilter, useAppState } from '@universe/cli/src/ui/hooks/useAppState' +import type { GitHubTeam } from '@universe/cli/src/ui/hooks/useTeams' +import { colors } from '@universe/cli/src/ui/utils/colors' +import { Box, Text } from 'ink' +import Spinner from 'ink-spinner' +import { useEffect, useState } from 'react' + +interface TeamDetailsScreenProps { + team: GitHubTeam + org: string + onSelect: (teamFilter: TeamFilter) => void + onBack: () => void +} + +export function TeamDetailsScreen({ team, org, onSelect, onBack }: TeamDetailsScreenProps): JSX.Element { + const { dispatch } = useAppState() + const [members, setMembers] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + let cancelled = false + + const loadMembers = async (): Promise => { + try { + setLoading(true) + setError(null) + + // Fetch members for display + const fetchedMembers = await fetchTeamMembers(org, team.slug) + + if (!cancelled) { + setMembers(fetchedMembers) + + // Also resolve to emails/usernames and cache for later use + const teamSlug = `@${org}/${team.slug}` + try { + const { emails, usernames } = await resolveTeam(teamSlug) + dispatch({ + type: 'CACHE_TEAM_MEMBERS', + teamSlug, + members: { emails, usernames }, + }) + } catch { + // If resolveTeam fails, continue with display but don't cache + // The user can still see members, just won't be cached + } + + setLoading(false) + } + } catch (err) { + if (!cancelled) { + setError(err instanceof Error ? err : new Error('Failed to fetch team members')) + setLoading(false) + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-floating-promises -- Intentionally fire-and-forget promise + loadMembers() + + return () => { + cancelled = true + } + }, [org, team.slug, dispatch]) + + const handleSelectTeam = (): void => { + const teamFilter: TeamFilter = { + teams: [`@${org}/${team.slug}`], + } + dispatch({ type: 'SET_TEAM_FILTER', filter: teamFilter }) + onSelect(teamFilter) + } + + return ( + + + + Team Details + + + + + + @{org}/{team.slug} + + {team.name} + {team.description && {team.description}} + + + + + Members + + + {loading && ( + + + + + Loading members... + + )} + + {error && ( + + {error.message} + + )} + + {!loading && !error && members.length === 0 && ( + + No members found + + )} + + {!loading && !error && members.length > 0 && ( + + + {members.length} member{members.length !== 1 ? 's' : ''} + + + {members.map((member) => ( + + • {member.name ? `${member.name} (@${member.login})` : `@${member.login}`} + + ))} + + + )} + + + { + if (item.value === 'confirm') { + handleManualConfirm() + } else { + setMode('quick') + setManualStep('teams') + } + }} + /> + + + + ) + } + } + + // Team details mode + if (mode === 'details' && selectedTeamForDetails && org) { + return ( + { + setMode('browse') + setSelectedTeamForDetails(null) + }} + /> + ) + } + + // Browse mode + if (mode === 'browse') { + const browseOptions = [ + { label: '← Back to Quick Actions', value: 'back' }, + ...teams.map((team: GitHubTeam) => ({ + label: `@${org}/${team.slug} - ${team.name}${team.description ? ` (${team.description})` : ''}`, + value: team.slug, + team, + })), + ] + + return ( + + + + Select Team + + + + {loading && ( + + Loading teams from {org}... + + )} + {error && ( + + Error: {error.message} + + )} + + {!loading && !error && ( + <> + + Found {teams.length} team{teams.length !== 1 ? 's' : ''} in {org} + + {focusedTeam && Press Tab to view team members} + { + if (item.value === 'back') { + setMode('quick') + setFocusedTeam(null) + } else if (item.team) { + handleBrowseSelect(item.team) + } + }} + onFocusChange={(item: { value: string; team?: GitHubTeam } | null) => { + setFocusedTeam(item?.team ?? null) + }} + /> + + )} + + + ) + } + + // Default: Quick actions mode + return ( + + + + Team Filter Selection + + + + + Choose how to filter contributors: + + + + + ) +} diff --git a/apps/cli/src/ui/services/orchestrator-service.ts b/apps/cli/src/ui/services/orchestrator-service.ts new file mode 100644 index 00000000000..fe8f3777de2 --- /dev/null +++ b/apps/cli/src/ui/services/orchestrator-service.ts @@ -0,0 +1,68 @@ +import type { OrchestratorConfig } from '@universe/cli/src/core/orchestrator' +import { Orchestrator } from '@universe/cli/src/core/orchestrator' +import { createVercelAIProvider } from '@universe/cli/src/lib/ai-provider-vercel' +import { SqliteCacheProvider } from '@universe/cli/src/lib/cache-provider-sqlite' +import { type ProgressEvent, ProgressLogger, type ProgressStage } from '@universe/cli/src/lib/logger' + +export type { ProgressStage, ProgressEvent } + +export type ProgressCallback = (event: ProgressEvent) => void + +export class OrchestratorService { + private orchestrator: Orchestrator | null = null + private progressCallback: ProgressCallback | null = null + + async execute(config: OrchestratorConfig, onProgress?: ProgressCallback): Promise> { + this.progressCallback = onProgress || null + + // Create cache provider (unless bypassing cache) + const bypassCache = config.bypassCache || false + const cacheProvider = bypassCache ? undefined : new SqliteCacheProvider() + + // Create AI provider + const apiKey = process.env.ANTHROPIC_API_KEY + if (!apiKey) { + throw new Error('ANTHROPIC_API_KEY environment variable is required') + } + const aiProvider = createVercelAIProvider(apiKey) + + // Ensure repoPath is set in collect options + const configWithRepoPath: OrchestratorConfig = { + ...config, + collect: { + ...config.collect, + repoPath: config.collect.repoPath || process.cwd(), + }, + } + + // Create progress logger that emits events for interactive UI mode + const logger = new ProgressLogger((event: ProgressEvent) => { + this.emitProgress(event) + }, config.verbose || false) + + // Create orchestrator with progress logger + this.orchestrator = new Orchestrator({ + config: configWithRepoPath, + aiProvider, + cacheProvider, + logger, + }) + + try { + // Execute and capture the analysis results + const results = await this.orchestrator.execute() + return results + } finally { + // Close cache connection if used + if (cacheProvider) { + cacheProvider.close() + } + } + } + + private emitProgress(event: ProgressEvent): void { + if (this.progressCallback) { + this.progressCallback(event) + } + } +} diff --git a/apps/cli/src/ui/utils/colors.ts b/apps/cli/src/ui/utils/colors.ts new file mode 100644 index 00000000000..624a198f6c4 --- /dev/null +++ b/apps/cli/src/ui/utils/colors.ts @@ -0,0 +1,13 @@ +/** + * Theme color constants for Ink UI + * Using Uniswap pink color palette + */ +export const colors = { + primary: '#FC74FE', // Uniswap pink for interactive elements + success: '#00FF00', // Green for completions + warning: '#FFFF00', // Yellow for important info + error: '#FF0000', // Red for errors + muted: '#888888', // Gray for secondary text +} as const + +export type ColorName = keyof typeof colors diff --git a/apps/cli/src/ui/utils/format.ts b/apps/cli/src/ui/utils/format.ts new file mode 100644 index 00000000000..689d619a803 --- /dev/null +++ b/apps/cli/src/ui/utils/format.ts @@ -0,0 +1,18 @@ +/** + * Text formatting utilities for UI components + */ + +export function truncate(text: string, maxLength: number): string { + if (text.length <= maxLength) { + return text + } + return `${text.slice(0, maxLength - 3)}...` +} + +export function formatVersion(version: string): string { + return version +} + +export function formatBranch(branch: string): string { + return branch.replace('origin/releases/', '') +} diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json new file mode 100644 index 00000000000..748b6c093fa --- /dev/null +++ b/apps/cli/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../config/tsconfig/app.json", + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.json", "src/global.d.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.spec.tsx", "src/**/*.test.ts", "src/**/*.test.tsx"], + "compilerOptions": { + "noEmit": false, + "module": "esnext", + "moduleResolution": "bundler" + }, + "references": [] +} diff --git a/apps/cli/tsconfig.lint.json b/apps/cli/tsconfig.lint.json new file mode 100644 index 00000000000..79659c26038 --- /dev/null +++ b/apps/cli/tsconfig.lint.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "preserveSymlinks": true + }, + "include": ["**/*.ts", "**/*.tsx", "**/*.json"], + "exclude": ["node_modules"] +} diff --git a/apps/extension/.gitignore b/apps/extension/.gitignore index d96df7d9000..57bab834221 100644 --- a/apps/extension/.gitignore +++ b/apps/extension/.gitignore @@ -35,3 +35,5 @@ tsconfig.tsbuildinfo # E2E test artifacts e2e/test-results/ + +coverage/ diff --git a/apps/extension/jest-setup.js b/apps/extension/jest-setup.js index fcf81fe78e0..beb80a32847 100644 --- a/apps/extension/jest-setup.js +++ b/apps/extension/jest-setup.js @@ -2,6 +2,7 @@ import 'utilities/jest-package-mocks' import 'uniswap/jest-package-mocks' import 'wallet/jest-package-mocks' import 'config/jest-presets/ui/ui-package-mocks' +import 'react-native-gesture-handler/jestSetup'; import { chrome } from 'jest-chrome' import { AppearanceSettingType } from 'wallet/src/features/appearance/slice' @@ -51,6 +52,39 @@ global.chrome = { i18n: { ...global.chrome.i18n, getUILanguage: jest.fn().mockReturnValue(MOCK_LANGUAGE) + }, + storage: { + ...chrome.storage, + local: { + ...chrome.storage.local, + addListener: jest.fn(), + }, + session: { + get: jest.fn().mockImplementation((_keys, callback) => { + if (callback) { + callback({}) + } + return Promise.resolve({}) + }), + set: jest.fn().mockImplementation((_items, callback) => { + if (callback) { + callback() + } + return Promise.resolve() + }), + remove: jest.fn().mockImplementation((_keys, callback) => { + if (callback) { + callback() + } + return Promise.resolve() + }), + clear: jest.fn().mockImplementation((callback) => { + if (callback) { + callback() + } + return Promise.resolve() + }) + } } } @@ -69,10 +103,52 @@ const mockAppearanceSetting = AppearanceSettingType.System jest.mock('wallet/src/features/appearance/hooks', () => { return { useCurrentAppearanceSetting: () => mockAppearanceSetting, - } -}) -jest.mock('wallet/src/features/appearance/hooks', () => { - return { useSelectedColorScheme: () => 'light', } }) + +// Mock IntersectionObserver for Tamagui's useElementLayout +const IntersectionObserverMock = jest.fn().mockImplementation((callback) => ({ + observe: jest.fn((element) => { + // Immediately call the callback with a mock entry + if (callback && element) { + callback([ + { + target: element, + isIntersecting: true, + intersectionRatio: 1, + boundingClientRect: { + x: 0, + y: 0, + width: 100, + height: 100, + top: 0, + right: 100, + bottom: 100, + left: 0, + }, + intersectionRect: { + x: 0, + y: 0, + width: 100, + height: 100, + top: 0, + right: 100, + bottom: 100, + left: 0, + }, + rootBounds: null, + time: 0, + }, + ]) + } + }), + unobserve: jest.fn(), + disconnect: jest.fn(), + takeRecords: jest.fn().mockReturnValue([]), + root: null, + rootMargin: '', + thresholds: [], +})) + +global.IntersectionObserver = IntersectionObserverMock diff --git a/apps/extension/jest.config.js b/apps/extension/jest.config.js index 383e89187d0..cf7a5007b46 100644 --- a/apps/extension/jest.config.js +++ b/apps/extension/jest.config.js @@ -38,6 +38,10 @@ module.exports = { ], resolver: "/src/test/jest-resolver.js", displayName: 'Extension Wallet', + testMatch: [ + '/src/**/*.(spec|test).[jt]s?(x)', + '/config/**/*.(spec|test).[jt]s?(x)', + ], testPathIgnorePatterns: [ ...preset.testPathIgnorePatterns, '/e2e/', @@ -58,6 +62,5 @@ module.exports = { setupFiles: [ '../../config/jest-presets/jest/setup.js', './jest-setup.js', - '../../node_modules/react-native-gesture-handler/jestSetup.js', ], } diff --git a/apps/extension/package.json b/apps/extension/package.json index 9ac9b2af0df..afe0d102858 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "browserslist": "last 2 chrome versions", "dependencies": { - "@apollo/client": "3.10.4", + "@apollo/client": "3.11.10", "@datadog/browser-rum": "5.23.3", "@ethersproject/bignumber": "5.7.0", "@ethersproject/providers": "5.7.2", @@ -12,16 +12,18 @@ "@metamask/rpc-errors": "6.2.1", "@reduxjs/toolkit": "1.9.3", "@svgr/webpack": "8.0.1", - "@tamagui/core": "1.125.17", + "@tamagui/core": "1.136.1", "@tanstack/react-query": "5.77.2", "@types/uuid": "9.0.1", "@uniswap/analytics-events": "2.43.0", "@uniswap/client-embeddedwallet": "0.0.16", - "@uniswap/uniswapx-sdk": "3.0.0-beta.7", + "@uniswap/sdk-core": "7.9.0", "@uniswap/universal-router-sdk": "4.19.5", "@uniswap/v3-sdk": "3.25.2", "@uniswap/v4-sdk": "1.21.2", "@universe/api": "workspace:^", + "@universe/gating": "workspace:^", + "@universe/sessions": "workspace:^", "@wxt-dev/module-react": "1.1.3", "confusing-browser-globals": "1.0.11", "dotenv-webpack": "8.0.1", @@ -30,13 +32,13 @@ "eventemitter3": "5.0.1", "i18next": "23.10.0", "node-polyfill-webpack-plugin": "2.0.1", - "react": "18.3.1", - "react-dom": "18.3.1", + "react": "19.0.0", + "react-dom": "19.0.0", "react-i18next": "14.1.0", - "react-native": "0.77.2", - "react-native-gesture-handler": "2.22.1", - "react-native-reanimated": "3.16.7", - "react-native-svg": "15.11.2", + "react-native": "0.79.5", + "react-native-gesture-handler": "2.24.0", + "react-native-reanimated": "3.19.3", + "react-native-svg": "15.13.0", "react-native-web": "0.19.13", "react-qr-code": "2.0.12", "react-redux": "8.0.5", @@ -66,24 +68,24 @@ "@playwright/test": "1.49.1", "@pmmmwh/react-refresh-webpack-plugin": "0.5.11", "@testing-library/dom": "10.4.0", - "@testing-library/react": "16.1.0", + "@testing-library/react": "16.3.0", "@types/chrome": "0.0.304", "@types/jest": "29.5.14", "@types/ms": "0.7.31", "@types/node": "22.13.1", - "@types/react": "18.3.18", - "@types/react-dom": "18.3.1", + "@types/react": "19.0.10", + "@types/react-dom": "19.0.6", "@types/redux-logger": "3.0.9", "@types/redux-persist-webextension-storage": "1.0.3", "@types/ua-parser-js": "0.7.31", "@uniswap/eslint-config": "workspace:^", - "@welldone-software/why-did-you-render": "8.0.1", + "@welldone-software/why-did-you-render": "10.0.1", "clean-webpack-plugin": "4.0.0", "concurrently": "8.2.2", "copy-webpack-plugin": "11.0.0", "css-loader": "6.11.0", "esbuild-loader": "3.2.0", - "eslint": "8.44.0", + "eslint": "8.57.1", "jest": "29.7.0", "jest-chrome": "0.8.0", "jest-environment-jsdom": "29.5.0", @@ -94,8 +96,8 @@ "serve": "14.2.4", "style-loader": "3.3.2", "swc-loader": "0.2.6", - "tamagui-loader": "1.125.17", - "typescript": "5.3.3", + "tamagui-loader": "1.136.1", + "typescript": "5.8.3", "webpack": "5.90.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.1" diff --git a/apps/extension/src/app/apollo.tsx b/apps/extension/src/app/apollo.tsx index 41416c70604..e006c3ed06a 100644 --- a/apps/extension/src/app/apollo.tsx +++ b/apps/extension/src/app/apollo.tsx @@ -1,8 +1,7 @@ import { ApolloProvider } from '@apollo/client/react/context' -import { PropsWithChildren, useEffect } from 'react' +import { PropsWithChildren } from 'react' import { localStorage } from 'redux-persist-webextension-storage' import { getReduxStore } from 'src/store/store' -import { initializePortfolioQueryOverrides } from 'uniswap/src/data/rest/portfolioBalanceOverrides' // biome-ignore lint/style/noRestrictedImports: Direct wallet import needed for Apollo client setup in extension context import { usePersistedApolloClient } from 'wallet/src/data/apollo/usePersistedApolloClient' @@ -16,14 +15,9 @@ export function GraphqlProvider({ children }: PropsWithChildren): JSX.E reduxStore: getReduxStore(), }) - useEffect(() => { - if (apolloClient) { - initializePortfolioQueryOverrides({ store: getReduxStore(), apolloClient }) - } - }, [apolloClient]) - if (!apolloClient) { return <> } + return {children} } diff --git a/apps/extension/src/app/components/AutoLockProvider.test.tsx b/apps/extension/src/app/components/AutoLockProvider.test.tsx index 0b082e9cc91..5e3f69795e7 100644 --- a/apps/extension/src/app/components/AutoLockProvider.test.tsx +++ b/apps/extension/src/app/components/AutoLockProvider.test.tsx @@ -1,252 +1,337 @@ -import { waitFor } from '@testing-library/react' import React from 'react' import { AutoLockProvider } from 'src/app/components/AutoLockProvider' import { render } from 'src/test/test-utils' import { FiatCurrency } from 'uniswap/src/features/fiatCurrency/constants' import { Language } from 'uniswap/src/features/language/constants' import { DeviceAccessTimeout } from 'uniswap/src/features/settings/constants' -import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants' -import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { logger } from 'utilities/src/logger/logger' -import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' // Mock dependencies jest.mock('uniswap/src/extension/useIsChromeWindowFocused') -jest.mock('uniswap/src/features/telemetry/send') jest.mock('utilities/src/logger/logger') -jest.mock('wallet/src/features/wallet/Keyring/Keyring') - -// Import mocked modules with proper typing -import { useIsChromeWindowFocusedWithTimeout } from 'uniswap/src/extension/useIsChromeWindowFocused' - -const mockUseIsChromeWindowFocusedWithTimeout = useIsChromeWindowFocusedWithTimeout as jest.MockedFunction< - typeof useIsChromeWindowFocusedWithTimeout -> -const mockSendAnalyticsEvent = sendAnalyticsEvent as jest.MockedFunction -const mockLogger = logger as jest.Mocked -const mockKeyring = Keyring as jest.Mocked - -// Helper functions for common test patterns -const renderAutoLockProvider = ( - deviceAccessTimeout: DeviceAccessTimeout, - children: React.ReactNode =
Test
, -) => { - return render({children}, { - preloadedState: { - userSettings: { - currentLanguage: Language.English, - currentCurrency: FiatCurrency.UnitedStatesDollar, - hideSmallBalances: true, - hideSpamTokens: true, - hapticsEnabled: true, - deviceAccessTimeout, - }, - }, - }) +jest.mock('src/app/hooks/useIsWalletUnlocked', () => ({ + useIsWalletUnlocked: jest.fn(), + isWalletUnlocked: null, +})) + +// Import mocked modules +import { useIsWalletUnlocked } from 'src/app/hooks/useIsWalletUnlocked' +import { useIsChromeWindowFocused } from 'uniswap/src/extension/useIsChromeWindowFocused' + +const mockUseIsChromeWindowFocused = jest.mocked(useIsChromeWindowFocused) +const mockUseIsWalletUnlocked = jest.mocked(useIsWalletUnlocked) +const mockLogger = jest.mocked(logger) + +// Mock chrome.alarms API +const mockChromeAlarms = { + create: jest.fn(), + clear: jest.fn(), } -const simulateFocusChange = (component: ReturnType) => (fromFocused: boolean, toFocused: boolean) => { - mockUseIsChromeWindowFocusedWithTimeout.mockReturnValue(fromFocused) - const { rerender } = component +global.chrome = { + ...global.chrome, + alarms: mockChromeAlarms as unknown as typeof chrome.alarms, +} - mockUseIsChromeWindowFocusedWithTimeout.mockReturnValue(toFocused) - rerender( +// Helper function +const renderAutoLockProvider = (deviceAccessTimeout: DeviceAccessTimeout) => { + return render(
Test
, + { + preloadedState: { + userSettings: { + currentLanguage: Language.English, + currentCurrency: FiatCurrency.UnitedStatesDollar, + hideSmallBalances: true, + hideSpamTokens: true, + hapticsEnabled: true, + deviceAccessTimeout, + }, + }, + }, ) } -const expectWalletLockCalled = async (times: number = 1) => { - await waitFor(() => { - expect(mockKeyring.lock).toHaveBeenCalledTimes(times) - }) -} +const simulateFocusChange = (component: ReturnType) => (fromFocused: boolean, toFocused: boolean) => { + mockUseIsChromeWindowFocused.mockReturnValue(fromFocused) + const { rerender } = component -const expectAnalyticsEventCalled = async () => { - await waitFor(() => { - expect(mockSendAnalyticsEvent).toHaveBeenCalledWith(ExtensionEventName.ChangeLockedState, { - locked: true, - location: 'background', - }) - }) + mockUseIsChromeWindowFocused.mockReturnValue(toFocused) + rerender() } describe('AutoLockProvider', () => { beforeEach(() => { jest.clearAllMocks() - mockUseIsChromeWindowFocusedWithTimeout.mockReturnValue(true) - mockKeyring.lock.mockResolvedValue(true) - mockSendAnalyticsEvent.mockImplementation(() => {}) + mockUseIsChromeWindowFocused.mockReturnValue(true) + mockUseIsWalletUnlocked.mockReturnValue(true) mockLogger.debug.mockImplementation(() => {}) mockLogger.error.mockImplementation(() => {}) + mockChromeAlarms.create.mockImplementation(() => {}) + mockChromeAlarms.clear.mockImplementation(() => {}) }) - describe('window focus monitoring', () => { - const testTimeoutValues = [ - { timeout: DeviceAccessTimeout.FiveMinutes, expectedMs: 5 * 60 * 1000, description: '5 minutes' }, - { timeout: DeviceAccessTimeout.ThirtyMinutes, expectedMs: 30 * 60 * 1000, description: '30 minutes' }, - { timeout: DeviceAccessTimeout.OneHour, expectedMs: 60 * 60 * 1000, description: '1 hour' }, - { timeout: DeviceAccessTimeout.TwentyFourHours, expectedMs: 24 * 60 * 60 * 1000, description: '24 hours' }, - { - timeout: DeviceAccessTimeout.Never, - expectedMs: Number.MAX_SAFE_INTEGER, - description: 'Never (MAX_SAFE_INTEGER)', - }, - ] + describe('mount behavior', () => { + it('should clear alarm on mount', () => { + renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - testTimeoutValues.forEach(({ timeout, expectedMs, description }) => { - it(`should call useIsChromeWindowFocusedWithTimeout with correct timeout for ${description}`, () => { - renderAutoLockProvider(timeout) - expect(mockUseIsChromeWindowFocusedWithTimeout).toHaveBeenCalledWith(expectedMs) - }) + expect(mockChromeAlarms.clear).toHaveBeenCalledWith('AutoLockAlarm') + }) + + it('should always render children', () => { + const { container } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + expect(container.textContent).toBe('Test') }) }) - describe('wallet locking behavior', () => { - it('should lock wallet when window loses focus and timeout is configured', async () => { + describe('unmount behavior', () => { + it('should not schedule alarm on unmount (handled by background port disconnect)', () => { + const { unmount } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + + unmount() + + // Unmount no longer schedules alarm - this is handled by background script + expect(mockChromeAlarms.create).not.toHaveBeenCalled() + }) + }) + + describe('focus change behavior (while sidebar is open)', () => { + it('should schedule alarm when window loses focus and wallet is unlocked', () => { const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - expect(mockKeyring.lock).not.toHaveBeenCalled() + mockChromeAlarms.create.mockClear() // Clear the mount call simulateFocusChange(component)(true, false) - await expectWalletLockCalled() + + expect(mockChromeAlarms.create).toHaveBeenCalledWith('AutoLockAlarm', { + delayInMinutes: 5, + }) }) - it('should not lock wallet when window is focused', () => { - mockUseIsChromeWindowFocusedWithTimeout.mockReturnValue(true) - renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - expect(mockKeyring.lock).not.toHaveBeenCalled() + it('should clear alarm when window regains focus', () => { + const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + mockChromeAlarms.clear.mockClear() // Clear the mount call + + // First lose focus (creates alarm) + simulateFocusChange(component)(true, false) + expect(mockChromeAlarms.create).toHaveBeenCalled() + + // Then regain focus + simulateFocusChange(component)(false, true) + + expect(mockChromeAlarms.clear).toHaveBeenCalledWith('AutoLockAlarm') }) - it('should not lock wallet when timeout is set to Never', () => { - mockUseIsChromeWindowFocusedWithTimeout.mockReturnValue(false) - renderAutoLockProvider(DeviceAccessTimeout.Never) - expect(mockKeyring.lock).not.toHaveBeenCalled() + it('should not schedule alarm when window loses focus and wallet is locked', () => { + mockUseIsWalletUnlocked.mockReturnValue(false) + const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + mockChromeAlarms.create.mockClear() // Clear the mount call + + simulateFocusChange(component)(true, false) + + expect(mockChromeAlarms.create).not.toHaveBeenCalled() }) - it('should send analytics event when locking wallet', async () => { - const component = renderAutoLockProvider(DeviceAccessTimeout.ThirtyMinutes) + it('should not schedule alarm when window loses focus and timeout is Never', () => { + const component = renderAutoLockProvider(DeviceAccessTimeout.Never) + mockChromeAlarms.create.mockClear() // Clear the mount call + simulateFocusChange(component)(true, false) - await expectAnalyticsEventCalled() - expect(mockSendAnalyticsEvent).toHaveBeenCalledTimes(1) + + expect(mockChromeAlarms.create).not.toHaveBeenCalled() }) }) - describe('error handling', () => { - it('should handle Keyring.lock() errors gracefully', async () => { - const lockError = new Error('Failed to lock keyring') - mockKeyring.lock.mockRejectedValue(lockError) + describe('wallet state changes', () => { + it('should clear alarm when wallet becomes locked', () => { + mockUseIsWalletUnlocked.mockReturnValue(true) + const { rerender } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - simulateFocusChange(component)(true, false) + // Clear the initial mount call + mockChromeAlarms.clear.mockClear() - await waitFor(() => { - expect(mockLogger.error).toHaveBeenCalledWith(lockError, { - tags: { - file: 'AutoLockProvider.tsx', - function: 'lockWallet', - }, - }) - }) + // Wallet becomes locked + mockUseIsWalletUnlocked.mockReturnValue(false) + rerender() + + expect(mockChromeAlarms.clear).toHaveBeenCalledWith('AutoLockAlarm') }) - it('should not send analytics event when lock fails', async () => { - const lockError = new Error('Failed to lock keyring') - mockKeyring.lock.mockRejectedValue(lockError) + it('should clear alarm when wallet becomes unlocked', () => { + mockUseIsWalletUnlocked.mockReturnValue(false) + const { rerender } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - simulateFocusChange(component)(true, false) + // Clear the initial mount call + mockChromeAlarms.clear.mockClear() - await waitFor(() => { - expect(mockLogger.error).toHaveBeenCalledWith(lockError, { - tags: { - file: 'AutoLockProvider.tsx', - function: 'lockWallet', - }, - }) - }) + // Wallet becomes unlocked + mockUseIsWalletUnlocked.mockReturnValue(true) + rerender() - expect(mockSendAnalyticsEvent).not.toHaveBeenCalled() - }) - - it('should handle undefined deviceAccessTimeout gracefully', () => { - render( - -
Test
-
, - { - preloadedState: { - userSettings: { - currentLanguage: Language.English, - currentCurrency: FiatCurrency.UnitedStatesDollar, - hideSmallBalances: true, - hideSpamTokens: true, - hapticsEnabled: true, - deviceAccessTimeout: undefined as any, - }, - }, - }, - ) + expect(mockChromeAlarms.clear).toHaveBeenCalledWith('AutoLockAlarm') + }) + + it('should not clear alarm on initial render (only mount clear)', () => { + mockUseIsWalletUnlocked.mockReturnValue(true) + renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + + // Only the mount clear should have been called + expect(mockChromeAlarms.clear).toHaveBeenCalledTimes(1) + }) + }) + + describe('combined scenarios', () => { + it('should handle mount -> unmount -> remount correctly', () => { + // First mount + const { unmount } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + expect(mockChromeAlarms.clear).toHaveBeenCalledTimes(1) + + // Unmount (alarm scheduling now handled in background) + unmount() + + // Second mount (clears alarm) + renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + expect(mockChromeAlarms.clear).toHaveBeenCalledTimes(2) + }) + + it('should handle wallet unlock during mounted state', () => { + mockUseIsWalletUnlocked.mockReturnValue(false) + const { rerender } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + + mockChromeAlarms.clear.mockClear() + + // Wallet unlocks while mounted + mockUseIsWalletUnlocked.mockReturnValue(true) + rerender() + + // Should clear alarm due to wallet state change + expect(mockChromeAlarms.clear).toHaveBeenCalled() + }) - expect(mockUseIsChromeWindowFocusedWithTimeout).toHaveBeenCalledWith(30 * 60 * 1000) + it('should handle wallet lock during mounted state', () => { + mockUseIsWalletUnlocked.mockReturnValue(true) + const { rerender } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + + mockChromeAlarms.clear.mockClear() + + // Wallet locks while mounted + mockUseIsWalletUnlocked.mockReturnValue(false) + rerender() + + // Should clear alarm due to wallet state change + expect(mockChromeAlarms.clear).toHaveBeenCalled() }) }) - describe('focus state changes', () => { - it('should react to focus state changes from focused to unfocused', async () => { + describe('error handling', () => { + it('should handle chrome.alarms.create errors gracefully', () => { + const error = new Error('Permission denied') + mockChromeAlarms.create.mockImplementationOnce(() => { + throw error + }) + const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - expect(mockKeyring.lock).not.toHaveBeenCalled() + mockChromeAlarms.create.mockClear() - simulateFocusChange(component)(true, false) - await expectWalletLockCalled() + // This should not throw, error should be logged + expect(() => { + simulateFocusChange(component)(true, false) + }).not.toThrow() + + expect(mockLogger.error).toHaveBeenCalledWith(error, { + tags: { file: 'AutoLockProvider', function: 'createAutoLockAlarm' }, + extra: { delayInMinutes: 5 }, + }) + }) + + it('should handle chrome.alarms.clear errors gracefully', () => { + const error = new Error('Permission denied') + mockChromeAlarms.clear.mockImplementationOnce(() => { + throw error + }) + + // This should not throw, error should be logged + expect(() => { + renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + }).not.toThrow() + + expect(mockLogger.error).toHaveBeenCalledWith(error, { + tags: { file: 'AutoLockProvider', function: 'clearAutoLockAlarm' }, + extra: { reason: 'Cleared auto-lock alarm (sidebar opened)' }, + }) }) - it('should not trigger multiple locks when already unfocused', async () => { + it('should continue to function after chrome.alarms errors', () => { + // First call fails + mockChromeAlarms.clear.mockImplementationOnce(() => { + throw new Error('Permission denied') + }) + const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + // Clear the error and mock calls + mockLogger.error.mockClear() + mockChromeAlarms.clear.mockClear() + mockChromeAlarms.create.mockClear() + + // Subsequent calls should still work simulateFocusChange(component)(true, false) - await expectWalletLockCalled() + expect(mockChromeAlarms.create).toHaveBeenCalledWith('AutoLockAlarm', { + delayInMinutes: 5, + }) + expect(mockLogger.error).not.toHaveBeenCalled() + }) + }) - // Rerender with same unfocused state - should not trigger additional calls - const { rerender } = component - rerender( - -
Test
-
, - ) + describe('edge cases', () => { + it('should handle rapid mount/unmount cycles', () => { + const { unmount: unmount1 } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + unmount1() - expect(mockKeyring.lock).toHaveBeenCalledTimes(1) + const { unmount: unmount2 } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + unmount2() + + // Should have cleared alarm twice (once per mount) + expect(mockChromeAlarms.clear).toHaveBeenCalledTimes(2) + // No create calls because unmount scheduling is handled in background + expect(mockChromeAlarms.create).not.toHaveBeenCalled() }) - it('should not lock again when returning to focused state', async () => { + it('should handle rapid focus changes', () => { const component = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) + mockChromeAlarms.create.mockClear() + mockChromeAlarms.clear.mockClear() + // Lose focus simulateFocusChange(component)(true, false) - await expectWalletLockCalled() + expect(mockChromeAlarms.create).toHaveBeenCalledTimes(1) - // Change back to focused + // Regain focus simulateFocusChange(component)(false, true) - expect(mockKeyring.lock).toHaveBeenCalledTimes(1) - }) - }) + expect(mockChromeAlarms.clear).toHaveBeenCalledTimes(1) - describe('timeout setting changes', () => { - it('should respond to timeout setting changes', () => { - renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - expect(mockUseIsChromeWindowFocusedWithTimeout).toHaveBeenLastCalledWith(5 * 60 * 1000) - - renderAutoLockProvider(DeviceAccessTimeout.OneHour) - expect(mockUseIsChromeWindowFocusedWithTimeout).toHaveBeenLastCalledWith(60 * 60 * 1000) + // Lose focus again + simulateFocusChange(component)(true, false) + expect(mockChromeAlarms.create).toHaveBeenCalledTimes(2) }) - it('should handle timeout change from configured to Never', () => { - mockUseIsChromeWindowFocusedWithTimeout.mockReturnValue(false) + it('should prioritize wallet state changes over focus changes (race condition)', () => { + mockUseIsWalletUnlocked.mockReturnValue(true) + mockUseIsChromeWindowFocused.mockReturnValue(true) + const { rerender } = renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - renderAutoLockProvider(DeviceAccessTimeout.FiveMinutes) - expect(mockUseIsChromeWindowFocusedWithTimeout).toHaveBeenCalledWith(5 * 60 * 1000) + mockChromeAlarms.clear.mockClear() + mockChromeAlarms.create.mockClear() + + // Simulate simultaneous wallet lock + focus loss + mockUseIsWalletUnlocked.mockReturnValue(false) + mockUseIsChromeWindowFocused.mockReturnValue(false) + rerender() - renderAutoLockProvider(DeviceAccessTimeout.Never) - expect(mockUseIsChromeWindowFocusedWithTimeout).toHaveBeenLastCalledWith(Number.MAX_SAFE_INTEGER) + // Should only clear alarm (wallet state change priority), not schedule + expect(mockChromeAlarms.clear).toHaveBeenCalledTimes(1) + expect(mockChromeAlarms.create).not.toHaveBeenCalled() }) }) }) diff --git a/apps/extension/src/app/components/AutoLockProvider.tsx b/apps/extension/src/app/components/AutoLockProvider.tsx index 4d7989e0fc7..68c18d819e6 100644 --- a/apps/extension/src/app/components/AutoLockProvider.tsx +++ b/apps/extension/src/app/components/AutoLockProvider.tsx @@ -1,74 +1,98 @@ -import { PropsWithChildren, useEffect } from 'react' +import { PropsWithChildren, useEffect, useRef } from 'react' import { useSelector } from 'react-redux' -import { ExtensionState } from 'src/store/extensionReducer' -import { useIsChromeWindowFocusedWithTimeout } from 'uniswap/src/extension/useIsChromeWindowFocused' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' -import { deviceAccessTimeoutToMs } from 'uniswap/src/features/settings/constants' -import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants' -import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' +import { useIsWalletUnlocked } from 'src/app/hooks/useIsWalletUnlocked' +import { useIsChromeWindowFocused } from 'uniswap/src/extension/useIsChromeWindowFocused' +import { selectDeviceAccessTimeoutMinutes } from 'uniswap/src/features/settings/selectors' import { logger } from 'utilities/src/logger/logger' -import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -const AUTO_LOCK_ALARM_NAME = 'AutoLockAlarm' +export const AUTO_LOCK_ALARM_NAME = 'AutoLockAlarm' /** - * AutoLockProvider monitors window focus and automatically locks the wallet - * after the configured inactivity timeout period. + * Helper to safely clear the auto-lock alarm with error handling + */ +function clearAutoLockAlarm(reason: string): void { + try { + chrome.alarms.clear(AUTO_LOCK_ALARM_NAME) + logger.debug('AutoLockProvider', 'clearAutoLockAlarm', reason) + } catch (error) { + logger.error(error, { + tags: { file: 'AutoLockProvider', function: 'clearAutoLockAlarm' }, + extra: { reason }, + }) + } +} + +/** + * Helper to safely create the auto-lock alarm with error handling + */ +function createAutoLockAlarm(delayInMinutes: number): void { + try { + chrome.alarms.create(AUTO_LOCK_ALARM_NAME, { delayInMinutes }) + logger.debug('AutoLockProvider', 'createAutoLockAlarm', `Scheduled auto-lock alarm for ${delayInMinutes} minutes`) + } catch (error) { + logger.error(error, { + tags: { file: 'AutoLockProvider', function: 'createAutoLockAlarm' }, + extra: { delayInMinutes }, + }) + } +} + +/** + * AutoLockProvider schedules chrome alarms to automatically lock the wallet + * after the configured timeout period when the sidebar is not focused. * - * This component should be placed high in the component tree to ensure - * it's always active when the extension is running. + * Uses chrome.alarms API which persists even when the extension is closed, + * ensuring reliable auto-lock behavior. */ export function AutoLockProvider({ children }: PropsWithChildren): JSX.Element { - const deviceAccessTimeout = useSelector((state: ExtensionState) => state.userSettings.deviceAccessTimeout) - const useAlarmsApi = useFeatureFlag(FeatureFlags.UseAlarmsApi) - const timeoutMs = deviceAccessTimeoutToMs(deviceAccessTimeout) + const delayInMinutes = useSelector(selectDeviceAccessTimeoutMinutes) + const isWalletUnlocked = useIsWalletUnlocked() + const isChromeWindowFocused = useIsChromeWindowFocused() - // Use the window focus hook with the configured timeout - // If timeoutMs is undefined (Never setting), use a very large number to effectively disable - const isChromeWindowFocused = useIsChromeWindowFocusedWithTimeout(timeoutMs ?? Number.MAX_SAFE_INTEGER) + // Ref to track previous focus state + const prevFocusedRef = useRef(true) + // Ref to track previous unlock state + const prevUnlockedRef = useRef(null) - // Maintain chrome.alarms usage behind feature flag + // On mount: Clear any existing alarm (sidebar just opened) useEffect(() => { - if (useAlarmsApi) { - chrome.alarms.create(AUTO_LOCK_ALARM_NAME, { - delayInMinutes: 1000, - }) + clearAutoLockAlarm('Cleared auto-lock alarm (sidebar opened)') + }, []) + + useEffect(() => { + // Skip if timeout not configured (Never) + if (delayInMinutes === undefined) { + clearAutoLockAlarm('Cleared auto-lock alarm (timeout not configured)') + return } - return () => { - chrome.alarms.clear(AUTO_LOCK_ALARM_NAME) + const prevFocused = prevFocusedRef.current + const prevUnlocked = prevUnlockedRef.current + prevFocusedRef.current = isChromeWindowFocused + prevUnlockedRef.current = isWalletUnlocked + + // Skip first render for unlock state + if (prevUnlocked === null) { + return } - }, [useAlarmsApi]) - useEffect(() => { - // Only lock if timeout is configured (not "Never") - if (timeoutMs === undefined) { + // Clear alarm when wallet state changes (locked or unlocked) + if (prevUnlocked !== isWalletUnlocked) { + clearAutoLockAlarm(`Cleared auto-lock alarm (wallet ${isWalletUnlocked ? 'unlocked' : 'locked'})`) return } - if (!isChromeWindowFocused) { - const lockWallet = async (): Promise => { - try { - logger.debug('AutoLockProvider', 'lockWallet', 'Locking wallet due to inactivity') - await Keyring.lock() - sendAnalyticsEvent(ExtensionEventName.ChangeLockedState, { - locked: true, - location: 'background', - }) - } catch (error) { - logger.error(error, { - tags: { - file: 'AutoLockProvider.tsx', - function: 'lockWallet', - }, - }) - } - } + // When window loses focus AND wallet is unlocked: schedule alarm + if (prevFocused && !isChromeWindowFocused && isWalletUnlocked) { + createAutoLockAlarm(delayInMinutes) + return + } - lockWallet() + // When window regains focus: clear alarm + if (!prevFocused && isChromeWindowFocused) { + clearAutoLockAlarm('Cleared auto-lock alarm (window focused)') } - }, [isChromeWindowFocused, timeoutMs]) + }, [isChromeWindowFocused, isWalletUnlocked, delayInMinutes]) return <>{children} } diff --git a/apps/extension/src/app/components/tabs/ActivityTab.tsx b/apps/extension/src/app/components/tabs/ActivityTab.tsx index a0a8074467f..95ca43b900f 100644 --- a/apps/extension/src/app/components/tabs/ActivityTab.tsx +++ b/apps/extension/src/app/components/tabs/ActivityTab.tsx @@ -1,5 +1,6 @@ import { memo } from 'react' -import { ScrollView } from 'ui/src' +import { Flex, Loader, ScrollView } from 'ui/src' +import { useInfiniteScroll } from 'utilities/src/react/useInfiniteScroll' import { useActivityDataWallet } from 'wallet/src/features/activity/useActivityDataWallet' export const ActivityTab = memo(function _ActivityTab({ @@ -9,9 +10,16 @@ export const ActivityTab = memo(function _ActivityTab({ address: Address skip?: boolean }): JSX.Element { - const { maybeEmptyComponent, renderActivityItem, sectionData } = useActivityDataWallet({ - evmOwner: address, - skip, + const { maybeEmptyComponent, renderActivityItem, sectionData, fetchNextPage, hasNextPage, isFetchingNextPage } = + useActivityDataWallet({ + evmOwner: address, + skip, + }) + + const { sentinelRef } = useInfiniteScroll({ + onLoadMore: fetchNextPage, + hasNextPage, + isFetching: isFetchingNextPage, }) if (maybeEmptyComponent) { @@ -22,6 +30,14 @@ export const ActivityTab = memo(function _ActivityTab({ {/* `sectionData` will be either an array of transactions or an array of loading skeletons */} {sectionData.map((item, index) => renderActivityItem({ item, index }))} + {/* Show skeleton loading indicator while fetching next page */} + {isFetchingNextPage && ( + + + + )} + {/* Intersection observer sentinel for infinite scroll */} + ) }) diff --git a/apps/extension/src/app/core/BaseAppContainer.tsx b/apps/extension/src/app/core/BaseAppContainer.tsx index 09f9327325c..c29cba1056e 100644 --- a/apps/extension/src/app/core/BaseAppContainer.tsx +++ b/apps/extension/src/app/core/BaseAppContainer.tsx @@ -1,3 +1,14 @@ +import { ApiInit, getEntryGatewayUrl, provideSessionService } from '@universe/api' +import { + getIsSessionServiceEnabled, + getIsSessionUpgradeAutoEnabled, + useIsSessionServiceEnabled, +} from '@universe/gating' +import { + createChallengeSolverService, + createSessionInitializationService, + SessionInitializationService, +} from '@universe/sessions' import { PropsWithChildren } from 'react' import { I18nextProvider } from 'react-i18next' import { GraphqlProvider } from 'src/app/apollo' @@ -14,6 +25,46 @@ import { ErrorBoundary } from 'wallet/src/components/ErrorBoundary/ErrorBoundary import { AccountsStoreContextProvider } from 'wallet/src/features/accounts/store/provider' import { SharedWalletProvider } from 'wallet/src/providers/SharedWalletProvider' +const provideSessionInitializationService = (): SessionInitializationService => + createSessionInitializationService({ + getSessionService: () => + provideSessionService({ + getBaseUrl: getEntryGatewayUrl, + getIsSessionServiceEnabled, + }), + challengeSolverService: createChallengeSolverService(), + getIsSessionUpgradeAutoEnabled, + }) + +function BaseAppContainerInner({ children }: PropsWithChildren): JSX.Element { + const isSessionServiceEnabled = useIsSessionServiceEnabled() + + return ( + + + + + + + + + + + {children} + + + + + + + + + ) +} + export function BaseAppContainer({ children, appName, @@ -21,24 +72,7 @@ export function BaseAppContainer({ return ( - - - - - - - - - - {children} - - - - - - - - + {children} ) diff --git a/apps/extension/src/app/core/SidebarApp.tsx b/apps/extension/src/app/core/SidebarApp.tsx index db280c45dfb..eeadc95bfa9 100644 --- a/apps/extension/src/app/core/SidebarApp.tsx +++ b/apps/extension/src/app/core/SidebarApp.tsx @@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from 'react' import { useDispatch } from 'react-redux' import { createHashRouter, RouterProvider } from 'react-router' import { PersistGate } from 'redux-persist/integration/react' +import { AUTO_LOCK_ALARM_NAME } from 'src/app/components/AutoLockProvider' import { ErrorElement } from 'src/app/components/ErrorElement' import { useTraceSidebarDappUrl } from 'src/app/components/Trace/useTraceSidebarDappUrl' import { BaseAppContainer } from 'src/app/core/BaseAppContainer' @@ -188,8 +189,21 @@ function useDappRequestPortListener(): void { }, PORT_PING_INTERVAL) } +/** + * Creates a connection so that the background script can detect when the sidebar is closed and schedule an auto-lock alarm. + */ +function useAutoLockAlarmConnection(): void { + useEffect(() => { + const port = chrome.runtime.connect({ name: AUTO_LOCK_ALARM_NAME }) + return () => { + port.disconnect() + } + }, []) +} + function SidebarWrapper(): JSX.Element { useDappRequestPortListener() + useAutoLockAlarmConnection() useTestnetModeForLoggingAndAnalytics() const resetUnitagsQueries = useResetUnitagsQueries() diff --git a/apps/extension/src/app/core/StatsigProvider.tsx b/apps/extension/src/app/core/StatsigProvider.tsx index 63f55e72d4f..7c6aeb01b76 100644 --- a/apps/extension/src/app/core/StatsigProvider.tsx +++ b/apps/extension/src/app/core/StatsigProvider.tsx @@ -1,10 +1,9 @@ import { useQuery } from '@tanstack/react-query' import { SharedQueryClient } from '@universe/api' +import { StatsigCustomAppValue, StatsigUser } from '@universe/gating' import { useEffect, useState } from 'react' import { makeStatsigUser } from 'src/app/core/initStatSigForBrowserScripts' -import { StatsigCustomAppValue } from 'uniswap/src/features/gating/constants' import { StatsigProviderWrapper } from 'uniswap/src/features/gating/StatsigProviderWrapper' -import { StatsigUser } from 'uniswap/src/features/gating/sdk/statsig' import { initializeDatadog } from 'uniswap/src/utils/datadog' import { uniqueIdQuery } from 'utilities/src/device/uniqueIdQuery' diff --git a/apps/extension/src/app/core/initStatSigForBrowserScripts.tsx b/apps/extension/src/app/core/initStatSigForBrowserScripts.tsx index 3e3ffaaa9ba..7a495477c72 100644 --- a/apps/extension/src/app/core/initStatSigForBrowserScripts.tsx +++ b/apps/extension/src/app/core/initStatSigForBrowserScripts.tsx @@ -1,6 +1,5 @@ +import { StatsigClient, StatsigCustomAppValue, StatsigUser } from '@universe/gating' import { config } from 'uniswap/src/config' -import { StatsigCustomAppValue } from 'uniswap/src/features/gating/constants' -import { StatsigClient, StatsigUser } from 'uniswap/src/features/gating/sdk/statsig' import { statsigBaseConfig } from 'uniswap/src/features/gating/statsigBaseConfig' import { getUniqueId } from 'utilities/src/device/uniqueId' import { logger } from 'utilities/src/logger/logger' diff --git a/apps/extension/src/app/features/accounts/AccountItem.tsx b/apps/extension/src/app/features/accounts/AccountItem.tsx index dc2db86aa01..99eb7319601 100644 --- a/apps/extension/src/app/features/accounts/AccountItem.tsx +++ b/apps/extension/src/app/features/accounts/AccountItem.tsx @@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next' import { useDispatch } from 'react-redux' import { EditLabelModal } from 'src/app/features/accounts/EditLabelModal' import { removeAllDappConnectionsForAccount } from 'src/app/features/dapp/actions' -import { AppRoutes, SettingsRoutes } from 'src/app/navigation/constants' -import { useExtensionNavigation } from 'src/app/navigation/utils' +import { AppRoutes, SettingsRoutes, UnitagClaimRoutes } from 'src/app/navigation/constants' +import { focusOrCreateUnitagTab, useExtensionNavigation } from 'src/app/navigation/utils' import { Flex, Text, TouchableArea } from 'ui/src' import { CopySheets, Edit, Ellipsis, Globe, TrashFilled } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' @@ -97,13 +97,17 @@ export function AccountItem({ address, onAccountSelect, balanceUSD }: AccountIte label: !accountHasUnitag ? t('account.wallet.menu.edit.title') : t('settings.setting.wallet.action.editProfile'), - onPress: (e: BaseSyntheticEvent): void => { + onPress: async (e: BaseSyntheticEvent): Promise => { // We have to manually prevent click-through because the way the context menu is inside of a TouchableArea in this component it // means that without it the TouchableArea handler will get called e.preventDefault() e.stopPropagation() - setShowEditLabelModal(true) + if (accountHasUnitag) { + await focusOrCreateUnitagTab(address, UnitagClaimRoutes.EditProfile) + } else { + setShowEditLabelModal(true) + } }, Icon: Edit, }, @@ -134,7 +138,7 @@ export function AccountItem({ address, onAccountSelect, balanceUSD }: AccountIte iconProps: { color: '$statusCritical' }, }, ] - }, [accountHasUnitag, onPressCopyAddress, navigateTo, t]) + }, [accountHasUnitag, onPressCopyAddress, navigateTo, t, address]) return ( <> diff --git a/apps/extension/src/app/features/accounts/AccountSwitcherScreen.tsx b/apps/extension/src/app/features/accounts/AccountSwitcherScreen.tsx index f73975084a1..41a94dd16f6 100644 --- a/apps/extension/src/app/features/accounts/AccountSwitcherScreen.tsx +++ b/apps/extension/src/app/features/accounts/AccountSwitcherScreen.tsx @@ -1,3 +1,4 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { BaseSyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -18,9 +19,13 @@ import { Button, Flex, Popover, ScrollView, Text, TouchableArea, useSporeColors import { Ellipsis, Globe, Person, TrashFilled, WalletFilled, X } from 'ui/src/components/icons' import { spacing } from 'ui/src/theme' import { AddressDisplay } from 'uniswap/src/components/accounts/AddressDisplay' +import { buildWrappedUrl } from 'uniswap/src/components/banners/shared/utils' +import { UniswapWrapped2025Card } from 'uniswap/src/components/banners/UniswapWrapped2025Card/UniswapWrapped2025Card' import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types' import { WarningModal } from 'uniswap/src/components/modals/WarningModal/WarningModal' +import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls' import { AccountType, DisplayNameType } from 'uniswap/src/features/accounts/types' +import { setHasDismissedUniswapWrapped2025Banner } from 'uniswap/src/features/behaviorHistory/slice' import { Platform } from 'uniswap/src/features/platforms/types/Platform' import { ModalName, WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' @@ -60,6 +65,8 @@ export function AccountSwitcherScreen(): JSX.Element { const activeAddress = activeAccount.address const isViewOnly = activeAccount.type === AccountType.Readonly + const isWrappedBannerEnabled = useFeatureFlag(FeatureFlags.UniswapWrapped2025) + const accounts = useSignerAccounts() const accountAddresses = useMemo( () => @@ -154,6 +161,17 @@ export function AccountSwitcherScreen(): JSX.Element { [connectedAccounts.length, dispatch, pendingWallet], ) + const onPressWrappedCard = useCallback(() => { + try { + const url = buildWrappedUrl(UNISWAP_WEB_URL, activeAddress) + window.open(url, '_blank') + dispatch(setHasDismissedUniswapWrapped2025Banner(true)) + navigate(-1) + } catch (error) { + logger.error(error, { tags: { file: 'AccountSwitcherScreen', function: 'onPressWrappedCard' } }) + } + }, [activeAddress, dispatch]) + const addWalletMenuOptions: MenuContentItem[] = [ { label: t('account.wallet.button.create'), @@ -287,6 +305,12 @@ export function AccountSwitcherScreen(): JSX.Element { + {isWrappedBannerEnabled && ( + + + + )} + {activeAccountHasUnitag ? ( @@ -355,7 +379,7 @@ export function AccountSwitcherScreen(): JSX.Element { borderColor="$surface3" borderRadius="$rounded16" borderWidth="$spacing1" - disableRemoveScroll={false} + enableRemoveScroll={true} enterStyle={{ y: -10, opacity: 0 }} exitStyle={{ y: -10, opacity: 0 }} p="$none" diff --git a/apps/extension/src/app/features/accounts/__snapshots__/AccountSwitcherScreen.test.tsx.snap b/apps/extension/src/app/features/accounts/__snapshots__/AccountSwitcherScreen.test.tsx.snap index 4541332866b..eed50298533 100644 --- a/apps/extension/src/app/features/accounts/__snapshots__/AccountSwitcherScreen.test.tsx.snap +++ b/apps/extension/src/app/features/accounts/__snapshots__/AccountSwitcherScreen.test.tsx.snap @@ -15,7 +15,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `

@@ -25,7 +25,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `

@@ -35,7 +35,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `

@@ -47,8 +47,10 @@ exports[`AccountSwitcherScreen renders correctly 1`] = ` >
-
-
+
- - - - + + + + + +
@@ -139,6 +148,8 @@ exports[`AccountSwitcherScreen renders correctly 1`] = ` data-testid="account-icon" >
@@ -312,6 +334,45 @@ exports[`AccountSwitcherScreen renders correctly 1`] = ` />
+ + + + + + + + + + + + + + + , "container":
@@ -334,7 +395,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `

@@ -344,7 +405,7 @@ exports[`AccountSwitcherScreen renders correctly 1`] = `

@@ -356,8 +417,10 @@ exports[`AccountSwitcherScreen renders correctly 1`] = ` >
-
-
+
- - - - + + + + + +
@@ -448,6 +518,8 @@ exports[`AccountSwitcherScreen renders correctly 1`] = ` data-testid="account-icon" >
diff --git a/apps/extension/src/app/features/biometricUnlock/BiometricUnlockStorage.ts b/apps/extension/src/app/features/biometricUnlock/BiometricUnlockStorage.ts index bd06afb9236..599a36b1c07 100644 --- a/apps/extension/src/app/features/biometricUnlock/BiometricUnlockStorage.ts +++ b/apps/extension/src/app/features/biometricUnlock/BiometricUnlockStorage.ts @@ -4,6 +4,7 @@ import { PersistedStorage } from 'wallet/src/utils/persistedStorage' export type BiometricUnlockStorageData = { credentialId: string + transports: AuthenticatorTransport[] secretPayload: Omit & { ciphertext: string } } diff --git a/apps/extension/src/app/features/biometricUnlock/biometricAuthUtils.ts b/apps/extension/src/app/features/biometricUnlock/biometricAuthUtils.ts index d82aeb49f58..21aa3d19515 100644 --- a/apps/extension/src/app/features/biometricUnlock/biometricAuthUtils.ts +++ b/apps/extension/src/app/features/biometricUnlock/biometricAuthUtils.ts @@ -16,9 +16,11 @@ import { */ export async function authenticateWithBiometricCredential({ credentialId, + transports, abortSignal, }: { credentialId: string + transports: AuthenticatorTransport[] abortSignal: AbortSignal }): Promise<{ publicKeyCredential: PublicKeyCredential; encryptionKey: CryptoKey }> { // Convert stored credential ID back to binary format @@ -31,6 +33,7 @@ export async function authenticateWithBiometricCredential({ { type: 'public-key', id: credentialIdBuffer, + transports, }, ], userVerification: 'required', @@ -70,10 +73,12 @@ export async function encryptPasswordWithBiometricData({ password, encryptionKey, credentialId, + transports, }: { password: string encryptionKey: CryptoKey credentialId: string + transports: AuthenticatorTransport[] }): Promise { // Create a new secret payload for the password const secretPayload = await createEmptySecretPayload() @@ -86,7 +91,7 @@ export async function encryptPasswordWithBiometricData({ additionalData: credentialId, // Use credential ID as additional authenticated data }) - return { credentialId, secretPayload: secretPayloadWithCiphertext } + return { credentialId, transports, secretPayload: secretPayloadWithCiphertext } } /** diff --git a/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.test.ts b/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.test.ts index 1b15deca6b5..1eb0c37cb0b 100644 --- a/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.test.ts +++ b/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.test.ts @@ -42,7 +42,12 @@ const mockGetEncryptionKeyFromBuffer = jest.requireMock( // Mock PublicKeyCredential (doesn't exist in Jest environment) class MockPublicKeyCredential { - constructor(public rawId: ArrayBuffer) {} + constructor( + public rawId: ArrayBuffer, + public response = { + getTransports: () => ['internal' as AuthenticatorTransport], + }, + ) {} } Object.defineProperty(global, 'PublicKeyCredential', { writable: true, @@ -124,10 +129,12 @@ describe('useBiometricUnlockSetupMutation', () => { // Verify the stored secret payload has all required properties const storedData = mockBiometricUnlockStorage.set.mock.calls[0]![0] as { credentialId: string + transports: AuthenticatorTransport[] secretPayload: typeof mockSecretPayload } expect(storedData.credentialId).toBe(expectedCredentialId) + expect(storedData.transports).toEqual(['internal']) expect(storedData.secretPayload).toEqual( expect.objectContaining({ diff --git a/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.ts b/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.ts index 345545a74c8..bd0e4616f51 100644 --- a/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.ts +++ b/apps/extension/src/app/features/biometricUnlock/useBiometricUnlockSetupMutation.ts @@ -69,7 +69,7 @@ async function createCredentialAndEncryptPassword({ const rawKey = await window.crypto.subtle.exportKey('raw', encryptionKey) - const { credentialId } = await createCredential({ + const { credentialId, transports } = await createCredential({ encryptionKey: rawKey, abortSignal, }) @@ -78,6 +78,7 @@ async function createCredentialAndEncryptPassword({ password, encryptionKey, credentialId, + transports, }) } @@ -116,7 +117,7 @@ async function createCredential({ }: { encryptionKey: ArrayBuffer abortSignal: AbortSignal -}): Promise<{ credentialId: string }> { +}): Promise<{ credentialId: string; transports: AuthenticatorTransport[] }> { // Create WebAuthn credential with platform authenticator (Touch ID, Windows Hello, etc.) forced const credential = await navigator.credentials.create({ publicKey: { @@ -149,5 +150,8 @@ async function createCredential({ // Convert raw ID to a storable string format const credentialId = btoa(String.fromCharCode(...new Uint8Array(publicKeyCredential.rawId))) - return { credentialId } + const response = publicKeyCredential.response as AuthenticatorAttestationResponse + const transports = response.getTransports() as AuthenticatorTransport[] + + return { credentialId, transports } } diff --git a/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.test.ts b/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.test.ts index fd312c48e6a..c8f23d90136 100644 --- a/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.test.ts +++ b/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.test.ts @@ -108,6 +108,7 @@ describe('useChangePasswordWithBiometricMutation', () => { // Setup default mocks mockBiometricUnlockStorage.get.mockResolvedValue({ credentialId: mockCredentialId, + transports: ['internal'], secretPayload: mockOldEncryptedPayload, }) @@ -146,6 +147,7 @@ describe('useChangePasswordWithBiometricMutation', () => { { type: 'public-key', id: credentialIdBuffer, + transports: ['internal'], }, ], userVerification: 'required', @@ -160,6 +162,7 @@ describe('useChangePasswordWithBiometricMutation', () => { // 4. Should update the stored biometric data with re-encrypted password expect(mockBiometricUnlockStorage.set).toHaveBeenCalledWith({ credentialId: mockCredentialId, + transports: ['internal'], secretPayload: expect.objectContaining({ ciphertext: expect.any(String), iv: expect.any(String), @@ -189,6 +192,7 @@ describe('useChangePasswordWithBiometricMutation', () => { const newBiometricData = setCall?.[0] expect(newBiometricData?.credentialId).toBe(mockCredentialId) + expect(newBiometricData?.transports).toEqual(['internal']) expect(newBiometricData?.secretPayload).toMatchObject({ ciphertext: expect.any(String), iv: expect.any(String), diff --git a/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.ts b/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.ts index 8da478fe45c..11361bdf03f 100644 --- a/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.ts +++ b/apps/extension/src/app/features/biometricUnlock/useChangePasswordWithBiometricMutation.ts @@ -25,6 +25,7 @@ export function useChangePasswordWithBiometricMutation(options?: { // Authenticate with WebAuthn to get the encryption key const { encryptionKey } = await authenticateWithBiometricCredential({ credentialId: biometricUnlockCredential.credentialId, + transports: biometricUnlockCredential.transports, abortSignal, }) @@ -36,6 +37,7 @@ export function useChangePasswordWithBiometricMutation(options?: { password: newPassword, encryptionKey, credentialId: biometricUnlockCredential.credentialId, + transports: biometricUnlockCredential.transports, }) // Update the stored biometric data diff --git a/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlock.ts b/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlock.ts index 3491a94d6bb..54582813431 100644 --- a/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlock.ts +++ b/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlock.ts @@ -1,7 +1,6 @@ import { useQuery } from '@tanstack/react-query' +import { DynamicConfigs, ExtensionBiometricUnlockConfigKey, useDynamicConfigValue } from '@universe/gating' import { biometricUnlockCredentialQuery } from 'src/app/features/biometricUnlock/biometricUnlockCredentialQuery' -import { DynamicConfigs, ExtensionBiometricUnlockConfigKey } from 'uniswap/src/features/gating/configs' -import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' export function useShouldShowBiometricUnlock(): boolean { const isEnabled = useDynamicConfigValue({ diff --git a/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlockEnrollment.ts b/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlockEnrollment.ts index 7c496296c1d..f642af2d9ad 100644 --- a/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlockEnrollment.ts +++ b/apps/extension/src/app/features/biometricUnlock/useShouldShowBiometricUnlockEnrollment.ts @@ -1,8 +1,7 @@ import { useQuery } from '@tanstack/react-query' +import { DynamicConfigs, ExtensionBiometricUnlockConfigKey, useDynamicConfigValue } from '@universe/gating' import { useTranslation } from 'react-i18next' import { builtInBiometricCapabilitiesQuery } from 'src/app/utils/device/builtInBiometricCapabilitiesQuery' -import { DynamicConfigs, ExtensionBiometricUnlockConfigKey } from 'uniswap/src/features/gating/configs' -import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' export function useShouldShowBiometricUnlockEnrollment({ flow }: { flow: 'onboarding' | 'settings' }): boolean { const { t } = useTranslation() diff --git a/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.test.ts b/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.test.ts index 832e727c382..5685ba37f1a 100644 --- a/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.test.ts +++ b/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.test.ts @@ -3,15 +3,13 @@ import { waitFor } from '@testing-library/react' import { BiometricUnlockStorage } from 'src/app/features/biometricUnlock/BiometricUnlockStorage' import { useUnlockWithBiometricCredentialMutation } from 'src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation' import { renderHookWithProviders } from 'src/test/render' -import { authActions } from 'wallet/src/features/auth/saga' -import { AuthActionType } from 'wallet/src/features/auth/types' import { encodeForStorage, encrypt, generateNew256BitRandomBuffer } from 'wallet/src/features/wallet/Keyring/crypto' jest.mock('src/app/features/biometricUnlock/BiometricUnlockStorage') -jest.mock('wallet/src/features/auth/saga', () => ({ - authActions: { - trigger: jest.fn(), - }, + +const mockUnlockWithPassword = jest.fn() +jest.mock('src/app/features/lockScreen/useUnlockWithPassword', () => ({ + useUnlockWithPassword: jest.fn(() => mockUnlockWithPassword), })) // Mock the Web Crypto API with Node.js built-in @@ -27,7 +25,6 @@ Object.defineProperty(navigator, 'credentials', { }) const mockBiometricUnlockStorage = BiometricUnlockStorage as jest.Mocked -const mockAuthActions = authActions as jest.Mocked // Mock AuthenticatorAssertionResponse class MockAuthenticatorAssertionResponse { @@ -100,6 +97,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { // Setup default mocks mockBiometricUnlockStorage.get.mockResolvedValue({ credentialId: mockCredentialId, + transports: ['internal'], secretPayload: mockEncryptedPayload, }) @@ -107,15 +105,9 @@ describe('useUnlockWithBiometricCredentialMutation', () => { const mockPublicKeyCredential = new MockPublicKeyCredential(mockAuthResponse) mockCredentialsGet.mockResolvedValue(mockPublicKeyCredential) - mockAuthActions.trigger.mockReturnValue({ - type: 'AUTH_TRIGGER', - payload: { - type: AuthActionType.Unlock, - password: mockPassword, - }, - }) - - jest.clearAllMocks() + // Reset and configure mockUnlockWithPassword + mockUnlockWithPassword.mockReset() + mockUnlockWithPassword.mockResolvedValue(undefined) }) describe('successful unlock', () => { @@ -140,6 +132,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { { type: 'public-key', id: credentialIdBuffer, + transports: ['internal'], }, ], userVerification: 'required', @@ -148,11 +141,8 @@ describe('useUnlockWithBiometricCredentialMutation', () => { signal: expect.any(AbortSignal), }) - // 3. Should dispatch unlock action with the decrypted password - expect(mockAuthActions.trigger).toHaveBeenCalledWith({ - type: AuthActionType.Unlock, - password: mockPassword, - }) + // 3. Should call unlockWithPassword with the decrypted password + expect(mockUnlockWithPassword).toHaveBeenCalledWith({ password: mockPassword }) }) }) @@ -170,7 +160,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { expect(result.current.error?.message).toBe('No biometric unlock credential found') expect(mockCredentialsGet).not.toHaveBeenCalled() - expect(mockAuthActions.trigger).not.toHaveBeenCalled() + expect(mockUnlockWithPassword).not.toHaveBeenCalled() }) it('should throw error when biometric authentication fails', async () => { @@ -185,7 +175,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { }) expect(result.current.error?.message).toBe('Failed to create credential') - expect(mockAuthActions.trigger).not.toHaveBeenCalled() + expect(mockUnlockWithPassword).not.toHaveBeenCalled() }) it('should throw error when no user handle returned from authentication', async () => { @@ -202,7 +192,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { }) expect(result.current.error?.message).toBe('No user handle returned from biometric authentication') - expect(mockAuthActions.trigger).not.toHaveBeenCalled() + expect(mockUnlockWithPassword).not.toHaveBeenCalled() }) it('should throw error when password decryption fails', async () => { @@ -226,7 +216,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { }) expect(result.current.error?.message).toBe('Failed to decrypt password') - expect(mockAuthActions.trigger).not.toHaveBeenCalled() + expect(mockUnlockWithPassword).not.toHaveBeenCalled() }) it('should handle WebAuthn API errors', async () => { @@ -242,7 +232,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { }) expect(result.current.error).toBe(webAuthnError) - expect(mockAuthActions.trigger).not.toHaveBeenCalled() + expect(mockUnlockWithPassword).not.toHaveBeenCalled() }) it('should handle storage retrieval errors', async () => { @@ -259,7 +249,7 @@ describe('useUnlockWithBiometricCredentialMutation', () => { expect(result.current.error).toBe(storageError) expect(mockCredentialsGet).not.toHaveBeenCalled() - expect(mockAuthActions.trigger).not.toHaveBeenCalled() + expect(mockUnlockWithPassword).not.toHaveBeenCalled() }) }) diff --git a/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.ts b/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.ts index 4eac011d28f..b37d3357cb1 100644 --- a/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.ts +++ b/apps/extension/src/app/features/biometricUnlock/useUnlockWithBiometricCredentialMutation.ts @@ -58,10 +58,10 @@ async function getPasswordFromBiometricCredential(abortSignal: AbortSignal): Pro throw new Error('No biometric unlock credential found') } - const { credentialId } = biometricUnlockCredential + const { credentialId, transports } = biometricUnlockCredential // Authenticate with WebAuthn using the stored credential and decrypt password - const { encryptionKey } = await authenticateWithBiometricCredential({ credentialId, abortSignal }) + const { encryptionKey } = await authenticateWithBiometricCredential({ credentialId, transports, abortSignal }) const password = await decryptPasswordFromBiometricData({ encryptionKey, biometricUnlockCredential }) return password } diff --git a/apps/extension/src/app/features/dappRequests/DappRequestContent.tsx b/apps/extension/src/app/features/dappRequests/DappRequestContent.tsx index b150cd8ff4a..06a1106bf53 100644 --- a/apps/extension/src/app/features/dappRequests/DappRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/DappRequestContent.tsx @@ -7,26 +7,8 @@ import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRe import { handleExternallySubmittedUniswapXOrder } from 'src/app/features/dappRequests/handleUniswapX' import { useIsDappRequestConfirming } from 'src/app/features/dappRequests/hooks' import { DappRequestStoreItem } from 'src/app/features/dappRequests/shared' -import { - DappRequest, - isBatchedSwapRequest, - isConnectionRequest, -} from 'src/app/features/dappRequests/types/DappRequestTypes' -import { - Anchor, - AnimatePresence, - Button, - Flex, - GetThemeValueForKey, - styled, - Text, - UniversalImage, - UniversalImageResizeMode, -} from 'ui/src' -import { Verified } from 'ui/src/components/icons' -import { borderRadii, iconSizes } from 'ui/src/theme' -import { DappIconPlaceholder } from 'uniswap/src/components/dapps/DappIconPlaceholder' -import { UNISWAP_WEB_HOSTNAME } from 'uniswap/src/constants/urls' +import { DappRequest, isBatchedSwapRequest } from 'src/app/features/dappRequests/types/DappRequestTypes' +import { AnimatePresence, Button, Flex, GetThemeValueForKey, styled, Text } from 'ui/src' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' import { UniverseChainId } from 'uniswap/src/features/chains/types' import { DappRequestType } from 'uniswap/src/features/dappRequests/types' @@ -35,18 +17,20 @@ import { hasSufficientFundsIncludingGas } from 'uniswap/src/features/gas/utils' import { useOnChainNativeCurrencyBalance } from 'uniswap/src/features/portfolio/api' import { TransactionTypeInfo } from 'uniswap/src/features/transactions/types/transactionDetails' import { extractNameFromUrl } from 'utilities/src/format/extractNameFromUrl' -import { formatDappURL } from 'utilities/src/format/urls' import { logger } from 'utilities/src/logger/logger' import { useEvent } from 'utilities/src/react/hooks' import { useThrottledCallback } from 'utilities/src/react/useThrottledCallback' import { MAX_HIDDEN_CALLS_BY_DEFAULT } from 'wallet/src/components/BatchedTransactions/BatchedTransactionDetails' +import { DappRequestHeader } from 'wallet/src/components/dappRequests/DappRequestHeader' import { WarningBox } from 'wallet/src/components/WarningBox/WarningBox' +import { DappVerificationStatus } from 'wallet/src/features/dappRequests/types' import { AddressFooter } from 'wallet/src/features/transactions/TransactionRequest/AddressFooter' import { NetworkFeeFooter } from 'wallet/src/features/transactions/TransactionRequest/NetworkFeeFooter' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' interface DappRequestHeaderProps { title: string + verificationStatus?: DappVerificationStatus headerIcon?: JSX.Element } @@ -57,9 +41,9 @@ interface DappRequestFooterProps { maybeCloseOnConfirm?: boolean onCancel?: (requestToConfirm?: DappRequestStoreItem, transactionTypeInfo?: TransactionTypeInfo) => void onConfirm?: (requestToCancel?: DappRequestStoreItem) => void - showAllNetworks?: boolean showNetworkCost?: boolean showSmartWalletActivation?: boolean + showAddressFooter?: boolean transactionGasFeeResult?: GasFeeResult isUniswapX?: boolean disableConfirm?: boolean @@ -96,26 +80,39 @@ export const AnimatedPane = styled(Flex, { export function DappRequestContent({ chainId, title, + verificationStatus, headerIcon, confirmText, connectedAccountAddress, maybeCloseOnConfirm, onCancel, onConfirm, - showAllNetworks, showNetworkCost, showSmartWalletActivation, transactionGasFeeResult, children, isUniswapX, disableConfirm, + showAddressFooter = true, contentHorizontalPadding = '$spacing12', }: PropsWithChildren): JSX.Element { - const { forwards, currentIndex } = useDappRequestQueueContext() + const { forwards, currentIndex, dappIconUrl, dappUrl } = useDappRequestQueueContext() + const hostname = extractNameFromUrl(dappUrl).toUpperCase() return ( <> - + + + {children} @@ -127,9 +124,9 @@ export function DappRequestContent({ connectedAccountAddress={connectedAccountAddress} isUniswapX={isUniswapX} maybeCloseOnConfirm={maybeCloseOnConfirm} - showAllNetworks={showAllNetworks} showNetworkCost={showNetworkCost} showSmartWalletActivation={showSmartWalletActivation} + showAddressFooter={showAddressFooter} transactionGasFeeResult={transactionGasFeeResult} disableConfirm={disableConfirm} onCancel={onCancel} @@ -139,48 +136,6 @@ export function DappRequestContent({ ) } -function DappRequestHeader({ headerIcon, title }: DappRequestHeaderProps): JSX.Element { - const { dappIconUrl, dappUrl, request } = useDappRequestQueueContext() - const hostname = extractNameFromUrl(dappUrl).toUpperCase() - const fallbackIcon = - const showVerified = - request && isConnectionRequest(request.dappRequest) && formatDappURL(dappUrl) === UNISWAP_WEB_HOSTNAME - - return ( - - - - {headerIcon || ( - - )} - - - - {title} - - - - - {formatDappURL(dappUrl)} - - {showVerified && } - - - - ) -} - const WINDOW_CLOSE_DELAY = 10 function DappRequestFooter({ @@ -192,6 +147,7 @@ function DappRequestFooter({ onConfirm, showNetworkCost, showSmartWalletActivation, + showAddressFooter, transactionGasFeeResult, isUniswapX, disableConfirm, @@ -275,7 +231,7 @@ function DappRequestFooter({ return ( <> - + {!hasSufficientGas && ( @@ -295,13 +251,15 @@ function DappRequestFooter({ showSmartWalletActivation={showSmartWalletActivation} /> )} - + {showAddressFooter && ( + + )} - + diff --git a/apps/extension/src/app/features/dappRequests/hooks/usePrepareAndSignSendCallsTransaction.ts b/apps/extension/src/app/features/dappRequests/hooks/usePrepareAndSignSendCallsTransaction.ts index c7cbb49bece..0ff33a6c29e 100644 --- a/apps/extension/src/app/features/dappRequests/hooks/usePrepareAndSignSendCallsTransaction.ts +++ b/apps/extension/src/app/features/dappRequests/hooks/usePrepareAndSignSendCallsTransaction.ts @@ -58,6 +58,7 @@ export function usePrepareAndSignSendCallsTransaction({ }) : [], smartContractDelegationAddress: UNISWAP_DELEGATION_ADDRESS, + // @ts-expect-error - TODO: no longer available in API types, verify if still needed walletAddress: account.address, }, }) diff --git a/apps/extension/src/app/features/dappRequests/requestContent/Connection/ConnectionRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/Connection/ConnectionRequestContent.tsx index be130ca36d9..424e65d7324 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/Connection/ConnectionRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/Connection/ConnectionRequestContent.tsx @@ -1,27 +1,35 @@ import { useTranslation } from 'react-i18next' import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' -import { Flex, Text } from 'ui/src' +import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRequestQueueContext' +import { AccountType } from 'uniswap/src/features/accounts/types' +import { DappConnectionContent } from 'wallet/src/components/dappRequests/DappConnectionContent' +import { useBlockaidVerification } from 'wallet/src/features/dappRequests/hooks/useBlockaidVerification' +import { useDappConnectionConfirmation } from 'wallet/src/features/dappRequests/hooks/useDappConnectionConfirmation' export function ConnectionRequestContent(): JSX.Element { const { t } = useTranslation() + const { currentAccount, dappUrl } = useDappRequestQueueContext() + const { verificationStatus } = useBlockaidVerification(dappUrl) + + const isViewOnly = currentAccount.type === AccountType.Readonly + const { confirmedWarning, setConfirmedWarning, disableConfirm } = useDappConnectionConfirmation({ + verificationStatus, + isViewOnly, + }) return ( - - - {t('dapp.request.connect.helptext')} - - + ) } diff --git a/apps/extension/src/app/features/dappRequests/requestContent/EthSend/EthSend.tsx b/apps/extension/src/app/features/dappRequests/requestContent/EthSend/EthSend.tsx index 7131e97a7f6..2c36e8b0657 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/EthSend/EthSend.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/EthSend/EthSend.tsx @@ -1,3 +1,4 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useCallback } from 'react' import { useDappLastChainId } from 'src/app/features/dapp/hooks' import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRequestQueueContext' @@ -5,6 +6,7 @@ import { usePrepareAndSignEthSendTransaction } from 'src/app/features/dappReques import { ApproveRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/Approve/ApproveRequestContent' import { FallbackEthSendRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/FallbackEthSend/FallbackEthSend' import { LPRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/LP/LPRequestContent' +import { ParsedTransactionRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/ParsedTransaction/ParsedTransactionRequestContent' import { Permit2ApproveRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/Permit2Approve/Permit2ApproveRequestContent' import { SwapRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/Swap/SwapRequestContent' import { WrapRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/Wrap/WrapRequestContent' @@ -15,7 +17,9 @@ import { isPermit2ApproveRequest, isSwapRequest, isWrapRequest, + SendTransactionRequest, } from 'src/app/features/dappRequests/types/DappRequestTypes' +import { GasFeeResult } from 'uniswap/src/features/gas/types' import { TransactionTypeInfo } from 'uniswap/src/features/transactions/types/transactionDetails' import { logger } from 'utilities/src/logger/logger' import { ErrorBoundary } from 'wallet/src/components/ErrorBoundary/ErrorBoundary' @@ -28,6 +32,7 @@ export function EthSendRequestContent({ request }: EthSendRequestContentProps): const { dappRequest } = request const { dappUrl, currentAccount, onConfirm, onCancel } = useDappRequestQueueContext() const chainId = useDappLastChainId(dappUrl) + const blockaidTransactionScanning = useFeatureFlag(FeatureFlags.BlockaidTransactionScanning) const { gasFeeResult: transactionGasFeeResult, @@ -54,91 +59,147 @@ export function EthSendRequestContent({ request }: EthSendRequestContentProps): await onCancel(requestWithGasValues) }, [onCancel, requestWithGasValues]) - let content + // If Blockaid transaction scanning is enabled, try to use it for ALL transaction types + // If the API fails, the ErrorBoundary will catch it and fallback to specialized UIs + if (blockaidTransactionScanning) { + return ( + + } + onError={(error) => { + if (error) { + logger.error(error, { + tags: { file: 'EthSend', function: 'ErrorBoundary-Blockaid' }, + extra: { + dappRequest, + useSimulationResultUI: true, + }, + }) + } + }} + > + + + ) + } + + // If feature flag is disabled, use specialized UIs + const content = ( + + ) + + return ( + + } + onError={(error) => { + if (error) { + logger.error(error, { + tags: { file: 'EthSend', function: 'ErrorBoundary-Specialized' }, + extra: { + dappRequest, + useSimulationResultUI: false, + }, + }) + } + }} + > + {content} + + ) +} + +/** + * Fallback component that renders the appropriate specialized UI based on transaction type + * Used when simulation result UI is disabled or fails + */ +function SpecializedTransactionFallback({ + dappRequest, + transactionGasFeeResult, + onCancel, + onConfirm, +}: { + dappRequest: SendTransactionRequest + transactionGasFeeResult: GasFeeResult + onCancel: () => Promise + onConfirm: (transactionTypeInfo?: TransactionTypeInfo) => Promise +}): JSX.Element { switch (true) { case isSwapRequest(dappRequest): - content = ( + return ( ) - break case isPermit2ApproveRequest(dappRequest): - content = ( + return ( ) - break case isWrapRequest(dappRequest): - content = ( + return ( ) - break case isLPRequest(dappRequest): - content = ( + return ( ) - break case isApproveRequest(dappRequest): - content = ( + return ( ) - break default: - content = ( + return ( ) } - - return ( - - } - onError={(error) => { - if (error) { - logger.error(error, { - tags: { file: 'EthSend', function: 'ErrorBoundary' }, - extra: { - dappRequest, - }, - }) - } - }} - > - {content} - - ) } diff --git a/apps/extension/src/app/features/dappRequests/requestContent/EthSend/ParsedTransaction/ParsedTransactionRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/EthSend/ParsedTransaction/ParsedTransactionRequestContent.tsx new file mode 100644 index 00000000000..ac63e20279a --- /dev/null +++ b/apps/extension/src/app/features/dappRequests/requestContent/EthSend/ParsedTransaction/ParsedTransactionRequestContent.tsx @@ -0,0 +1,74 @@ +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useDappLastChainId } from 'src/app/features/dapp/hooks' +import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' +import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRequestQueueContext' +import { SendTransactionRequest } from 'src/app/features/dappRequests/types/DappRequestTypes' +import { GasFeeResult } from 'uniswap/src/features/gas/types' +import { useBooleanState } from 'utilities/src/react/useBooleanState' +import { DappTransactionScanningContent } from 'wallet/src/components/dappRequests/DappTransactionScanningContent' +import { TransactionRiskLevel } from 'wallet/src/features/dappRequests/types' +import { shouldDisableConfirm } from 'wallet/src/features/dappRequests/utils/riskUtils' + +interface ParsedTransactionRequestContentProps { + transactionGasFeeResult: GasFeeResult + dappRequest: SendTransactionRequest + onCancel: () => Promise + onConfirm: () => Promise +} + +/** + * Transaction request content with Blockaid security scanning + * Parses transaction data and displays it with asset transfers, security warnings, and detailed information + */ +export function ParsedTransactionRequestContent({ + dappRequest, + transactionGasFeeResult, + onCancel, + onConfirm, +}: ParsedTransactionRequestContentProps): JSX.Element | null { + const { t } = useTranslation() + const { dappUrl, currentAccount } = useDappRequestQueueContext() + const activeChain = useDappLastChainId(dappUrl) + const { value: confirmedRisk, setValue: setConfirmedRisk } = useBooleanState(false) + // Initialize with null to indicate scan hasn't completed yet + const [riskLevel, setRiskLevel] = useState(null) + + const { chainId: transactionChainId } = dappRequest.transaction + const chainId = transactionChainId ?? activeChain + + // If no valid chainId, throw so that we fall back to the legacy UI + if (!chainId) { + throw new Error('No valid chainId available for transaction scanning') + } + + const disableConfirm = shouldDisableConfirm({ + riskLevel, + confirmedRisk, + hasGasFee: !!transactionGasFeeResult.value, + }) + + return ( + + + + ) +} diff --git a/apps/extension/src/app/features/dappRequests/requestContent/EthSend/Swap/SwapRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/EthSend/Swap/SwapRequestContent.tsx index 4aec47e93aa..05578f8627c 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/EthSend/Swap/SwapRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/EthSend/Swap/SwapRequestContent.tsx @@ -2,7 +2,6 @@ import { useDappLastChainId } from 'src/app/features/dapp/hooks' import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRequestQueueContext' import { SwapDisplay } from 'src/app/features/dappRequests/requestContent/EthSend/Swap/SwapDisplay' import { formatUnits, useSwapDetails } from 'src/app/features/dappRequests/requestContent/EthSend/Swap/utils' -import { UniswapXSwapRequest } from 'src/app/features/dappRequests/types/Permit2Types' import { UniversalRouterCall } from 'src/app/features/dappRequests/types/UniversalRouterTypes' import { DEFAULT_NATIVE_ADDRESS, DEFAULT_NATIVE_ADDRESS_LEGACY } from 'uniswap/src/features/chains/evm/defaults' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' @@ -13,6 +12,7 @@ import { useCurrencyInfo, useNativeCurrencyInfo } from 'uniswap/src/features/tok import { TransactionType, TransactionTypeInfo } from 'uniswap/src/features/transactions/types/transactionDetails' import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { assert } from 'utilities/src/errors' +import { UniswapXSwapRequest } from 'wallet/src/components/dappRequests/types/Permit2Types' function getTransactionTypeInfo({ inputCurrencyInfo, diff --git a/apps/extension/src/app/features/dappRequests/requestContent/PersonalSign/PersonalSignRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/PersonalSign/PersonalSignRequestContent.tsx index 82183fc49fe..fe179239aa6 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/PersonalSign/PersonalSignRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/PersonalSign/PersonalSignRequestContent.tsx @@ -1,11 +1,21 @@ import { toUtf8String } from '@ethersproject/strings' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useDappLastChainId } from 'src/app/features/dapp/hooks' import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' +import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRequestQueueContext' import { SignMessageRequest } from 'src/app/features/dappRequests/types/DappRequestTypes' import { Flex, IconButton, Text, Tooltip } from 'ui/src' import { AlertTriangleFilled, Code, StickyNoteTextSquare } from 'ui/src/components/icons' +import { zIndexes } from 'ui/src/theme' +import { EthMethod } from 'uniswap/src/features/dappRequests/types' +import { logger } from 'utilities/src/logger/logger' import { containsNonPrintableChars } from 'utilities/src/primitives/string' +import { useBooleanState } from 'utilities/src/react/useBooleanState' +import { DappPersonalSignContent } from 'wallet/src/components/dappRequests/DappPersonalSignContent' +import { TransactionRiskLevel } from 'wallet/src/features/dappRequests/types' +import { shouldDisableConfirm } from 'wallet/src/features/dappRequests/utils/riskUtils' enum ViewEncoding { UTF8 = 0, @@ -16,26 +26,111 @@ interface PersonalSignRequestProps { } export function PersonalSignRequestContent({ dappRequest }: PersonalSignRequestProps): JSX.Element | null { + const blockaidTransactionScanning = useFeatureFlag(FeatureFlags.BlockaidTransactionScanning) + + // Decode message to UTF-8 + const hexMessage = dappRequest.messageHex + const [utf8Message, setUtf8Message] = useState() + + useEffect(() => { + try { + const decodedMessage = toUtf8String(hexMessage) + setUtf8Message(decodedMessage) + } catch { + // If the message is not valid UTF-8, we'll show the hex message instead + setUtf8Message(undefined) + } + }, [hexMessage]) + + if (blockaidTransactionScanning) { + return + } + + return +} + +/** + * Implementation with Blockaid scanning + */ +function PersonalSignRequestContentWithScanning({ + dappRequest, + utf8Message, +}: { + dappRequest: SignMessageRequest + utf8Message: string | undefined +}): JSX.Element { + const { t } = useTranslation() + const { dappUrl, currentAccount } = useDappRequestQueueContext() + const activeChain = useDappLastChainId(dappUrl) + const { value: confirmedRisk, setValue: setConfirmedRisk } = useBooleanState(false) + // Initialize with null to indicate scan hasn't completed yet + const [riskLevel, setRiskLevel] = useState(null) + + const hexMessage = dappRequest.messageHex + const isDecoded = Boolean(utf8Message && !containsNonPrintableChars(utf8Message)) + const message = (isDecoded ? utf8Message : hexMessage) || hexMessage + const hasLoggedError = useRef(false) + + if (!activeChain) { + if (!hasLoggedError.current) { + logger.error(new Error('No active chain found'), { + tags: { file: 'PersonalSignRequestContent', function: 'PersonalSignRequestContentWithScanning' }, + }) + hasLoggedError.current = true + } + return + } + + const disableConfirm = shouldDisableConfirm({ riskLevel, confirmedRisk }) + + return ( + + + + ) +} + +/** + * Legacy implementation (existing behavior when feature flag is off) + */ +function PersonalSignRequestContentLegacy({ + dappRequest, + utf8Message, +}: { + dappRequest: SignMessageRequest + utf8Message: string | undefined +}): JSX.Element { const { t } = useTranslation() const [viewEncoding, setViewEncoding] = useState(ViewEncoding.UTF8) - const [utf8Message, setUtf8Message] = useState() const toggleViewEncoding = (): void => setViewEncoding(viewEncoding === ViewEncoding.UTF8 ? ViewEncoding.HEX : ViewEncoding.UTF8) const hexMessage = dappRequest.messageHex const containsUnrenderableCharacters = !utf8Message || containsNonPrintableChars(utf8Message) + useEffect(() => { - try { - const decodedMessage = toUtf8String(hexMessage) - setUtf8Message(decodedMessage) - } catch { - // If the message is not valid UTF-8, we'll show the hex message instead (e.g. Polymark claim deposit message ) + if (!utf8Message) { setViewEncoding(ViewEncoding.HEX) - setUtf8Message(undefined) } - }, [hexMessage]) + }, [utf8Message]) const [isScrollable, setIsScrollable] = useState(false) const messageRef = useRef(null) @@ -96,7 +191,7 @@ export function PersonalSignRequestContent({ dappRequest }: PersonalSignRequestP /> - + {viewEncoding === ViewEncoding.UTF8 diff --git a/apps/extension/src/app/features/dappRequests/requestContent/SendCalls/SendCallsRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/SendCalls/SendCallsRequestContent.tsx index 3245fde4e74..db74d62bf53 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/SendCalls/SendCallsRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/SendCalls/SendCallsRequestContent.tsx @@ -1,4 +1,5 @@ -import { useCallback, useMemo } from 'react' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDappLastChainId } from 'src/app/features/dapp/hooks' import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' @@ -12,9 +13,14 @@ import { ParsedCall, SendCallsRequest, } from 'src/app/features/dappRequests/types/DappRequestTypes' +import { UniverseChainId } from 'uniswap/src/features/chains/types' import { GasFeeResult } from 'uniswap/src/features/gas/types' import { TransactionType, TransactionTypeInfo } from 'uniswap/src/features/transactions/types/transactionDetails' +import { useBooleanState } from 'utilities/src/react/useBooleanState' import { BatchedRequestDetailsContent } from 'wallet/src/components/BatchedTransactions/BatchedTransactionDetails' +import { DappSendCallsScanningContent } from 'wallet/src/components/dappRequests/DappSendCallsScanningContent' +import { TransactionRiskLevel } from 'wallet/src/features/dappRequests/types' +import { shouldDisableConfirm } from 'wallet/src/features/dappRequests/utils/riskUtils' interface SendCallsRequestContentProps { dappRequest: SendCallsRequest @@ -24,7 +30,60 @@ interface SendCallsRequestContentProps { onCancel: () => Promise } -function SendCallsRequestContent({ +/** + * Implementation with Blockaid scanning + */ +function SendCallsRequestContentWithScanning({ + dappRequest, + chainId, + transactionGasFeeResult, + showSmartWalletActivation, + onConfirm, + onCancel, +}: SendCallsRequestContentProps & { chainId: UniverseChainId }): JSX.Element { + const { t } = useTranslation() + const { dappUrl, currentAccount } = useDappRequestQueueContext() + const { value: confirmedRisk, setValue: setConfirmedRisk } = useBooleanState(false) + // Initialize with null to indicate scan hasn't completed yet + const [riskLevel, setRiskLevel] = useState(null) + + const disableConfirm = shouldDisableConfirm({ + riskLevel, + confirmedRisk, + hasGasFee: !!transactionGasFeeResult.value, + }) + + return ( + onConfirm()} + showAddressFooter={false} + > + + + ) +} + +/** + * Legacy implementation (existing behavior when feature flag is off) + */ +function SendCallsRequestContentLegacy({ dappRequest, transactionGasFeeResult, showSmartWalletActivation, @@ -56,6 +115,7 @@ function SendCallsRequestContent({ export function SendCallsRequestHandler({ request }: { request: DappRequestStoreItemForSendCallsTxn }): JSX.Element { const { dappUrl, currentAccount, onConfirm, onCancel } = useDappRequestQueueContext() const chainId = useDappLastChainId(dappUrl) ?? request.dappInfo?.lastChainId + const blockaidTransactionScanning = useFeatureFlag(FeatureFlags.BlockaidTransactionScanning) const { dappRequest } = request @@ -92,7 +152,16 @@ export function SendCallsRequestHandler({ request }: { request: DappRequestStore await onCancel(request) }, [onCancel, request]) - return parsedSwapCalldata ? ( + return blockaidTransactionScanning && chainId ? ( + + ) : parsedSwapCalldata ? ( ) : ( - ) } diff --git a/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/NonStandardTypedDataRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/NonStandardTypedDataRequestContent.tsx index 36ae109cf85..37eff930616 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/NonStandardTypedDataRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/NonStandardTypedDataRequestContent.tsx @@ -2,10 +2,7 @@ import { useState } from 'react' import { useTranslation } from 'react-i18next' import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' import { SignTypedDataRequest } from 'src/app/features/dappRequests/types/DappRequestTypes' -import { Flex, Separator, Text } from 'ui/src' -import { Clear, Signature } from 'ui/src/components/icons' -import { InlineWarningCard } from 'uniswap/src/components/InlineWarningCard/InlineWarningCard' -import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types' +import { NonStandardTypedDataContent } from 'wallet/src/components/dappRequests/SignTypedData/NonStandardTypedDataContent' interface NonStandardTypedDataRequestContentProps { dappRequest: SignTypedDataRequest @@ -17,8 +14,6 @@ export function NonStandardTypedDataRequestContent({ const { t } = useTranslation() const [checked, setChecked] = useState(false) - const hasMessageToShow = !!dappRequest.typedData - return ( - - - - - - {t('dapp.request.signature.decodeError')} - - - {hasMessageToShow && } - {hasMessageToShow && ( - - - - - {t('common.message')} - - - - {dappRequest.typedData} - - - )} - - - + ) } diff --git a/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/Permit2/Permit2RequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/Permit2/Permit2RequestContent.tsx deleted file mode 100644 index 346fb612654..00000000000 --- a/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/Permit2/Permit2RequestContent.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' -import { DomainContent } from 'src/app/features/dappRequests/requestContent/SignTypeData/DomainContent' -import { MaybeExplorerLinkedAddress } from 'src/app/features/dappRequests/requestContent/SignTypeData/MaybeExplorerLinkedAddress' -import { SignTypedDataRequest } from 'src/app/features/dappRequests/types/DappRequestTypes' -import { Flex, Text, TouchableArea } from 'ui/src' -import { RotatableChevron } from 'ui/src/components/icons' -import { toSupportedChainId } from 'uniswap/src/features/chains/utils' -import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking' - -interface Permit2RequestProps { - dappRequest: SignTypedDataRequest -} - -export function Permit2RequestContent({ dappRequest }: Permit2RequestProps): JSX.Element | null { - const { t } = useTranslation() - - const parsedTypedData = JSON.parse(dappRequest.typedData) - const { name, chainId: domainChainId, verifyingContract } = parsedTypedData?.domain || {} - const chainId = toSupportedChainId(domainChainId) - - const { token: address, amount, expiration, nonce } = parsedTypedData?.message?.details || {} - const { spender, sigDeadline } = parsedTypedData?.message || {} - const [open, setOpen] = useState(false) - const toggleOpen = (): void => setOpen(!open) - - const spenderLink = chainId ? getExplorerLink({ chainId, data: spender, type: ExplorerDataType.ADDRESS }) : undefined - const tokenLink = chainId ? getExplorerLink({ chainId, data: address, type: ExplorerDataType.TOKEN }) : undefined - - return ( - - - - - {t('dapp.request.permit2.description')} - - - - - - {open && ( - <> - - - - token - - - - - - amount - - - {amount} - - - - - expiration - - - {expiration} - - - - - nonce - - - {nonce} - - - - - spender - - - - - - signature deadline - - - {sigDeadline} - - - - )} - - - ) -} diff --git a/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/SignTypedDataRequestContent.tsx b/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/SignTypedDataRequestContent.tsx index 19606509983..8954eefedf2 100644 --- a/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/SignTypedDataRequestContent.tsx +++ b/apps/extension/src/app/features/dappRequests/requestContent/SignTypeData/SignTypedDataRequestContent.tsx @@ -1,29 +1,33 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { DappRequestContent } from 'src/app/features/dappRequests/DappRequestContent' +import { useDappRequestQueueContext } from 'src/app/features/dappRequests/DappRequestQueueContext' import { ActionCanNotBeCompletedContent } from 'src/app/features/dappRequests/requestContent/ActionCanNotBeCompleted/ActionCanNotBeCompletedContent' import { UniswapXSwapRequestContent } from 'src/app/features/dappRequests/requestContent/EthSend/Swap/SwapRequestContent' -import { DomainContent } from 'src/app/features/dappRequests/requestContent/SignTypeData/DomainContent' -import { MaybeExplorerLinkedAddress } from 'src/app/features/dappRequests/requestContent/SignTypeData/MaybeExplorerLinkedAddress' import { NonStandardTypedDataRequestContent } from 'src/app/features/dappRequests/requestContent/SignTypeData/NonStandardTypedDataRequestContent' -import { Permit2RequestContent } from 'src/app/features/dappRequests/requestContent/SignTypeData/Permit2/Permit2RequestContent' import { SignTypedDataRequest } from 'src/app/features/dappRequests/types/DappRequestTypes' -import { EIP712Message, isEIP712TypedData } from 'src/app/features/dappRequests/types/EIP712Types' -import { isPermit2, isUniswapXSwapRequest } from 'src/app/features/dappRequests/types/Permit2Types' -import { Flex, Text } from 'ui/src' +import { Flex } from 'ui/src' import { toSupportedChainId } from 'uniswap/src/features/chains/utils' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { useHasAccountMismatchCallback } from 'uniswap/src/features/smartWallet/mismatch/hooks' -import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking' -import { isEVMAddressWithChecksum } from 'utilities/src/addresses/evm/evm' import { logger } from 'utilities/src/logger/logger' +import { useBooleanState } from 'utilities/src/react/useBooleanState' +import { DappSignTypedDataContent } from 'wallet/src/components/dappRequests/DappSignTypedDataContent' +import { Permit2Content } from 'wallet/src/components/dappRequests/SignTypedData/Permit2Content' +import { StandardTypedDataContent } from 'wallet/src/components/dappRequests/SignTypedData/StandardTypedDataContent' +import { isEIP712TypedData } from 'wallet/src/components/dappRequests/types/EIP712Types' +import { isPermit2, isUniswapXSwapRequest } from 'wallet/src/components/dappRequests/types/Permit2Types' import { ErrorBoundary } from 'wallet/src/components/ErrorBoundary/ErrorBoundary' +import { TransactionRiskLevel } from 'wallet/src/features/dappRequests/types' +import { shouldDisableConfirm } from 'wallet/src/features/dappRequests/utils/riskUtils' interface SignTypedDataRequestProps { dappRequest: SignTypedDataRequest } export function SignTypedDataRequestContent({ dappRequest }: SignTypedDataRequestProps): JSX.Element | null { + const blockaidTransactionScanning = useFeatureFlag(FeatureFlags.BlockaidTransactionScanning) + return ( } @@ -39,12 +43,76 @@ export function SignTypedDataRequestContent({ dappRequest }: SignTypedDataReques } }} > - + {blockaidTransactionScanning ? ( + + ) : ( + + )} ) } -function SignTypedDataRequestContentInner({ dappRequest }: SignTypedDataRequestProps): JSX.Element | null { +/** + * Implementation with Blockaid scanning + */ +function SignTypedDataRequestContentWithScanning({ dappRequest }: SignTypedDataRequestProps): JSX.Element | null { + const { t } = useTranslation() + const { dappUrl, currentAccount } = useDappRequestQueueContext() + const { value: confirmedRisk, setValue: setConfirmedRisk } = useBooleanState(false) + const enablePermitMismatchUx = useFeatureFlag(FeatureFlags.EnablePermitMismatchUX) + const getHasMismatch = useHasAccountMismatchCallback() + + // Initialize with null to indicate scan hasn't completed yet + const [riskLevel, setRiskLevel] = useState(null) + + const parsedTypedData = JSON.parse(dappRequest.typedData) + const { chainId: domainChainId } = parsedTypedData.domain || {} + const chainId = toSupportedChainId(domainChainId) + + const hasMismatch = chainId ? getHasMismatch(chainId) : false + if (enablePermitMismatchUx && hasMismatch) { + return + } + + if (!chainId) { + // chainId is required for Blockaid scanning + return + } + + // Extension SignTypedData requests default to v4 method (modern standard) + const method = 'eth_signTypedData_v4' + + // For eth_signTypedData_v4, params are [account, typedData] + const params = [currentAccount.address, dappRequest.typedData] + + const disableConfirm = shouldDisableConfirm({ riskLevel, confirmedRisk }) + + return ( + + + + ) +} + +/** + * Legacy implementation (existing behavior when feature flag is off) + */ +function SignTypedDataRequestContentLegacy({ dappRequest }: SignTypedDataRequestProps): JSX.Element | null { const { t } = useTranslation() const enablePermitMismatchUx = useFeatureFlag(FeatureFlags.EnablePermitMismatchUX) const getHasMismatch = useHasAccountMismatchCallback() @@ -55,7 +123,7 @@ function SignTypedDataRequestContentInner({ dappRequest }: SignTypedDataRequestP return } - const { name, version, chainId: domainChainId, verifyingContract, salt } = parsedTypedData.domain || {} + const { chainId: domainChainId } = parsedTypedData.domain || {} const chainId = toSupportedChainId(domainChainId) const hasMismatch = chainId ? getHasMismatch(chainId) : false @@ -67,59 +135,13 @@ function SignTypedDataRequestContentInner({ dappRequest }: SignTypedDataRequestP return } - if (isPermit2(parsedTypedData)) { - return - } - - // todo(EXT-883): remove this when we start rejecting unsupported chain signTypedData requests - const renderMessageContent = ( - message: EIP712Message | EIP712Message[keyof EIP712Message], - i = 1, - ): Maybe => { - if (message === null || message === undefined) { - return ( - - {String(message)} - - ) - } - if (typeof message === 'string' && isEVMAddressWithChecksum(message) && chainId) { - const href = getExplorerLink({ chainId, data: message, type: ExplorerDataType.ADDRESS }) - return - } - if (typeof message === 'string' || typeof message === 'number' || typeof message === 'boolean') { - return ( - - {message.toString()} - - ) - } else if (Array.isArray(message)) { - return ( - - {JSON.stringify(message)} - - ) - } else if (typeof message === 'object') { - return Object.entries(message).map(([key, value], index) => ( - - - {key} - - - {renderMessageContent(value, i + 1)} - - - )) - } - - return undefined - } + const isPermit2Request = isPermit2(parsedTypedData) return ( - - {renderMessageContent(parsedTypedData.message)} + {isPermit2Request ? ( + + ) : ( + + )} ) diff --git a/apps/extension/src/app/features/dappRequests/saga.ts b/apps/extension/src/app/features/dappRequests/saga.ts index 8ce3f50de43..1c2a07a605a 100644 --- a/apps/extension/src/app/features/dappRequests/saga.ts +++ b/apps/extension/src/app/features/dappRequests/saga.ts @@ -1,6 +1,7 @@ /* eslint-disable max-lines */ import { Provider } from '@ethersproject/providers' import { providerErrors, rpcErrors, serializeError } from '@metamask/rpc-errors' +import { FeatureFlags, getFeatureFlag } from '@universe/gating' import { createSearchParams } from 'react-router' import { changeChain } from 'src/app/features/dapp/changeChain' import { DappInfo, dappStore } from 'src/app/features/dapp/store' @@ -45,8 +46,6 @@ import getCalldataInfoFromTransaction from 'src/background/utils/getCalldataInfo import { call, put, select, take } from 'typed-redux-saga' import { hexadecimalStringToInt, toSupportedChainId } from 'uniswap/src/features/chains/utils' import { DappRequestType, DappResponseType } from 'uniswap/src/features/dappRequests/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { getFeatureFlag } from 'uniswap/src/features/gating/hooks' import { pushNotification } from 'uniswap/src/features/notifications/slice/slice' import { AppNotificationType } from 'uniswap/src/features/notifications/slice/types' import { Platform } from 'uniswap/src/features/platforms/types/Platform' @@ -60,7 +59,7 @@ import { extractBaseUrl } from 'utilities/src/format/urls' import { logger } from 'utilities/src/logger/logger' import { getCallsStatusHelper } from 'wallet/src/features/batchedTransactions/eip5792Utils' import { addBatchedTransaction } from 'wallet/src/features/batchedTransactions/slice' -import { generateBatchId, getCapabilitiesCore } from 'wallet/src/features/batchedTransactions/utils' +import { generateBatchId, getCapabilitiesResponse } from 'wallet/src/features/batchedTransactions/utils' import { Call } from 'wallet/src/features/dappRequests/types' import { ExecuteTransactionParams, @@ -531,7 +530,7 @@ export function* handleGetCapabilities(request: GetCapabilitiesRequest, senderTa const hasSmartWalletConsent = yield* select(selectHasSmartWalletConsent, request.address) const chainIds = request.chainIds?.map(hexadecimalStringToInt) ?? enabledChains.map((chain) => chain.valueOf()) - const response = yield* call(getCapabilitiesCore, { + const response = yield* call(getCapabilitiesResponse, { request, chainIds, hasSmartWalletConsent, diff --git a/apps/extension/src/app/features/dappRequests/types/EthersTypes.ts b/apps/extension/src/app/features/dappRequests/types/EthersTypes.ts index df5e5d1d9dc..37307d7e8af 100644 --- a/apps/extension/src/app/features/dappRequests/types/EthersTypes.ts +++ b/apps/extension/src/app/features/dappRequests/types/EthersTypes.ts @@ -44,7 +44,7 @@ export const EthersTransactionRequestSchema = z.object({ data: BytesLikeSchema.optional(), value: BigNumberishSchema.optional(), chainId: HexadecimalNumberSchema.optional(), - type: z.number().optional(), + type: z.union([z.number(), HexadecimalNumberSchema]).optional(), accessList: AccessListishSchema.optional(), maxPriorityFeePerGas: BigNumberishSchema.optional(), maxFeePerGas: BigNumberishSchema.optional(), diff --git a/apps/extension/src/app/features/home/HomeScreen.tsx b/apps/extension/src/app/features/home/HomeScreen.tsx index f7a5e10a591..d1a7e928009 100644 --- a/apps/extension/src/app/features/home/HomeScreen.tsx +++ b/apps/extension/src/app/features/home/HomeScreen.tsx @@ -1,5 +1,6 @@ import { useApolloClient } from '@apollo/client' import { SharedEventName } from '@uniswap/analytics-events' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { memo, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -20,9 +21,12 @@ import { HomeQueryParams, HomeTabs } from 'src/app/navigation/constants' import { navigate } from 'src/app/navigation/state' import { Flex, Loader, styled, Text, TouchableArea } from 'ui/src' import { SMART_WALLET_UPGRADE_VIDEO } from 'ui/src/assets' +import { buildWrappedUrl } from 'uniswap/src/components/banners/shared/utils' +import { UniswapWrapped2025Banner } from 'uniswap/src/components/banners/UniswapWrapped2025Banner/UniswapWrapped2025Banner' import { NFTS_TAB_DATA_DEPENDENCIES } from 'uniswap/src/components/nfts/constants' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' +import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls' +import { selectHasDismissedUniswapWrapped2025Banner } from 'uniswap/src/features/behaviorHistory/selectors' +import { setHasDismissedUniswapWrapped2025Banner } from 'uniswap/src/features/behaviorHistory/slice' import { useSelectAddressHasNotifications } from 'uniswap/src/features/notifications/slice/hooks' import { setNotificationStatus } from 'uniswap/src/features/notifications/slice/slice' import { PortfolioBalance } from 'uniswap/src/features/portfolio/PortfolioBalance/PortfolioBalance' @@ -73,6 +77,25 @@ export const HomeScreen = memo(function _HomeScreen(): JSX.Element { const [isSmartWalletEnabledModalOpen, setIsSmartWalletEnabledModalOpen] = useState(false) const dispatch = useDispatch() + // UniswapWrapped2025 banner state + const isWrappedBannerEnabled = useFeatureFlag(FeatureFlags.UniswapWrapped2025) + const hasDismissedWrappedBanner = useSelector(selectHasDismissedUniswapWrapped2025Banner) + const shouldShowWrappedBanner = isWrappedBannerEnabled && !hasDismissedWrappedBanner + + const handleDismissWrappedBanner = useCallback(() => { + dispatch(setHasDismissedUniswapWrapped2025Banner(true)) + }, [dispatch]) + + const handlePressWrappedBanner = useCallback(() => { + try { + const url = buildWrappedUrl(UNISWAP_WEB_URL, address) + window.open(url, '_blank') + dispatch(setHasDismissedUniswapWrapped2025Banner(true)) + } catch (error) { + logger.error(error, { tags: { file: 'HomeScreen', function: 'handlePressWrappedBanner' } }) + } + }, [address, dispatch]) + useEffect(() => { if (selectedTab) { sendAnalyticsEvent(SharedEventName.PAGE_VIEWED, { @@ -170,12 +193,32 @@ export const HomeScreen = memo(function _HomeScreen(): JSX.Element { )} + {shouldShowWrappedBanner && ( + + + + + )} - + diff --git a/apps/extension/src/app/features/home/PortfolioActionButtons.tsx b/apps/extension/src/app/features/home/PortfolioActionButtons.tsx index a0ac5b56037..9a7a08625ce 100644 --- a/apps/extension/src/app/features/home/PortfolioActionButtons.tsx +++ b/apps/extension/src/app/features/home/PortfolioActionButtons.tsx @@ -1,4 +1,5 @@ import { SharedEventName } from '@uniswap/analytics-events' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { cloneElement, memo, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useInterfaceBuyNavigator } from 'src/app/features/for/utils' @@ -7,8 +8,6 @@ import { navigate } from 'src/app/navigation/state' import { Flex, getTokenValue, Text, TouchableArea, useMedia } from 'ui/src' import { ArrowDownCircle, Bank, CoinConvert, SendAction } from 'ui/src/components/icons' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { TestnetModeModal } from 'uniswap/src/features/testnets/TestnetModeModal' diff --git a/apps/extension/src/app/features/home/PortfolioHeader.tsx b/apps/extension/src/app/features/home/PortfolioHeader.tsx index 45df41829bc..1a13b6b6b9f 100644 --- a/apps/extension/src/app/features/home/PortfolioHeader.tsx +++ b/apps/extension/src/app/features/home/PortfolioHeader.tsx @@ -55,7 +55,7 @@ const RotatingSettingsIcon = ({ onPressSettings }: { onPressSettings(): void }): }), ) } - }, [isScreenFocused, pressProgress]) + }, [isScreenFocused]) const onBegin = (): void => { pressProgress.value = withTiming(1) @@ -189,7 +189,7 @@ export const PortfolioHeader = memo(function _PortfolioHeader({ address }: Portf borderColor="$surface2" borderRadius="$rounded20" borderWidth="$spacing1" - disableRemoveScroll={false} + enableRemoveScroll={true} zIndex="$default" {...animationPresets.fadeInDownOutUp} shadowColor="$shadowColor" diff --git a/apps/extension/src/app/features/home/TokenBalanceList.tsx b/apps/extension/src/app/features/home/TokenBalanceList.tsx index b29a53a4a2c..ebbed006847 100644 --- a/apps/extension/src/app/features/home/TokenBalanceList.tsx +++ b/apps/extension/src/app/features/home/TokenBalanceList.tsx @@ -1,9 +1,13 @@ -import { memo } from 'react' +import { Currency } from '@uniswap/sdk-core' +import { memo, useState } from 'react' import { useInterfaceBuyNavigator } from 'src/app/features/for/utils' import { AppRoutes } from 'src/app/navigation/constants' import { navigate } from 'src/app/navigation/state' import { TokenBalanceListWeb } from 'uniswap/src/components/portfolio/TokenBalanceListWeb' +import { ReportTokenIssueModal } from 'uniswap/src/components/reporting/ReportTokenIssueModal' import { ElementName } from 'uniswap/src/features/telemetry/constants' +import { useEvent } from 'utilities/src/react/hooks' +import { useBooleanState } from 'utilities/src/react/useBooleanState' import { usePortfolioEmptyStateBackground } from 'wallet/src/components/portfolio/empty' export const ExtensionTokenBalanceList = memo(function _ExtensionTokenBalanceList({ @@ -15,13 +19,35 @@ export const ExtensionTokenBalanceList = memo(function _ExtensionTokenBalanceLis navigate(`/${AppRoutes.Receive}`) } const onPressBuy = useInterfaceBuyNavigator(ElementName.EmptyStateBuy) + + const { value: isReportTokenModalOpen, setTrue: openModal, setFalse: closeReportTokenModal } = useBooleanState(false) + const [reportTokenCurrency, setReportTokenCurrency] = useState(undefined) + const [isMarkedSpam, setIsMarkedSpam] = useState>(undefined) + + const openReportTokenModal = useEvent((currency: Currency, isMarkedSpam: Maybe) => { + setReportTokenCurrency(currency) + setIsMarkedSpam(isMarkedSpam) + openModal() + }) + const backgroundImageWrapperCallback = usePortfolioEmptyStateBackground() return ( - + <> + + {reportTokenCurrency && ( + + )} + ) }) diff --git a/apps/extension/src/app/features/home/introCards/HomeIntroCardStack.tsx b/apps/extension/src/app/features/home/introCards/HomeIntroCardStack.tsx index 119a661a5d0..45723370f88 100644 --- a/apps/extension/src/app/features/home/introCards/HomeIntroCardStack.tsx +++ b/apps/extension/src/app/features/home/introCards/HomeIntroCardStack.tsx @@ -1,8 +1,10 @@ -import { useCallback } from 'react' +import { useCallback, useState } from 'react' import { AppRoutes, SettingsRoutes, UnitagClaimRoutes } from 'src/app/navigation/constants' import { focusOrCreateUnitagTab, useExtensionNavigation } from 'src/app/navigation/utils' import { Flex } from 'ui/src' +import { MonadAnnouncementModal } from 'uniswap/src/components/notifications/MonadAnnouncementModal' import { AccountType } from 'uniswap/src/features/accounts/types' +import { useEvent } from 'utilities/src/react/hooks' import { IntroCardStack } from 'wallet/src/components/introCards/IntroCardStack' import { useSharedIntroCards } from 'wallet/src/components/introCards/useSharedIntroCards' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' @@ -11,6 +13,7 @@ export function HomeIntroCardStack(): JSX.Element | null { const activeAccount = useActiveAccountWithThrow() const isSignerAccount = activeAccount.type === AccountType.SignerMnemonic const { navigateTo } = useExtensionNavigation() + const [isMonadModalOpen, setIsMonadModalOpen] = useState(false) const navigateToUnitagClaim = useCallback(async () => { await focusOrCreateUnitagTab(activeAccount.address, UnitagClaimRoutes.ClaimIntro) @@ -20,10 +23,16 @@ export function HomeIntroCardStack(): JSX.Element | null { navigateTo(`/${AppRoutes.Settings}/${SettingsRoutes.BackupRecoveryPhrase}`) }, [navigateTo]) + const handleMonadExplorePress = useEvent(() => { + window.open('https://app.uniswap.org/explore/tokens/monad', '_blank') + setIsMonadModalOpen(false) + }) + const { cards } = useSharedIntroCards({ navigateToUnitagClaim, navigateToUnitagIntro: navigateToUnitagClaim, // No need to differentiate for extension navigateToBackupFlow, + onMonadAnnouncementPress: () => setIsMonadModalOpen(true), }) // Don't show cards if there are none @@ -33,8 +42,17 @@ export function HomeIntroCardStack(): JSX.Element | null { } return ( - - - + <> + + + + {isMonadModalOpen && ( + setIsMonadModalOpen(false)} + onExplorePress={handleMonadExplorePress} + /> + )} + ) } diff --git a/apps/extension/src/app/features/onboarding/import/ImportMnemonic.tsx b/apps/extension/src/app/features/onboarding/import/ImportMnemonic.tsx index 0378892eb3a..2c89cc0cad8 100644 --- a/apps/extension/src/app/features/onboarding/import/ImportMnemonic.tsx +++ b/apps/extension/src/app/features/onboarding/import/ImportMnemonic.tsx @@ -234,7 +234,9 @@ export function ImportMnemonic(): JSX.Element { (inputRefs[index] = ref)} + ref={(ref) => { + inputRefs[index] = ref + }} handleBlur={handleBlur} handleChange={handleChange} handleKeyPress={handleKeyPress} diff --git a/apps/extension/src/app/features/onboarding/import/SelectWallets.tsx b/apps/extension/src/app/features/onboarding/import/SelectWallets.tsx index dcf3940bb5e..3f12f876606 100644 --- a/apps/extension/src/app/features/onboarding/import/SelectWallets.tsx +++ b/apps/extension/src/app/features/onboarding/import/SelectWallets.tsx @@ -1,4 +1,5 @@ import { useQuery } from '@tanstack/react-query' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { ComponentProps, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { SelectWalletsSkeleton } from 'src/app/components/loading/SelectWalletSkeleton' @@ -7,10 +8,8 @@ import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps' import { useSubmitOnEnter } from 'src/app/features/onboarding/utils' import { Flex, ScrollView, SpinningLoader, Square, Text, Tooltip, TouchableArea } from 'ui/src' import { WalletFilled } from 'ui/src/components/icons' -import { iconSizes } from 'ui/src/theme' +import { iconSizes, zIndexes } from 'ui/src/theme' import { uniswapUrls } from 'uniswap/src/constants/urls' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import Trace from 'uniswap/src/features/telemetry/Trace' import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension' import { openUri } from 'uniswap/src/utils/linking' @@ -133,7 +132,7 @@ function SmartWalletTooltip(): JSX.Element | undefined { - + diff --git a/apps/extension/src/app/features/onboarding/scan/OTPInput.tsx b/apps/extension/src/app/features/onboarding/scan/OTPInput.tsx index 4075ff3544d..9d3f1093616 100644 --- a/apps/extension/src/app/features/onboarding/scan/OTPInput.tsx +++ b/apps/extension/src/app/features/onboarding/scan/OTPInput.tsx @@ -51,7 +51,7 @@ export function OTPInput(): JSX.Element { const [failedAttemptCount, setFailedAttemptCount] = useState(0) const [characterSequence, setCharacterSequence] = useState(INITIAL_CHARACTER_SEQUENCE) - const inputRefs = useRef[]>([]) + const inputRefs = useRef[]>([]) inputRefs.current = new Array(6).fill(null).map((_, i) => inputRefs.current[i] || createRef()) // Add all accounts from mnemonic. diff --git a/apps/extension/src/app/features/onboarding/scan/ScanToOnboard.tsx b/apps/extension/src/app/features/onboarding/scan/ScanToOnboard.tsx index 30080a5b13f..3e01afb4a99 100644 --- a/apps/extension/src/app/features/onboarding/scan/ScanToOnboard.tsx +++ b/apps/extension/src/app/features/onboarding/scan/ScanToOnboard.tsx @@ -174,7 +174,7 @@ export function ScanToOnboard(): JSX.Element { ) return () => cancelAnimation(qrScale) - }, [isLoadingUUID, qrScale]) + }, [isLoadingUUID]) // Using useAnimatedStyle and AnimatedFlex because tamagui scale animation not working const qrAnimatedStyle = useAnimatedStyle(() => { return { diff --git a/apps/extension/src/app/features/receive/__snapshots__/ReceiveScreen.test.tsx.snap b/apps/extension/src/app/features/receive/__snapshots__/ReceiveScreen.test.tsx.snap index 4fe7b9ea7e4..d178a5ab606 100644 --- a/apps/extension/src/app/features/receive/__snapshots__/ReceiveScreen.test.tsx.snap +++ b/apps/extension/src/app/features/receive/__snapshots__/ReceiveScreen.test.tsx.snap @@ -20,8 +20,10 @@ exports[`ReceiveScreen renders without error 1`] = ` >
- Ethereum address + Wallet address
@@ -6090,6 +6100,19 @@ exports[`ReceiveScreen renders without error 1`] = ` />
+ + + + + , "container":
- Ethereum address + Wallet address
@@ -12236,4 +12269,4 @@ exports[`ReceiveScreen renders without error 1`] = ` }, "unmount": [Function], } -`; +`; \ No newline at end of file diff --git a/apps/extension/src/app/features/send/SendFlow.tsx b/apps/extension/src/app/features/send/SendFlow.tsx index 5136b6f464f..716c9cd81ff 100644 --- a/apps/extension/src/app/features/send/SendFlow.tsx +++ b/apps/extension/src/app/features/send/SendFlow.tsx @@ -6,7 +6,9 @@ import { useExtensionNavigation } from 'src/app/navigation/utils' import { Flex } from 'ui/src' import { X } from 'ui/src/components/icons' import { ModalName } from 'uniswap/src/features/telemetry/constants' +import { TransactionSettingsStoreContextProvider } from 'uniswap/src/features/transactions/components/settings/stores/transactionSettingsStore/TransactionSettingsStoreContextProvider' import { TransactionModal } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModal' +import { SwapFormStoreContextProvider } from 'uniswap/src/features/transactions/swap/stores/swapFormStore/SwapFormStoreContextProvider' import { SendContextProvider } from 'wallet/src/features/transactions/contexts/SendContext' export function SendFlow(): JSX.Element { @@ -15,16 +17,20 @@ export function SendFlow(): JSX.Element { return ( null}> - - - - - - - - - - + + + + + + + + + + + + + + ) } diff --git a/apps/extension/src/app/features/settings/DeviceAccessScreen.tsx b/apps/extension/src/app/features/settings/DeviceAccessScreen.tsx index c9b6d70c21b..c739b0cbe06 100644 --- a/apps/extension/src/app/features/settings/DeviceAccessScreen.tsx +++ b/apps/extension/src/app/features/settings/DeviceAccessScreen.tsx @@ -48,6 +48,7 @@ export function DeviceAccessScreen(): JSX.Element { const { flowState, + oldPassword, startPasswordReset, closeModal, onPasswordModalNext, @@ -98,6 +99,7 @@ export function DeviceAccessScreen(): JSX.Element { return ( closeModal(PasswordResetFlowState.EnterNewPassword)} /> diff --git a/apps/extension/src/app/features/settings/SettingsDropdown.tsx b/apps/extension/src/app/features/settings/SettingsDropdown.tsx index 8df26d5fe71..bcee8207b0f 100644 --- a/apps/extension/src/app/features/settings/SettingsDropdown.tsx +++ b/apps/extension/src/app/features/settings/SettingsDropdown.tsx @@ -46,7 +46,7 @@ export function SettingsDropdown({ selected, items, disableDropdown, onSelect }: /> - + setIsPortfolioBalanceModalOpen(true)} /> {isSmartWalletEnabled ? ( diff --git a/apps/extension/src/app/features/settings/password/ChangePasswordForm.test.tsx b/apps/extension/src/app/features/settings/password/ChangePasswordForm.test.tsx new file mode 100644 index 00000000000..f4721fb4899 --- /dev/null +++ b/apps/extension/src/app/features/settings/password/ChangePasswordForm.test.tsx @@ -0,0 +1,166 @@ +import { act, fireEvent, waitFor } from '@testing-library/react' +import { ChangePasswordForm } from 'src/app/features/settings/password/ChangePasswordForm' +import { cleanup, render, screen } from 'src/test/test-utils' +import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' + +// Mock the Keyring +jest.mock('wallet/src/features/wallet/Keyring/Keyring', () => ({ + Keyring: { + changePassword: jest.fn().mockResolvedValue(undefined), + }, +})) + +// Mock analytics +jest.mock('uniswap/src/features/telemetry/send', () => ({ + sendAnalyticsEvent: jest.fn(), +})) + +const mockChangePassword = Keyring.changePassword as jest.MockedFunction + +describe('ChangePasswordForm', () => { + const mockOnNext = jest.fn() + const oldPassword = 'MyOldPassword123!' + + beforeEach(() => { + jest.clearAllMocks() + mockChangePassword.mockClear() + }) + + afterEach(() => { + cleanup() + }) + + it('renders without error', () => { + const tree = render() + expect(tree).toMatchSnapshot() + }) + + it('renders password input fields', () => { + render() + + // Check for translated placeholders + expect(screen.getByPlaceholderText('New password')).toBeDefined() + expect(screen.getByPlaceholderText('Confirm password')).toBeDefined() + }) + + it('renders continue button', () => { + render() + + expect(screen.getByText('Continue')).toBeDefined() + }) + + it('shows error when new password matches old password', async () => { + render() + + const newPasswordInput = screen.getByPlaceholderText('New password') + const confirmPasswordInput = screen.getByPlaceholderText('Confirm password') + + // Type the same password as the old password + act(() => { + fireEvent.change(newPasswordInput, { target: { value: oldPassword } }) + fireEvent.change(confirmPasswordInput, { target: { value: oldPassword } }) + }) + + // Wait for error to appear + await waitFor(() => { + const errorText = screen.getByText('New password must be different from current password') + expect(errorText).toBeDefined() + }) + }) + + it('clears error when password changes to be different', async () => { + render() + + const newPasswordInput = screen.getByPlaceholderText('New password') + const confirmPasswordInput = screen.getByPlaceholderText('Confirm password') + + // First, type the same password as old password + act(() => { + fireEvent.change(newPasswordInput, { target: { value: oldPassword } }) + fireEvent.change(confirmPasswordInput, { target: { value: oldPassword } }) + }) + + // Wait for error to appear + await waitFor(() => { + expect(screen.getByText('New password must be different from current password')).toBeDefined() + }) + + // Clear and type a different password + act(() => { + fireEvent.change(newPasswordInput, { target: { value: 'DifferentPassword789!' } }) + fireEvent.change(confirmPasswordInput, { target: { value: 'DifferentPassword789!' } }) + }) + + // Error should be cleared + await waitFor(() => { + expect(screen.queryByText('New password must be different from current password')).toBeNull() + }) + }) + + it('does not call onNext when passwords match old password', async () => { + render() + + const newPasswordInput = screen.getByPlaceholderText('New password') + const confirmPasswordInput = screen.getByPlaceholderText('Confirm password') + const submitButton = screen.getByText('Continue') + + // Type the same password as the old password + act(() => { + fireEvent.change(newPasswordInput, { target: { value: oldPassword } }) + fireEvent.change(confirmPasswordInput, { target: { value: oldPassword } }) + }) + + // Try to submit + await act(async () => { + fireEvent.click(submitButton) + }) + + expect(mockOnNext).not.toHaveBeenCalled() + expect(mockChangePassword).not.toHaveBeenCalled() + }) + + it('calls onNext and changePassword when passwords are different and valid', async () => { + render() + + const newPasswordInput = screen.getByPlaceholderText('New password') + const confirmPasswordInput = screen.getByPlaceholderText('Confirm password') + const submitButton = screen.getByText('Continue') + + const newPassword = 'MyNewStrongPassword456!' + + // Type a different password + act(() => { + fireEvent.change(newPasswordInput, { target: { value: newPassword } }) + fireEvent.change(confirmPasswordInput, { target: { value: newPassword } }) + }) + + // Submit the form + await act(async () => { + fireEvent.click(submitButton) + }) + + await waitFor(() => { + expect(mockChangePassword).toHaveBeenCalledWith(newPassword) + expect(mockOnNext).toHaveBeenCalledWith(newPassword) + }) + }) + + it('handles undefined oldPassword gracefully', async () => { + render() + + const newPasswordInput = screen.getByPlaceholderText('New password') + const confirmPasswordInput = screen.getByPlaceholderText('Confirm password') + + const newPassword = 'AnyPassword123!' + + // Type any password - should not show "same password" error since oldPassword is undefined + act(() => { + fireEvent.change(newPasswordInput, { target: { value: newPassword } }) + fireEvent.change(confirmPasswordInput, { target: { value: newPassword } }) + }) + + await waitFor(() => { + expect(screen.queryByText('New password must be different from current password')).toBeNull() + }) + }) +}) diff --git a/apps/extension/src/app/features/settings/password/ChangePasswordForm.tsx b/apps/extension/src/app/features/settings/password/ChangePasswordForm.tsx index c03b41a1cac..d82b56ba129 100644 --- a/apps/extension/src/app/features/settings/password/ChangePasswordForm.tsx +++ b/apps/extension/src/app/features/settings/password/ChangePasswordForm.tsx @@ -1,13 +1,19 @@ -import { useCallback } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { PADDING_STRENGTH_INDICATOR, PasswordInput } from 'src/app/components/PasswordInput' import { Button, Flex, Text } from 'ui/src' import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { usePasswordForm } from 'wallet/src/utils/password' +import { PasswordErrors, usePasswordForm } from 'wallet/src/utils/password' -export function ChangePasswordForm({ onNext }: { onNext: (password: string) => void }): JSX.Element { +export function ChangePasswordForm({ + oldPassword, + onNext, +}: { + oldPassword: string | undefined + onNext: (password: string) => void +}): JSX.Element { const { t } = useTranslation() const { @@ -20,18 +26,45 @@ export function ChangePasswordForm({ onNext }: { onNext: (password: string) => v confirmPassword, onChangeConfirmPassword, setHideInput, - errorText, + errorText: baseErrorText, checkSubmit, } = usePasswordForm() + const [customError, setCustomError] = useState(undefined) + + // Check if new password is same as old password + const isSamePassword = useMemo( + () => Boolean(password && oldPassword && password === oldPassword), + [password, oldPassword], + ) + + // Update custom error when password matches old password + useEffect(() => { + setCustomError(isSamePassword ? PasswordErrors.SamePassword : undefined) + }, [isSamePassword]) + + // Override error text if custom error exists + const errorText = useMemo(() => { + if (customError === PasswordErrors.SamePassword) { + return t('common.input.password.error.same') + } + return baseErrorText + }, [customError, baseErrorText, t]) + const onSubmit = useCallback(async () => { + // Check for same password error + if (isSamePassword) { + setCustomError(PasswordErrors.SamePassword) + return + } + if (checkSubmit()) { // Just change the password and pass it to the parent await Keyring.changePassword(password) sendAnalyticsEvent(ExtensionEventName.PasswordChanged) onNext(password) } - }, [checkSubmit, password, onNext]) + }, [checkSubmit, password, onNext, isSamePassword]) return ( @@ -70,7 +103,7 @@ export function ChangePasswordForm({ onNext }: { onNext: (password: string) => v - diff --git a/apps/extension/src/app/features/settings/password/CreateNewPasswordModal.tsx b/apps/extension/src/app/features/settings/password/CreateNewPasswordModal.tsx index 65860afaa14..9411c1fa006 100644 --- a/apps/extension/src/app/features/settings/password/CreateNewPasswordModal.tsx +++ b/apps/extension/src/app/features/settings/password/CreateNewPasswordModal.tsx @@ -7,10 +7,12 @@ import { ModalName } from 'uniswap/src/features/telemetry/constants' export function CreateNewPasswordModal({ isOpen, + oldPassword, onNext, onClose, }: { isOpen: boolean + oldPassword: string | undefined onNext: (password: string) => void onClose: () => void }): JSX.Element { @@ -36,7 +38,7 @@ export function CreateNewPasswordModal({ {t('settings.setting.password.change.title')} - + ) diff --git a/apps/extension/src/app/features/settings/password/__snapshots__/ChangePasswordForm.test.tsx.snap b/apps/extension/src/app/features/settings/password/__snapshots__/ChangePasswordForm.test.tsx.snap new file mode 100644 index 00000000000..8dc96964d8e --- /dev/null +++ b/apps/extension/src/app/features/settings/password/__snapshots__/ChangePasswordForm.test.tsx.snap @@ -0,0 +1,287 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ChangePasswordForm renders without error 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ +
+
+
+ + + +
+
+ + + +
+ + + +
+
+ +
+
+ +
+
+
+ +
+ , + "container":
+ +
+
+
+ + + +
+
+ + + +
+ + + +
+
+ +
+
+ +
+
+
+ +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "store": { + "dispatch": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], + Symbol(observable): [Function], + }, + "unmount": [Function], +} +`; diff --git a/apps/extension/src/app/features/settings/password/usePasswordResetFlow.test.ts b/apps/extension/src/app/features/settings/password/usePasswordResetFlow.test.ts index 9ccb4c71a73..3685be9bde1 100644 --- a/apps/extension/src/app/features/settings/password/usePasswordResetFlow.test.ts +++ b/apps/extension/src/app/features/settings/password/usePasswordResetFlow.test.ts @@ -67,6 +67,12 @@ describe('usePasswordResetFlow', () => { expect(result.current.flowState).toBe(PasswordResetFlowState.None) }) + it('should initialize with undefined oldPassword', () => { + const { result } = renderHook(() => usePasswordResetFlow()) + + expect(result.current.oldPassword).toBeUndefined() + }) + it('should transition to EnterCurrentPassword when starting password reset', () => { const { result } = renderHook(() => usePasswordResetFlow()) @@ -91,6 +97,22 @@ describe('usePasswordResetFlow', () => { expect(result.current.flowState).toBe(PasswordResetFlowState.EnterNewPassword) }) + it('should store oldPassword when valid password is provided', () => { + const { result } = renderHook(() => usePasswordResetFlow()) + const testPassword = 'myOldPassword123!' + + act(() => { + result.current.startPasswordReset() + }) + + act(() => { + result.current.onPasswordModalNext(testPassword) + }) + + expect(result.current.oldPassword).toBe(testPassword) + expect(result.current.flowState).toBe(PasswordResetFlowState.EnterNewPassword) + }) + it('should return to None state when no password is provided', () => { const { result } = renderHook(() => usePasswordResetFlow()) @@ -105,6 +127,32 @@ describe('usePasswordResetFlow', () => { expect(result.current.flowState).toBe(PasswordResetFlowState.None) }) + it('should clear oldPassword when no password is provided', () => { + const { result } = renderHook(() => usePasswordResetFlow()) + + act(() => { + result.current.startPasswordReset() + }) + + act(() => { + result.current.onPasswordModalNext('validPassword') + }) + + expect(result.current.oldPassword).toBe('validPassword') + + // Go back and provide no password + act(() => { + result.current.startPasswordReset() + }) + + act(() => { + result.current.onPasswordModalNext() + }) + + expect(result.current.oldPassword).toBeUndefined() + expect(result.current.flowState).toBe(PasswordResetFlowState.None) + }) + it('should transition to BiometricAuth when biometric is enabled', () => { mockUseHasBiometricUnlockCredential.mockReturnValue(true) const { result } = renderHook(() => usePasswordResetFlow()) @@ -160,6 +208,28 @@ describe('usePasswordResetFlow', () => { expect(result.current.flowState).toBe(PasswordResetFlowState.None) }) + it('should clear oldPassword when closeModal is called with matching state', () => { + const { result } = renderHook(() => usePasswordResetFlow()) + + act(() => { + result.current.startPasswordReset() + }) + + act(() => { + result.current.onPasswordModalNext('testPassword123') + }) + + expect(result.current.oldPassword).toBe('testPassword123') + expect(result.current.flowState).toBe(PasswordResetFlowState.EnterNewPassword) + + act(() => { + result.current.closeModal(PasswordResetFlowState.EnterNewPassword) + }) + + expect(result.current.flowState).toBe(PasswordResetFlowState.None) + expect(result.current.oldPassword).toBeUndefined() + }) + it('should not close modal when closeModal is called with non-matching state', () => { const { result } = renderHook(() => usePasswordResetFlow()) @@ -176,6 +246,29 @@ describe('usePasswordResetFlow', () => { expect(result.current.flowState).toBe(PasswordResetFlowState.EnterCurrentPassword) }) + it('should not clear oldPassword when closeModal is called with non-matching state', () => { + const { result } = renderHook(() => usePasswordResetFlow()) + + act(() => { + result.current.startPasswordReset() + }) + + act(() => { + result.current.onPasswordModalNext('testPassword456') + }) + + expect(result.current.oldPassword).toBe('testPassword456') + expect(result.current.flowState).toBe(PasswordResetFlowState.EnterNewPassword) + + act(() => { + result.current.closeModal(PasswordResetFlowState.BiometricAuth) + }) + + // Should not clear oldPassword or change state + expect(result.current.flowState).toBe(PasswordResetFlowState.EnterNewPassword) + expect(result.current.oldPassword).toBe('testPassword456') + }) + it('should transition to BiometricAuth state when biometric is enabled and trigger internal mutation', () => { mockUseHasBiometricUnlockCredential.mockReturnValue(true) const { result } = renderHook(() => usePasswordResetFlow()) diff --git a/apps/extension/src/app/features/settings/password/usePasswordResetFlow.ts b/apps/extension/src/app/features/settings/password/usePasswordResetFlow.ts index 48e02692ef5..9c2b61b4eda 100644 --- a/apps/extension/src/app/features/settings/password/usePasswordResetFlow.ts +++ b/apps/extension/src/app/features/settings/password/usePasswordResetFlow.ts @@ -54,6 +54,7 @@ export enum PasswordResetFlowState { interface PasswordResetFlowResult { // State flowState: PasswordResetFlowState + oldPassword: string | undefined // Actions startPasswordReset: () => void @@ -68,6 +69,7 @@ interface PasswordResetFlowResult { export function usePasswordResetFlow(): PasswordResetFlowResult { const dispatch = useDispatch() const [flowState, setFlowState] = useState(PasswordResetFlowState.None) + const [oldPassword, setOldPassword] = useState(undefined) const hasBiometricUnlockCredential = useHasBiometricUnlockCredential() @@ -95,15 +97,18 @@ export function usePasswordResetFlow(): PasswordResetFlowResult { // This check ensures the close action is from user interaction, not from modal state changes. if (flowState === expectedState) { setFlowState(PasswordResetFlowState.None) + setOldPassword(undefined) } }) const onPasswordModalNext = useEvent((password?: string): void => { if (!password) { setFlowState(PasswordResetFlowState.None) + setOldPassword(undefined) return } + setOldPassword(password) setFlowState(PasswordResetFlowState.EnterNewPassword) }) @@ -127,6 +132,7 @@ export function usePasswordResetFlow(): PasswordResetFlowResult { return { // State flowState, + oldPassword, // Actions startPasswordReset, diff --git a/apps/extension/src/app/hooks/useIsExtensionPasskeyImportEnabled.ts b/apps/extension/src/app/hooks/useIsExtensionPasskeyImportEnabled.ts index 8abf941051b..823ac6ba868 100644 --- a/apps/extension/src/app/hooks/useIsExtensionPasskeyImportEnabled.ts +++ b/apps/extension/src/app/hooks/useIsExtensionPasskeyImportEnabled.ts @@ -1,5 +1,4 @@ -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' export function useIsExtensionPasskeyImportEnabled(): boolean { return useFeatureFlag(FeatureFlags.EmbeddedWallet) diff --git a/apps/extension/src/app/navigation/navigation.tsx b/apps/extension/src/app/navigation/navigation.tsx index 7c0848212a5..a9934d18846 100644 --- a/apps/extension/src/app/navigation/navigation.tsx +++ b/apps/extension/src/app/navigation/navigation.tsx @@ -202,7 +202,7 @@ const AnimatedPane = styled(Flex, { const isVertical = (dir: Direction): boolean => dir === 'up' || dir === 'down' function useConstant(c: A): A { - const out = useRef() + const out = useRef(undefined) if (!out.current) { out.current = c } diff --git a/apps/extension/src/app/saga.ts b/apps/extension/src/app/saga.ts index a5e6ddbf951..e4c17a340bd 100644 --- a/apps/extension/src/app/saga.ts +++ b/apps/extension/src/app/saga.ts @@ -28,13 +28,6 @@ import { prepareAndSignSwapSaga, prepareAndSignSwapSagaName, } from 'wallet/src/features/transactions/swap/configuredSagas' -import { swapActions, swapReducer, swapSaga, swapSagaName } from 'wallet/src/features/transactions/swap/swapSaga' -import { - tokenWrapActions, - tokenWrapReducer, - tokenWrapSaga, - tokenWrapSagaName, -} from 'wallet/src/features/transactions/swap/wrapSaga' import { watchTransactionEvents } from 'wallet/src/features/transactions/watcher/transactionFinalizationSaga' import { transactionWatcher } from 'wallet/src/features/transactions/watcher/transactionWatcherSaga' import { @@ -83,18 +76,6 @@ export const monitoredSagas: Record = { reducer: executeSwapReducer, actions: executeSwapActions, }, - [swapSagaName]: { - name: swapSagaName, - wrappedSaga: swapSaga, - reducer: swapReducer, - actions: swapActions, - }, - [tokenWrapSagaName]: { - name: tokenWrapSagaName, - wrappedSaga: tokenWrapSaga, - reducer: tokenWrapReducer, - actions: tokenWrapActions, - }, [removeDelegationSagaName]: { name: removeDelegationSagaName, wrappedSaga: removeDelegationSaga, diff --git a/apps/extension/src/app/utils/analytics.ts b/apps/extension/src/app/utils/analytics.ts index b24911e58b1..41fb45f6b41 100644 --- a/apps/extension/src/app/utils/analytics.ts +++ b/apps/extension/src/app/utils/analytics.ts @@ -5,11 +5,18 @@ import 'symbol-observable' // Needed by `reduxed-chrome-storage` as polyfill, or import { EXTENSION_ORIGIN_APPLICATION } from 'src/app/version' import { uniswapUrls } from 'uniswap/src/constants/urls' import { getUniqueId } from 'utilities/src/device/uniqueId' +import { isTestEnv } from 'utilities/src/environment/env' +import { logger } from 'utilities/src/logger/logger' import { ApplicationTransport } from 'utilities/src/telemetry/analytics/ApplicationTransport' // biome-ignore lint/style/noRestrictedImports: Direct utilities import required for analytics initialization import { analytics, getAnalyticsAtomDirect } from 'utilities/src/telemetry/analytics/analytics' export async function initExtensionAnalytics(): Promise { + if (isTestEnv()) { + logger.debug('analytics.ts', 'initExtensionAnalytics', 'Skipping Amplitude initialization in test environment') + return + } + const analyticsAllowed = await getAnalyticsAtomDirect(true) await analytics.init({ transportProvider: new ApplicationTransport({ diff --git a/apps/extension/src/background/backgroundDappRequests.ts b/apps/extension/src/background/backgroundDappRequests.ts index 968024943b1..e256ed0fb19 100644 --- a/apps/extension/src/background/backgroundDappRequests.ts +++ b/apps/extension/src/background/backgroundDappRequests.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import { rpcErrors, serializeError } from '@metamask/rpc-errors' import { removeDappConnection } from 'src/app/features/dapp/actions' import { changeChain } from 'src/app/features/dapp/changeChain' @@ -32,7 +33,7 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { WindowEthereumRequestProperties } from 'uniswap/src/features/telemetry/types' import { extractBaseUrl } from 'utilities/src/format/urls' import { logger } from 'utilities/src/logger/logger' -import { getCapabilitiesCore } from 'wallet/src/features/batchedTransactions/utils' +import { getCapabilitiesResponse } from 'wallet/src/features/batchedTransactions/utils' import { walletContextValue } from 'wallet/src/features/wallet/context' import { selectHasSmartWalletConsent } from 'wallet/src/features/wallet/selectors' @@ -320,7 +321,7 @@ async function handleGetCapabilities({ }) const chainIds = request.chainIds?.map(hexadecimalStringToInt) ?? enabledChains.map((chain) => chain.valueOf()) - const response = await getCapabilitiesCore({ + const response = await getCapabilitiesResponse({ request, chainIds, hasSmartWalletConsent, diff --git a/apps/extension/src/background/utils/persistedStateUtils.ts b/apps/extension/src/background/utils/persistedStateUtils.ts index 50f2b49cdc3..2b646c64565 100644 --- a/apps/extension/src/background/utils/persistedStateUtils.ts +++ b/apps/extension/src/background/utils/persistedStateUtils.ts @@ -2,6 +2,7 @@ import { isOnboardedSelector } from 'src/app/utils/isOnboardedSelector' import { STATE_STORAGE_KEY } from 'src/store/constants' import { ExtensionState } from 'src/store/extensionReducer' import { EXTENSION_STATE_VERSION } from 'src/store/migrations' +import { deviceAccessTimeoutToMinutes } from 'uniswap/src/features/settings/constants' import { logger } from 'utilities/src/logger/logger' export async function readReduxStateFromStorage(storageChanges?: { @@ -30,6 +31,11 @@ export async function readIsOnboardedFromStorage(): Promise { return state ? isOnboardedSelector(state) : false } +export async function readDeviceAccessTimeoutMinutesFromStorage(): Promise { + const state = await readReduxStateFromStorage() + return state ? deviceAccessTimeoutToMinutes(state.userSettings.deviceAccessTimeout) : undefined +} + /** * Checks if Redux migrations are pending by comparing persisted version with current version * @returns true if migrations are pending and sidebar should handle the request diff --git a/apps/extension/src/entrypoints/background.ts b/apps/extension/src/entrypoints/background.ts index f75bfd172b9..773b1b9114a 100644 --- a/apps/extension/src/entrypoints/background.ts +++ b/apps/extension/src/entrypoints/background.ts @@ -1,5 +1,6 @@ import 'symbol-observable' // Needed by `reduxed-chrome-storage` as polyfill, order matters +import { AUTO_LOCK_ALARM_NAME } from 'src/app/components/AutoLockProvider' import { initStatSigForBrowserScripts } from 'src/app/core/initStatSigForBrowserScripts' import { focusOrCreateOnboardingTab } from 'src/app/navigation/focusOrCreateOnboardingTab' import { initExtensionAnalytics } from 'src/app/utils/analytics' @@ -14,9 +15,15 @@ import { ContentScriptUtilityMessageType, } from 'src/background/messagePassing/types/requests' import { setSidePanelBehavior, setSidePanelOptions } from 'src/background/utils/chromeSidePanelUtils' -import { readIsOnboardedFromStorage } from 'src/background/utils/persistedStateUtils' +import { + readDeviceAccessTimeoutMinutesFromStorage, + readIsOnboardedFromStorage, +} from 'src/background/utils/persistedStateUtils' import { uniswapUrls } from 'uniswap/src/constants/urls' +import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants' +import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { logger } from 'utilities/src/logger/logger' +import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' import { defineBackground } from 'wxt/utils/define-background' async function enableSidebar(): Promise { @@ -69,6 +76,53 @@ function makeBackground(): void { await checkAndHandleOnboarding() }) + // Auto-lock alarm listener + chrome.alarms.onAlarm.addListener((alarm) => { + if (alarm.name === AUTO_LOCK_ALARM_NAME) { + Keyring.lock() + .then(() => { + sendAnalyticsEvent(ExtensionEventName.ChangeLockedState, { + locked: true, + location: 'background', + }) + }) + .catch((error) => { + logger.error(error, { + tags: { + file: 'background.ts', + function: 'alarms.onAlarm', + }, + }) + }) + } + }) + + // Listen for sidebar port disconnects to schedule auto-lock alarm + chrome.runtime.onConnect.addListener((port) => { + if (port.name === AUTO_LOCK_ALARM_NAME) { + port.onDisconnect.addListener(async () => { + try { + // Get timeout setting from Redux state + const delayInMinutes = await readDeviceAccessTimeoutMinutesFromStorage() + if (delayInMinutes === undefined) { + return + } + + // Schedule alarm + chrome.alarms.create(AUTO_LOCK_ALARM_NAME, { delayInMinutes }) + logger.debug('background', 'port.onDisconnect', `Scheduled auto-lock alarm for ${delayInMinutes} minutes`) + } catch (error) { + logger.error(error, { + tags: { + file: 'background.ts', + function: 'port.onDisconnect', + }, + }) + } + }) + } + }) + // on arc browser, show unsupported browser page (lives on onboarding flow) // this is because arc doesn't support the sidebar contentScriptUtilityMessageChannel.addMessageListener( diff --git a/apps/extension/src/entrypoints/sidepanel/main.tsx b/apps/extension/src/entrypoints/sidepanel/main.tsx index 4cbeaaa35d0..5465a71e672 100644 --- a/apps/extension/src/entrypoints/sidepanel/main.tsx +++ b/apps/extension/src/entrypoints/sidepanel/main.tsx @@ -11,8 +11,10 @@ import { createRoot } from 'react-dom/client' import SidebarApp from 'src/app/core/SidebarApp' import { onboardingMessageChannel } from 'src/background/messagePassing/messageChannels' import { OnboardingMessageType } from 'src/background/messagePassing/types/ExtensionMessages' +import { getReduxStore } from 'src/store/store' import { ExtensionAppLocation, StoreSynchronization } from 'src/store/storeSynchronization' import { initializeScrollWatcher } from 'uniswap/src/components/modals/ScrollLock' +import { initializePortfolioQueryOverrides } from 'uniswap/src/data/rest/portfolioBalanceOverrides' import { logger } from 'utilities/src/logger/logger' // biome-ignore lint/suspicious/noExplicitAny: Global polyfill cleanup requires any type for runtime modification ;(globalThis as any).regeneratorRuntime = undefined @@ -44,6 +46,7 @@ export function makeSidebar(): void { } StoreSynchronization.init(ExtensionAppLocation.SidePanel) + initializePortfolioQueryOverrides({ store: getReduxStore() }) initSidebar() initializeScrollWatcher() } diff --git a/apps/extension/src/manifest.json b/apps/extension/src/manifest.json index 4b599a3f4bd..e7bfeae6572 100644 --- a/apps/extension/src/manifest.json +++ b/apps/extension/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "Uniswap Extension", "description": "The Uniswap Extension is a self-custody crypto wallet that's built for swapping.", - "version": "1.61.0", + "version": "1.64.0", "minimum_chrome_version": "116", "icons": { "16": "assets/icon16.png", diff --git a/apps/extension/src/store/migrations.test.ts b/apps/extension/src/store/migrations.test.ts index f78dd2d2f16..fe092e7fcae 100644 --- a/apps/extension/src/store/migrations.test.ts +++ b/apps/extension/src/store/migrations.test.ts @@ -36,7 +36,10 @@ import { v24Schema, v25Schema, v26Schema, + v27Schema, + v29Schema, } from 'src/store/schema' +import { USDC } from 'uniswap/src/constants/tokens' import { initialUniswapBehaviorHistoryState } from 'uniswap/src/features/behaviorHistory/slice' import { UniverseChainId } from 'uniswap/src/features/chains/types' import { initialFavoritesState } from 'uniswap/src/features/favorites/slice' @@ -44,11 +47,16 @@ import { FiatCurrency } from 'uniswap/src/features/fiatCurrency/constants' import { initialNotificationsState } from 'uniswap/src/features/notifications/slice/slice' import { initialSearchHistoryState } from 'uniswap/src/features/search/searchHistorySlice' import { initialUserSettingsState } from 'uniswap/src/features/settings/slice' -import { initialTokensState } from 'uniswap/src/features/tokens/slice/slice' +import { initialTokensState } from 'uniswap/src/features/tokens/warnings/slice/slice' import { initialTransactionsState } from 'uniswap/src/features/transactions/slice' import { TransactionStatus, TransactionType } from 'uniswap/src/features/transactions/types/transactionDetails' import { initialVisibilityState } from 'uniswap/src/features/visibility/slice' -import { testMigrateSearchHistory, testRemoveTHBFromCurrency } from 'uniswap/src/state/uniswapMigrationTests' +import { + testAddActivityVisibility, + testMigrateDismissedTokenWarnings, + testMigrateSearchHistory, + testRemoveTHBFromCurrency, +} from 'uniswap/src/state/uniswapMigrationTests' import { getAllKeysOfNestedObject } from 'utilities/src/primitives/objects' import { initialAppearanceSettingsState } from 'wallet/src/features/appearance/slice' import { initialBatchedTransactionsState } from 'wallet/src/features/batchedTransactions/slice' @@ -347,4 +355,24 @@ describe('Redux state migrations', () => { it('migrates from v26 to v27', () => { testMigrateSearchHistory(migrations[27], v26Schema) }) + + it('migrates from v27 to v29', () => { + testAddActivityVisibility(migrations[29], v27Schema) + }) + + it('migrates from v29 to v30', () => { + testMigrateDismissedTokenWarnings(migrations[30], { + ...v29Schema, + tokens: { + dismissedTokenWarnings: { + [UniverseChainId.Mainnet]: { + [USDC.address]: { + chainId: UniverseChainId.Mainnet, + address: USDC.address, + }, + }, + }, + }, + }) + }) }) diff --git a/apps/extension/src/store/migrations.ts b/apps/extension/src/store/migrations.ts index e9534188698..54f1e83a420 100644 --- a/apps/extension/src/store/migrations.ts +++ b/apps/extension/src/store/migrations.ts @@ -7,7 +7,9 @@ import { removeDappInfoToChromeLocalStorage, } from 'src/store/extensionMigrations' import { + addActivityVisibility, addDismissedBridgedAndCompatibleWarnings, + migrateDismissedTokenWarnings, migrateSearchHistory, removeThaiBahtFromFiatCurrency, unchecksumDismissedTokenWarningKeys, @@ -67,6 +69,8 @@ export const migrations = { 26: migrateLiquidityTransactionInfo, 27: migrateSearchHistory, 28: addDismissedBridgedAndCompatibleWarnings, + 29: addActivityVisibility, + 30: migrateDismissedTokenWarnings, } -export const EXTENSION_STATE_VERSION = 28 +export const EXTENSION_STATE_VERSION = 30 diff --git a/apps/extension/src/store/schema.ts b/apps/extension/src/store/schema.ts index 0fd7dc87697..f7c21de465c 100644 --- a/apps/extension/src/store/schema.ts +++ b/apps/extension/src/store/schema.ts @@ -276,6 +276,10 @@ export const v25Schema = { ...v24Schema } export const v26Schema = { ...v25Schema } -const v27Schema = { ...v26Schema } +export const v27Schema = { ...v26Schema } -export const getSchema = (): typeof v27Schema => v27Schema +export const v29Schema = { ...v27Schema, visibility: { ...v27Schema.visibility, activity: {} } } + +const v30Schema = { ...v29Schema } + +export const getSchema = (): typeof v30Schema => v30Schema diff --git a/apps/extension/src/test/babel.config.js b/apps/extension/src/test/babel.config.js index 9118275b087..7913db21158 100644 --- a/apps/extension/src/test/babel.config.js +++ b/apps/extension/src/test/babel.config.js @@ -16,10 +16,11 @@ module.exports = function (api) { ], // https://github.com/software-mansion/react-native-reanimated/issues/3364#issuecomment-1268591867 '@babel/plugin-proposal-export-namespace-from', + 'react-native-reanimated/plugin', ].filter(Boolean) return { - presets: ['module:@react-native/babel-preset'], + presets: ['babel-preset-expo'], plugins, } } diff --git a/apps/extension/src/test/fixtures/redux.ts b/apps/extension/src/test/fixtures/redux.ts index fbdbe85ec9b..773a07165bf 100644 --- a/apps/extension/src/test/fixtures/redux.ts +++ b/apps/extension/src/test/fixtures/redux.ts @@ -5,8 +5,13 @@ import { preloadedWalletPackageState } from 'wallet/src/test/fixtures' type PreloadedExtensionStateOptions = Record -export const preloadedExtensionState = createFixture, PreloadedExtensionStateOptions>( - {}, -)(() => ({ +type PreloadedExtensionStateFactory = ( + overrides?: Partial & PreloadedExtensionStateOptions>, +) => PreloadedState + +export const preloadedExtensionState: PreloadedExtensionStateFactory = createFixture< + PreloadedState, + PreloadedExtensionStateOptions +>({})(() => ({ ...preloadedWalletPackageState(), })) diff --git a/apps/extension/tsconfig.json b/apps/extension/tsconfig.json index 007f0494490..72cc9db7534 100644 --- a/apps/extension/tsconfig.json +++ b/apps/extension/tsconfig.json @@ -17,6 +17,12 @@ { "path": "../../packages/ui" }, + { + "path": "../../packages/sessions" + }, + { + "path": "../../packages/gating" + }, { "path": "../../packages/api" } diff --git a/apps/extension/webpack.config.js b/apps/extension/webpack.config.js index 2893c62943f..06ffc015ebf 100644 --- a/apps/extension/webpack.config.js +++ b/apps/extension/webpack.config.js @@ -29,6 +29,7 @@ const compileNodeModules = [ 'expo-linear-gradient', 'react-native-image-picker', 'expo-modules-core', + 'react-native-reanimated', ] // This is needed for webpack to compile JavaScript. @@ -48,8 +49,8 @@ const babelLoaderConfiguration = { loader: 'babel-loader', options: { cacheDirectory: true, - // The 'metro-react-native-babel-preset' preset is recommended to match React Native's packager - presets: ['module:@react-native/babel-preset'], + // The 'babel-preset-expo' preset is recommended to match React Native's packager + presets: ['babel-preset-expo'], // Re-write paths to import only the modules needed by the app plugins: ['react-native-web'], }, diff --git a/apps/extension/wxt.config.ts b/apps/extension/wxt.config.ts index 37e4fc458e7..6fa77e81623 100644 --- a/apps/extension/wxt.config.ts +++ b/apps/extension/wxt.config.ts @@ -16,7 +16,7 @@ const icons = { const BASE_NAME = 'Uniswap Extension' const BASE_DESCRIPTION = "The Uniswap Extension is a self-custody crypto wallet that's built for swapping." -const BASE_VERSION = '1.61.0' +const BASE_VERSION = '1.64.0' const BUILD_NUM = parseInt(process.env.BUILD_NUM || '0') const EXTENSION_VERSION = `${BASE_VERSION}.${BUILD_NUM}` @@ -187,10 +187,14 @@ export default defineConfig({ plugins: [ { - name: 'transform-expo-blur-jsx', + name: 'transform-react-native-jsx', async transform(code, id) { - // Only transform expo-blur .js files - if (!id.includes('node_modules/expo-blur') || !id.endsWith('.js')) { + // Transform JSX in react-native libraries that ship JSX in .js files + const needsJsxTransform = ['node_modules/expo-blur', 'node_modules/react-native-reanimated'].some((path) => + id.includes(path), + ) + + if (!needsJsxTransform || !id.endsWith('.js')) { return null } diff --git a/apps/mobile/.depcheckrc b/apps/mobile/.depcheckrc index 6190990de06..3ef594a9703 100644 --- a/apps/mobile/.depcheckrc +++ b/apps/mobile/.depcheckrc @@ -12,7 +12,7 @@ ignores: [ "babel-plugin-transform-remove-console", "cross-fetch", "@datadog/datadog-ci", - "@rnef/cli", + "dotenv", "expo-localization", "expo-linking", "expo-modules-core", @@ -43,4 +43,7 @@ ignores: [ "metro-config", ## used in sessions/api packages "expo-secure-store", + ## used for expo remote build caching + "eas-build-cache-provider", + "expo-dev-client", ] diff --git a/apps/mobile/.eslintrc.js b/apps/mobile/.eslintrc.js index 8a2ee3a2cee..26a62d9f007 100644 --- a/apps/mobile/.eslintrc.js +++ b/apps/mobile/.eslintrc.js @@ -58,13 +58,6 @@ module.exports = { 'import/no-unused-modules': 'off', }, }, - { - // Allow test files to exceed max-lines limit - files: ['**/*.test.ts', '**/*.test.tsx', '**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], - rules: { - 'max-lines': 'off', - }, - }, ], rules: { 'rulesdir/i18n': 'error', diff --git a/apps/mobile/.fingerprintignore b/apps/mobile/.fingerprintignore new file mode 100644 index 00000000000..2bcbe3b6c40 --- /dev/null +++ b/apps/mobile/.fingerprintignore @@ -0,0 +1,49 @@ +# Generated files that shouldn't trigger rebuilds +ios/WidgetsCore/MobileSchema/**/*.swift +ios/WidgetsCore/Env.swift +ios/OneSignalNotificationServiceExtension/Env.swift +.maestro/scripts/dist/**/* +.maestro/scripts/performance/dist/**/* + +# Cache/temporary files +.expo/**/* +coverage/**/* +.tamagui/**/* +storybook-static/**/* +dist/**/* +build/**/* + +# Environment files +.env* +!.env.example + +# All node_modules - native deps are tracked via lock files +node_modules/**/* +../../node_modules/**/* + +# Build configuration that doesn't affect native +.gitignore + +# Autolinking outputs (redundant with lock files) +# These are derived from node_modules and package.json +expoAutolinkingConfig:android +expoAutolinkingConfig:ios +rncoreAutolinkingConfig:android +rncoreAutolinkingConfig:ios + +# Android build artifacts (if not already in native .gitignore) +android/app/build/**/* +android/.gradle/**/* +android/build/**/* +android/.cxx/**/* + +# iOS build artifacts (if not already in native .gitignore) +ios/build/**/* +ios/Pods/**/* +!ios/Podfile +!ios/Podfile.lock +ios/*.xcworkspace/**/* +!ios/*.xcworkspace/contents.xcworkspacedata + +# ensure patches are tracked +!../../patches/**/* diff --git a/apps/mobile/.gitignore b/apps/mobile/.gitignore index f6487b4f10a..6d1ce5cd0e8 100644 --- a/apps/mobile/.gitignore +++ b/apps/mobile/.gitignore @@ -36,6 +36,7 @@ local.properties *.jks keystore.properties *.aab +.kotlin/ # node.js # @@ -120,8 +121,16 @@ ios/WidgetsCore/Env.swift ios/OneSignalNotificationServiceExtension/Env.swift # Expo -.expo/ +.expo +dist/ +web-build/ # Maestro E2E Scripts (compiled/generated) .maestro/scripts/dist/ .maestro/scripts/performance/dist/ + +# rnef (deprecated) +.rnef/ + +coverage/ + diff --git a/apps/mobile/.maestro/flows/deeplinks/deeplink-comprehensive.yaml b/apps/mobile/.maestro/flows/deeplinks/deeplink-comprehensive.yaml index 7986a843f72..71df2ffe3af 100644 --- a/apps/mobile/.maestro/flows/deeplinks/deeplink-comprehensive.yaml +++ b/apps/mobile/.maestro/flows/deeplinks/deeplink-comprehensive.yaml @@ -12,7 +12,7 @@ env: - runScript: file: ../../scripts/performance/dist/actions/start-flow.js env: - FLOW_NAME: 'deeplink-comprehensive' + FLOW_NAME: "deeplink-comprehensive" # Run prerequisite flows (tracked as sub-flows) - runFlow: ../../shared-flows/start.yaml @@ -22,15 +22,15 @@ env: - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'onramp-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "onramp-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://app/fiatonramp?userAddress=0xEEf806b3Cae8fcecAe1793EE1e0B2c738F61C6bB&source=push' + link: "uniswap://app/fiatonramp?userAddress=0xEEf806b3Cae8fcecAe1793EE1e0B2c738F61C6bB&source=push" autoVerify: true # Handle iOS deeplink permission dialog (optional - only appears on first run) - tapOn: - text: 'Open' + text: "Open" optional: true - waitForAnimationToEnd: timeout: 5000 @@ -41,43 +41,43 @@ env: - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'onramp-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "onramp-deeplink" + PHASE: "end" - killApp # Open widget deeplink - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'widget-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "widget-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://widget/#/tokens/ethereum/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984' + link: "uniswap://widget/#/tokens/ethereum/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" autoVerify: true - waitForAnimationToEnd: timeout: 3000 - assertVisible: id: ${output.testIds.TokenDetailsHeaderText} - text: 'Uniswap' + text: "Uniswap" - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'widget-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "widget-deeplink" + PHASE: "end" - killApp # Open swap deeplink - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'swap-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "swap-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://redirect?screen=swap&userAddress=0xEEf806b3Cae8fcecAe1793EE1e0B2c738F61C6bB&inputCurrencyId=1-0x6B175474E89094C44Da98b954EedeAC495271d0F&outputCurrencyId=1-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984¤cyField=input&amount=100' + link: "uniswap://redirect?screen=swap&userAddress=0xEEf806b3Cae8fcecAe1793EE1e0B2c738F61C6bB&inputCurrencyId=1-0x6B175474E89094C44Da98b954EedeAC495271d0F&outputCurrencyId=1-0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984¤cyField=input&amount=100" autoVerify: true - waitForAnimationToEnd: timeout: 3000 @@ -92,20 +92,20 @@ env: - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'swap-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "swap-deeplink" + PHASE: "end" - killApp # Open token details deeplink - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'token-details-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "token-details-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://app/tokendetails?currencyId=10-0x6fd9d7ad17242c41f7131d257212c54a0e816691&source=push' + link: "uniswap://app/tokendetails?currencyId=10-0x6fd9d7ad17242c41f7131d257212c54a0e816691&source=push" autoVerify: true - waitForAnimationToEnd: timeout: 3000 @@ -116,20 +116,20 @@ env: - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'token-details-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "token-details-deeplink" + PHASE: "end" - killApp # Open transaction history deeplink - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'transaction-history-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "transaction-history-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://redirect?screen=transaction&fiatOnRamp=true&userAddress=0xEEf806b3Cae8fcecAe1793EE1e0B2c738F61C6bB' + link: "uniswap://redirect?screen=transaction&fiatOnRamp=true&userAddress=0xEEf806b3Cae8fcecAe1793EE1e0B2c738F61C6bB" autoVerify: true - waitForAnimationToEnd: timeout: 3000 @@ -138,20 +138,20 @@ env: - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'transaction-history-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "transaction-history-deeplink" + PHASE: "end" - killApp # Invalid deeplink (should fail gracefully and remain functional) - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'invalid-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "invalid-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://invalid-path' + link: "uniswap://invalid-path" autoVerify: true - waitForAnimationToEnd: timeout: 3000 @@ -160,46 +160,46 @@ env: - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'invalid-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "invalid-deeplink" + PHASE: "end" - killApp # Open moonpayOnly onramp deeplink - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'moonpay-onramp-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "moonpay-onramp-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://app/fiatonramp?source=push&moonpayOnly=true&moonpayCurrencyCode=usdc&amount=200' + link: "uniswap://app/fiatonramp?source=push&moonpayOnly=true&moonpayCurrencyCode=usdc&amount=200" autoVerify: true - waitForAnimationToEnd: timeout: 5000 - assertVisible: id: ${output.testIds.ForFormTokenSelected} - text: 'USD Coin' + text: "USD Coin" - assertVisible: id: ${output.testIds.BuyFormAmountInput} - text: '200' + text: "200" - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'moonpay-onramp-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "moonpay-onramp-deeplink" + PHASE: "end" - killApp # Open scantastic deeplink (when user scans QR code on the extension) - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'scantastic-deeplink' - PHASE: 'start' + ACTION: "openLink" + TARGET: "scantastic-deeplink" + PHASE: "start" - openLink: - link: 'uniswap://scantastic?pubKey=%7B%22alg%22%3A%22RSA-OAEP-256%22%2C%22kty%22%3A%22RSA%22%2C%22n%22%3A%224X4nRAEZ8FWoVmoQ5KrxcssIR7XpdcVo_y7yD1SgmYuXekvHMIYuLxxkxVTjsyxj2s9jctIHOhZ-g96w4oM8-HXjCJG_v55w6FZyDskllcmaGeUlZFwWkiqZ-PKkHCWxCe_dZGvL33sazS_L8P3eAxXEPEJMG9p9lxsIlPp7ki0GSyVjq4rrHgW0lIz6qy6WqHbnyJWQAMSPnZTGM697ZCdkW_GTD3MyqitBwK5xNQN8Pxgbu6S7xbQglanYNBbeMYpJ3X1PDl37sp16YwPm6ryGaX1ESDPHa3M7-_we_yQEUQvtU5t2dd8chISJX8L1D7s8iNxM1LxG_nZTwKnccRPtrzKj-osBMbfCoU4fiNS2LC7q6zsyHxgDpeFlrV--iboQ9TsaQ7RGaFOSKs0l74_dt8GvX2JtNJ0ah8K__eNg9q0xBD8DTdeY2duMTEKJZIKgEyX0KUiRpsbsNmm_76iqhhZyYvcb6mwvNnVcXPg_TabX7lQEEippd7JTWVnF2LKzldlUonchQSsbLEUlN_ALa0Nuq6GG1MVJ0JjSsNMcpin6rH9fPzmDKkqzM2qvhdyuV66vkS82Wj9tQpqXL_jkRk7bQsDlB-HiVbzM2oNPk6or5u6p5tJni0th6BZm4z-sYgmMj3D5xHeusyap-8dmS9J4mXDxGLL_NloaHY8%22%2C%22e%22%3A%22AQAB%22%7D&uuid=28c01911-8e69-46e9-b2f0-f5e719bb714b&vendor=Apple&model=Macintosh&browser=Chrome' + link: "uniswap://scantastic?pubKey=%7B%22alg%22%3A%22RSA-OAEP-256%22%2C%22kty%22%3A%22RSA%22%2C%22n%22%3A%224X4nRAEZ8FWoVmoQ5KrxcssIR7XpdcVo_y7yD1SgmYuXekvHMIYuLxxkxVTjsyxj2s9jctIHOhZ-g96w4oM8-HXjCJG_v55w6FZyDskllcmaGeUlZFwWkiqZ-PKkHCWxCe_dZGvL33sazS_L8P3eAxXEPEJMG9p9lxsIlPp7ki0GSyVjq4rrHgW0lIz6qy6WqHbnyJWQAMSPnZTGM697ZCdkW_GTD3MyqitBwK5xNQN8Pxgbu6S7xbQglanYNBbeMYpJ3X1PDl37sp16YwPm6ryGaX1ESDPHa3M7-_we_yQEUQvtU5t2dd8chISJX8L1D7s8iNxM1LxG_nZTwKnccRPtrzKj-osBMbfCoU4fiNS2LC7q6zsyHxgDpeFlrV--iboQ9TsaQ7RGaFOSKs0l74_dt8GvX2JtNJ0ah8K__eNg9q0xBD8DTdeY2duMTEKJZIKgEyX0KUiRpsbsNmm_76iqhhZyYvcb6mwvNnVcXPg_TabX7lQEEippd7JTWVnF2LKzldlUonchQSsbLEUlN_ALa0Nuq6GG1MVJ0JjSsNMcpin6rH9fPzmDKkqzM2qvhdyuV66vkS82Wj9tQpqXL_jkRk7bQsDlB-HiVbzM2oNPk6or5u6p5tJni0th6BZm4z-sYgmMj3D5xHeusyap-8dmS9J4mXDxGLL_NloaHY8%22%2C%22e%22%3A%22AQAB%22%7D&uuid=28c01911-8e69-46e9-b2f0-f5e719bb714b&vendor=Apple&model=Macintosh&browser=Chrome" autoVerify: true - waitForAnimationToEnd: timeout: 3000 @@ -207,16 +207,16 @@ env: id: ${output.testIds.ScantasticConfirmationTitle} - assertVisible: id: ${output.testIds.ScantasticDevice} - text: 'Apple Macintosh' + text: "Apple Macintosh" - assertVisible: id: ${output.testIds.ScantasticBrowser} - text: 'Chrome' + text: "Chrome" - runScript: file: ../../scripts/performance/dist/actions/track-action.js env: - ACTION: 'openLink' - TARGET: 'scantastic-deeplink' - PHASE: 'end' + ACTION: "openLink" + TARGET: "scantastic-deeplink" + PHASE: "end" - killApp # End flow tracking @@ -228,4 +228,4 @@ env: file: ../../scripts/performance/upload-metrics.js env: DATADOG_API_KEY: ${DATADOG_API_KEY} - ENVIRONMENT: 'maestro_cloud' + ENVIRONMENT: "maestro_cloud" diff --git a/apps/mobile/.maestro/flows/explore/filters-and-sorts.yaml b/apps/mobile/.maestro/flows/explore/filters-and-sorts.yaml index 89740a599f7..76c7fa635fe 100644 --- a/apps/mobile/.maestro/flows/explore/filters-and-sorts.yaml +++ b/apps/mobile/.maestro/flows/explore/filters-and-sorts.yaml @@ -1,7 +1,5 @@ appId: com.uniswap.mobile.dev jsEngine: graaljs -tags: - - language-agnostic env: E2E_RECOVERY_PHRASE: ${E2E_RECOVERY_PHRASE} DATADOG_API_KEY: ${DATADOG_API_KEY} @@ -72,11 +70,11 @@ env: # Wait for sort options to appear - extendedWaitUntil: visible: - id: "MARKET_CAP" + text: ".*Market.*" timeout: 3000 - tapOn: - id: "MARKET_CAP" + text: ".*Market.*" - runScript: file: ../../scripts/performance/dist/actions/track-action.js @@ -106,11 +104,11 @@ env: - waitForAnimationToEnd - extendedWaitUntil: visible: - id: "TOTAL_VALUE_LOCKED" + text: ".*TVL.*" timeout: 3000 - tapOn: - id: "TOTAL_VALUE_LOCKED" + text: ".*TVL.*" - runScript: file: ../../scripts/performance/dist/actions/track-action.js @@ -142,11 +140,11 @@ env: # Wait for sort options to appear - extendedWaitUntil: visible: - id: "PRICE_PERCENT_CHANGE_1_DAY_DESC" + text: ".*Price.*increase.*" timeout: 3000 - tapOn: - id: "PRICE_PERCENT_CHANGE_1_DAY_DESC" + text: ".*Price.*increase.*" - runScript: file: ../../scripts/performance/dist/actions/track-action.js @@ -178,11 +176,11 @@ env: # Wait for sort options to appear - extendedWaitUntil: visible: - id: "PRICE_PERCENT_CHANGE_1_DAY_ASC" + text: ".*Price.*decrease.*" timeout: 3000 - tapOn: - id: "PRICE_PERCENT_CHANGE_1_DAY_ASC" + text: ".*Price.*decrease.*" - runScript: file: ../../scripts/performance/dist/actions/track-action.js @@ -214,11 +212,11 @@ env: # Wait for sort options to appear - extendedWaitUntil: visible: - id: "VOLUME" + text: ".*Volume.*" timeout: 3000 - tapOn: - id: "VOLUME" + text: ".*Volume.*" - runScript: file: ../../scripts/performance/dist/actions/track-action.js diff --git a/apps/mobile/.maestro/flows/restore/restore-new-device.yaml b/apps/mobile/.maestro/flows/restore/restore-new-device.yaml index 0f73d3201f3..08ab78e29fa 100644 --- a/apps/mobile/.maestro/flows/restore/restore-new-device.yaml +++ b/apps/mobile/.maestro/flows/restore/restore-new-device.yaml @@ -184,10 +184,29 @@ env: PHASE: 'end' - waitForAnimationToEnd +# Wait for cloud backup to fail - handle both possible error states +# First try waiting for "No backups found" - extendedWaitUntil: visible: text: 'No backups found' - timeout: 5000 # wait for cloud backup to fail + timeout: 5000 + optional: true + +# If that didn't appear, wait for "Error while importing backups" +- extendedWaitUntil: + visible: + text: 'Error while importing backups' + timeout: 5000 + optional: true + +# If error while importing backups appeared, tap to enter recovery phrase manually +- runFlow: + when: + visible: + text: 'Enter recovery phrase' + commands: + - tapOn: + text: 'Enter recovery phrase' # Track seed phrase input - runScript: diff --git a/apps/mobile/.maestro/shared-flows/navigate-to-explore.yaml b/apps/mobile/.maestro/shared-flows/navigate-to-explore.yaml index 3431fe0cb24..200f366aeaf 100644 --- a/apps/mobile/.maestro/shared-flows/navigate-to-explore.yaml +++ b/apps/mobile/.maestro/shared-flows/navigate-to-explore.yaml @@ -1,20 +1,37 @@ appId: com.uniswap.mobile.dev --- # This flow handles the common action of navigating to the Explore tab -# from the main wallet screen. +# from the main wallet screen. It supports both bottom tabs navigation (new) +# and the legacy floating navigation bar. # Wait for the home screen to be visible - extendedWaitUntil: - visible: 'noop' + visible: "noop" timeout: 2000 optional: true -# Tap on the Explore/Search button in the navigation bar +# OPTION 1: Try bottom tabs navigation first (new UI pattern) +# This will be available when BottomTabs feature flag is enabled +- tapOn: + id: ${output.testIds.ExploreTab} + optional: true + +# TODO: INFRA-1074 - Remove this fallback when we no longer support the legacy navigation bar +# OPTION 2: Fallback to legacy floating navigation bar +# This will be used when BottomTabs feature flag is disabled - tapOn: id: ${output.testIds.SearchTokensAndWallets} + optional: true +# Wait for tab animations to complete (200ms animation duration) - waitForAnimationToEnd # Verify we're in the Explore screen by checking for the search input +# This verification works for both navigation patterns +- extendedWaitUntil: + visible: + id: ${output.testIds.ExploreSearchInput} + timeout: 3000 + - assertVisible: id: ${output.testIds.ExploreSearchInput} diff --git a/apps/mobile/Gemfile b/apps/mobile/Gemfile index a07e6ca69f3..a914ea80d6f 100644 --- a/apps/mobile/Gemfile +++ b/apps/mobile/Gemfile @@ -1,11 +1,16 @@ source "https://rubygems.org" -gem 'fastlane', '2.214.0' -# Exclude problematic versions of cocoapods and activesupport that causes build failures. -gem 'cocoapods', '1.14.3' +gem 'fastlane', '2.228.0' +gem 'cocoapods', '1.16.2' gem 'activesupport', '7.1.2' gem 'xcodeproj', '1.27.0' gem 'concurrent-ruby', '1.3.4' +# Ruby 3.4.0 has removed some libraries from the standard library. +gem 'bigdecimal' +gem 'logger' +gem 'benchmark' +gem 'mutex_m' + plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/apps/mobile/Gemfile.lock b/apps/mobile/Gemfile.lock index b5891798a1a..6e79e1283b8 100644 --- a/apps/mobile/Gemfile.lock +++ b/apps/mobile/Gemfile.lock @@ -18,32 +18,36 @@ GEM algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) - artifactory (3.0.15) + artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.877.0) - aws-sdk-core (3.190.1) + aws-eventstream (1.4.0) + aws-partitions (1.1162.0) + aws-sdk-core (3.232.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.75.0) - aws-sdk-core (~> 3, >= 3.188.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.142.0) - aws-sdk-core (~> 3, >= 3.189.0) + logger + aws-sdk-kms (1.112.0) + aws-sdk-core (~> 3, >= 3.231.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.199.0) + aws-sdk-core (~> 3, >= 3.231.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) + benchmark (0.4.1) bigdecimal (3.1.9) claide (1.1.0) - cocoapods (1.14.3) + cocoapods (1.16.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.14.3) + cocoapods-core (= 1.16.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -57,8 +61,8 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.14.3) + xcodeproj (>= 1.27.0, < 2.0) + cocoapods-core (1.16.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -84,17 +88,17 @@ GEM concurrent-ruby (1.3.4) connection_pool (2.5.0) declarative (0.0.20) - digest-crc (0.6.5) + digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) - domain_name (0.6.20231109) + domain_name (0.6.20240107) dotenv (2.8.1) drb (2.2.1) emoji_regex (3.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.15.0) ffi (>= 1.15.0) - excon (0.109.0) - faraday (1.10.3) + excon (0.112.0) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -110,27 +114,27 @@ GEM faraday (>= 0.8.0) http-cookie (~> 1.0.0) faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) + faraday-em_synchrony (1.0.1) faraday-excon (1.1.0) faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-multipart (1.1.1) + multipart-post (~> 2.0) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) - fastimage (2.3.0) - fastlane (2.214.0) + fastimage (2.4.0) + fastlane (2.228.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) - colored + colored (~> 1.2) commander (~> 4.6) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) @@ -139,38 +143,43 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) google-cloud-storage (~> 1.31) highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) - optparse (~> 0.1.1) + optparse (>= 0.1.1, < 1.0.0) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) + security (= 0.1.5) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) fastlane-plugin-get_version_name (0.2.2) fastlane-plugin-versioning_android (0.1.1) - ffi (1.17.1) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + ffi (1.17.2-arm64-darwin) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.2) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -178,93 +187,93 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-playcustomapp_v1 (0.13.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.29.0) + google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.6.1) + google-cloud-core (1.8.0) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) - google-cloud-env (2.1.0) - faraday (>= 1.0, < 3.a) - google-cloud-errors (1.3.1) - google-cloud-storage (1.45.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.5.0) + google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.29.0) + google-apis-storage_v1 (~> 0.31.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.9.1) - faraday (>= 1.0, < 3.a) - google-cloud-env (~> 2.1) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.5) + http-cookie (1.0.8) domain_name (~> 0.5) httpclient (2.8.3) i18n (1.14.7) concurrent-ruby (~> 1.0) jmespath (1.6.2) json (2.7.1) - jwt (2.7.1) - mini_magick (4.12.0) + jwt (2.10.2) + base64 + logger (1.7.0) + mini_magick (4.13.2) mini_mime (1.1.5) minitest (5.25.4) molinillo (0.8.0) - multi_json (1.15.0) - multipart-post (2.3.0) + multi_json (1.17.0) + multipart-post (2.4.1) mutex_m (0.3.0) nanaimo (0.4.0) nap (1.1.0) - naturally (2.2.1) + naturally (2.3.0) netrc (0.11.0) - optparse (0.1.1) + optparse (0.6.0) os (1.1.4) - plist (3.7.1) + plist (3.7.2) public_suffix (4.0.7) - rake (13.1.0) + rake (13.3.0) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) rexml (3.4.1) - rouge (2.0.7) + rouge (3.28.0) ruby-macho (2.5.1) ruby2_keywords (0.0.5) - rubyzip (2.3.2) - security (0.1.3) - signet (0.18.0) + rubyzip (2.4.1) + security (0.1.5) + signet (0.21.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) + jwt (>= 1.5, < 4.0) multi_json (~> 1.10) simctl (1.6.10) CFPropertyList naturally + sysrandom (1.0.5) terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) - typhoeus (1.4.1) - ethon (>= 0.9.0) + typhoeus (1.5.0) + ethon (>= 0.9.0, < 0.16.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unicode-display_width (1.8.0) - webrick (1.8.1) + unicode-display_width (2.6.0) word_wrap (1.0.0) xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) @@ -273,21 +282,26 @@ GEM colored2 (~> 3.1) nanaimo (~> 0.4.0) rexml (>= 3.3.6, < 4.0) - xcpretty (0.3.0) - rouge (~> 2.0.7) + xcpretty (0.4.1) + rouge (~> 3.28.0) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) PLATFORMS - ruby + arm64-darwin-23 + arm64-darwin-24 DEPENDENCIES activesupport (= 7.1.2) - cocoapods (= 1.14.3) + benchmark + bigdecimal + cocoapods (= 1.16.2) concurrent-ruby (= 1.3.4) - fastlane (= 2.214.0) + fastlane (= 2.228.0) fastlane-plugin-get_version_name fastlane-plugin-versioning_android + logger + mutex_m xcodeproj (= 1.27.0) BUNDLED WITH diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle index cf470808dde..8f7e2bd848a 100644 --- a/apps/mobile/android/app/build.gradle +++ b/apps/mobile/android/app/build.gradle @@ -1,40 +1,35 @@ -import com.android.build.OutputFile - -plugins { - id 'com.android.application' - id 'com.facebook.react' - id 'com.google.gms.google-services' - id 'maven-publish' - id 'kotlin-android' - id 'org.jetbrains.kotlin.plugin.compose' -} +apply plugin: "com.android.application" +apply plugin: "org.jetbrains.kotlin.android" +apply plugin: "com.google.gms.google-services" +apply plugin: "maven-publish" +apply plugin: "kotlin-android" +apply plugin: "org.jetbrains.kotlin.plugin.compose" +apply plugin: "com.facebook.react" -def nodeModulesPath = "../../../../node_modules" -def rnRoot = "../.." +def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() -def keystorePropertiesFile = rootProject.file("keystore.properties"); -def keystoreProperties = new Properties() -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} +def nodeModulesPath = "../../../../node_modules" react { - root = file("$rnRoot/") + // From expo docs: https://docs.expo.dev/brownfield/get-started + entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", rootDir.getAbsoluteFile().getParentFile().getAbsolutePath(), "android", "absolute"].execute(null, rootDir).text.trim()) + reactNativeDir = file("$nodeModulesPath/react-native") - codegenDir = file("$nodeModulesPath/react-native-codegen") - cliFile = file("$nodeModulesPath/@rnef/cli/dist/src/bin.js") + + hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" + + codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() + + enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean() + + cliFile = new File(["node", "--print", "require.resolve('@expo/cli')"].execute(null, rootDir).text.trim()) + bundleCommand = "export:embed" + debuggableVariants = ["devDebug", "betaDebug", "prodDebug"] - hermesCommand = "../../node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc" // This is relative to the project root. + /* Autolinking */ autolinkLibrariesWithApp() } -/** - * Set this to true to create four separate APKs instead of one, - * one for each native architecture. This is useful if you don't - * use App Bundles (https://developer.android.com/guide/app-bundle/) - * and want to have separate APKs to upload to the Play Store. - */ -def enableSeparateBuildPerCPUArchitecture = false /** * Set this to true to Run Proguard on Release builds to minify the Java bytecode. @@ -45,14 +40,14 @@ def enableProguardInReleaseBuilds = false * The preferred build flavor of JavaScriptCore (JSC) * * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ -def jscFlavor = 'org.webkit:android-jsc-intl:+' +def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' /** * Private function to get the list of Native Architectures you want to build. @@ -72,9 +67,9 @@ if (isCI && datadogPropertiesAvailable) { apply from: "../../../../node_modules/@datadog/mobile-react-native/datadog-sourcemaps.gradle" } -def devVersionName = "1.61" -def betaVersionName = "1.61" -def prodVersionName = "1.61" +def devVersionName = "1.64.1" +def betaVersionName = "1.64.1" +def prodVersionName = "1.64.1" android { ndkVersion rootProject.ext.ndkVersion @@ -91,7 +86,7 @@ android { splits { abi { reset() - enable enableSeparateBuildPerCPUArchitecture + enable false universalApk false // If true, also generate a universal APK include (*reactNativeArchitectures()) } @@ -107,10 +102,18 @@ android { keyPassword 'android' } release { - storeFile file(System.getenv("ANDROID_KEYSTORE_FILE") ?: 'keystore.jks') - storePassword System.getenv("ANDROID_STORE_PASSWORD") ?: keystoreProperties.getProperty("STORE_PASSWORD") - keyAlias System.getenv("ANDROID_KEYSTORE_ALIAS") ?: keystoreProperties.getProperty("KEYSTORE_ALIAS") - keyPassword System.getenv("ANDROID_KEY_PASSWORD") ?: keystoreProperties.getProperty("KEY_PASSWORD") + def useDebugKeystore = System.getenv("ANDROID_USE_DEBUG_KEYSTORE") == "true" + if (useDebugKeystore) { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } else { + storeFile file(System.getenv("ANDROID_KEYSTORE_FILE") ?: 'keystore.jks') + storePassword System.getenv("ANDROID_STORE_PASSWORD") ?: "" + keyAlias System.getenv("ANDROID_KEYSTORE_ALIAS") ?: "" + keyPassword System.getenv("ANDROID_KEY_PASSWORD") ?: "" + } } } @@ -119,12 +122,12 @@ android { productFlavors { dev { isDefault(true) - applicationIdSuffix ".dev" + applicationId "com.uniswap.mobile.dev" versionName devVersionName dimension "variant" } beta { - applicationIdSuffix ".beta" + applicationId "com.uniswap.mobile.beta" versionName betaVersionName dimension "variant" } @@ -146,18 +149,33 @@ android { } // applicationVariants are e.g. debug, release - applicationVariants.all { variant -> + applicationVariants.configureEach { variant -> + // Prevent using debug keystore for production builds + if (variant.flavorName == "prod" && variant.buildType.name == "release") { + def useDebugKeystore = System.getenv("ANDROID_USE_DEBUG_KEYSTORE") == "true" + if (useDebugKeystore) { + def blockTask = tasks.register("blockDebugKeystoreFor${variant.name.capitalize()}") { + doLast { + throw new GradleException( + "ANDROID_USE_DEBUG_KEYSTORE cannot be used for production builds.\n" + + "This prevents accidentally publishing an improperly signed APK." + ) + } + } + variant.assembleProvider.configure { dependsOn blockTask } + } + } + variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] - def abi = output.getFilter(OutputFile.ABI) + def abi = output.getFilter(com.android.build.VariantOutput.FilterType.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = defaultConfig.versionCode * 1000 + versionCodes.get(abi) } - } } @@ -193,6 +211,7 @@ android { dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation "com.facebook.react:react-android" + implementation("com.facebook.react:hermes-android") implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" @@ -247,10 +266,4 @@ dependencies { implementation 'androidx.compose.ui:ui-tooling-preview-android:1.8.1' implementation project(':react-native-video') - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } } diff --git a/apps/mobile/android/app/src/main/AndroidManifest.xml b/apps/mobile/android/app/src/main/AndroidManifest.xml index 66bdd248726..aed03ffb4b9 100644 --- a/apps/mobile/android/app/src/main/AndroidManifest.xml +++ b/apps/mobile/android/app/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ - + + + - + - - + + - - + + @@ -77,39 +81,39 @@ + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/nfts/asset/" /> + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/nfts/collection/" /> + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/tokens" /> + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/address/" /> + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/explore/" /> + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/swap" /> + android:scheme="https" + android:host="app.uniswap.org" + android:pathPrefix="/buy" /> @@ -118,14 +122,14 @@ + android:scheme="https" + android:host="uniswap.org" + android:pathPrefix="/app" /> + android:scheme="https" + android:host="uniswap.org" + android:pathPrefix="/app/wc" /> diff --git a/apps/mobile/android/app/src/main/java/com/uniswap/MainActivity.kt b/apps/mobile/android/app/src/main/java/com/uniswap/MainActivity.kt index 370ffb03d1f..ee40024b0a3 100644 --- a/apps/mobile/android/app/src/main/java/com/uniswap/MainActivity.kt +++ b/apps/mobile/android/app/src/main/java/com/uniswap/MainActivity.kt @@ -44,10 +44,6 @@ class MainActivity : ReactActivity() { * (aka React 18) with two boolean flags. */ override fun createReactActivityDelegate(): ReactActivityDelegate? { - return ReactActivityDelegateWrapper( - this, - BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, - DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) - ) + return ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)) } } diff --git a/apps/mobile/android/app/src/main/java/com/uniswap/MainApplication.kt b/apps/mobile/android/app/src/main/java/com/uniswap/MainApplication.kt index b914ee2c34a..21c5c32714a 100644 --- a/apps/mobile/android/app/src/main/java/com/uniswap/MainApplication.kt +++ b/apps/mobile/android/app/src/main/java/com/uniswap/MainApplication.kt @@ -19,7 +19,7 @@ import expo.modules.ReactNativeHostWrapper class MainApplication : Application(), ReactApplication { override val reactNativeHost: ReactNativeHost = - ReactNativeHostWrapper(this, object : DefaultReactNativeHost(this) { + ReactNativeHostWrapper(this, object : DefaultReactNativeHost(this) { override fun getPackages(): List = PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: @@ -29,9 +29,7 @@ class MainApplication : Application(), ReactApplication { add(ScantasticEncryptionModule()) add(RedirectToSourceAppPackage()) } - override fun getJSMainModuleName(): String { - return "index" - } + override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" override fun getUseDeveloperSupport(): Boolean { return BuildConfig.DEBUG @@ -39,22 +37,23 @@ class MainApplication : Application(), ReactApplication { override val isNewArchEnabled: Boolean get() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - - override val isHermesEnabled: Boolean - get() = BuildConfig.IS_HERMES_ENABLED }) override val reactHost: ReactHost - get() = getDefaultReactHost(applicationContext, reactNativeHost) + get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) override fun onCreate() { ReactNativePerformance.onAppStarted() super.onCreate() + + // Initialize SoLoader before any code that might load native libraries SoLoader.init(this, OpenSourceMergedSoMapping) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. load() } + + // Initialize Expo modules after SoLoader ApplicationLifecycleDispatcher.onApplicationCreate(this) } diff --git a/apps/mobile/android/app/src/main/java/com/uniswap/UniswapPackage.kt b/apps/mobile/android/app/src/main/java/com/uniswap/UniswapPackage.kt index 5463faf66a8..16776573e76 100644 --- a/apps/mobile/android/app/src/main/java/com/uniswap/UniswapPackage.kt +++ b/apps/mobile/android/app/src/main/java/com/uniswap/UniswapPackage.kt @@ -6,6 +6,7 @@ import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ReactShadowNode import com.facebook.react.uimanager.ViewManager +import com.uniswap.notifications.SilentPushEventEmitterModule import com.uniswap.onboarding.backup.MnemonicConfirmationViewManager import com.uniswap.onboarding.backup.MnemonicDisplayViewManager import com.uniswap.onboarding.import.SeedPhraseInputViewManager @@ -28,5 +29,6 @@ class UniswapPackage : ReactPackage { RNEthersRSModule(reactContext), EmbeddedWalletModule(reactContext), ThemeModule(reactContext), + SilentPushEventEmitterModule(reactContext), ) } diff --git a/apps/mobile/android/app/src/main/java/com/uniswap/notifications/SilentPushEventEmitterModule.kt b/apps/mobile/android/app/src/main/java/com/uniswap/notifications/SilentPushEventEmitterModule.kt new file mode 100644 index 00000000000..9c2504ec875 --- /dev/null +++ b/apps/mobile/android/app/src/main/java/com/uniswap/notifications/SilentPushEventEmitterModule.kt @@ -0,0 +1,129 @@ +package com.uniswap.notifications + +import android.util.Log +import androidx.annotation.Keep +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.modules.core.DeviceEventManagerModule +import com.uniswap.utils.toWritableMap +import org.json.JSONObject + +@Keep +@ReactModule(name = SilentPushEventEmitterModule.MODULE_NAME) +class SilentPushEventEmitterModule( + reactContext: ReactApplicationContext +) : ReactContextBaseJavaModule(reactContext) { + + override fun getName() = MODULE_NAME + + override fun initialize() { + super.initialize() + instance = this + listenerCount = 0 + Log.d(TAG, "SilentPushEventEmitter initialized") + flushPendingEvents() + } + + override fun onCatalystInstanceDestroy() { + super.onCatalystInstanceDestroy() + if (instance === this) { + instance = null + } + listenerCount = 0 + } + + @ReactMethod + fun addListener(eventName: String) { + if (eventName != EVENT_NAME) { + return + } + listenerCount += 1 + flushPendingEvents() + } + + @ReactMethod + fun removeListeners(count: Int) { + if (count <= 0) { + return + } + listenerCount = (listenerCount - count).coerceAtLeast(0) + } + + private fun flushPendingEvents() { + if (!hasListeners()) { + return + } + + val events = synchronized(pendingPayloads) { + if (pendingPayloads.isEmpty()) { + null + } else { + val copy = ArrayList(pendingPayloads) + pendingPayloads.clear() + copy + } + } ?: return + + Log.d(TAG, "Flushing ${events.size} queued silent push events") + events.forEach { sendEvent(it) } + } + + private fun sendEvent(payload: JSONObject) { + if (!reactApplicationContext.hasActiveCatalystInstance()) { + synchronized(pendingPayloads) { + Log.d(TAG, "No active Catalyst instance; queueing payload: ${payload.toString()}") + pendingPayloads.add(JSONObject(payload.toString())) + } + return + } + + val map = payload.toWritableMap() + reactApplicationContext.runOnUiQueueThread { + if (!reactApplicationContext.hasActiveCatalystInstance()) { + synchronized(pendingPayloads) { + Log.d(TAG, "Catalyst inactive on UI thread; re-queueing payload: ${payload.toString()}") + pendingPayloads.add(JSONObject(payload.toString())) + } + return@runOnUiQueueThread + } + + Log.d(TAG, "Emitting silent push payload to JS: ${payload.toString()}") + reactApplicationContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + .emit(EVENT_NAME, map) + } + } + + private fun hasListeners(): Boolean = instance != null && listenerCount > 0 + + companion object { + const val MODULE_NAME = "SilentPushEventEmitter" + private const val EVENT_NAME = "SilentPushReceived" + private const val TAG = "SilentPushEmitter" + private val pendingPayloads = mutableListOf() + + @Volatile + private var instance: SilentPushEventEmitterModule? = null + + @Volatile + private var listenerCount: Int = 0 + + fun emitEvent(payload: JSONObject?) { + val eventPayload = payload?.let { JSONObject(it.toString()) } ?: JSONObject() + val currentInstance = instance + + if (currentInstance != null && currentInstance.hasListeners()) { + Log.d(TAG, "Sending silent push event to JS immediately: $eventPayload") + currentInstance.sendEvent(eventPayload) + return + } + + synchronized(pendingPayloads) { + Log.d(TAG, "Queueing silent push payload until listeners attach: $eventPayload") + pendingPayloads.add(eventPayload) + } + } + } +} diff --git a/apps/mobile/android/app/src/main/java/com/uniswap/notifications/SilentPushNotificationServiceExtension.kt b/apps/mobile/android/app/src/main/java/com/uniswap/notifications/SilentPushNotificationServiceExtension.kt new file mode 100644 index 00000000000..90321dc944d --- /dev/null +++ b/apps/mobile/android/app/src/main/java/com/uniswap/notifications/SilentPushNotificationServiceExtension.kt @@ -0,0 +1,123 @@ +package com.uniswap.notifications + +import android.util.Log +import androidx.annotation.Keep +import com.onesignal.notifications.INotification +import com.onesignal.notifications.INotificationReceivedEvent +import com.onesignal.notifications.INotificationServiceExtension +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.json.JSONException +import org.json.JSONObject + +@Keep +class SilentPushNotificationServiceExtension : INotificationServiceExtension { + override fun onNotificationReceived(event: INotificationReceivedEvent) { + val notification = event.notification + val payload = buildPayload(notification) + + val hasContentAvailableFlag = hasContentAvailable(payload) + val isMissingVisibleContent = notification.isMissingVisibleContent() + + Log.d( + TAG, + "Notification received. hasContentAvailable=$hasContentAvailableFlag, " + + "missingVisibleContent=$isMissingVisibleContent, payload=$payload", + ) + + if (!hasContentAvailableFlag && !isMissingVisibleContent) { + return + } + + Log.d(TAG, "Emitting silent push event: $payload") + val payloadForEmission = try { + JSONObject(payload.toString()) + } catch (error: JSONException) { + Log.w(TAG, "Failed to clone payload for emission: ${error.message}") + payload + } + + CoroutineScope(Dispatchers.Default).launch { + withContext(Dispatchers.Main) { + SilentPushEventEmitterModule.emitEvent(payloadForEmission) + } + } + + if (isMissingVisibleContent) { + event.preventDefault() + } + } + + private fun INotification.isMissingVisibleContent(): Boolean { + val title: String? = this.title + val body: String? = this.body + return title.isNullOrBlank() && body.isNullOrBlank() + } + + private fun buildPayload(notification: INotification): JSONObject { + val rawPayload = notification.rawPayload + val payload = try { + if (rawPayload.isNullOrBlank()) JSONObject() else JSONObject(rawPayload) + } catch (error: JSONException) { + Log.w(TAG, "Failed parsing raw payload: ${error.message}") + JSONObject() + } + + notification.additionalData?.let { additionalData -> + try { + payload.put("additionalData", additionalData) + } catch (error: JSONException) { + Log.w(TAG, "Failed to append additional data: ${error.message}") + } + } + + return payload + } + + private fun hasContentAvailable(payload: JSONObject?): Boolean { + if (payload == null) { + return false + } + + if (payload.hasContentAvailableFlag()) { + return true + } + + val aps = payload.optJSONObject("aps") + if (aps != null && aps.hasContentAvailableFlag()) { + return true + } + + val additionalData = payload.optJSONObject("additionalData") + if (additionalData != null && additionalData.hasContentAvailableFlag()) { + return true + } + + return false + } + + private fun JSONObject.hasContentAvailableFlag(): Boolean { + return opt(CONTENT_AVAILABLE_UNDERSCORE).isTruthy() || opt(CONTENT_AVAILABLE_HYPHEN).isTruthy() + } + + private fun Any?.isTruthy(): Boolean { + return when (this) { + null, JSONObject.NULL -> false + is Boolean -> this + is Int -> this == 1 + is Long -> this == 1L + is Double -> this == 1.0 + is Float -> this == 1f + is String -> equals("1", ignoreCase = true) || equals("true", ignoreCase = true) + else -> false + } + } + + companion object { + private const val TAG = "SilentPushExt" + private const val CONTENT_AVAILABLE_UNDERSCORE = "content_available" + private const val CONTENT_AVAILABLE_HYPHEN = "content-available" + } +} diff --git a/apps/mobile/android/app/src/main/java/com/uniswap/utils/JsonWritableExtensions.kt b/apps/mobile/android/app/src/main/java/com/uniswap/utils/JsonWritableExtensions.kt new file mode 100644 index 00000000000..81415055e6d --- /dev/null +++ b/apps/mobile/android/app/src/main/java/com/uniswap/utils/JsonWritableExtensions.kt @@ -0,0 +1,61 @@ +package com.uniswap.utils + +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableArray +import com.facebook.react.bridge.WritableMap +import org.json.JSONArray +import org.json.JSONObject + +fun JSONObject.toWritableMap(): WritableMap { + val map = Arguments.createMap() + val iterator = keys() + while (iterator.hasNext()) { + val key = iterator.next() + when (val value = opt(key)) { + null, JSONObject.NULL -> map.putNull(key) + is JSONObject -> map.putMap(key, value.toWritableMap()) + is JSONArray -> map.putArray(key, value.toWritableArray()) + is Boolean -> map.putBoolean(key, value) + is Int -> map.putInt(key, value) + is Long -> { + if (value in Int.MIN_VALUE..Int.MAX_VALUE) { + map.putInt(key, value.toInt()) + } else { + map.putDouble(key, value.toDouble()) + } + } + is Double -> map.putDouble(key, value) + is Float -> map.putDouble(key, value.toDouble()) + is Number -> map.putDouble(key, value.toDouble()) + is String -> map.putString(key, value) + else -> map.putString(key, value.toString()) + } + } + return map +} + +fun JSONArray.toWritableArray(): WritableArray { + val array = Arguments.createArray() + for (index in 0 until length()) { + when (val value = opt(index)) { + null, JSONObject.NULL -> array.pushNull() + is JSONObject -> array.pushMap(value.toWritableMap()) + is JSONArray -> array.pushArray(value.toWritableArray()) + is Boolean -> array.pushBoolean(value) + is Int -> array.pushInt(value) + is Long -> { + if (value in Int.MIN_VALUE..Int.MAX_VALUE) { + array.pushInt(value.toInt()) + } else { + array.pushDouble(value.toDouble()) + } + } + is Double -> array.pushDouble(value) + is Float -> array.pushDouble(value.toDouble()) + is Number -> array.pushDouble(value.toDouble()) + is String -> array.pushString(value) + else -> array.pushString(value.toString()) + } + } + return array +} diff --git a/apps/mobile/android/build.gradle b/apps/mobile/android/build.gradle index 9ca0ef7eecb..5aa41d8be2a 100644 --- a/apps/mobile/android/build.gradle +++ b/apps/mobile/android/build.gradle @@ -2,11 +2,6 @@ buildscript { ext { - buildToolsVersion = "35.0.0" - minSdkVersion = 28 - compileSdkVersion = 35 - targetSdkVersion = 35 - ndkVersion = "27.1.12297006" kotlinVersion = "2.0.21" appCompat = "1.6.1" @@ -36,6 +31,14 @@ plugins { id 'org.jetbrains.kotlin.plugin.compose' version "$kotlinVersion" apply false } +def reactNativeAndroidDir = new File( + providers.exec { + workingDir(rootDir) + commandLine("node", "--print", "require.resolve('react-native/package.json')") + }.standardOutput.asText.get().trim(), + "../android" +) + allprojects { project.pluginManager.withPlugin("com.facebook.react") { react { @@ -45,11 +48,15 @@ allprojects { } repositories { - maven { - // expo-camera bundles a custom com.google.android:cameraview - url "$rootDir/../../../node_modules/expo-camera/android/maven" - } + google() + mavenCentral() + maven { url 'https://www.jitpack.io' } + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + maven { url(reactNativeAndroidDir) } + // expo-camera bundles a custom com.google.android:cameraview + maven { url "$rootDir/../../../node_modules/expo-camera/android/maven" } } } +apply plugin: "expo-root-project" apply plugin: "com.facebook.react.rootproject" diff --git a/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties b/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties index d5edd72a874..4dd5cf37e34 100644 --- a/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ #Wed April 10 16:30:26 EDT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/apps/mobile/android/gradlew b/apps/mobile/android/gradlew index 98d9216fb08..faf93008b77 100755 --- a/apps/mobile/android/gradlew +++ b/apps/mobile/android/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,11 +15,53 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME @@ -29,158 +71,181 @@ app_path=$0 # Need this for daisy-chained symlinks. while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] do - ls=$(ls -ld "$app_path") - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum -warn() { - echo "$*" -} +warn () { + echo "$*" +} >&2 -die() { - echo - echo "$*" - echo - exit 1 -} +die () { + echo + echo "$*" + echo + exit 1 +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "$(uname)" in -CYGWIN*) - cygwin=true - ;; -Darwin*) - darwin=true - ;; -MINGW*) - msys=true - ;; -NONSTOP*) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ]; then - if [ -x "$JAVA_HOME/jre/sh/java" ]; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ]; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi + fi else - JAVACMD="java" - if ! command -v java >/dev/null 2>&1; then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then - MAX_FD_LIMIT=$(ulimit -H -n) - if [ $? -eq 0 ]; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ]; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ]; then - APP_HOME=$(cygpath --path --mixed "$APP_HOME") - CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") - - JAVACMD=$(cygpath --unix "$JAVACMD") - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) - SEP="" - for dir in $ROOTDIRSRAW; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ]; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@"; do - CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) - CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition - eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") - else - eval $(echo args$i)="\"$arg\"" - fi - i=$(expr $i + 1) - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi -# Escape application args -save() { - for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done - echo " " -} -APP_ARGS=$(save "$@") +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/apps/mobile/android/settings.gradle b/apps/mobile/android/settings.gradle index 663fcfbe17b..8d322bc3be2 100644 --- a/apps/mobile/android/settings.gradle +++ b/apps/mobile/android/settings.gradle @@ -1,10 +1,35 @@ -pluginManagement { includeBuild("../../../node_modules/@react-native/gradle-plugin") } -plugins { id("com.facebook.react.settings") } -extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand(['bunx', 'rnef', 'config', '-p', 'android']) } +pluginManagement { + def reactNativeGradlePlugin = new File( + providers.exec { + workingDir(rootDir) + commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })") + }.standardOutput.asText.get().trim() + ).getParentFile().absolutePath + includeBuild(reactNativeGradlePlugin) + + def expoPluginsPath = new File( + providers.exec { + workingDir(rootDir) + commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })") + }.standardOutput.asText.get().trim(), + "../android/expo-gradle-plugin" + ).absolutePath + includeBuild(expoPluginsPath) +} + +plugins { + id("com.facebook.react.settings") + id("expo-autolinking-settings") +} + +extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> + ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand) +} + +expoAutolinking.useExpoModules() rootProject.name = 'Uniswap' -include ':app' -includeBuild('../../../node_modules/@react-native/gradle-plugin') -apply from: new File(["node", "--print", "require.resolve('../../../node_modules/expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle") -useExpoModules() +expoAutolinking.useExpoVersionCatalog() +includeBuild(expoAutolinking.reactNativeGradlePlugin) +include ':app' diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts new file mode 100644 index 00000000000..03aa4e6f462 --- /dev/null +++ b/apps/mobile/app.config.ts @@ -0,0 +1,18 @@ +import { ExpoConfig } from 'expo/config' + +const config: ExpoConfig = { + name: 'Uniswap', + slug: 'uniswapmobile', + scheme: 'uniswap', + owner: 'uniswap', + extra: { + eas: { + projectId: 'f1be3813-43d7-49ac-a792-7f42cf8500f5', + }, + }, + experiments: { + buildCacheProvider: 'eas', + }, +} + +export default config diff --git a/apps/mobile/app.json b/apps/mobile/app.json deleted file mode 100644 index b9b05c82db4..00000000000 --- a/apps/mobile/app.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "Uniswap", - "displayName": "Uniswap", - "appStoreUrl": "https://apps.apple.com/app/apple-store/id6443944476", - "playStoreUrl": "https://play.google.com/store/apps/details?id=com.uniswap.mobile", - "expo": { - "ios": { - "infoPlist": { - "CFBundleAllowMixedLocalizations": true - } - }, - "plugins": ["expo-localization", "expo-camera", "expo-local-authentication", "expo-secure-store"] - } -} diff --git a/apps/mobile/eas.json b/apps/mobile/eas.json new file mode 100644 index 00000000000..081b1650bfb --- /dev/null +++ b/apps/mobile/eas.json @@ -0,0 +1,72 @@ +{ + "cli": { + "version": ">= 15.0.15", + "appVersionSource": "remote" + }, + "build": { + "development": { + "bun": "1.3.1", + "node": "22.13.1", + "developmentClient": true, + "distribution": "internal", + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleDevDebug" + } + }, + "development-simulator": { + "bun": "1.3.1", + "node": "22.13.1", + "developmentClient": true, + "distribution": "internal", + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleDevDebug", + "withoutCredentials": true + }, + "ios": { + "simulator": true, + "withoutCredentials": true + } + }, + "development-release": { + "bun": "1.3.1", + "node": "22.13.1", + "distribution": "internal", + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleDevRelease" + } + }, + "beta": { + "bun": "1.3.1", + "node": "22.13.1", + "distribution": "internal", + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleBetaDebug" + } + }, + "beta-release": { + "bun": "1.3.1", + "node": "22.13.1", + "distribution": "internal", + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleBetaRelease" + } + }, + "production": { + "bun": "1.3.1", + "node": "22.13.1", + "autoIncrement": true, + "android": { + "buildType": "apk", + "gradleCommand": ":app:assembleProdRelease" + } + } + }, + "submit": { + "production": {} + } +} diff --git a/apps/mobile/fingerprint.config.js b/apps/mobile/fingerprint.config.js new file mode 100644 index 00000000000..0f3617ae437 --- /dev/null +++ b/apps/mobile/fingerprint.config.js @@ -0,0 +1,8 @@ +/** @type {import('@expo/fingerprint').Config} */ +const config = { + sourceSkips: [ + 'PackageJsonScriptsAll', // Skip all package.json scripts + 'ExpoConfigVersions', // Skip version bumps if you want + ], +} +module.exports = config diff --git a/apps/mobile/index.js b/apps/mobile/index.js index 46c83394bd3..d587e067bfc 100644 --- a/apps/mobile/index.js +++ b/apps/mobile/index.js @@ -14,6 +14,6 @@ import 'src/logbox' import 'src/polyfills' // biome-ignore assist/source/organizeImports: we want to keep the import order import App from 'src/app/App' -import { name as appName } from './app.json' +import AppConfig from './app.config' -AppRegistry.registerComponent(appName, () => App) +AppRegistry.registerComponent(AppConfig.name, () => App) diff --git a/apps/mobile/ios/Podfile b/apps/mobile/ios/Podfile index 7add18aba38..05b0c427464 100644 --- a/apps/mobile/ios/Podfile +++ b/apps/mobile/ios/Podfile @@ -26,11 +26,27 @@ target 'Uniswap' do Pod::UI.warn e end end - use_expo_modules!(exclude: ['expo-constants','expo-file-system', 'expo-font', 'expo-keep-awake', 'expo-error-recovery']) - config = use_native_modules!(['bunx', 'rnef', 'config', '-p', 'ios']) + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) use_react_native!( :path => config[:reactNativePath], + :fabric_enabled => false, # to enable hermes on iOS, change `false` to `true` and then install pods :hermes_enabled => true ) diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock index 41bed31112c..10cf77ed1a6 100644 --- a/apps/mobile/ios/Podfile.lock +++ b/apps/mobile/ios/Podfile.lock @@ -1161,60 +1161,326 @@ PODS: - BoringSSL-GRPC/Implementation (0.0.36): - BoringSSL-GRPC/Interface (= 0.0.36) - BoringSSL-GRPC/Interface (0.0.36) - - DatadogCore (2.27.0): - - DatadogInternal (= 2.27.0) - - DatadogCrashReporting (2.27.0): - - DatadogInternal (= 2.27.0) + - DatadogCore (2.30.0): + - DatadogInternal (= 2.30.0) + - DatadogCrashReporting (2.30.0): + - DatadogInternal (= 2.30.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (2.27.0) - - DatadogLogs (2.27.0): - - DatadogInternal (= 2.27.0) - - DatadogRUM (2.27.0): - - DatadogInternal (= 2.27.0) - - DatadogSDKReactNative (2.8.2): - - DatadogCore (~> 2.27.0) - - DatadogCrashReporting (~> 2.27.0) - - DatadogLogs (~> 2.27.0) - - DatadogRUM (~> 2.27.0) - - DatadogTrace (~> 2.27.0) - - DatadogWebViewTracking (~> 2.27.0) - - React-Core - - DatadogTrace (2.27.0): - - DatadogInternal (= 2.27.0) + - DatadogInternal (2.30.0) + - DatadogLogs (2.30.0): + - DatadogInternal (= 2.30.0) + - DatadogRUM (2.30.0): + - DatadogInternal (= 2.30.0) + - DatadogSDKReactNative (2.12.2): + - DatadogCore (= 2.30.0) + - DatadogCrashReporting (= 2.30.0) + - DatadogLogs (= 2.30.0) + - DatadogRUM (= 2.30.0) + - DatadogTrace (= 2.30.0) + - DatadogWebViewTracking (= 2.30.0) + - React-Core + - DatadogTrace (2.30.0): + - DatadogInternal (= 2.30.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (2.27.0): - - DatadogInternal (= 2.27.0) + - DatadogWebViewTracking (2.30.0): + - DatadogInternal (= 2.30.0) - DoubleConversion (1.1.6) - EthersRS (0.0.5) - - Expo (52.0.46): + - EXConstants (17.1.7): + - ExpoModulesCore + - EXJSONUtils (0.15.0) + - EXManifests (0.16.6): + - ExpoModulesCore + - Expo (53.0.22): + - DoubleConversion + - ExpoModulesCore + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactAppDependencyProvider + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-client (5.2.4): + - EXManifests + - expo-dev-launcher + - expo-dev-menu + - expo-dev-menu-interface + - EXUpdatesInterface + - expo-dev-launcher (5.1.16): + - DoubleConversion + - EXManifests + - expo-dev-launcher/Main (= 5.1.16) + - expo-dev-menu + - expo-dev-menu-interface + - ExpoModulesCore + - EXUpdatesInterface + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactAppDependencyProvider + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-launcher/Main (5.1.16): + - DoubleConversion + - EXManifests + - expo-dev-launcher/Unsafe + - expo-dev-menu + - expo-dev-menu-interface - ExpoModulesCore - - ExpoAsset (11.0.5): + - EXUpdatesInterface + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactAppDependencyProvider + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-launcher/Unsafe (5.1.16): + - DoubleConversion + - EXManifests + - expo-dev-menu + - expo-dev-menu-interface - ExpoModulesCore - - ExpoBlur (14.0.3): + - EXUpdatesInterface + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactAppDependencyProvider + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-menu (6.1.14): + - DoubleConversion + - expo-dev-menu/Main (= 6.1.14) + - expo-dev-menu/ReactNativeCompatibles (= 6.1.14) + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-menu-interface (1.10.0) + - expo-dev-menu/Main (6.1.14): + - DoubleConversion + - EXManifests + - expo-dev-menu-interface + - expo-dev-menu/Vendored - ExpoModulesCore - - ExpoCamera (16.0.18): + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-jsinspector + - React-NativeModulesApple + - React-RCTFabric + - React-rendererconsistency + - React-renderercss + - React-rendererdebug + - React-utils + - ReactAppDependencyProvider + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-menu/ReactNativeCompatibles (6.1.14): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-menu/SafeAreaView (6.1.14): + - DoubleConversion + - ExpoModulesCore + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - expo-dev-menu/Vendored (6.1.14): + - DoubleConversion + - expo-dev-menu/SafeAreaView + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - ExpoAsset (11.1.7): + - ExpoModulesCore + - ExpoBlur (14.1.5): + - ExpoModulesCore + - ExpoCamera (16.1.11): - ExpoModulesCore - ZXingObjC/OneD - ZXingObjC/PDF417 - - ExpoClipboard (7.0.1): + - ExpoClipboard (7.1.5): - ExpoModulesCore - - ExpoFileSystem (18.0.12): + - ExpoFileSystem (18.1.11): - ExpoModulesCore - - ExpoFont (13.0.4): + - ExpoFont (13.3.2): - ExpoModulesCore - ExpoHaptics (14.0.1): - ExpoModulesCore - - ExpoKeepAwake (14.0.3): + - ExpoKeepAwake (14.1.4): - ExpoModulesCore - - ExpoLinearGradient (14.0.2): + - ExpoLinearGradient (14.1.5): - ExpoModulesCore - - ExpoLinking (7.0.5): + - ExpoLinking (7.1.7): - ExpoModulesCore - - ExpoLocalAuthentication (15.0.2): + - ExpoLocalAuthentication (16.0.5): - ExpoModulesCore - - ExpoLocalization (16.0.1): + - ExpoLocalization (16.1.6): - ExpoModulesCore - - ExpoModulesCore (2.2.3): + - ExpoModulesCore (2.5.0): - DoubleConversion - glog - hermes-engine @@ -1226,28 +1492,31 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-jsinspector - React-NativeModulesApple - - React-RCTAppDelegate - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - - ReactAppDependencyProvider - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - ExpoScreenCapture (7.0.1): + - ExpoScreenCapture (7.2.0): - ExpoModulesCore - ExpoSecureStore (14.0.1): - ExpoModulesCore - - ExpoStoreReview (8.0.1): + - ExpoStoreReview (8.1.5): + - ExpoModulesCore + - ExpoWebBrowser (14.2.0): - ExpoModulesCore - - ExpoWebBrowser (14.0.2): + - EXUpdatesInterface (1.1.0): - ExpoModulesCore - fast_float (6.1.4) - - FBLazyVector (0.77.2) + - FBLazyVector (0.79.5) - Firebase/Auth (11.2.0): - Firebase/CoreOnly - FirebaseAuth (~> 11.2.0) @@ -1256,7 +1525,7 @@ PODS: - Firebase/Firestore (11.2.0): - Firebase/CoreOnly - FirebaseFirestore (~> 11.2.0) - - FirebaseAppCheckInterop (11.14.0) + - FirebaseAppCheckInterop (11.15.0) - FirebaseAuth (11.2.0): - FirebaseAppCheckInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0) @@ -1266,14 +1535,14 @@ PODS: - GoogleUtilities/Environment (~> 8.0) - GTMSessionFetcher/Core (~> 3.4) - RecaptchaInterop (~> 100.0) - - FirebaseAuthInterop (11.14.0) + - FirebaseAuthInterop (11.15.0) - FirebaseCore (11.2.0): - FirebaseCoreInternal (~> 11.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Logger (~> 8.0) - FirebaseCoreExtension (11.4.1): - FirebaseCore (~> 11.0) - - FirebaseCoreInternal (11.14.0): + - FirebaseCoreInternal (11.15.0): - "GoogleUtilities/NSData+zlib (~> 8.1)" - FirebaseFirestore (11.2.0): - FirebaseCore (~> 11.0) @@ -1295,7 +1564,7 @@ PODS: - gRPC-Core (~> 1.65.0) - leveldb-library (~> 1.22) - nanopb (~> 3.30910.0) - - FirebaseSharedSwift (11.14.0) + - FirebaseSharedSwift (11.15.0) - fmt (11.0.2) - glog (0.3.5) - GoogleUtilities/AppDelegateSwizzler (8.1.0): @@ -1410,9 +1679,9 @@ PODS: - gRPC-Core/Interface (1.65.5) - gRPC-Core/Privacy (1.65.5) - GTMSessionFetcher/Core (3.5.0) - - hermes-engine (0.77.2): - - hermes-engine/Pre-built (= 0.77.2) - - hermes-engine/Pre-built (0.77.2) + - hermes-engine (0.79.5): + - hermes-engine/Pre-built (= 0.79.5) + - hermes-engine/Pre-built (0.79.5) - leveldb-library (1.22.6) - libwebp (1.5.0): - libwebp/demux (= 1.5.0) @@ -1426,9 +1695,9 @@ PODS: - libwebp/sharpyuv (1.5.0) - libwebp/webp (1.5.0): - libwebp/sharpyuv - - MMKV (2.2.2): - - MMKVCore (~> 2.2.2) - - MMKVCore (2.2.2) + - MMKV (2.2.4): + - MMKVCore (~> 2.2.4) + - MMKVCore (2.2.4) - nanopb (3.30910.0): - nanopb/decode (= 3.30910.0) - nanopb/encode (= 3.30910.0) @@ -1501,44 +1770,45 @@ PODS: - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - - RCTDeprecation (0.77.2) - - RCTRequired (0.77.2) - - RCTTypeSafety (0.77.2): - - FBLazyVector (= 0.77.2) - - RCTRequired (= 0.77.2) - - React-Core (= 0.77.2) - - React (0.77.2): - - React-Core (= 0.77.2) - - React-Core/DevSupport (= 0.77.2) - - React-Core/RCTWebSocket (= 0.77.2) - - React-RCTActionSheet (= 0.77.2) - - React-RCTAnimation (= 0.77.2) - - React-RCTBlob (= 0.77.2) - - React-RCTImage (= 0.77.2) - - React-RCTLinking (= 0.77.2) - - React-RCTNetwork (= 0.77.2) - - React-RCTSettings (= 0.77.2) - - React-RCTText (= 0.77.2) - - React-RCTVibration (= 0.77.2) - - React-callinvoker (0.77.2) - - React-Core (0.77.2): + - RCTDeprecation (0.79.5) + - RCTRequired (0.79.5) + - RCTTypeSafety (0.79.5): + - FBLazyVector (= 0.79.5) + - RCTRequired (= 0.79.5) + - React-Core (= 0.79.5) + - React (0.79.5): + - React-Core (= 0.79.5) + - React-Core/DevSupport (= 0.79.5) + - React-Core/RCTWebSocket (= 0.79.5) + - React-RCTActionSheet (= 0.79.5) + - React-RCTAnimation (= 0.79.5) + - React-RCTBlob (= 0.79.5) + - React-RCTImage (= 0.79.5) + - React-RCTLinking (= 0.79.5) + - React-RCTNetwork (= 0.79.5) + - React-RCTSettings (= 0.79.5) + - React-RCTText (= 0.79.5) + - React-RCTVibration (= 0.79.5) + - React-callinvoker (0.79.5) + - React-Core (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - - React-Core/Default (= 0.77.2) + - React-Core/Default (= 0.79.5) - React-cxxreact - React-featureflags - React-hermes - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/CoreModulesHeaders (0.77.2): + - React-Core/CoreModulesHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1550,12 +1820,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/Default (0.77.2): + - React-Core/Default (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1566,30 +1837,32 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/DevSupport (0.77.2): + - React-Core/DevSupport (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - - React-Core/Default (= 0.77.2) - - React-Core/RCTWebSocket (= 0.77.2) + - React-Core/Default (= 0.79.5) + - React-Core/RCTWebSocket (= 0.79.5) - React-cxxreact - React-featureflags - React-hermes - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTActionSheetHeaders (0.77.2): + - React-Core/RCTActionSheetHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1601,12 +1874,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTAnimationHeaders (0.77.2): + - React-Core/RCTAnimationHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1618,12 +1892,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTBlobHeaders (0.77.2): + - React-Core/RCTBlobHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1635,12 +1910,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTImageHeaders (0.77.2): + - React-Core/RCTImageHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1652,12 +1928,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTLinkingHeaders (0.77.2): + - React-Core/RCTLinkingHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1669,12 +1946,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTNetworkHeaders (0.77.2): + - React-Core/RCTNetworkHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1686,12 +1964,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTSettingsHeaders (0.77.2): + - React-Core/RCTSettingsHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1703,12 +1982,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTTextHeaders (0.77.2): + - React-Core/RCTTextHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1720,12 +2000,13 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTVibrationHeaders (0.77.2): + - React-Core/RCTVibrationHeaders (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -1737,44 +2018,47 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTWebSocket (0.77.2): + - React-Core/RCTWebSocket (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - - React-Core/Default (= 0.77.2) + - React-Core/Default (= 0.79.5) - React-cxxreact - React-featureflags - React-hermes - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-CoreModules (0.77.2): + - React-CoreModules (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - RCT-Folly (= 2024.11.18.00) - - RCTTypeSafety (= 0.77.2) - - React-Core/CoreModulesHeaders (= 0.77.2) - - React-jsi (= 0.77.2) + - RCTTypeSafety (= 0.79.5) + - React-Core/CoreModulesHeaders (= 0.79.5) + - React-jsi (= 0.79.5) - React-jsinspector + - React-jsinspectortracing - React-NativeModulesApple - React-RCTBlob - React-RCTFBReactNativeSpec - - React-RCTImage (= 0.77.2) + - React-RCTImage (= 0.79.5) - ReactCommon - SocketRocket (= 0.7.1) - - React-cxxreact (0.77.2): + - React-cxxreact (0.79.5): - boost - DoubleConversion - fast_float (= 6.1.4) @@ -1782,37 +2066,40 @@ PODS: - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.77.2) - - React-debug (= 0.77.2) - - React-jsi (= 0.77.2) + - React-callinvoker (= 0.79.5) + - React-debug (= 0.79.5) + - React-jsi (= 0.79.5) - React-jsinspector - - React-logger (= 0.77.2) - - React-perflogger (= 0.77.2) - - React-runtimeexecutor (= 0.77.2) - - React-timing (= 0.77.2) - - React-debug (0.77.2) - - React-defaultsnativemodule (0.77.2): + - React-jsinspectortracing + - React-logger (= 0.79.5) + - React-perflogger (= 0.79.5) + - React-runtimeexecutor (= 0.79.5) + - React-timing (= 0.79.5) + - React-debug (0.79.5) + - React-defaultsnativemodule (0.79.5): - hermes-engine - RCT-Folly - React-domnativemodule - React-featureflagsnativemodule + - React-hermes - React-idlecallbacksnativemodule - React-jsi - React-jsiexecutor - React-microtasksnativemodule - React-RCTFBReactNativeSpec - - React-domnativemodule (0.77.2): + - React-domnativemodule (0.79.5): - hermes-engine - RCT-Folly - React-Fabric - React-FabricComponents - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - Yoga - - React-Fabric (0.77.2): + - React-Fabric (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1824,23 +2111,25 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 0.77.2) - - React-Fabric/attributedstring (= 0.77.2) - - React-Fabric/componentregistry (= 0.77.2) - - React-Fabric/componentregistrynative (= 0.77.2) - - React-Fabric/components (= 0.77.2) - - React-Fabric/core (= 0.77.2) - - React-Fabric/dom (= 0.77.2) - - React-Fabric/imagemanager (= 0.77.2) - - React-Fabric/leakchecker (= 0.77.2) - - React-Fabric/mounting (= 0.77.2) - - React-Fabric/observers (= 0.77.2) - - React-Fabric/scheduler (= 0.77.2) - - React-Fabric/telemetry (= 0.77.2) - - React-Fabric/templateprocessor (= 0.77.2) - - React-Fabric/uimanager (= 0.77.2) + - React-Fabric/animations (= 0.79.5) + - React-Fabric/attributedstring (= 0.79.5) + - React-Fabric/componentregistry (= 0.79.5) + - React-Fabric/componentregistrynative (= 0.79.5) + - React-Fabric/components (= 0.79.5) + - React-Fabric/consistency (= 0.79.5) + - React-Fabric/core (= 0.79.5) + - React-Fabric/dom (= 0.79.5) + - React-Fabric/imagemanager (= 0.79.5) + - React-Fabric/leakchecker (= 0.79.5) + - React-Fabric/mounting (= 0.79.5) + - React-Fabric/observers (= 0.79.5) + - React-Fabric/scheduler (= 0.79.5) + - React-Fabric/telemetry (= 0.79.5) + - React-Fabric/templateprocessor (= 0.79.5) + - React-Fabric/uimanager (= 0.79.5) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1848,7 +2137,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/animations (0.77.2): + - React-Fabric/animations (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1862,6 +2151,29 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/attributedstring (0.79.5): + - DoubleConversion + - fast_float (= 6.1.4) + - fmt (= 11.0.2) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1869,7 +2181,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.77.2): + - React-Fabric/componentregistry (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1883,6 +2195,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1890,7 +2203,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.77.2): + - React-Fabric/componentregistrynative (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1904,6 +2217,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1911,7 +2225,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.77.2): + - React-Fabric/components (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1923,8 +2237,13 @@ PODS: - React-Core - React-cxxreact - React-debug + - React-Fabric/components/legacyviewmanagerinterop (= 0.79.5) + - React-Fabric/components/root (= 0.79.5) + - React-Fabric/components/scrollview (= 0.79.5) + - React-Fabric/components/view (= 0.79.5) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1932,7 +2251,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components (0.77.2): + - React-Fabric/components/legacyviewmanagerinterop (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1944,11 +2263,9 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.77.2) - - React-Fabric/components/root (= 0.77.2) - - React-Fabric/components/view (= 0.77.2) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1956,7 +2273,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.77.2): + - React-Fabric/components/root (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1970,6 +2287,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1977,7 +2295,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.77.2): + - React-Fabric/components/scrollview (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -1991,6 +2309,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -1998,7 +2317,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.77.2): + - React-Fabric/components/view (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2012,15 +2331,39 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger + - React-renderercss - React-rendererdebug - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - Yoga - - React-Fabric/core (0.77.2): + - React-Fabric/consistency (0.79.5): + - DoubleConversion + - fast_float (= 6.1.4) + - fmt (= 11.0.2) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/core (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2034,6 +2377,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2041,7 +2385,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/dom (0.77.2): + - React-Fabric/dom (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2055,6 +2399,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2062,7 +2407,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.77.2): + - React-Fabric/imagemanager (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2076,6 +2421,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2083,7 +2429,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.77.2): + - React-Fabric/leakchecker (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2097,6 +2443,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2104,7 +2451,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.77.2): + - React-Fabric/mounting (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2118,6 +2465,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2125,7 +2473,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/observers (0.77.2): + - React-Fabric/observers (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2137,9 +2485,10 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/observers/events (= 0.77.2) + - React-Fabric/observers/events (= 0.79.5) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2147,7 +2496,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/observers/events (0.77.2): + - React-Fabric/observers/events (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2161,6 +2510,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2168,7 +2518,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.77.2): + - React-Fabric/scheduler (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2183,6 +2533,7 @@ PODS: - React-Fabric/observers/events - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2191,7 +2542,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.77.2): + - React-Fabric/telemetry (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2205,6 +2556,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2212,7 +2564,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.77.2): + - React-Fabric/templateprocessor (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2226,6 +2578,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2233,7 +2586,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.77.2): + - React-Fabric/uimanager (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2245,9 +2598,10 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/uimanager/consistency (= 0.77.2) + - React-Fabric/uimanager/consistency (= 0.79.5) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2256,7 +2610,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager/consistency (0.77.2): + - React-Fabric/uimanager/consistency (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2270,6 +2624,7 @@ PODS: - React-debug - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2278,7 +2633,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-FabricComponents (0.77.2): + - React-FabricComponents (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2291,10 +2646,11 @@ PODS: - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components (= 0.77.2) - - React-FabricComponents/textlayoutmanager (= 0.77.2) + - React-FabricComponents/components (= 0.79.5) + - React-FabricComponents/textlayoutmanager (= 0.79.5) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2303,7 +2659,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components (0.77.2): + - React-FabricComponents/components (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2316,17 +2672,18 @@ PODS: - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.77.2) - - React-FabricComponents/components/iostextinput (= 0.77.2) - - React-FabricComponents/components/modal (= 0.77.2) - - React-FabricComponents/components/rncore (= 0.77.2) - - React-FabricComponents/components/safeareaview (= 0.77.2) - - React-FabricComponents/components/scrollview (= 0.77.2) - - React-FabricComponents/components/text (= 0.77.2) - - React-FabricComponents/components/textinput (= 0.77.2) - - React-FabricComponents/components/unimplementedview (= 0.77.2) + - React-FabricComponents/components/inputaccessory (= 0.79.5) + - React-FabricComponents/components/iostextinput (= 0.79.5) + - React-FabricComponents/components/modal (= 0.79.5) + - React-FabricComponents/components/rncore (= 0.79.5) + - React-FabricComponents/components/safeareaview (= 0.79.5) + - React-FabricComponents/components/scrollview (= 0.79.5) + - React-FabricComponents/components/text (= 0.79.5) + - React-FabricComponents/components/textinput (= 0.79.5) + - React-FabricComponents/components/unimplementedview (= 0.79.5) - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2335,7 +2692,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/inputaccessory (0.77.2): + - React-FabricComponents/components/inputaccessory (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2350,6 +2707,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2358,7 +2716,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/iostextinput (0.77.2): + - React-FabricComponents/components/iostextinput (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2373,6 +2731,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2381,7 +2740,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/modal (0.77.2): + - React-FabricComponents/components/modal (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2396,6 +2755,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2404,7 +2764,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/rncore (0.77.2): + - React-FabricComponents/components/rncore (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2419,6 +2779,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2427,7 +2788,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/safeareaview (0.77.2): + - React-FabricComponents/components/safeareaview (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2442,6 +2803,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2450,7 +2812,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/scrollview (0.77.2): + - React-FabricComponents/components/scrollview (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2465,6 +2827,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2473,7 +2836,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/text (0.77.2): + - React-FabricComponents/components/text (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2488,6 +2851,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2496,7 +2860,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/textinput (0.77.2): + - React-FabricComponents/components/textinput (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2511,6 +2875,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2519,7 +2884,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/unimplementedview (0.77.2): + - React-FabricComponents/components/unimplementedview (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2534,6 +2899,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2542,7 +2908,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/textlayoutmanager (0.77.2): + - React-FabricComponents/textlayoutmanager (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2557,6 +2923,7 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-logger @@ -2565,66 +2932,74 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-FabricImage (0.77.2): + - React-FabricImage (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) - - RCTRequired (= 0.77.2) - - RCTTypeSafety (= 0.77.2) + - RCTRequired (= 0.79.5) + - RCTTypeSafety (= 0.79.5) - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.77.2) + - React-jsiexecutor (= 0.79.5) - React-logger - React-rendererdebug - React-utils - ReactCommon - Yoga - - React-featureflags (0.77.2) - - React-featureflagsnativemodule (0.77.2): + - React-featureflags (0.79.5): + - RCT-Folly (= 2024.11.18.00) + - React-featureflagsnativemodule (0.79.5): - hermes-engine - RCT-Folly - React-featureflags + - React-hermes - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - React-graphics (0.77.2): + - React-graphics (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog + - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) + - React-hermes - React-jsi - React-jsiexecutor - React-utils - - React-hermes (0.77.2): + - React-hermes (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-cxxreact (= 0.77.2) + - React-cxxreact (= 0.79.5) - React-jsi - - React-jsiexecutor (= 0.77.2) + - React-jsiexecutor (= 0.79.5) - React-jsinspector - - React-perflogger (= 0.77.2) + - React-jsinspectortracing + - React-perflogger (= 0.79.5) - React-runtimeexecutor - - React-idlecallbacksnativemodule (0.77.2): + - React-idlecallbacksnativemodule (0.79.5): + - glog - hermes-engine - RCT-Folly + - React-hermes - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec - React-runtimescheduler - ReactCommon/turbomodule/core - - React-ImageManager (0.77.2): + - React-ImageManager (0.79.5): - glog - RCT-Folly/Fabric - React-Core/Default @@ -2633,7 +3008,7 @@ PODS: - React-graphics - React-rendererdebug - React-utils - - React-jserrorhandler (0.77.2): + - React-jserrorhandler (0.79.5): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) @@ -2642,7 +3017,7 @@ PODS: - React-featureflags - React-jsi - ReactCommon/turbomodule/bridging - - React-jsi (0.77.2): + - React-jsi (0.79.5): - boost - DoubleConversion - fast_float (= 6.1.4) @@ -2650,36 +3025,52 @@ PODS: - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-jsiexecutor (0.77.2): + - React-jsiexecutor (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-cxxreact (= 0.77.2) - - React-jsi (= 0.77.2) + - React-cxxreact (= 0.79.5) + - React-jsi (= 0.79.5) - React-jsinspector - - React-perflogger (= 0.77.2) - - React-jsinspector (0.77.2): + - React-jsinspectortracing + - React-perflogger (= 0.79.5) + - React-jsinspector (0.79.5): - DoubleConversion - glog - hermes-engine - - RCT-Folly (= 2024.11.18.00) + - RCT-Folly - React-featureflags - React-jsi - - React-perflogger (= 0.77.2) - - React-runtimeexecutor (= 0.77.2) - - React-jsitracing (0.77.2): + - React-jsinspectortracing + - React-perflogger (= 0.79.5) + - React-runtimeexecutor (= 0.79.5) + - React-jsinspectortracing (0.79.5): + - RCT-Folly + - React-oscompat + - React-jsitooling (0.79.5): + - DoubleConversion + - fast_float (= 6.1.4) + - fmt (= 11.0.2) + - glog + - RCT-Folly (= 2024.11.18.00) + - React-cxxreact (= 0.79.5) + - React-jsi (= 0.79.5) + - React-jsinspector + - React-jsinspectortracing + - React-jsitracing (0.79.5): - React-jsi - - React-logger (0.77.2): + - React-logger (0.79.5): - glog - - React-Mapbuffer (0.77.2): + - React-Mapbuffer (0.79.5): - glog - React-debug - - React-microtasksnativemodule (0.77.2): + - React-microtasksnativemodule (0.79.5): - hermes-engine - RCT-Folly + - React-hermes - React-jsi - React-jsiexecutor - React-RCTFBReactNativeSpec @@ -2687,7 +3078,7 @@ PODS: - react-native-appsflyer (6.13.1): - AppsFlyerFramework (= 6.13.1) - React - - react-native-compat (2.21.4): + - react-native-compat (2.23.0): - DoubleConversion - glog - hermes-engine @@ -2699,9 +3090,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2724,9 +3118,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2745,9 +3142,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2762,7 +3162,7 @@ PODS: - react-native-onesignal (5.2.9): - OneSignalXCFramework (= 5.2.10) - React (< 1.0.0, >= 0.13.0) - - react-native-pager-view (6.5.1): + - react-native-pager-view (6.7.1): - DoubleConversion - glog - hermes-engine @@ -2774,9 +3174,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2787,9 +3190,9 @@ PODS: - React-Core - react-native-restart (0.0.27): - React-Core - - react-native-safe-area-context (5.1.0): + - react-native-safe-area-context (5.4.0): - React-Core - - react-native-skia (1.12.4): + - react-native-skia (2.2.4): - DoubleConversion - glog - hermes-engine @@ -2803,16 +3206,19 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-slider (4.5.5): + - react-native-slider (4.5.6): - DoubleConversion - glog - hermes-engine @@ -2824,9 +3230,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2845,10 +3254,13 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - react-native-video/Video (= 6.13.0) - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2867,9 +3279,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2888,9 +3303,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -2899,29 +3317,33 @@ PODS: - Yoga - react-native-widgetkit (1.0.9): - React - - React-nativeconfig (0.77.2) - - React-NativeModulesApple (0.77.2): + - React-NativeModulesApple (0.79.5): - glog - hermes-engine - React-callinvoker - React-Core - React-cxxreact + - React-featureflags + - React-hermes - React-jsi - React-jsinspector - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (0.77.2): + - React-oscompat (0.79.5) + - React-perflogger (0.79.5): - DoubleConversion - RCT-Folly (= 2024.11.18.00) - - React-performancetimeline (0.77.2): + - React-performancetimeline (0.79.5): - RCT-Folly (= 2024.11.18.00) - React-cxxreact - React-featureflags + - React-jsinspectortracing + - React-perflogger - React-timing - - React-RCTActionSheet (0.77.2): - - React-Core/RCTActionSheetHeaders (= 0.77.2) - - React-RCTAnimation (0.77.2): + - React-RCTActionSheet (0.79.5): + - React-Core/RCTActionSheetHeaders (= 0.79.5) + - React-RCTAnimation (0.79.5): - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTAnimationHeaders @@ -2929,7 +3351,8 @@ PODS: - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - React-RCTAppDelegate (0.77.2): + - React-RCTAppDelegate (0.79.5): + - hermes-engine - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety @@ -2941,20 +3364,20 @@ PODS: - React-featureflags - React-graphics - React-hermes - - React-nativeconfig + - React-jsitooling - React-NativeModulesApple - React-RCTFabric - React-RCTFBReactNativeSpec - React-RCTImage - React-RCTNetwork + - React-RCTRuntime - React-rendererdebug - React-RuntimeApple - React-RuntimeCore - - React-RuntimeHermes - React-runtimescheduler - React-utils - ReactCommon - - React-RCTBlob (0.77.2): + - React-RCTBlob (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) @@ -2968,7 +3391,7 @@ PODS: - React-RCTFBReactNativeSpec - React-RCTNetwork - ReactCommon - - React-RCTFabric (0.77.2): + - React-RCTFabric (0.79.5): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) @@ -2979,29 +3402,33 @@ PODS: - React-FabricImage - React-featureflags - React-graphics + - React-hermes - React-ImageManager - React-jsi - React-jsinspector - - React-nativeconfig + - React-jsinspectortracing - React-performancetimeline + - React-RCTAnimation - React-RCTImage - React-RCTText - React-rendererconsistency + - React-renderercss - React-rendererdebug - React-runtimescheduler - React-utils - Yoga - - React-RCTFBReactNativeSpec (0.77.2): + - React-RCTFBReactNativeSpec (0.79.5): - hermes-engine - RCT-Folly - RCTRequired - RCTTypeSafety - React-Core + - React-hermes - React-jsi - React-jsiexecutor - React-NativeModulesApple - ReactCommon - - React-RCTImage (0.77.2): + - React-RCTImage (0.79.5): - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTImageHeaders @@ -3010,14 +3437,14 @@ PODS: - React-RCTFBReactNativeSpec - React-RCTNetwork - ReactCommon - - React-RCTLinking (0.77.2): - - React-Core/RCTLinkingHeaders (= 0.77.2) - - React-jsi (= 0.77.2) + - React-RCTLinking (0.79.5): + - React-Core/RCTLinkingHeaders (= 0.79.5) + - React-jsi (= 0.79.5) - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - ReactCommon/turbomodule/core (= 0.77.2) - - React-RCTNetwork (0.77.2): + - ReactCommon/turbomodule/core (= 0.79.5) + - React-RCTNetwork (0.79.5): - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTNetworkHeaders @@ -3025,7 +3452,20 @@ PODS: - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - React-RCTSettings (0.77.2): + - React-RCTRuntime (0.79.5): + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.11.18.00) + - React-Core + - React-hermes + - React-jsi + - React-jsinspector + - React-jsinspectortracing + - React-jsitooling + - React-RuntimeApple + - React-RuntimeCore + - React-RuntimeHermes + - React-RCTSettings (0.79.5): - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTSettingsHeaders @@ -3033,25 +3473,28 @@ PODS: - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - React-RCTText (0.77.2): - - React-Core/RCTTextHeaders (= 0.77.2) + - React-RCTText (0.79.5): + - React-Core/RCTTextHeaders (= 0.79.5) - Yoga - - React-RCTVibration (0.77.2): + - React-RCTVibration (0.79.5): - RCT-Folly (= 2024.11.18.00) - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - React-rendererconsistency (0.77.2) - - React-rendererdebug (0.77.2): + - React-rendererconsistency (0.79.5) + - React-renderercss (0.79.5): + - React-debug + - React-utils + - React-rendererdebug (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - RCT-Folly (= 2024.11.18.00) - React-debug - - React-rncore (0.77.2) - - React-RuntimeApple (0.77.2): + - React-rncore (0.79.5) + - React-RuntimeApple (0.79.5): - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) - React-callinvoker @@ -3063,6 +3506,7 @@ PODS: - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-Mapbuffer - React-NativeModulesApple - React-RCTFabric @@ -3072,35 +3516,38 @@ PODS: - React-RuntimeHermes - React-runtimescheduler - React-utils - - React-RuntimeCore (0.77.2): + - React-RuntimeCore (0.79.5): - glog - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) - React-cxxreact - React-Fabric - React-featureflags + - React-hermes - React-jserrorhandler - React-jsi - React-jsiexecutor - React-jsinspector + - React-jsitooling - React-performancetimeline - React-runtimeexecutor - React-runtimescheduler - React-utils - - React-runtimeexecutor (0.77.2): - - React-jsi (= 0.77.2) - - React-RuntimeHermes (0.77.2): + - React-runtimeexecutor (0.79.5): + - React-jsi (= 0.79.5) + - React-RuntimeHermes (0.79.5): - hermes-engine - RCT-Folly/Fabric (= 2024.11.18.00) - React-featureflags - React-hermes - React-jsi - React-jsinspector + - React-jsinspectortracing + - React-jsitooling - React-jsitracing - - React-nativeconfig - React-RuntimeCore - React-utils - - React-runtimescheduler (0.77.2): + - React-runtimescheduler (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) @@ -3108,23 +3555,26 @@ PODS: - React-cxxreact - React-debug - React-featureflags + - React-hermes - React-jsi + - React-jsinspectortracing - React-performancetimeline - React-rendererconsistency - React-rendererdebug - React-runtimeexecutor - React-timing - React-utils - - React-timing (0.77.2) - - React-utils (0.77.2): + - React-timing (0.79.5) + - React-utils (0.79.5): - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - React-debug - - React-jsi (= 0.77.2) - - ReactAppDependencyProvider (0.77.2): + - React-hermes + - React-jsi (= 0.79.5) + - ReactAppDependencyProvider (0.79.5): - ReactCodegen - - ReactCodegen (0.77.2): + - ReactCodegen (0.79.5): - DoubleConversion - glog - hermes-engine @@ -3137,6 +3587,7 @@ PODS: - React-FabricImage - React-featureflags - React-graphics + - React-hermes - React-jsi - React-jsiexecutor - React-NativeModulesApple @@ -3145,55 +3596,55 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - ReactCommon (0.77.2): - - ReactCommon/turbomodule (= 0.77.2) - - ReactCommon/turbomodule (0.77.2): + - ReactCommon (0.79.5): + - ReactCommon/turbomodule (= 0.79.5) + - ReactCommon/turbomodule (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.77.2) - - React-cxxreact (= 0.77.2) - - React-jsi (= 0.77.2) - - React-logger (= 0.77.2) - - React-perflogger (= 0.77.2) - - ReactCommon/turbomodule/bridging (= 0.77.2) - - ReactCommon/turbomodule/core (= 0.77.2) - - ReactCommon/turbomodule/bridging (0.77.2): + - React-callinvoker (= 0.79.5) + - React-cxxreact (= 0.79.5) + - React-jsi (= 0.79.5) + - React-logger (= 0.79.5) + - React-perflogger (= 0.79.5) + - ReactCommon/turbomodule/bridging (= 0.79.5) + - ReactCommon/turbomodule/core (= 0.79.5) + - ReactCommon/turbomodule/bridging (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.77.2) - - React-cxxreact (= 0.77.2) - - React-jsi (= 0.77.2) - - React-logger (= 0.77.2) - - React-perflogger (= 0.77.2) - - ReactCommon/turbomodule/core (0.77.2): + - React-callinvoker (= 0.79.5) + - React-cxxreact (= 0.79.5) + - React-jsi (= 0.79.5) + - React-logger (= 0.79.5) + - React-perflogger (= 0.79.5) + - ReactCommon/turbomodule/core (0.79.5): - DoubleConversion - fast_float (= 6.1.4) - fmt (= 11.0.2) - glog - hermes-engine - RCT-Folly (= 2024.11.18.00) - - React-callinvoker (= 0.77.2) - - React-cxxreact (= 0.77.2) - - React-debug (= 0.77.2) - - React-featureflags (= 0.77.2) - - React-jsi (= 0.77.2) - - React-logger (= 0.77.2) - - React-perflogger (= 0.77.2) - - React-utils (= 0.77.2) + - React-callinvoker (= 0.79.5) + - React-cxxreact (= 0.79.5) + - React-debug (= 0.79.5) + - React-featureflags (= 0.79.5) + - React-jsi (= 0.79.5) + - React-logger (= 0.79.5) + - React-perflogger (= 0.79.5) + - React-utils (= 0.79.5) - ReactNativePerformance (4.1.2): - React-Core - RecaptchaInterop (100.0.0) - - RNBootSplash (6.3.1): + - RNBootSplash (6.3.10): - React-Core - - RNCAsyncStorage (1.23.1): + - RNCAsyncStorage (2.1.2): - React-Core - RNCMaskedView (0.3.2): - DoubleConversion @@ -3207,16 +3658,19 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNDateTimePicker (8.2.0): + - RNDateTimePicker (8.4.1): - React-Core - RNDeviceInfo (10.11.0): - React-Core @@ -3235,7 +3689,7 @@ PODS: - Firebase/Firestore (= 11.2.0) - React-Core - RNFBApp - - RNFlashList (1.7.3): + - RNFlashList (1.7.6): - DoubleConversion - glog - hermes-engine @@ -3247,16 +3701,19 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNGestureHandler (2.22.1): + - RNGestureHandler (2.24.0): - DoubleConversion - glog - hermes-engine @@ -3268,9 +3725,12 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen @@ -3286,7 +3746,7 @@ PODS: - RNQrGenerator (1.4.3): - React - ZXingObjC - - RNReanimated (3.16.7): + - RNReanimated (3.19.3): - DoubleConversion - glog - hermes-engine @@ -3298,18 +3758,21 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 3.16.7) - - RNReanimated/worklets (= 3.16.7) + - RNReanimated/reanimated (= 3.19.3) + - RNReanimated/worklets (= 3.19.3) - Yoga - - RNReanimated/reanimated (3.16.7): + - RNReanimated/reanimated (3.19.3): - DoubleConversion - glog - hermes-engine @@ -3321,17 +3784,20 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 3.16.7) + - RNReanimated/reanimated/apple (= 3.19.3) - Yoga - - RNReanimated/reanimated/apple (3.16.7): + - RNReanimated/reanimated/apple (3.19.3): - DoubleConversion - glog - hermes-engine @@ -3343,16 +3809,19 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReanimated/worklets (3.16.7): + - RNReanimated/worklets (3.19.3): - DoubleConversion - glog - hermes-engine @@ -3364,16 +3833,20 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - RNReanimated/worklets/apple (= 3.19.3) - Yoga - - RNScreens (4.11.0): + - RNReanimated/worklets/apple (3.19.3): - DoubleConversion - glog - hermes-engine @@ -3385,21 +3858,48 @@ PODS: - React-Fabric - React-featureflags - React-graphics + - React-hermes - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNScreens (4.11.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi - React-NativeModulesApple - React-RCTFabric - React-RCTImage + - React-renderercss - React-rendererdebug - React-utils - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.11.2): + - RNSVG (15.13.0): - React-Core - - SDWebImage (5.21.1): - - SDWebImage/Core (= 5.21.1) - - SDWebImage/Core (5.21.1) + - SDWebImage (5.21.3): + - SDWebImage/Core (= 5.21.3) + - SDWebImage/Core (5.21.3) - SDWebImageWebPCoder (0.14.6): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.17) @@ -3425,7 +3925,14 @@ DEPENDENCIES: - "DatadogSDKReactNative (from `../../../node_modules/@datadog/mobile-react-native`)" - DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - "EthersRS (from `../../../node_modules/@uniswap/ethers-rs-mobile`)" + - EXConstants (from `../../../node_modules/expo-constants/ios`) + - EXJSONUtils (from `../../../node_modules/expo-json-utils/ios`) + - EXManifests (from `../../../node_modules/expo-manifests/ios`) - Expo (from `../../../node_modules/expo`) + - expo-dev-client (from `../../../node_modules/expo-dev-client/ios`) + - expo-dev-launcher (from `../../../node_modules/expo-dev-launcher`) + - expo-dev-menu (from `../../../node_modules/expo-dev-menu`) + - expo-dev-menu-interface (from `../../../node_modules/expo-dev-menu-interface/ios`) - ExpoAsset (from `../../../node_modules/expo-asset/ios`) - ExpoBlur (from `../../../node_modules/expo-blur/ios`) - ExpoCamera (from `../../../node_modules/expo-camera/ios`) @@ -3443,6 +3950,7 @@ DEPENDENCIES: - ExpoSecureStore (from `../../../node_modules/expo-secure-store/ios`) - ExpoStoreReview (from `../../../node_modules/expo-store-review/ios`) - ExpoWebBrowser (from `../../../node_modules/expo-web-browser/ios`) + - EXUpdatesInterface (from `../../../node_modules/expo-updates-interface/ios`) - fast_float (from `../../../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`) - fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`) @@ -3476,6 +3984,8 @@ DEPENDENCIES: - React-jsi (from `../../../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../../../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern`) + - React-jsinspectortracing (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`) + - React-jsitooling (from `../../../node_modules/react-native/ReactCommon/jsitooling`) - React-jsitracing (from `../../../node_modules/react-native/ReactCommon/hermes/executor/`) - React-logger (from `../../../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../../../node_modules/react-native/ReactCommon`) @@ -3498,8 +4008,8 @@ DEPENDENCIES: - react-native-video (from `../../../node_modules/react-native-video`) - react-native-webview (from `../../../node_modules/react-native-webview`) - react-native-widgetkit (from `../../../node_modules/react-native-widgetkit`) - - React-nativeconfig (from `../../../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-oscompat (from `../../../node_modules/react-native/ReactCommon/oscompat`) - React-perflogger (from `../../../node_modules/react-native/ReactCommon/reactperflogger`) - React-performancetimeline (from `../../../node_modules/react-native/ReactCommon/react/performance/timeline`) - React-RCTActionSheet (from `../../../node_modules/react-native/Libraries/ActionSheetIOS`) @@ -3511,10 +4021,12 @@ DEPENDENCIES: - React-RCTImage (from `../../../node_modules/react-native/Libraries/Image`) - React-RCTLinking (from `../../../node_modules/react-native/Libraries/LinkingIOS`) - React-RCTNetwork (from `../../../node_modules/react-native/Libraries/Network`) + - React-RCTRuntime (from `../../../node_modules/react-native/React/Runtime`) - React-RCTSettings (from `../../../node_modules/react-native/Libraries/Settings`) - React-RCTText (from `../../../node_modules/react-native/Libraries/Text`) - React-RCTVibration (from `../../../node_modules/react-native/Libraries/Vibration`) - React-rendererconsistency (from `../../../node_modules/react-native/ReactCommon/react/renderer/consistency`) + - React-renderercss (from `../../../node_modules/react-native/ReactCommon/react/renderer/css`) - React-rendererdebug (from `../../../node_modules/react-native/ReactCommon/react/renderer/debug`) - React-rncore (from `../../../node_modules/react-native/ReactCommon`) - React-RuntimeApple (from `../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) @@ -3604,8 +4116,22 @@ EXTERNAL SOURCES: :podspec: "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" EthersRS: :path: "../../../node_modules/@uniswap/ethers-rs-mobile" + EXConstants: + :path: "../../../node_modules/expo-constants/ios" + EXJSONUtils: + :path: "../../../node_modules/expo-json-utils/ios" + EXManifests: + :path: "../../../node_modules/expo-manifests/ios" Expo: :path: "../../../node_modules/expo" + expo-dev-client: + :path: "../../../node_modules/expo-dev-client/ios" + expo-dev-launcher: + :path: "../../../node_modules/expo-dev-launcher" + expo-dev-menu: + :path: "../../../node_modules/expo-dev-menu" + expo-dev-menu-interface: + :path: "../../../node_modules/expo-dev-menu-interface/ios" ExpoAsset: :path: "../../../node_modules/expo-asset/ios" ExpoBlur: @@ -3640,6 +4166,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/expo-store-review/ios" ExpoWebBrowser: :path: "../../../node_modules/expo-web-browser/ios" + EXUpdatesInterface: + :path: "../../../node_modules/expo-updates-interface/ios" fast_float: :podspec: "../../../node_modules/react-native/third-party-podspecs/fast_float.podspec" FBLazyVector: @@ -3650,7 +4178,7 @@ EXTERNAL SOURCES: :podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2024-11-25-RNv0.77.0-d4f25d534ab744866448b36ca3bf3d97c08e638c + :tag: hermes-2025-06-04-RNv0.79.3-7f9a871eefeb2c3852365ee80f0b6733ec12ac3b RCT-Folly: :podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -3701,6 +4229,10 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native/ReactCommon/jsiexecutor" React-jsinspector: :path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern" + React-jsinspectortracing: + :path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing" + React-jsitooling: + :path: "../../../node_modules/react-native/ReactCommon/jsitooling" React-jsitracing: :path: "../../../node_modules/react-native/ReactCommon/hermes/executor/" React-logger: @@ -3745,10 +4277,10 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native-webview" react-native-widgetkit: :path: "../../../node_modules/react-native-widgetkit" - React-nativeconfig: - :path: "../../../node_modules/react-native/ReactCommon" React-NativeModulesApple: :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + React-oscompat: + :path: "../../../node_modules/react-native/ReactCommon/oscompat" React-perflogger: :path: "../../../node_modules/react-native/ReactCommon/reactperflogger" React-performancetimeline: @@ -3771,6 +4303,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native/Libraries/LinkingIOS" React-RCTNetwork: :path: "../../../node_modules/react-native/Libraries/Network" + React-RCTRuntime: + :path: "../../../node_modules/react-native/React/Runtime" React-RCTSettings: :path: "../../../node_modules/react-native/Libraries/Settings" React-RCTText: @@ -3779,6 +4313,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native/Libraries/Vibration" React-rendererconsistency: :path: "../../../node_modules/react-native/ReactCommon/react/renderer/consistency" + React-renderercss: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/css" React-rendererdebug: :path: "../../../node_modules/react-native/ReactCommon/react/renderer/debug" React-rncore: @@ -3854,165 +4390,177 @@ SPEC CHECKSUMS: Argon2Swift: 99482c1b8122a03524b61e41c4903a9548e7c33b boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 BoringSSL-GRPC: ca6a8e5d04812fce8ffd6437810c2d46f925eaeb - DatadogCore: 68aee4ffcc3ea17a3b0aa527907757883fc72c84 - DatadogCrashReporting: e6a83b143394e28c9c1cb48c5cfb18eff507b3be - DatadogInternal: 3c5cae6772295fd175a9de11e4747a9322aaa4e7 - DatadogLogs: 09d6358dc7682f9d3eaea85dd418f82d2db3560c - DatadogRUM: 0f267df8c9c8579a291870c2bce4549587391a07 - DatadogSDKReactNative: 55c5868f9321a483bb6f592c1b2948345137a394 - DatadogTrace: f46c8220c73463d09741013f385a6e27cd39185b - DatadogWebViewTracking: dc8376420c8686efd09d00752bc1034b639d180b + DatadogCore: 5c01290a3b60b27bf49aa958f2e339c738364d9e + DatadogCrashReporting: 11286d48ab61baeb2b41b945c7c0d4ef23db317d + DatadogInternal: 7aeb48e254178a0c462c3953dc0a8a8d64499a93 + DatadogLogs: 4324739de62a6059e07d70bf6ceceed78764edeb + DatadogRUM: f36949a38285f3b240a7be577d425f8518e087d4 + DatadogSDKReactNative: 58d9a3f2005f0f9b47c057929c021fcb3b5201e4 + DatadogTrace: bfea32b6ed2870829629a9296cf526221493cc3e + DatadogWebViewTracking: 78c20d8e5f1ade506f4aadaec5690c1a63283fe2 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb EthersRS: 56b70e73d22d4e894b7e762eef1129159bcd3135 - Expo: 3e53243e3281214a7d613f8a875c0b732d7512c2 - ExpoAsset: 0687fe05f5d051c4a34dd1f9440bd00858413cfe - ExpoBlur: 567af66164e3043a9a30069594aed1ddf0a88d97 - ExpoCamera: 173e000631122854b87c20310513981e89030bc6 - ExpoClipboard: 5250b207b6d545f4e9aac5ea3c6e61c4f16d0aed - ExpoFileSystem: c8c19bf80d914c83dda3beb8569d7fb603be0970 - ExpoFont: 773955186469acc5108ff569712a2d243857475f + EXConstants: 9d62a46a36eae6d28cb978efcbc68aef354d1704 + EXJSONUtils: 1d3e4590438c3ee593684186007028a14b3686cd + EXManifests: f4cc4a62ee4f1c8a9cf2bb79d325eac6cb9f5684 + Expo: 2a8d20c4498052d30c3b198e98cf8c19a137ecd7 + expo-dev-client: f1b99dfea0c9174d2e4ec96c2c5461587dda1e86 + expo-dev-launcher: 73e0cc1a270486501011fd8bed4cb096cc431a43 + expo-dev-menu: b2554d3971b251b2c1f0f5c9c3da50855150f195 + expo-dev-menu-interface: 609c35ae8b97479cdd4c9e23c8cf6adc44beea0e + ExpoAsset: 7bdbbacf4e6752ae6e3cf70555cee076f6229e6e + ExpoBlur: 846780b2c90f59e964b9a50385d4deb67174ebfb + ExpoCamera: fc1ab0e1c665b543a307c577df107e37cc2edc8e + ExpoClipboard: 6b9aae54fd48a579473fb101051ad693435b9294 + ExpoFileSystem: 9681caebda23fa1b38a12a9c68b2bade7072ce20 + ExpoFont: 091a47eeaa1b30b0b760aa1d0a2e7814e8bf6fe6 ExpoHaptics: e01cce0741d68c281853118eb0267f88d42c6b7a - ExpoKeepAwake: 2a5f15dd4964cba8002c9a36676319a3394c85c7 - ExpoLinearGradient: ee9efc5acb988b911320e964fab9b4cbdeb198c4 - ExpoLinking: 0381341519ca7180a3a057d20edb1cf6a908aaf4 - ExpoLocalAuthentication: 64bf2cbee456f5639d69a853684c285afc0602d8 - ExpoLocalization: e36b911e04d371c6c6624ef818e56229bf51c498 - ExpoModulesCore: 87f0b8b38f9d4c8a983212ba54119f11f3fcb615 - ExpoScreenCapture: 29ab5480e0d2b7849691d17f00a70b279cbe6a65 + ExpoKeepAwake: e8dedc115d9f6f24b153ccd2d1d8efcdfd68a527 + ExpoLinearGradient: ce334cff9859da4635c1d8eff6e291b11b04ccbb + ExpoLinking: 343a89ea864a851831fd4495e8aea01cf0f6a36f + ExpoLocalAuthentication: 78f74d187ee51126e1a789d73fee32d6d7e60f1f + ExpoLocalization: 677e45c2536bf918119962f78d7ffeeea317e07d + ExpoModulesCore: 8030601b6028c50a3adf8864dabf43c84c913f43 + ExpoScreenCapture: 329c26be22741077b81612de1edaee8648fb209e ExpoSecureStore: d006eea5e316283099d46f80a6b10055b89a6008 - ExpoStoreReview: 32f7186925fdecacddf3c1bc9628dd11b10c3ddd - ExpoWebBrowser: 6890a769e6c9d83da938dceb9a03e764afc3ec9c + ExpoStoreReview: bed43bea90a5876a6a480504f95fea1521dacaa7 + ExpoWebBrowser: eeb47f52e85b2686b56178749675cf90d0822f86 + EXUpdatesInterface: 64f35449b8ef89ce08cdd8952a4d119b5de6821d fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 - FBLazyVector: 4c16dde959a9d6b24f2aa32cb87cb919a1ace3f3 + FBLazyVector: d2a9cd223302b6c9aa4aa34c1a775e9db609eb52 Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c - FirebaseAppCheckInterop: a92ba81d0ee3c4cddb1a2e52c668ea51dc63c3ae + FirebaseAppCheckInterop: 06fe5a3799278ae4667e6c432edd86b1030fa3df FirebaseAuth: 2a198b8cdbbbd457f08d74df7040feb0a0e7777a - FirebaseAuthInterop: e25b58ecb90f3285085fa2118861a3c9dfdc62ad + FirebaseAuthInterop: 7087d7a4ee4bc4de019b2d0c240974ed5d89e2fd FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da FirebaseCoreExtension: f1bc67a4702931a7caa097d8e4ac0a1b0d16720e - FirebaseCoreInternal: 6a3b668197644aa858fc4127578637c6767ba123 + FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4 FirebaseFirestore: 62708adbc1dfcd6d165a7c0a202067b441912dc9 FirebaseFirestoreInternal: ad9b9ee2d3d430c8f31333a69b3b6737a7206232 - FirebaseSharedSwift: bdd5c8674c4712a98e70287c936bc5cca5d640f6 + FirebaseSharedSwift: e17c654ef1f1a616b0b33054e663ad1035c8fd40 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd - glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8 + glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 "gRPC-C++": 2fa52b3141e7789a28a737f251e0c45b4cb20a87 gRPC-Core: a27c294d6149e1c39a7d173527119cfbc3375ce4 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - hermes-engine: 8eb265241fa1d7095d3a40d51fd90f7dce68217c + hermes-engine: f03b0e06d3882d71e67e45b073bb827da1a21aae leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 - MMKV: b4802ebd5a7c68fc0c4a5ccb4926fbdfb62d68e0 - MMKVCore: a255341a3746955f50da2ad9121b18cb2b346e61 + MMKV: 1a8e7dbce7f9cad02c52e1b1091d07bd843aefaf + MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774 OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104 PLCrashReporter: db59ef96fa3d25f3650040d02ec2798cffee75f2 RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 - RCTDeprecation: 85b72250b63cfb54f29ca96ceb108cb9ef3c2079 - RCTRequired: 567cb8f5d42b990331bfd93faad1d8999b1c1736 - RCTTypeSafety: 5e57924492a5e0a762654f814dd018953274eca9 - React: 53c9bd6f974c5dd019ee466e46477eb679149c38 - React-callinvoker: d6484472c1c742917b51338525336d6a74ab8a9f - React-Core: 043aaf319142ecc02db6fffccb780186e6e7462a - React-CoreModules: abe0b2089368e420b7beaa5e140771181e2f6edb - React-cxxreact: 8b678dd36228089b6ee19d62f2bfb8935ea1c63b - React-debug: af25f71a2ea800559f591ef9e9e6495a206c4f7c - React-defaultsnativemodule: 7883e6ef963ee6a6346eb43b73967919029f1033 - React-domnativemodule: eafabfac38103187bfd0bc8b4c64d1ebc35444c2 - React-Fabric: 72038b554a0e4791432a7c161e6ea52bdc854d7c - React-FabricComponents: 47e25c62a3fdc4d6630dc44f41c5552a80202a2a - React-FabricImage: 95c3a49e22cc8c5c33cf282287c73cf261edd104 - React-featureflags: 05545ed41078babfec20095fd7825f029709cde6 - React-featureflagsnativemodule: 75f805793e3feb0dea7055e92540624965543de9 - React-graphics: e33b1bff03c62a7293991bcc28ceb946740bae0f - React-hermes: c07c778ab9cb80a943116ef4574087e8570206cf - React-idlecallbacksnativemodule: f800ae00e3427cbf0ae1f7d880ccbbe8ffea5e16 - React-ImageManager: ca548168b2efedd1e017dc6d715f5e0028eb446a - React-jserrorhandler: 8a7c11b5691b798c9b25b8e4cfbda02742828602 - React-jsi: 1c901c8f6e8d4555b6a5747c5a5b7c7cf757da71 - React-jsiexecutor: d59faf2904bb0bd1daec18bc59731623e79a74eb - React-jsinspector: eb7486a37a90aa2e896d7c67d7ea04c1c466b4ef - React-jsitracing: a417d71b554e891ccab72510f9482e6c53d0b09a - React-logger: 5320a2acb25baa566cda59b611231929dd3ed7fc - React-Mapbuffer: 9af3695e354816d30d4429992714e0c8cefac25f - React-microtasksnativemodule: ab4c1c9d4841e71a4116f167a4cd3f8dc1ab50ba + RCTDeprecation: 5f638f65935e273753b1f31a365db6a8d6dc53b5 + RCTRequired: 8b46a520ea9071e2bc47d474aa9ca31b4a935bd8 + RCTTypeSafety: cc4740278c2a52cbf740592b0a0a40df1587c9ab + React: 6393ae1807614f017a84805bf2417e3497f518a6 + React-callinvoker: c34f666f551f05a325b87e7e3e6df0e082fa3d99 + React-Core: fc07a4b69a963880b25142c51178f4cb75628c7d + React-CoreModules: 94d39315cfa791f6c477712fea47c34f8ecb26c6 + React-cxxreact: 628c28cdb3fdef93ee3bfc2bec8e2d776e81ae49 + React-debug: c1b10e5982b961738eab5b1d66fa31572ca28b5e + React-defaultsnativemodule: dd13932a4a4b0f1d556c9a4e76cc00fa05207126 + React-domnativemodule: 44bd8074cffa6a8ed298a45e2f7d1b519fb15a23 + React-Fabric: 5efe9e2171352089ded33d73e177345e8eb74e00 + React-FabricComponents: 359ea9205fc116ec52c90186ace048391ba477f7 + React-FabricImage: e536ff5e1c1f081dc5953696287e33d4d3b543bd + React-featureflags: 1e3a098a98c63a339a8b5ef4014ba4c4b43fb1f6 + React-featureflagsnativemodule: c926c24fda31daab491e25f4003413b49a5dfaec + React-graphics: 1476634e2deaf13dad3ab7ddfa33f78933445e07 + React-hermes: af1b3d79491295abc9d1b11f84e77d5dc00095b6 + React-idlecallbacksnativemodule: 43fc456e78c7dd7a342a9f185ef7c931d8c44ab0 + React-ImageManager: bd97427edf2df85e7e162e2161c9c981a6185915 + React-jserrorhandler: 44ebfb576a9ce098205b246a4bb81c9ad55ffbb6 + React-jsi: e9c3019e00db5d144e0a660616a52a605e12c39a + React-jsiexecutor: 3ed70a394b76f33e6c4ec4b382a457df7309d96c + React-jsinspector: d0c7ef76573b8e2b362ebc570aecd43b0c88a282 + React-jsinspectortracing: 551b7981d2a0b6a7829fd8c8c310ca51b5b323f8 + React-jsitooling: 5d06fc7c61ac1d260a553a9a9cffcd78865e430c + React-jsitracing: 2ecfa3ccc58e876a8c4f76a2cbdd920fc1ccfbb0 + React-logger: e6e6164f1753e46d1b7e2c8f0949cd7937eaf31b + React-Mapbuffer: a83853bd80bb31a4451e64af91a86c02f7df4f80 + React-microtasksnativemodule: 964a2c1213bb39fa5cc8d5ee4f55846d67747f32 react-native-appsflyer: 2cc1f96348065fc23e976fc7a27e371789fb349e - react-native-compat: 17cc6a63937e3fc291b92250f56ce5e9c0c3aa53 + react-native-compat: 1e09d1a14355b6a0383fb371fe98509fa8c3f4fd react-native-context-menu-view: dcec18eb8882e20596dbb75802e7d19cb87dac02 react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 - react-native-image-picker: 2064a7a43d1e204c3b42ce6d2208df32e881fc9d - react-native-keyboard-controller: 2fdaf70d94da51a982c702720fdc7b051db8140b + react-native-image-picker: 75cc6db21e264e573456725c71ad21828a82c455 + react-native-keyboard-controller: 8698f5ff79ab0a4a8f0259a40b4c3c7f23cae7fd react-native-mmkv: dea675cf9697ad35940f1687e98e133e1358ef9f react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac react-native-onesignal: 33ade92bd91578374c31c5a5a91f45f49c2d6614 - react-native-pager-view: b8b7c09ce10ed0ca632689570aa1020271b44156 + react-native-pager-view: d6f91626b36fcca51d28a9c5ec109a9309242089 react-native-passkey: 69bede03f6bb35fad8117cad73155231cc31066c react-native-restart: 7595693413fe3ca15893702f2c8306c62a708162 - react-native-safe-area-context: 04803a01f39f31cc6605a5531280b477b48f8a88 - react-native-skia: 628fde753bb219b462d97d761f028076d289b1a1 - react-native-slider: bf50824dd00db1e7b66eaec12598883cea3c79e7 - react-native-video: 85e6571bb240a1f084cc7fc9b2f2f0c27b51b622 - react-native-webview: 6c92617eff2519f6359440508c603d3ecda50984 + react-native-safe-area-context: 8870dc3e45c8d241336cd8ee3fa3fc76f3a040ac + react-native-skia: eee8f5b2560445bea34c27bb9f0b2dc7adb04e53 + react-native-slider: d3ddeb61d8c4c4d99f19194338d8d2c33957e717 + react-native-video: a6a2ad5d778133dee45875faf44c6ce0d61cac0e + react-native-webview: 94294e5a5d8cf4f53793aee7ef69ebd14c79e397 react-native-widgetkit: efb6680df237463bbe1be3a4d1a1578a1b0bb08f - React-nativeconfig: 75658bde8f977492f668e94ae8eb9c0dfef7ed94 - React-NativeModulesApple: 1bd9fa09c40204ac489acbe7da239efa4aa47244 - React-perflogger: a0f49e229d1252d683102df60d2392229ece5837 - React-performancetimeline: d0fb47dfc5d55ec6b7c4a79c83912ff59bb8df51 - React-RCTActionSheet: 150cfe1df4275db2251a2a4a1b22be3294e94ef7 - React-RCTAnimation: 67a303df28e0981f58a26968b3c15252a6b277b8 - React-RCTAppDelegate: 5ffb34a2bc043815e470d64feb8510d8930d7e40 - React-RCTBlob: 2ccb60fd765ed41292c495e4374eaf7c23a3c81c - React-RCTFabric: ab786f111eb621bcf1e5b3e6f91eaecaf99eadeb - React-RCTFBReactNativeSpec: e943ce6a5952941d730758fcd5b4a603c6b5ea0a - React-RCTImage: f235db428b4c98cbb1ad6304f826695f19e82c57 - React-RCTLinking: 523a01769de55660743d6332157dfb4dbad817b8 - React-RCTNetwork: d99ee5bf1f15ad8521d30c9da477906d7fb00110 - React-RCTSettings: 4165a44c6f51787980634bd522f3b379ee8531a6 - React-RCTText: 462ab8d4f2f180be83e3983307ce5ef5fa58210e - React-RCTVibration: ca784cb7e30c21852d4b78b1621bfa11940ab061 - React-rendererconsistency: 28f87593201bca785e0bbdc94bff4d92ee2d32ee - React-rendererdebug: c3121b6c4f0873ad2cf1bb63947c0a8a0ae8a1ab - React-rncore: df9c0360d3f28371a103921890e20c309c906407 - React-RuntimeApple: 4b8060742249e0ede1e186e3df656cea119ac9da - React-RuntimeCore: c622d85e3fd6f60ccb7f54bb0d583a85dc1dff25 - React-runtimeexecutor: e6e7af01f9989f931289250ee9060604bc0f0144 - React-RuntimeHermes: 5062f63b39a375c63909c16b33bbc24e2c8e7cf6 - React-runtimescheduler: b6788737ee06eec93c63a2cda4b20c5ca916f5b5 - React-timing: dfac299d2afa69272d469c8e5fd4d4328fe41d1e - React-utils: 280b2ed61cfb45ad94fc8e86d7be402689714962 - ReactAppDependencyProvider: 8955603808eb24bbfde3511d2bcc8362d1d5860e - ReactCodegen: 1e8981b03b6389301f819833f8b3b498d9d4da8d - ReactCommon: ad39e4549e2920b3b065a28603921a75619d0639 + React-NativeModulesApple: d2b9bd7d55dfd864e56c76592777ad32e8ab1f3d + React-oscompat: 0592889a9fcf0eacb205532028e4a364e22907dd + React-perflogger: 634408a9a0f5753faa577dfa81bc009edca01062 + React-performancetimeline: b58a6e65c9fbe1aa02b97152edd6ad5275aef36b + React-RCTActionSheet: ce67bdc050cc1d9ef673c7a93e9799288a183f24 + React-RCTAnimation: 12193c2092a78012c7f77457806dcc822cc40d2c + React-RCTAppDelegate: b0a8aa38e4791915673a7a3ae80b2840a81ec255 + React-RCTBlob: 923cf9b0098b9a641cb1e454c30a444d9d3cda70 + React-RCTFabric: 3f2f2980ad1d426f3a03c9183521b835e0bbbe83 + React-RCTFBReactNativeSpec: b7671d70d65f61326805725b24c7855aab0befb2 + React-RCTImage: 580a5d0a6fdf9b69629d0582e5fb5a173e152099 + React-RCTLinking: 4ed7c5667709099bfd6b2b6246b1dfd79c89f7cb + React-RCTNetwork: 06a22dd0088392694df4fd098634811aa0b3e166 + React-RCTRuntime: 38591d6246389f4f8b93f0f94f565f8448805581 + React-RCTSettings: 9dbf433f302c8ebe43b280453e74624098fbc706 + React-RCTText: 92fcd78d6c44dbe64d147bb63f53698bcba7c971 + React-RCTVibration: 513659394c92491e6c749e981424f6e1e0abdb3c + React-rendererconsistency: c9c28e3b0834d9be2e6aa0ba2d1fd77c76441658 + React-renderercss: 700c57db7fcb36a1e5a9b3645f4cc22f4de43899 + React-rendererdebug: 8ce5f50fd01160e1d1bfc9ec34dac0ca5411afc2 + React-rncore: 289894dda4ebcca06104070f1a9c9283f37dd123 + React-RuntimeApple: 5e5315e698c4fc4e5a3e6b610e4e0ae135f3718c + React-RuntimeCore: 23b6e0e4e2b1cb8a81a14db68814ade8998a5fb0 + React-runtimeexecutor: ebfd71307b3166c73ac0c441c1ea42e0f17f821d + React-RuntimeHermes: baef54b36a6623ea8cd7442027744b08ad06d01b + React-runtimescheduler: 4d9a1afaa16d7dd11a909a9103b18b63995c5683 + React-timing: 0f749e1c5ca1147b699b25ec79003950e6366056 + React-utils: 52ce70a20366bb7f1b416c57281f40ffafeb8a8a + ReactAppDependencyProvider: c42e7abdd2228ae583bdabc3dcd8e5cda6bef944 + ReactCodegen: 0851536ada69d85536f54a7239956a50b6dcd42e + ReactCommon: 3dbed0a44e9e5d7b77b0f35983633aa5d33c9694 ReactNativePerformance: ab7dee4c4862623d72c1530a9fc71b55458edf71 RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 - RNBootSplash: 66c8458007bda40cc25a3f25e4326244a71d9a73 - RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c - RNCMaskedView: 9953b54e4f488389ec45d8befd8687d71e7c121e - RNDateTimePicker: 40ffda97d071a98a10fdca4fa97e3977102ccd14 + RNBootSplash: 2c6b226e3ad3c97d16b6d53bd75d0cd281646bfb + RNCAsyncStorage: addfc2cb6511dbe199c56c6b26ede383b6c38919 + RNCMaskedView: 42f3684c136239957b410dbfa81978b25f2c0e18 + RNDateTimePicker: 279bad2682d9ebdd4151cb71d88f3b460f818fc8 RNDeviceInfo: bf8a32acbcb875f568217285d1793b0e8588c974 RNFastImage: 074e3c1a0d65e2971f28299a85d0155c3b2c948e RNFBApp: 4122dd41d8d7ff017b6ecf777a6224f5b349ca04 RNFBAuth: 1632cefd787a43ba952fa52ff016e7b69fe355cb RNFBFirestore: 5f110e37b7f7f3d6e03c85044dd4cf3ebacec38b - RNFlashList: 4afe189d83616f240be187f717320ad966f6024f - RNGestureHandler: ab4058d59c000e7df387ad9a973e93f7e40de331 + RNFlashList: 7c43eac420e04bfa7798d40c7246c7067e8a4c2c + RNGestureHandler: eb5ad44465a546182d05aebae304e45c881d2f22 RNImageColors: 9ac05083b52d5c350e6972650ae3ba0e556466c1 RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 RNPermissions: 87aac13521bea6dcb6dfd60b03ac69741ccef2b4 RNQrGenerator: ac6a6c766e80dd3625038929ed2b13e2f3edcafb - RNReanimated: 9f96886ec1e1772ed7462cc80da82b7bf1ba984d - RNScreens: 567d3119f7d5d1041090eab25027667c505e4e75 - RNSVG: 4cbae6c6f0ef2b0aa277c5fce8447d3e4cd97cd0 - SDWebImage: f29024626962457f3470184232766516dee8dfea + RNReanimated: ad46062e119fcf93712dfe9dcf72b45ea16892e4 + RNScreens: edd4795b025d94f879e20cc346b844176d938f0c + RNSVG: 204b068da3a7416d22840cd3233bcf745443a455 + SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 sparkfabrik-react-native-idfa-aaid: 1b72a6264a2175473e309ffa6434db87c58af264 UIImageColors: d2ef3b0877d203cbb06489eeb78ea8b7788caabe - Yoga: fdc0542faa3ba87e56f2030b3f3f2e21bc3ba01c + Yoga: bfcce202dba74007f8974ee9c5f903a9a286c445 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 -PODFILE CHECKSUM: 5438afe50eaeebf354579fd9c40fb3c93d56ce9b +PODFILE CHECKSUM: 9a816e7213ef1e27594bb84425b559319371f327 -COCOAPODS: 1.14.3 +COCOAPODS: 1.16.2 diff --git a/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj b/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj index 6cdc18319f2..3373460591b 100644 --- a/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj @@ -10,13 +10,14 @@ 0013F5F72C93399400D6EF09 /* ProtectionInfo.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0013F5F62C93399400D6EF09 /* ProtectionInfo.graphql.swift */; }; 00265F792C933CE300A5DA57 /* ProtectionResult.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00265F772C933CE300A5DA57 /* ProtectionResult.graphql.swift */; }; 00265F7A2C933CE300A5DA57 /* ProtectionAttackType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00265F782C933CE300A5DA57 /* ProtectionAttackType.graphql.swift */; }; - 0094F4FBBC1C0A2FFABF7157 /* TokenProject.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4981A14906A35E8A0B7F9457 /* TokenProject.graphql.swift */; }; 00E356F31AD99517003FC87E /* UniswapTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* UniswapTests.m */; }; - 03291E0DA448AF438F97EA5D /* DescriptionTranslations.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E1F288B7EDDE906C4C00C46 /* DescriptionTranslations.graphql.swift */; }; + 02B7B534DFEC9DA0F7F06B8D /* TokenBalanceQuantityParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0A1F5F6FC3E1141E228B7D /* TokenBalanceQuantityParts.graphql.swift */; }; 037C5AAA2C04970B00B1D808 /* CopyIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037C5AA92C04970B00B1D808 /* CopyIcon.swift */; }; + 03AE019583139330A3E408AB /* SwapOrderDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F4E5E9341BE66984935185 /* SwapOrderDetails.graphql.swift */; }; 03C788232C10E7390011E5DC /* ActionButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C788222C10E7390011E5DC /* ActionButtons.swift */; }; 03D2F3182C218D390030D987 /* RelativeOffsetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D2F3172C218D380030D987 /* RelativeOffsetView.swift */; }; - 03E3515E38E247F459218CAA /* SwapOrderDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = C955CE2592785712A11892DE /* SwapOrderDetails.graphql.swift */; }; + 03EEEDA1A3D5EF23C0D14B08 /* OnRampTransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87CF9B03AE29A19C5B0F4E35 /* OnRampTransactionDetails.graphql.swift */; }; + 06C9F16E22B8B64855980A69 /* TokenMarketParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55081ADF188C967FC1ECD289 /* TokenMarketParts.graphql.swift */; }; 0703EE032A5734A600AED1DA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0703EE022A5734A600AED1DA /* UserDefaults.swift */; }; 0703EE052A57351800AED1DA /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072F6C372A44BECC00DA720A /* Logging.swift */; }; 072E238E2A44D5BD006AD6C9 /* WidgetsCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 072E23862A44D5BC006AD6C9 /* WidgetsCore.framework */; }; @@ -109,6 +110,7 @@ 0743223D2A83E3CA00F8518D /* IAmount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074321E92A83E3C900F8518D /* IAmount.graphql.swift */; }; 0743223E2A83E3CA00F8518D /* IContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074321EA2A83E3C900F8518D /* IContract.graphql.swift */; }; 074322402A841BBD00F8518D /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0743223F2A841BBD00F8518D /* Constants.swift */; }; + 0764D0BF05C44615BDD9AD65 /* NftBalanceAssetInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 696F6C5220D0072E380E198F /* NftBalanceAssetInput.graphql.swift */; }; 0767E0382A65C8330042ADA2 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0767E0372A65C8330042ADA2 /* Colors.swift */; }; 0767E03B2A65D2550042ADA2 /* Styling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0767E03A2A65D2550042ADA2 /* Styling.swift */; }; 077E60392A85587800ABC4B9 /* TokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077E60382A85587800ABC4B9 /* TokensQuery.graphql.swift */; }; @@ -123,10 +125,10 @@ 07F136422A5763480067004F /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07F136412A5763480067004F /* Network.swift */; }; 07F5CF712A6AD97D00C648A5 /* Chart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07F5CF702A6AD97D00C648A5 /* Chart.swift */; }; 07F5CF752A7020FD00C648A5 /* Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07F5CF742A7020FD00C648A5 /* Format.swift */; }; - 09E8C497051C37FE604FD40E /* OnRampTransactionsAuth.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 315B8A905FA2C60C053F138B /* OnRampTransactionsAuth.graphql.swift */; }; - 09F9DEB33392F3051BEA8D52 /* TokenFeeDataParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018603D0FA4D05DBF16F1441 /* TokenFeeDataParts.graphql.swift */; }; - 0C6FC49E0FC9BCB2DF5B627E /* TokenSortableField.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32012AC135EBD991C8F02F92 /* TokenSortableField.graphql.swift */; }; - 0CEBEB8BE3AB95C3F584F6CE /* Image.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ED613D63FE9C56D9270517 /* Image.graphql.swift */; }; + 092E5F0FE7DE6113285AF5E3 /* NftsTabQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3BCD49511DE84892B34ED0 /* NftsTabQuery.graphql.swift */; }; + 0AC6A2E1C5837AAD902742DA /* TokenBalanceMainParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD452B34344D670BA23EA331 /* TokenBalanceMainParts.graphql.swift */; }; + 0AD9B4E176D2E9FE86E6ED21 /* AssetActivity.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05C66C49AF2B78703FCE4C7C /* AssetActivity.graphql.swift */; }; + 0D87DBFD19D5A6DF176C0AB7 /* MobileSchema.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87442A651C068E552BEA89C6 /* MobileSchema.graphql.swift */; }; 0DB282262CDADB260014CF77 /* EmbeddedWallet.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB282242CDADB260014CF77 /* EmbeddedWallet.m */; }; 0DB282272CDADB260014CF77 /* EmbeddedWallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB282252CDADB260014CF77 /* EmbeddedWallet.swift */; }; 0DC6ADF02B1E2C100092909C /* PortfolioValueModifier.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC6ADEF2B1E2C0F0092909C /* PortfolioValueModifier.graphql.swift */; }; @@ -134,61 +136,56 @@ 0DE251472C13B69D005F47F9 /* OnRampTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DE251442C13B69D005F47F9 /* OnRampTransfer.graphql.swift */; }; 0DE251482C13B69D005F47F9 /* OnRampServiceProvider.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DE251452C13B69D005F47F9 /* OnRampServiceProvider.graphql.swift */; }; 0DE251492C13B69D005F47F9 /* OnRampTransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DE251462C13B69D005F47F9 /* OnRampTransactionDetails.graphql.swift */; }; - 0F282C26344F1AE3622232B0 /* OffRampTransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 396B6AB3BB41898B56EB3828 /* OffRampTransactionDetails.graphql.swift */; }; - 126CC4BC99F3F15CC1E2F1C3 /* NftAssetTrait.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70851E3D1A4B296A1CE22A20 /* NftAssetTrait.graphql.swift */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 143C638C2CCAC689371BFC93 /* NftActivityType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D95FE36D2667E070345CDD0 /* NftActivityType.graphql.swift */; }; 1440B371A1C9A42F3E91DAAE /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DF5F26A06553EFDD4D99214 /* ExpoModulesProvider.swift */; }; - 171DD1C966C7FBF9DD68CFAC /* TopTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04E5C2900254E7BA7F10CE51 /* TopTokensQuery.graphql.swift */; }; - 1B59968CF49C45B127E9C768 /* NftOrder.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E3A78386B3B779B688021FF /* NftOrder.graphql.swift */; }; - 1BC3A49161EB906542F8E23B /* ProtectionAttackType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E034582FBD2C374CEF808E /* ProtectionAttackType.graphql.swift */; }; - 1C3559D557BC09C709104802 /* NftsTabQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 730CA356D6E1D498CC0C1472 /* NftsTabQuery.graphql.swift */; }; - 1CE49305114BF4792290DDC6 /* AmountChange.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = E63EA0C75D8011BCC182492C /* AmountChange.graphql.swift */; }; - 1DC34AE3E11264475E8B9F62 /* NftApproval.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E32BBB65A6DD9CFF608C8E8 /* NftApproval.graphql.swift */; }; - 1E2AF2C38C8FBEB2A95B644E /* OffRampTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D5E6A07F404974045BD525 /* OffRampTransfer.graphql.swift */; }; - 252BD40CB9B46FE47B2440B1 /* NftCollection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63ACFF2A1D5AE3498731AC69 /* NftCollection.graphql.swift */; }; - 257C7A9F2C4AC3C99C6B7348 /* TransactionHistoryUpdaterQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3D7A923E5E1FB504E1F64D /* TransactionHistoryUpdaterQuery.graphql.swift */; }; - 2B12DFE797E2CBF314565D81 /* TokenProtectionInfoParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB4A40E30E1D985519DB3CF /* TokenProtectionInfoParts.graphql.swift */; }; - 2B422D705C68BB51FBDA9895 /* NFTItemScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207F4DF7664454C31DE069F8 /* NFTItemScreenQuery.graphql.swift */; }; - 2E05037B51AF526A47701B09 /* TransactionDirection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2FBE9F353D3753FB94B8BF /* TransactionDirection.graphql.swift */; }; - 302E24504C4FCE674CF95984 /* TokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F596250F55A3D6E9E1F499 /* TokenParts.graphql.swift */; }; - 30872BFB39EEC66944E3EFD7 /* MobileSchema.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4E0C448C5D45BF1071FA458 /* MobileSchema.graphql.swift */; }; - 31661D70B58410EA030E2C53 /* TimestampedAmount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0DB67D6C438F623976727E9 /* TimestampedAmount.graphql.swift */; }; - 326CFFDBB89D95FA4CB95EBB /* HistoryDuration.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF42F88002431BFDC9FCA9F /* HistoryDuration.graphql.swift */; }; - 3361FE5837059AEF4AA877BE /* TransactionListQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 224CA82F1871016F4173F69E /* TransactionListQuery.graphql.swift */; }; - 33928A7366AFE7F892CDC89F /* NftBalanceConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FC7B80EC0E0D2134B67575B /* NftBalanceConnection.graphql.swift */; }; - 35B8176433A98BA798BBEE79 /* PageInfo.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = E475EBABF7EA310435E2F19C /* PageInfo.graphql.swift */; }; - 36E601F269D40A67FC353947 /* AssetChange.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F60B2B54796DC978765C99 /* AssetChange.graphql.swift */; }; - 3E338E14E33C721DAB708664 /* NftActivityFilterInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E2ECCEFBC3BE8D13BE6CAB /* NftActivityFilterInput.graphql.swift */; }; - 3FE25F715DFFD1D03DCDA57D /* NftContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B9EB43501127BE1B14F1A5 /* NftContract.graphql.swift */; }; + 147F4DFD43CE86324A066003 /* FeedTransactionListQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EFADDBB38BB9261933C1349 /* FeedTransactionListQuery.graphql.swift */; }; + 15193A0A3CE80FF72AAB54B4 /* SafetyLevel.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22F8093091A9E5FC0D6284BA /* SafetyLevel.graphql.swift */; }; + 15A72E27235628C56431EC98 /* FeeData.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE1CB436B299B0349BA9957 /* FeeData.graphql.swift */; }; + 15F6E43DA2BD9A8A681EC70C /* TokenTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BEBBC668E69EE3A1FC6AB05 /* TokenTransfer.graphql.swift */; }; + 16506815CDEE17670D3DD363 /* TimestampedAmount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31CBA4397C44A04E218AEF28 /* TimestampedAmount.graphql.swift */; }; + 19BC2371F6BE6CAD5218AA53 /* NftActivityConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE0699A65BEEEE6E9A6B03F /* NftActivityConnection.graphql.swift */; }; + 1C84E14C7F52CF6C9A6D2930 /* TokenProjectDescriptionQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1276D33B06E840B8E6839F39 /* TokenProjectDescriptionQuery.graphql.swift */; }; + 1CA252C75CD5291E2A0B308B /* NftCollection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25620D6E0E297C56198F190 /* NftCollection.graphql.swift */; }; + 206E0025B8FCE0EA96AC744A /* NftCollectionScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA3BFDC64C5E0A058877A348 /* NftCollectionScreenQuery.graphql.swift */; }; + 23B195B262495EABE4A6CDF4 /* HistoryDuration.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1053483279043EED7612BE90 /* HistoryDuration.graphql.swift */; }; + 24B4011ADC672F470EC515AC /* BridgedWithdrawalInfo.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC62CDEEBE5712219A457C7 /* BridgedWithdrawalInfo.graphql.swift */; }; + 252A28057D1D481D14D2F5E5 /* TransactionDirection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D494D944670E557175A694 /* TransactionDirection.graphql.swift */; }; + 2925C6E0262B95C5EA0C2AE7 /* TransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55240C84512D3851C8B9F027 /* TransactionDetails.graphql.swift */; }; + 2B2738BAB9E906B561E0D4D6 /* TokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76D1207400E308430A837F /* TokenParts.graphql.swift */; }; + 2C9935142A9582467B5B5FC5 /* ProtectionInfo.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD12FD368B3A96F323444D8E /* ProtectionInfo.graphql.swift */; }; + 2D73D2D80BC8C455DB35CE57 /* TokenApproval.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD56CBC543897BE8E37CC2C1 /* TokenApproval.graphql.swift */; }; + 2FC53D1C58218F0BF1D4E5F3 /* IContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9189C818AFF8E988F861D0 /* IContract.graphql.swift */; }; + 31A4EC91F1924E25AECA2F4E /* NftCollectionMarket.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F286C514E849B29F5235310A /* NftCollectionMarket.graphql.swift */; }; + 3389D45727F15B8E4F59B99F /* NftApproval.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD111A711D83D1D82E4025A9 /* NftApproval.graphql.swift */; }; + 34610491FF9A452F4F806157 /* TokenPriceHistoryQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9602E2A290179F8741EDD35A /* TokenPriceHistoryQuery.graphql.swift */; }; + 3720F641F397A2819500B1B1 /* SwapOrderStatus.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0359DDEA2E2E17759FCC5CF /* SwapOrderStatus.graphql.swift */; }; + 391FD815120230BDAF53F6F0 /* BlockaidFees.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF9AEC00868BFD7CEBF202C /* BlockaidFees.graphql.swift */; }; + 39DEA445993253BCC1201199 /* NftApproveForAll.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1B893C0275DAAA156B0108 /* NftApproveForAll.graphql.swift */; }; + 4260B9F719F8E25DFBF99D73 /* OnRampTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E5D1A04B42B25218C53C107 /* OnRampTransfer.graphql.swift */; }; + 438115E2759B1194751A8021 /* NftContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A243177E9FA92A0524A222 /* NftContract.graphql.swift */; }; + 45FFF7DF2E8C2A8100362570 /* SilentPushEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 45FFF7DE2E8C2A6400362570 /* SilentPushEventEmitter.m */; }; + 45FFF7E12E8C2E6900362570 /* SilentPushEventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FFF7E02E8C2E6100362570 /* SilentPushEventEmitter.swift */; }; 463BA791004B1B7AC1773914 /* Pods_Uniswap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2226DF79BEAFECEE11A51347 /* Pods_Uniswap.framework */; }; - 4917B04DB81579CF4243A1DA /* Chain.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8990A17F489F284E0DB1B3 /* Chain.graphql.swift */; }; - 4BB9E218D89B8AF5E4E27CCB /* NftOrderEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BD6C776029FAF4FC711DE7 /* NftOrderEdge.graphql.swift */; }; - 4C187202229BDC4D8898E067 /* Dimensions.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B1C71AED738D17898103A6 /* Dimensions.graphql.swift */; }; - 4EF8D293BB1EBCFBFC65A330 /* TokenTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA4E7C052A6C4AC3A9DCDA6 /* TokenTransfer.graphql.swift */; }; - 4FED6AAF896BA371C56D88B1 /* Portfolio.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1849039FD4A210DA990363C8 /* Portfolio.graphql.swift */; }; - 50C89DFAF2DBC80D6343B164 /* NftAssetTraitInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 556339705BD103365D4ED07B /* NftAssetTraitInput.graphql.swift */; }; - 553E6B467BEE49C9984691C5 /* OnRampTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2139ABF56EA067ED792A48C9 /* OnRampTransfer.graphql.swift */; }; - 5804A1B1BB144D9EFBBE177D /* NftAssetEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF7C8888BE6AFA2F22889FE /* NftAssetEdge.graphql.swift */; }; + 47797825D18637A2FE57AC1F /* TokenProtectionInfoParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835D9DF76533D22EDF0E9D67 /* TokenProtectionInfoParts.graphql.swift */; }; + 4DB6B64CFF3B00FCF0258336 /* NftMarketplace.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FB27D93A9468E6DB47231C /* NftMarketplace.graphql.swift */; }; + 4DB6D0FB611F6C68EADFB948 /* TokenProjectMarket.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0505DB30407C221B6A8491 /* TokenProjectMarket.graphql.swift */; }; + 4DB88A2CBFF5FF356CA757F0 /* ApplicationContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59DD4D834D7B42A6D02F4F12 /* ApplicationContract.graphql.swift */; }; + 4FD78AA88D2EB8E22144BCDF /* NftAssetTraitInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1731869775D16A9EAB475C /* NftAssetTraitInput.graphql.swift */; }; + 51362EC6F0E6D8928C067F5E /* NftBalancesFilterInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1810C60B56CC74150C868801 /* NftBalancesFilterInput.graphql.swift */; }; + 5551DDD6A8B9B28E03459816 /* TokenFeeDataParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1166529407CA13E4D4F24F12 /* TokenFeeDataParts.graphql.swift */; }; + 5676CCD265609B71D3B1DB7C /* NftCollectionEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70593F94C8B315F5304B9BE /* NftCollectionEdge.graphql.swift */; }; + 57ECD348EA564F0CBF715E9E /* PortfolioBalancesQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0A53C3A09AD259B9A8EDBDD /* PortfolioBalancesQuery.graphql.swift */; }; 5B4398EC2DD3B22C00F6BE08 /* PrivateKeyDisplayManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B4398E82DD3B22C00F6BE08 /* PrivateKeyDisplayManager.m */; }; 5B4398ED2DD3B22C00F6BE08 /* PrivateKeyDisplayManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4398E92DD3B22C00F6BE08 /* PrivateKeyDisplayManager.swift */; }; 5B4398EE2DD3B22C00F6BE08 /* PrivateKeyDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4398EA2DD3B22C00F6BE08 /* PrivateKeyDisplayView.swift */; }; 5B4CEC5F2DD65DD4009F082B /* CopyIconOutline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4CEC5E2DD65DD4009F082B /* CopyIconOutline.swift */; }; - 5C3958DA046B5A84FE90C1BD /* FeeData.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17819A49DE69723EC60D9D72 /* FeeData.graphql.swift */; }; - 5D06BAB366D14A5AFE2355E8 /* TransactionType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F2FFD11FB5E462CC454A15 /* TransactionType.graphql.swift */; }; 5E5E0A632D380F5800E166AA /* Env.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5E0A622D380F5700E166AA /* Env.swift */; }; 5EFB78362B1E585000E77EAC /* ConvertQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFB78352B1E585000E77EAC /* ConvertQuery.graphql.swift */; }; - 614FCC3D8E9AA8175E950726 /* Currency.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25B4C7C7FEF32EAF10D030F9 /* Currency.graphql.swift */; }; - 61988A922656857278F7CBF9 /* TokenBalanceMainParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 137696908D703632CEE3AB28 /* TokenBalanceMainParts.graphql.swift */; }; - 62B32E9623DA0E939F062033 /* TokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3C3F0403FAEFDD632AC6CCE /* TokensQuery.graphql.swift */; }; - 63BDDE4499AC6B2375C9F795 /* FavoriteTokenCardQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F0EEAC1DADE827AE38EB8A6 /* FavoriteTokenCardQuery.graphql.swift */; }; - 648B919F00D069DE1F0040F6 /* NftMarketplace.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 600250B00B6C02AAB063818B /* NftMarketplace.graphql.swift */; }; + 6250D4ACD5696D845FD83DFD /* HomeScreenTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7FF51A507BF788E64E3EFE /* HomeScreenTokensQuery.graphql.swift */; }; + 63FBF9C23ED568816590F093 /* ActivityDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075E4C1DFAA00EE5B3CE792D /* ActivityDetails.graphql.swift */; }; 649A7A782D9AE70B00B53589 /* KeychainUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649A7A772D9AE70B00B53589 /* KeychainUtils.swift */; }; 649A7A792D9AE70B00B53589 /* KeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649A7A762D9AE70B00B53589 /* KeychainConstants.swift */; }; - 66D2765A00D8CE3136D28F1F /* SafetyLevel.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ACFA5B4204741038C986C5D /* SafetyLevel.graphql.swift */; }; - 677FC15A05D9BD23930FAC95 /* TokenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74885865ED8CFEA875E40916 /* TokenQuery.graphql.swift */; }; - 6A60BDC9D46A710D871DEC6E /* NftProfile.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB35E518AD7FBFEBEA9CAB95 /* NftProfile.graphql.swift */; }; + 6882C4768011FDD4D746BDDB /* TokenBasicInfoParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = D13E592852F52B2855F05A5E /* TokenBasicInfoParts.graphql.swift */; }; 6BC7D07E2B5FF02400617C95 /* ScantasticEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BC7D07B2B5FF02400617C95 /* ScantasticEncryption.m */; }; 6BC7D07F2B5FF02400617C95 /* ScantasticEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC7D07C2B5FF02400617C95 /* ScantasticEncryption.swift */; }; 6BC7D0802B5FF02400617C95 /* EncryptionUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC7D07D2B5FF02400617C95 /* EncryptionUtils.swift */; }; @@ -198,21 +195,26 @@ 6CA91BE12A95226200C4063E /* RNCloudStorageBackupsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CA91BDE2A95226200C4063E /* RNCloudStorageBackupsManager.m */; }; 6CA91BE22A95226200C4063E /* EncryptionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA91BDF2A95226200C4063E /* EncryptionHelper.swift */; }; 6CA91BE32A95226200C4063E /* RNCloudStorageBackupsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA91BE02A95226200C4063E /* RNCloudStorageBackupsManager.swift */; }; + 70CD2D0665E5AC6A0DF6F697 /* NFTItemScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AB6B21A1BFB3A18982B47E /* NFTItemScreenQuery.graphql.swift */; }; 70EB8338CA39744B7DBD553E /* Pods_WidgetIntentExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1064E23E366D0C2C2B20C30E /* Pods_WidgetIntentExtension.framework */; }; - 71CF37F19F1C138B57CC135C /* TopTokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5640432978873FB030467750 /* TopTokenParts.graphql.swift */; }; + 71954B20E4341CEC36203F87 /* NftActivityEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043C7D80D6722AFCF1715D07 /* NftActivityEdge.graphql.swift */; }; + 7700BED7CD7F52C83A3FF430 /* NftAssetEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFD4C1E5A4B30DE3CE02DB4 /* NftAssetEdge.graphql.swift */; }; 77CF6065C8A24FE48204A2C1 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF9176E944C84910B1C0B057 /* SplashScreen.storyboard */; }; - 7BE97D20155AE69FF36C4CB4 /* NftStandard.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B308EC3DB7C3C1DD5DDE03D /* NftStandard.graphql.swift */; }; - 818179F5724AA35E1B1D5A9C /* ProtectionInfo.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0702203B17BDD858CED28F8B /* ProtectionInfo.graphql.swift */; }; + 7B557C3224F990851430DBD2 /* Portfolio.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397E77A577E9952D2477020 /* Portfolio.graphql.swift */; }; + 7C886F9D4C02EF4E5F67B011 /* NftsQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE735ED4766E65C0D6808F9A /* NftsQuery.graphql.swift */; }; + 7E3412EB776E1F43D6904BA0 /* PageInfo.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97FBBE677B56FFE07052EF1D /* PageInfo.graphql.swift */; }; + 8117BFB02DCF0F315FD83E67 /* TokenBalance.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 181209B4CDE80BDC80891CFD /* TokenBalance.graphql.swift */; }; + 8141B38CCDB09F91180C0EBD /* NftCollectionConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D09FFE2B5E56CDCAB2F9455 /* NftCollectionConnection.graphql.swift */; }; + 81E55E8D3056E2378A332B31 /* TokenDetailsScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957A35D2DE7A140BA198A0F1 /* TokenDetailsScreenQuery.graphql.swift */; }; 8273FC23FB1AE47B80C5E09F /* Pods_OneSignalNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15092E550A1C78508ABA3280 /* Pods_OneSignalNotificationServiceExtension.framework */; }; 8385A47D3C765B841F450090 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26D739993D5C939C6FBB58A /* ExpoModulesProvider.swift */; }; - 8410B98A8D7974A941AAF299 /* WidgetTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA33683D2BA6BBE3251D67C /* WidgetTokensQuery.graphql.swift */; }; - 85ADD03923353DB3D6CD7301 /* SwapOrderStatus.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = D574AB5194929038A54B4D4B /* SwapOrderStatus.graphql.swift */; }; - 85D0E81B798D0F2D841386E9 /* NftCollectionMarket.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4F47D22B749A438B5BF674 /* NftCollectionMarket.graphql.swift */; }; - 869F3639FBC6D8156FFE3BD3 /* TokenProjectMarket.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B9A505509D9A85BE1DB7D05 /* TokenProjectMarket.graphql.swift */; }; - 8950F8354810AA673E1E35DA /* SchemaMetadata.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65195A1CC9894C6341385C3 /* SchemaMetadata.graphql.swift */; }; - 8ADDD2E2AB1FD9D1D9AF5627 /* BlockaidFees.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD35300F81746B2D4A23065A /* BlockaidFees.graphql.swift */; }; - 8CC06A8205186D0640F0BC55 /* NftAssetConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218B8AB05C80D919671D3970 /* NftAssetConnection.graphql.swift */; }; - 8CEA6459A5B739C3A0382000 /* TokenProjectsQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A49D011C60137D4034CAC4 /* TokenProjectsQuery.graphql.swift */; }; + 845327D60EFBB850189D6AB3 /* Amount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C3461E088654B80ABE3DE7 /* Amount.graphql.swift */; }; + 86038259E487A108DDC2948B /* Image.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9047B981FA322A727947F7 /* Image.graphql.swift */; }; + 8971E73E041A6283E2684481 /* TokenProject.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8856DFFC7FDC9BF105B0B4E3 /* TokenProject.graphql.swift */; }; + 8B2A92172EB3E78E00990413 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B2A92162EB3E78E00990413 /* AppDelegate.swift */; }; + 8BCFF1F648887F78894F1071 /* ContractInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AED050A208757F137DBDA57D /* ContractInput.graphql.swift */; }; + 8D85F349FEF81A52FB93EAAB /* AssetChange.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4C849A7805FCCEE8C8C3E6 /* AssetChange.graphql.swift */; }; + 8E6DA65AC5CAE9F9A67F9E38 /* TopTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243576FAC8D2801F9D99ECC9 /* TopTokensQuery.graphql.swift */; }; 8E89C3AE2AB8AAA400C84DE5 /* MnemonicConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E89C3A62AB8AAA400C84DE5 /* MnemonicConfirmationView.swift */; }; 8E89C3AF2AB8AAA400C84DE5 /* MnemonicDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E89C3A72AB8AAA400C84DE5 /* MnemonicDisplayView.swift */; }; 8E89C3B12AB8AAA400C84DE5 /* MnemonicConfirmationWordBankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E89C3A92AB8AAA400C84DE5 /* MnemonicConfirmationWordBankView.swift */; }; @@ -227,7 +229,7 @@ 8EBFB1552ABA6AA6006B32A8 /* PasteIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EBFB1542ABA6AA6006B32A8 /* PasteIcon.swift */; }; 8ED0562C2AA78E2C009BD5A2 /* ScrollFadeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ED0562B2AA78E2C009BD5A2 /* ScrollFadeExtensions.swift */; }; 8EE7C0582AFD7B2100E0D9CD /* DescriptionTranslations.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE7C0572AFD7B2100E0D9CD /* DescriptionTranslations.graphql.swift */; }; - 8FE0F30936E893297558F467 /* ProtectionResult.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0FC2D534CB82B151C7F9B7 /* ProtectionResult.graphql.swift */; }; + 9061ACE5AF99CEDE7BA51C94 /* NftProfile.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE2A9FBFB7F718246B668A60 /* NftProfile.graphql.swift */; }; 9127D1362CC2D3D00096F134 /* TokenBalanceMainParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9127D1342CC2D3D00096F134 /* TokenBalanceMainParts.graphql.swift */; }; 9127D1372CC2D3D00096F134 /* TokenBalanceQuantityParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9127D1352CC2D3D00096F134 /* TokenBalanceQuantityParts.graphql.swift */; }; 9173CEBC2D03C6F30036DA28 /* TokenBalanceParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9173CEBB2D03C6F30036DA28 /* TokenBalanceParts.graphql.swift */; }; @@ -243,15 +245,14 @@ 91D501792CDBEAE700B09B7F /* TopTokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91D5016E2CDBEAE700B09B7F /* TopTokenParts.graphql.swift */; }; 91D5017A2CDBEAE700B09B7F /* TokenFeeDataParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91D5016F2CDBEAE700B09B7F /* TokenFeeDataParts.graphql.swift */; }; 91D5017E2CDBEAF600B09B7F /* HomeScreenTokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91D5017C2CDBEAF600B09B7F /* HomeScreenTokenParts.graphql.swift */; }; - 9301D18644F3DFA9ABB8F0BE /* TokenApproval.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E975BEDBED5FE51D0DC6E96 /* TokenApproval.graphql.swift */; }; - 93566FBDE94E1A2D8CC5AB62 /* ConvertQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD3F0794908635CDFA7DAB6 /* ConvertQuery.graphql.swift */; }; - 9381B5EA5839DA17D07A45F0 /* TokenDetailsScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7A33933670CCAD9E62A1A80 /* TokenDetailsScreenQuery.graphql.swift */; }; - 93AEECDBDB160B5E0C9E3149 /* TokenProjectDescriptionQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF8B660E9C2C0D8DDF7588A /* TokenProjectDescriptionQuery.graphql.swift */; }; - 97CA219E1FFD832D8FA02C20 /* TransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABE51731EB853137302A7B0D /* TransactionDetails.graphql.swift */; }; - 9822D243ED2F5C350404A375 /* HomeScreenTokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27F8B915042BD8D67D9627EA /* HomeScreenTokenParts.graphql.swift */; }; - 9A3E861F5D7B0F2CB0EFA7A6 /* AssetActivity.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4624A41EC1468712BD77653 /* AssetActivity.graphql.swift */; }; - 9AF0D1FDF2BFB17FB732C5FD /* SchemaConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E49E724432C70A2551FB2C7 /* SchemaConfiguration.swift */; }; - 9B6C88F7D8D542AAC353A3EA /* NftOrderConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39FC2A13550FA6754EC1A7FC /* NftOrderConnection.graphql.swift */; }; + 91DBDA9B4F4006350AEB8E4B /* NftStandard.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0373C61A7AF015058A502CE2 /* NftStandard.graphql.swift */; }; + 9260D04A891FEDE2BF511707 /* Currency.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0CA37FB709C06F123EBF74 /* Currency.graphql.swift */; }; + 997BFD5324E4BDE2422FEFED /* NftAssetsFilterInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310373063D8A11E9E02314FE /* NftAssetsFilterInput.graphql.swift */; }; + 9A9B39BC22F3BDFCD0917B66 /* OffRampTransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CA32B2B2997D73A0185635 /* OffRampTransactionDetails.graphql.swift */; }; + 9D638326CD705ABE549C8CA7 /* TokenProjectMarketsParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C2E70E43A02405C5CEAD7C /* TokenProjectMarketsParts.graphql.swift */; }; + 9E7EC26AC45301198E280DC7 /* NftOrderEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD158681F2E84A5E49656B11 /* NftOrderEdge.graphql.swift */; }; + 9EBC41A90B1A80653960045E /* SchemaMetadata.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522135E54BDB258B45B5A694 /* SchemaMetadata.graphql.swift */; }; + 9EC9E4AB5C3FD254EDB28D7A /* PortfolioValueModifier.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = E09D12605BAA868AB51573BE /* PortfolioValueModifier.graphql.swift */; }; 9F00A43A2B33894C0088A0D0 /* ApplicationContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F00A4392B33894C0088A0D0 /* ApplicationContract.graphql.swift */; }; 9F29D4ED2B47126D004D003A /* NftBalanceAssetInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F29D4EC2B47126D004D003A /* NftBalanceAssetInput.graphql.swift */; }; 9F78980B2A819CC4004D5A98 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 072F6C222A44A32E00DA720A /* SwiftUI.framework */; }; @@ -267,73 +268,74 @@ 9FCEBF002A95A8E00079EDDB /* RNWalletConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FCEBEFE2A95A8E00079EDDB /* RNWalletConnect.m */; }; 9FCEBF012A95A8E00079EDDB /* RNWalletConnect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCEBEFF2A95A8E00079EDDB /* RNWalletConnect.swift */; }; 9FCEBF042A95A99C0079EDDB /* RCTThemeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FCEBF032A95A99B0079EDDB /* RCTThemeModule.m */; }; - 9FEC9B8B2A858CF1003CD019 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC9B8A2A858CF1003CD019 /* AppDelegate.m */; }; - A104024A1861354EC1DD53C1 /* PortfolioBalancesQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 985DDC8324B872DD44E87781 /* PortfolioBalancesQuery.graphql.swift */; }; + A250CF455F6CEFB64ABFC277 /* HomeScreenTokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB772B265A35BFBC3A7761AD /* HomeScreenTokenParts.graphql.swift */; }; A32F9FBD272343C9002CFCDB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A32F9FBC272343C8002CFCDB /* GoogleService-Info.plist */; }; - A3318C676D7FBF78A1583B16 /* NftCollectionEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37038CCEB11E70E2027EF4EA /* NftCollectionEdge.graphql.swift */; }; A3551F2CAC134AD49D40927F /* Basel-Grotesk-Book.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6F33E8069B7B40AFB313B8B0 /* Basel-Grotesk-Book.otf */; }; A3F0A5B1272B1DFA00895B25 /* KeychainSwiftDistrib.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F0A5B0272B1DFA00895B25 /* KeychainSwiftDistrib.swift */; }; A70E4DD42C25DA0A002D6D86 /* NetworkFee.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70E4DD32C25DA0A002D6D86 /* NetworkFee.graphql.swift */; }; A70E4DD72C260416002D6D86 /* SwapOrderType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70E4DD52C260416002D6D86 /* SwapOrderType.graphql.swift */; }; A70E4DD82C260416002D6D86 /* SwapOrderStatus.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70E4DD62C260416002D6D86 /* SwapOrderStatus.graphql.swift */; }; A7B8EFCB2BF68F0D00CA4A1C /* FeeData.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B8EFCA2BF68F0D00CA4A1C /* FeeData.graphql.swift */; }; - A974633048E27D5D23420F34 /* TokenBasicInfoParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3377F7BB38BD5A45AE31909D /* TokenBasicInfoParts.graphql.swift */; }; - AA3AE4E5C2AAC3F9460A3637 /* NftBalanceAssetInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5FA43C5E31035775917C4E /* NftBalanceAssetInput.graphql.swift */; }; + A88A26642AC25B022F428953 /* OnRampServiceProvider.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BED80DB034487FCA1450EDB /* OnRampServiceProvider.graphql.swift */; }; + A949039A6A9EB3584B5644F3 /* IAmount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37376E4CF3A4B76E9DAE150C /* IAmount.graphql.swift */; }; + A9AF7B483E9666E88CD6253E /* NftOrderConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F489175099FE697721272D /* NftOrderConnection.graphql.swift */; }; + AAB837C01239A62D00068853 /* TransactionType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D48B25E03122149B0D1304 /* TransactionType.graphql.swift */; }; AC0EE0982BD826E700BCCF07 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */; }; AC2EF4032C914B1600EEEFDB /* fonts in Resources */ = {isa = PBXBuildFile; fileRef = AC2EF4022C914B1600EEEFDB /* fonts */; }; - AC70FF8207ED26561B634E30 /* NftsQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9030AC59702756C39396FFE /* NftsQuery.graphql.swift */; }; - ADE104A101B3DFFBDB308189 /* Query.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED157D3DB73302C8A1B9522E /* Query.graphql.swift */; }; - AF83E7713BB625D787DD1A1D /* TokenMarket.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD833F6580F9B82A68D4A98 /* TokenMarket.graphql.swift */; }; + ACA7AE75760B76F7B30DCD43 /* AmountChange.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 066BBBD815CF2DE8803338BC /* AmountChange.graphql.swift */; }; + AD6036D32C44048781B728E1 /* SchemaConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF4115ACC8D8F0C7529AABA7 /* SchemaConfiguration.swift */; }; B193AD315CF844A3BDC3D11D /* Basel-Grotesk-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3C606D2C81014A0A8898F38E /* Basel-Grotesk-Medium.otf */; }; - B377DB0418EA15695F208D9F /* NetworkFee.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BEADEB4AE05B860CF2232D1 /* NetworkFee.graphql.swift */; }; - B61E182CF4937B5169885C95 /* OnRampTransactionDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D3AB6C2FC9513E9F224B7C /* OnRampTransactionDetails.graphql.swift */; }; - B746C09DA19B4C7F9C700989 /* NftActivityConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F912AB9AB67808B740246798 /* NftActivityConnection.graphql.swift */; }; - BA83003638263D702D03C3C1 /* ActivityDetails.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4EB7BAD2C90E61FD6C30AF /* ActivityDetails.graphql.swift */; }; + B64BD6DEB100AEB723DB640D /* NftBalanceConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DDC1F42E7172783EAF3776 /* NftBalanceConnection.graphql.swift */; }; BA869E372D56B0B600D7A718 /* WidgetTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA869E362D56B0B600D7A718 /* WidgetTokensQuery.graphql.swift */; }; BA8FC9627A40644259D9E2F9 /* Pods_WidgetsCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB29AC0C0907A833F23D2C30 /* Pods_WidgetsCore.framework */; }; - BD1FF76A8E50EFEA0720CC04 /* NftAsset.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2932B486DD7070DF226A27B /* NftAsset.graphql.swift */; }; - BD59A9C8414D6A4BFE9C9A2E /* NftActivity.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A24D364C0D262A14BB7182 /* NftActivity.graphql.swift */; }; - C1FC1F8B4A2725A195F66D60 /* PortfolioValueModifier.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41003491EF939043CF9D0F4E /* PortfolioValueModifier.graphql.swift */; }; - C791C5505DBB3036B9D5066D /* SwapOrderType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148617FD73F2CD7117960DBA /* SwapOrderType.graphql.swift */; }; - C7ABAADA504107D152A52FD4 /* NftBalancesFilterInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41BA2129FFE961C8549C8A30 /* NftBalancesFilterInput.graphql.swift */; }; - C8338C5BE951EF9111C48217 /* TokenBasicProjectParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2F4BE3080D4129D4478528 /* TokenBasicProjectParts.graphql.swift */; }; - C8402101FF510BAA358DCA46 /* TokenBalanceParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C34E94906CB9F0A016D566 /* TokenBalanceParts.graphql.swift */; }; - CA0EDABCA1B6C2B0936BF5CF /* NftBalanceEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B132543F80E3C9191219E6D /* NftBalanceEdge.graphql.swift */; }; - CCBC45FD4310D8153D859361 /* TransactionStatus.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABA47847A73130D356EB6BF /* TransactionStatus.graphql.swift */; }; - CE1DF567D58943ACCBB8360C /* Amount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20E9461A6C9C1CD6A24BEED /* Amount.graphql.swift */; }; - CF2BAECCF9A2EC43AC3EC583 /* OnRampServiceProvider.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A84A192F750A0F1BC8D7A69 /* OnRampServiceProvider.graphql.swift */; }; - CF36F741187285B430EFE557 /* NftBalance.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05BE2F49DD1FB9A692E21C13 /* NftBalance.graphql.swift */; }; + BAB54489223FD0027F7A8DBE /* Dimensions.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 764C4757EB194D13A8857DFA /* Dimensions.graphql.swift */; }; + BB28AF35DC102F8DE2A4BE9D /* OnRampTransactionsAuth.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3CC3D9DE64A39897BA580B3 /* OnRampTransactionsAuth.graphql.swift */; }; + BE0DBD01CF4DCA6D3CDDA369 /* NftActivity.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55C67E53A2C8AEB6AE92E33F /* NftActivity.graphql.swift */; }; + BED0DD22C0D9E09C85DF010D /* NftAsset.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9F3B31FC6AF8CE30CD7DA2 /* NftAsset.graphql.swift */; }; + BFB114718A0D262E3AEAEDC0 /* TransactionStatus.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945EF0A3BD6F19DA14E40AE1 /* TransactionStatus.graphql.swift */; }; + C064037906B259088B6B78B2 /* NetworkFee.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BE4B220A4F8465C3D01BA5 /* NetworkFee.graphql.swift */; }; + C0BBEE1CAEA4B2F778426BDE /* SelectWalletScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9CACD175D6C20B6D4707C8 /* SelectWalletScreenQuery.graphql.swift */; }; + C8F5AF75BDB439143FE1C173 /* NftActivityFilterInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C3E075E850CEC1714BE52C /* NftActivityFilterInput.graphql.swift */; }; + CA966CBD02A7B16BC55F1B8E /* ProtectionResult.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB6B8C64F7525D0D0E978C6 /* ProtectionResult.graphql.swift */; }; + CBEB9122D993BCB0E9A604B7 /* Query.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5956ECBE73216D266D8D2E86 /* Query.graphql.swift */; }; + CE8C365D91CADE7A5A8F4D52 /* TopTokenParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07F0E504152E22110917353E /* TopTokenParts.graphql.swift */; }; + CEE9E9912D5621F6E8F819B7 /* MultiplePortfolioBalancesQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 595699514939A2CE780AF0CF /* MultiplePortfolioBalancesQuery.graphql.swift */; }; + D0B9B6DEC559290B7F64B24F /* TokenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0914ECF70C274E2B064BCD42 /* TokenQuery.graphql.swift */; }; + D1FB4E293EC152C12A495ACD /* TokenStandard.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34AC1057AD14362BB91E88B /* TokenStandard.graphql.swift */; }; D3B63ACA9B0C42F68080B080 /* InputMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1834199AFFB04D91B05FFB64 /* InputMono-Regular.ttf */; }; - D660CA59775C3634324037ED /* MultiplePortfolioBalancesQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4429CA6DA254ECC24A65DA14 /* MultiplePortfolioBalancesQuery.graphql.swift */; }; - D680C4844F2C5DACF5D0892E /* NftAssetsFilterInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B477EBEA1EDA3914C77E8A6 /* NftAssetsFilterInput.graphql.swift */; }; + D48D1B7A4469BAAA22608058 /* TokenSortableField.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F133663B435829DE47E3CE /* TokenSortableField.graphql.swift */; }; + D5F36D3EDC206DF15EC368AC /* NftActivityType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CDAC035887C3908C3BD1A77 /* NftActivityType.graphql.swift */; }; + D6149AE9ED70F546DF5841BD /* ConvertQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E4DEE7FC8D7BCD030CFA4D3 /* ConvertQuery.graphql.swift */; }; D7926D4A878B2237137B300F /* Pods_WidgetsCoreTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 021E59CE7ECBD4FE0F3BFCFD /* Pods_WidgetsCoreTests.framework */; }; - D7E3642851C618D369D26B82 /* TokenMarketParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC36D963A2A52D1E7CA77E4 /* TokenMarketParts.graphql.swift */; }; - D81BAFFCC105668B88B607FC /* NftCollectionConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DE390311705FF1EE65C0E6 /* NftCollectionConnection.graphql.swift */; }; - DC0641C5870F91D26C914F1D /* TokenPriceHistoryQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DFBD29C0BDFDB4464B8189C /* TokenPriceHistoryQuery.graphql.swift */; }; + D8A0C6D04FF53BA4F50543FE /* NftAssetTrait.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B43D489CC5ACF1EA34E9EB /* NftAssetTrait.graphql.swift */; }; + DB752E8F664505726ABDBCD3 /* NftBalance.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2B02717B1A87BD41920151 /* NftBalance.graphql.swift */; }; + DC4AA6DE28B9F40A3D7600F1 /* TokenProjectsQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98CEFE7189D63880FD5A702 /* TokenProjectsQuery.graphql.swift */; }; DE2F24512E7204C2CA255C50 /* Pods_Widgets.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E8B7D36D2E14D9488F351EB /* Pods_Widgets.framework */; }; - E091F379106E92D3181103CB /* IAmount.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AD8C2B1226EBE1524F2573 /* IAmount.graphql.swift */; }; - E0F63BA3A99DB10FA1CCAB5E /* TokenProjectUrlsParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 236126979349098B98C70F27 /* TokenProjectUrlsParts.graphql.swift */; }; - E24ED8688E9B18BBD543F8F0 /* TokenBalance.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B149117182C6CC58DD570C /* TokenBalance.graphql.swift */; }; + E1009979B48ACF5017C12F09 /* TransactionListQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95283AC560844B50E95934E4 /* TransactionListQuery.graphql.swift */; }; + E2C528F617A9675690171D54 /* DescriptionTranslations.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = D91432EF6C35F5A6301340A2 /* DescriptionTranslations.graphql.swift */; }; + E475BA358DD8BE30A6FDC051 /* NftBalanceEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD55DFFC717D37DD960B82E /* NftBalanceEdge.graphql.swift */; }; + E4985BA9090E013F2C9FC190 /* TokenMarket.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E5B09464C768590127BFA6 /* TokenMarket.graphql.swift */; }; E4B3067A930D2E57558E5229 /* Pods_Uniswap_UniswapTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0929C0B4AE1570B8C0B45D4D /* Pods_Uniswap_UniswapTests.framework */; }; - E54093DC857B8C634804D42B /* NftTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3317EBD4E3B11E2F2A1CE58 /* NftTransfer.graphql.swift */; }; - EF05F69E61EA8A16C6A66D53 /* IContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E0D68CC8D1F13D864F069BB /* IContract.graphql.swift */; }; - EF3D92CA76DEE66090F18D0C /* NftCollectionScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0973BB445AAD8FAA25757F /* NftCollectionScreenQuery.graphql.swift */; }; - F0D6A92BB4FCE19CDFA5BBE1 /* HomeScreenTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7E03AA02B39A7A90C96AA76 /* HomeScreenTokensQuery.graphql.swift */; }; - F2EB62621E64B57B07DE8B13 /* Token.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647933A5DC35BDFEEF3620A1 /* Token.graphql.swift */; }; + E654760CA0FF19139A85472A /* TokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C8174A3A9EB3244AA844F4 /* TokensQuery.graphql.swift */; }; + E7B6F1CA0E30585C949C9D9A /* NftAssetConnection.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD86EFAA0D6B8FBECCC4828B /* NftAssetConnection.graphql.swift */; }; + E7D4A29333634717D099F80E /* SwapOrderType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFDF569E43F173C2F5A07AF /* SwapOrderType.graphql.swift */; }; + E7EDBB8CDF65D5D6602BF8FC /* NftOrder.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C0D95F7EA9E7ACA9B18692 /* NftOrder.graphql.swift */; }; + EB0A75424F8EEF6612D28D52 /* NftTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AD16E33DC518A42C386FE2 /* NftTransfer.graphql.swift */; }; + ED606CD83873CC9DFCCA44F1 /* FavoriteTokenCardQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B03A0A6AB0957A512A2D225 /* FavoriteTokenCardQuery.graphql.swift */; }; + EE4B861AE191A1BF70CF3784 /* WidgetTokensQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0A91F56B1CA2E71B479BAD /* WidgetTokensQuery.graphql.swift */; }; + F09E3F15A36A9050B028B3E3 /* TokenBasicProjectParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8286984864AD69A0FAE3BC /* TokenBasicProjectParts.graphql.swift */; }; + F1193089AE71CA3C4C101EA5 /* OffRampTransfer.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 166A9AE7203545C397E683E8 /* OffRampTransfer.graphql.swift */; }; F35AFD3E27EE49990011A725 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F35AFD3D27EE49990011A725 /* NotificationService.swift */; }; F35AFD4227EE49990011A725 /* OneSignalNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F35AFD3B27EE49990011A725 /* OneSignalNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - F53121FB1B070DB9A81347DF /* TokenProjectMarketsParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC055C3EDFDE2966FCA83C9E /* TokenProjectMarketsParts.graphql.swift */; }; - F5B577CA1201CB6FF04A9A58 /* ApplicationContract.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608B07304ED1387B5AEAA3BB /* ApplicationContract.graphql.swift */; }; - F772B09295DABC5E8895C1C5 /* SelectWalletScreenQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B033206DC974BCB6AF8CC613 /* SelectWalletScreenQuery.graphql.swift */; }; - FA8EB6A63AB3F56194C6CFB8 /* TokenBalanceQuantityParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2370BAD675DC8064C00AC037 /* TokenBalanceQuantityParts.graphql.swift */; }; - FBE634E2AEC7A62B89863366 /* ContractInput.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B3C6DF5DED22E892851B10 /* ContractInput.graphql.swift */; }; - FC7C117CEA5EA63460E6A2C6 /* NftApproveForAll.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BCBB5D46312050E4A5BD64 /* NftApproveForAll.graphql.swift */; }; - FD4E55146E046C7F5983A379 /* FeedTransactionListQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA0ACE5B9B2C41187CD41F3 /* FeedTransactionListQuery.graphql.swift */; }; + F49C1C4E9175DFE7EEF9FB51 /* Token.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FB1590D18A58D37C750D9AC /* Token.graphql.swift */; }; + F77F1A182DCA1D8DFE46ACC8 /* TokenBalanceParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AD13BFAD4EBA18AA6205E3 /* TokenBalanceParts.graphql.swift */; }; + F814C0144D90ADB4A8E0A34E /* Chain.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBCFBC330BF97BA2F630908 /* Chain.graphql.swift */; }; + F86D86FC31BBDA7246C50392 /* ProtectionAttackType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F4D5E8B9ED6E0FE29EA779 /* ProtectionAttackType.graphql.swift */; }; + FA318FB37223FAF04A5C887B /* TokenProjectUrlsParts.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190523DBBA423868C74FF80D /* TokenProjectUrlsParts.graphql.swift */; }; + FC61EDE606C85346CE069C5D /* TransactionHistoryUpdaterQuery.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559CBC7F568C769C48E7C6F4 /* TransactionHistoryUpdaterQuery.graphql.swift */; }; FD54D51D296C79A4007A37E9 /* GoogleServiceInfo in Resources */ = {isa = PBXBuildFile; fileRef = FD54D51C296C79A4007A37E9 /* GoogleServiceInfo */; }; FD7304CE28A364FC0085BDEA /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FD7304CD28A364FC0085BDEA /* Colors.xcassets */; }; FD7304D028A3650A0085BDEA /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7304CF28A3650A0085BDEA /* Colors.swift */; }; - FD84AA379C4562598807843D /* TokenStandard.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC419570F5A025E80AB10A72 /* TokenStandard.graphql.swift */; }; - FE9CE5C3B5A456B249FC34C5 /* NftActivityEdge.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB4379B5E222207CA7328D0 /* NftActivityEdge.graphql.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -436,16 +438,16 @@ 00E356EE1AD99517003FC87E /* UniswapTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UniswapTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* UniswapTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UniswapTests.m; sourceTree = ""; }; - 018603D0FA4D05DBF16F1441 /* TokenFeeDataParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenFeeDataParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenFeeDataParts.graphql.swift; sourceTree = ""; }; 021E59CE7ECBD4FE0F3BFCFD /* Pods_WidgetsCoreTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WidgetsCoreTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0373C61A7AF015058A502CE2 /* NftStandard.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftStandard.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/NftStandard.graphql.swift; sourceTree = ""; }; 037C5AA92C04970B00B1D808 /* CopyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyIcon.swift; sourceTree = ""; }; - 03AD8C2B1226EBE1524F2573 /* IAmount.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IAmount.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Interfaces/IAmount.graphql.swift; sourceTree = ""; }; + 03AB6B21A1BFB3A18982B47E /* NFTItemScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NFTItemScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NFTItemScreenQuery.graphql.swift; sourceTree = ""; }; 03C788222C10E7390011E5DC /* ActionButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtons.swift; sourceTree = ""; }; 03D2F3172C218D380030D987 /* RelativeOffsetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelativeOffsetView.swift; sourceTree = ""; }; - 04E5C2900254E7BA7F10CE51 /* TopTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TopTokensQuery.graphql.swift; sourceTree = ""; }; - 05BE2F49DD1FB9A692E21C13 /* NftBalance.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalance.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftBalance.graphql.swift; sourceTree = ""; }; + 043C7D80D6722AFCF1715D07 /* NftActivityEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftActivityEdge.graphql.swift; sourceTree = ""; }; + 05C66C49AF2B78703FCE4C7C /* AssetActivity.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssetActivity.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/AssetActivity.graphql.swift; sourceTree = ""; }; 065A981F892F7A06A900FCD5 /* Pods-WidgetsCoreTests.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCoreTests.dev.xcconfig"; path = "Target Support Files/Pods-WidgetsCoreTests/Pods-WidgetsCoreTests.dev.xcconfig"; sourceTree = ""; }; - 0702203B17BDD858CED28F8B /* ProtectionInfo.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProtectionInfo.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/ProtectionInfo.graphql.swift; sourceTree = ""; }; + 066BBBD815CF2DE8803338BC /* AmountChange.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AmountChange.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/AmountChange.graphql.swift; sourceTree = ""; }; 0703EE022A5734A600AED1DA /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = ""; }; 070480372A58A507009006CE /* WidgetIntentExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetIntentExtension.entitlements; sourceTree = ""; }; 0712B3629C74D1F958DF35FB /* Pods-Uniswap-UniswapTests.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap-UniswapTests.dev.xcconfig"; path = "Target Support Files/Pods-Uniswap-UniswapTests/Pods-Uniswap-UniswapTests.dev.xcconfig"; sourceTree = ""; }; @@ -544,6 +546,7 @@ 074321E92A83E3C900F8518D /* IAmount.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IAmount.graphql.swift; sourceTree = ""; }; 074321EA2A83E3C900F8518D /* IContract.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IContract.graphql.swift; sourceTree = ""; }; 0743223F2A841BBD00F8518D /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 075E4C1DFAA00EE5B3CE792D /* ActivityDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ActivityDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Unions/ActivityDetails.graphql.swift; sourceTree = ""; }; 0767E0372A65C8330042ADA2 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; 0767E03A2A65D2550042ADA2 /* Styling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styling.swift; sourceTree = ""; }; 077E60382A85587800ABC4B9 /* TokensQuery.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensQuery.graphql.swift; sourceTree = ""; }; @@ -556,17 +559,20 @@ 07B0676A2A7D6EC8001DD9B9 /* RNWidgets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RNWidgets.swift; sourceTree = ""; }; 07B0676B2A7D6EC8001DD9B9 /* RNWidgets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNWidgets.m; sourceTree = ""; }; 07F0C28E2A5F3E2E00D5353E /* Env.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Env.swift; sourceTree = ""; }; + 07F0E504152E22110917353E /* TopTokenParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopTokenParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TopTokenParts.graphql.swift; sourceTree = ""; }; 07F1363F2A575EC00067004F /* DataQueries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataQueries.swift; sourceTree = ""; }; 07F136412A5763480067004F /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 07F5CF702A6AD97D00C648A5 /* Chart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chart.swift; sourceTree = ""; }; 07F5CF742A7020FD00C648A5 /* Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Format.swift; sourceTree = ""; }; 08C60D53AB82A6D0D31D0F78 /* Pods-WidgetIntentExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetIntentExtension.release.xcconfig"; path = "Target Support Files/Pods-WidgetIntentExtension/Pods-WidgetIntentExtension.release.xcconfig"; sourceTree = ""; }; 08EBF075A4482F701892270B /* Pods-Widgets.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Widgets.dev.xcconfig"; path = "Target Support Files/Pods-Widgets/Pods-Widgets.dev.xcconfig"; sourceTree = ""; }; + 0914ECF70C274E2B064BCD42 /* TokenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenQuery.graphql.swift; sourceTree = ""; }; 0929C0B4AE1570B8C0B45D4D /* Pods_Uniswap_UniswapTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Uniswap_UniswapTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0B132543F80E3C9191219E6D /* NftBalanceEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalanceEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftBalanceEdge.graphql.swift; sourceTree = ""; }; + 09BE4B220A4F8465C3D01BA5 /* NetworkFee.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkFee.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NetworkFee.graphql.swift; sourceTree = ""; }; + 0B0505DB30407C221B6A8491 /* TokenProjectMarket.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectMarket.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenProjectMarket.graphql.swift; sourceTree = ""; }; 0B7E5D62E11408EB5F0F5A80 /* Pods-WidgetsCore.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCore.beta.xcconfig"; path = "Target Support Files/Pods-WidgetsCore/Pods-WidgetsCore.beta.xcconfig"; sourceTree = ""; }; - 0BB4A40E30E1D985519DB3CF /* TokenProtectionInfoParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProtectionInfoParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenProtectionInfoParts.graphql.swift; sourceTree = ""; }; 0C19DE44A750FB17647FF2B6 /* Pods-Widgets.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Widgets.beta.xcconfig"; path = "Target Support Files/Pods-Widgets/Pods-Widgets.beta.xcconfig"; sourceTree = ""; }; + 0D09FFE2B5E56CDCAB2F9455 /* NftCollectionConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollectionConnection.graphql.swift; sourceTree = ""; }; 0DB282242CDADB260014CF77 /* EmbeddedWallet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EmbeddedWallet.m; sourceTree = ""; }; 0DB282252CDADB260014CF77 /* EmbeddedWallet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmbeddedWallet.swift; sourceTree = ""; }; 0DC6ADEF2B1E2C0F0092909C /* PortfolioValueModifier.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortfolioValueModifier.graphql.swift; sourceTree = ""; }; @@ -574,91 +580,80 @@ 0DE251442C13B69D005F47F9 /* OnRampTransfer.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnRampTransfer.graphql.swift; sourceTree = ""; }; 0DE251452C13B69D005F47F9 /* OnRampServiceProvider.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnRampServiceProvider.graphql.swift; sourceTree = ""; }; 0DE251462C13B69D005F47F9 /* OnRampTransactionDetails.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnRampTransactionDetails.graphql.swift; sourceTree = ""; }; + 0E7FF51A507BF788E64E3EFE /* HomeScreenTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeScreenTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/HomeScreenTokensQuery.graphql.swift; sourceTree = ""; }; + 1053483279043EED7612BE90 /* HistoryDuration.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HistoryDuration.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/HistoryDuration.graphql.swift; sourceTree = ""; }; 1064E23E366D0C2C2B20C30E /* Pods_WidgetIntentExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WidgetIntentExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1166529407CA13E4D4F24F12 /* TokenFeeDataParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenFeeDataParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenFeeDataParts.graphql.swift; sourceTree = ""; }; 1193B3A845BC3BE8CAA00D01 /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.release.xcconfig"; path = "Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; - 12D3AB6C2FC9513E9F224B7C /* OnRampTransactionDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampTransactionDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OnRampTransactionDetails.graphql.swift; sourceTree = ""; }; - 137696908D703632CEE3AB28 /* TokenBalanceMainParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalanceMainParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBalanceMainParts.graphql.swift; sourceTree = ""; }; + 1276D33B06E840B8E6839F39 /* TokenProjectDescriptionQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectDescriptionQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenProjectDescriptionQuery.graphql.swift; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Uniswap.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Uniswap.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Uniswap/AppDelegate.h; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Uniswap/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Uniswap/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Uniswap/main.m; sourceTree = ""; }; - 148617FD73F2CD7117960DBA /* SwapOrderType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwapOrderType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/SwapOrderType.graphql.swift; sourceTree = ""; }; 15092E550A1C78508ABA3280 /* Pods_OneSignalNotificationServiceExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OneSignalNotificationServiceExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 17819A49DE69723EC60D9D72 /* FeeData.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FeeData.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/FeeData.graphql.swift; sourceTree = ""; }; + 166A9AE7203545C397E683E8 /* OffRampTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OffRampTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OffRampTransfer.graphql.swift; sourceTree = ""; }; 178644A78AB62609EFDB66B3 /* Pods-Uniswap.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.release.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.release.xcconfig"; sourceTree = ""; }; + 1810C60B56CC74150C868801 /* NftBalancesFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalancesFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftBalancesFilterInput.graphql.swift; sourceTree = ""; }; + 181209B4CDE80BDC80891CFD /* TokenBalance.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalance.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenBalance.graphql.swift; sourceTree = ""; }; 1834199AFFB04D91B05FFB64 /* InputMono-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "InputMono-Regular.ttf"; path = "../src/assets/fonts/InputMono-Regular.ttf"; sourceTree = ""; }; - 1849039FD4A210DA990363C8 /* Portfolio.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Portfolio.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Portfolio.graphql.swift; sourceTree = ""; }; - 18B9EB43501127BE1B14F1A5 /* NftContract.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftContract.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftContract.graphql.swift; sourceTree = ""; }; + 18D48B25E03122149B0D1304 /* TransactionType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TransactionType.graphql.swift; sourceTree = ""; }; + 190523DBBA423868C74FF80D /* TokenProjectUrlsParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectUrlsParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenProjectUrlsParts.graphql.swift; sourceTree = ""; }; 1CC6ADAADCA38FDAEB181E86 /* Pods-WidgetIntentExtension.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetIntentExtension.dev.xcconfig"; path = "Target Support Files/Pods-WidgetIntentExtension/Pods-WidgetIntentExtension.dev.xcconfig"; sourceTree = ""; }; - 1FC7B80EC0E0D2134B67575B /* NftBalanceConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalanceConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftBalanceConnection.graphql.swift; sourceTree = ""; }; - 207F4DF7664454C31DE069F8 /* NFTItemScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NFTItemScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NFTItemScreenQuery.graphql.swift; sourceTree = ""; }; - 2139ABF56EA067ED792A48C9 /* OnRampTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OnRampTransfer.graphql.swift; sourceTree = ""; }; - 218B8AB05C80D919671D3970 /* NftAssetConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAssetConnection.graphql.swift; sourceTree = ""; }; + 1CDAC035887C3908C3BD1A77 /* NftActivityType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/NftActivityType.graphql.swift; sourceTree = ""; }; + 1D9189C818AFF8E988F861D0 /* IContract.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IContract.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Interfaces/IContract.graphql.swift; sourceTree = ""; }; + 1F1731869775D16A9EAB475C /* NftAssetTraitInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetTraitInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftAssetTraitInput.graphql.swift; sourceTree = ""; }; 2226DF79BEAFECEE11A51347 /* Pods_Uniswap.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Uniswap.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 224CA82F1871016F4173F69E /* TransactionListQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionListQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TransactionListQuery.graphql.swift; sourceTree = ""; }; - 236126979349098B98C70F27 /* TokenProjectUrlsParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectUrlsParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenProjectUrlsParts.graphql.swift; sourceTree = ""; }; - 2370BAD675DC8064C00AC037 /* TokenBalanceQuantityParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalanceQuantityParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBalanceQuantityParts.graphql.swift; sourceTree = ""; }; - 25B4C7C7FEF32EAF10D030F9 /* Currency.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Currency.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/Currency.graphql.swift; sourceTree = ""; }; - 27F8B915042BD8D67D9627EA /* HomeScreenTokenParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeScreenTokenParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/HomeScreenTokenParts.graphql.swift; sourceTree = ""; }; - 2ABA47847A73130D356EB6BF /* TransactionStatus.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionStatus.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TransactionStatus.graphql.swift; sourceTree = ""; }; - 2BA0ACE5B9B2C41187CD41F3 /* FeedTransactionListQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FeedTransactionListQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/FeedTransactionListQuery.graphql.swift; sourceTree = ""; }; - 2C2F4BE3080D4129D4478528 /* TokenBasicProjectParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBasicProjectParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBasicProjectParts.graphql.swift; sourceTree = ""; }; - 2D95FE36D2667E070345CDD0 /* NftActivityType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/NftActivityType.graphql.swift; sourceTree = ""; }; - 2E49E724432C70A2551FB2C7 /* SchemaConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SchemaConfiguration.swift; path = WidgetsCore/MobileSchema/Schema/SchemaConfiguration.swift; sourceTree = ""; }; + 22F8093091A9E5FC0D6284BA /* SafetyLevel.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SafetyLevel.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/SafetyLevel.graphql.swift; sourceTree = ""; }; + 243576FAC8D2801F9D99ECC9 /* TopTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TopTokensQuery.graphql.swift; sourceTree = ""; }; + 2E8286984864AD69A0FAE3BC /* TokenBasicProjectParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBasicProjectParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBasicProjectParts.graphql.swift; sourceTree = ""; }; 2E8B7D36D2E14D9488F351EB /* Pods_Widgets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Widgets.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 315B8A905FA2C60C053F138B /* OnRampTransactionsAuth.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampTransactionsAuth.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/OnRampTransactionsAuth.graphql.swift; sourceTree = ""; }; - 31ED613D63FE9C56D9270517 /* Image.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Image.graphql.swift; sourceTree = ""; }; - 32012AC135EBD991C8F02F92 /* TokenSortableField.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenSortableField.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TokenSortableField.graphql.swift; sourceTree = ""; }; - 3377F7BB38BD5A45AE31909D /* TokenBasicInfoParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBasicInfoParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBasicInfoParts.graphql.swift; sourceTree = ""; }; - 37038CCEB11E70E2027EF4EA /* NftCollectionEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollectionEdge.graphql.swift; sourceTree = ""; }; - 396B6AB3BB41898B56EB3828 /* OffRampTransactionDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OffRampTransactionDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OffRampTransactionDetails.graphql.swift; sourceTree = ""; }; - 39F60B2B54796DC978765C99 /* AssetChange.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssetChange.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Unions/AssetChange.graphql.swift; sourceTree = ""; }; - 39FC2A13550FA6754EC1A7FC /* NftOrderConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftOrderConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftOrderConnection.graphql.swift; sourceTree = ""; }; + 2FB1590D18A58D37C750D9AC /* Token.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Token.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Token.graphql.swift; sourceTree = ""; }; + 310373063D8A11E9E02314FE /* NftAssetsFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetsFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftAssetsFilterInput.graphql.swift; sourceTree = ""; }; + 31CBA4397C44A04E218AEF28 /* TimestampedAmount.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TimestampedAmount.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TimestampedAmount.graphql.swift; sourceTree = ""; }; + 37376E4CF3A4B76E9DAE150C /* IAmount.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IAmount.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Interfaces/IAmount.graphql.swift; sourceTree = ""; }; 3A2186B1FF7FB85663D96EA9 /* Pods-OneSignalNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.debug.xcconfig"; path = "Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.debug.xcconfig"; sourceTree = ""; }; - 3B9A505509D9A85BE1DB7D05 /* TokenProjectMarket.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectMarket.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenProjectMarket.graphql.swift; sourceTree = ""; }; - 3BF42F88002431BFDC9FCA9F /* HistoryDuration.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HistoryDuration.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/HistoryDuration.graphql.swift; sourceTree = ""; }; + 3B03A0A6AB0957A512A2D225 /* FavoriteTokenCardQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FavoriteTokenCardQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/FavoriteTokenCardQuery.graphql.swift; sourceTree = ""; }; 3C606D2C81014A0A8898F38E /* Basel-Grotesk-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Basel-Grotesk-Medium.otf"; path = "../src/assets/fonts/Basel-Grotesk-Medium.otf"; sourceTree = ""; }; 3D8FCE4CD401350CA74DCC89 /* Pods-WidgetsCoreTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCoreTests.debug.xcconfig"; path = "Target Support Files/Pods-WidgetsCoreTests/Pods-WidgetsCoreTests.debug.xcconfig"; sourceTree = ""; }; 3E279F675B02CBC50D3B57D5 /* Pods-WidgetsCore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCore.release.xcconfig"; path = "Target Support Files/Pods-WidgetsCore/Pods-WidgetsCore.release.xcconfig"; sourceTree = ""; }; - 3F0EEAC1DADE827AE38EB8A6 /* FavoriteTokenCardQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FavoriteTokenCardQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/FavoriteTokenCardQuery.graphql.swift; sourceTree = ""; }; - 40E034582FBD2C374CEF808E /* ProtectionAttackType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProtectionAttackType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/ProtectionAttackType.graphql.swift; sourceTree = ""; }; - 41003491EF939043CF9D0F4E /* PortfolioValueModifier.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PortfolioValueModifier.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/PortfolioValueModifier.graphql.swift; sourceTree = ""; }; - 41BA2129FFE961C8549C8A30 /* NftBalancesFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalancesFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftBalancesFilterInput.graphql.swift; sourceTree = ""; }; - 43E2ECCEFBC3BE8D13BE6CAB /* NftActivityFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftActivityFilterInput.graphql.swift; sourceTree = ""; }; - 4429CA6DA254ECC24A65DA14 /* MultiplePortfolioBalancesQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultiplePortfolioBalancesQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/MultiplePortfolioBalancesQuery.graphql.swift; sourceTree = ""; }; + 3E5D1A04B42B25218C53C107 /* OnRampTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OnRampTransfer.graphql.swift; sourceTree = ""; }; + 3EF9AEC00868BFD7CEBF202C /* BlockaidFees.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlockaidFees.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/BlockaidFees.graphql.swift; sourceTree = ""; }; + 45FFF7DE2E8C2A6400362570 /* SilentPushEventEmitter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SilentPushEventEmitter.m; sourceTree = ""; }; + 45FFF7E02E8C2E6100362570 /* SilentPushEventEmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SilentPushEventEmitter.swift; sourceTree = ""; }; 4781CD4CDD95B5792B793F75 /* Pods-Uniswap-UniswapTests.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap-UniswapTests.beta.xcconfig"; path = "Target Support Files/Pods-Uniswap-UniswapTests/Pods-Uniswap-UniswapTests.beta.xcconfig"; sourceTree = ""; }; - 4981A14906A35E8A0B7F9457 /* TokenProject.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProject.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenProject.graphql.swift; sourceTree = ""; }; - 49BD6C776029FAF4FC711DE7 /* NftOrderEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftOrderEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftOrderEdge.graphql.swift; sourceTree = ""; }; - 4A8990A17F489F284E0DB1B3 /* Chain.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Chain.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/Chain.graphql.swift; sourceTree = ""; }; - 4ACFA5B4204741038C986C5D /* SafetyLevel.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SafetyLevel.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/SafetyLevel.graphql.swift; sourceTree = ""; }; - 4B308EC3DB7C3C1DD5DDE03D /* NftStandard.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftStandard.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/NftStandard.graphql.swift; sourceTree = ""; }; 4C445DB9798210862C34D0E0 /* Pods-WidgetsCoreTests.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCoreTests.beta.xcconfig"; path = "Target Support Files/Pods-WidgetsCoreTests/Pods-WidgetsCoreTests.beta.xcconfig"; sourceTree = ""; }; - 4DB4379B5E222207CA7328D0 /* NftActivityEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftActivityEdge.graphql.swift; sourceTree = ""; }; + 4DD55DFFC717D37DD960B82E /* NftBalanceEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalanceEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftBalanceEdge.graphql.swift; sourceTree = ""; }; 4DF5F26A06553EFDD4D99214 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Uniswap/ExpoModulesProvider.swift"; sourceTree = ""; }; - 4E32BBB65A6DD9CFF608C8E8 /* NftApproval.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftApproval.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftApproval.graphql.swift; sourceTree = ""; }; - 4E5FA43C5E31035775917C4E /* NftBalanceAssetInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalanceAssetInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftBalanceAssetInput.graphql.swift; sourceTree = ""; }; - 4F4F47D22B749A438B5BF674 /* NftCollectionMarket.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionMarket.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollectionMarket.graphql.swift; sourceTree = ""; }; - 54B1C71AED738D17898103A6 /* Dimensions.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Dimensions.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Dimensions.graphql.swift; sourceTree = ""; }; - 556339705BD103365D4ED07B /* NftAssetTraitInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetTraitInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftAssetTraitInput.graphql.swift; sourceTree = ""; }; - 5640432978873FB030467750 /* TopTokenParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopTokenParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TopTokenParts.graphql.swift; sourceTree = ""; }; + 50C3E075E850CEC1714BE52C /* NftActivityFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftActivityFilterInput.graphql.swift; sourceTree = ""; }; + 51F133663B435829DE47E3CE /* TokenSortableField.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenSortableField.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TokenSortableField.graphql.swift; sourceTree = ""; }; + 522135E54BDB258B45B5A694 /* SchemaMetadata.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SchemaMetadata.graphql.swift; path = WidgetsCore/MobileSchema/Schema/SchemaMetadata.graphql.swift; sourceTree = ""; }; + 5397E77A577E9952D2477020 /* Portfolio.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Portfolio.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Portfolio.graphql.swift; sourceTree = ""; }; + 53F4E5E9341BE66984935185 /* SwapOrderDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwapOrderDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/SwapOrderDetails.graphql.swift; sourceTree = ""; }; + 55081ADF188C967FC1ECD289 /* TokenMarketParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenMarketParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenMarketParts.graphql.swift; sourceTree = ""; }; + 55240C84512D3851C8B9F027 /* TransactionDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TransactionDetails.graphql.swift; sourceTree = ""; }; + 559CBC7F568C769C48E7C6F4 /* TransactionHistoryUpdaterQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionHistoryUpdaterQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TransactionHistoryUpdaterQuery.graphql.swift; sourceTree = ""; }; + 55A243177E9FA92A0524A222 /* NftContract.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftContract.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftContract.graphql.swift; sourceTree = ""; }; + 55C67E53A2C8AEB6AE92E33F /* NftActivity.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivity.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftActivity.graphql.swift; sourceTree = ""; }; 56FE9C9AF785221B7E3F4C04 /* Pods-Uniswap.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.dev.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.dev.xcconfig"; sourceTree = ""; }; + 595699514939A2CE780AF0CF /* MultiplePortfolioBalancesQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultiplePortfolioBalancesQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/MultiplePortfolioBalancesQuery.graphql.swift; sourceTree = ""; }; + 5956ECBE73216D266D8D2E86 /* Query.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Query.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Query.graphql.swift; sourceTree = ""; }; + 59DD4D834D7B42A6D02F4F12 /* ApplicationContract.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ApplicationContract.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/ApplicationContract.graphql.swift; sourceTree = ""; }; + 5B0A1F5F6FC3E1141E228B7D /* TokenBalanceQuantityParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalanceQuantityParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBalanceQuantityParts.graphql.swift; sourceTree = ""; }; 5B4398E82DD3B22C00F6BE08 /* PrivateKeyDisplayManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateKeyDisplayManager.m; sourceTree = ""; }; 5B4398E92DD3B22C00F6BE08 /* PrivateKeyDisplayManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateKeyDisplayManager.swift; sourceTree = ""; }; 5B4398EA2DD3B22C00F6BE08 /* PrivateKeyDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateKeyDisplayView.swift; sourceTree = ""; }; 5B4CEC5E2DD65DD4009F082B /* CopyIconOutline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyIconOutline.swift; sourceTree = ""; }; - 5E0D68CC8D1F13D864F069BB /* IContract.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IContract.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Interfaces/IContract.graphql.swift; sourceTree = ""; }; + 5BED80DB034487FCA1450EDB /* OnRampServiceProvider.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampServiceProvider.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OnRampServiceProvider.graphql.swift; sourceTree = ""; }; + 5C9F3B31FC6AF8CE30CD7DA2 /* NftAsset.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAsset.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAsset.graphql.swift; sourceTree = ""; }; 5E5E0A622D380F5700E166AA /* Env.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Env.swift; sourceTree = ""; }; 5EFB78352B1E585000E77EAC /* ConvertQuery.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvertQuery.graphql.swift; sourceTree = ""; }; - 600250B00B6C02AAB063818B /* NftMarketplace.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftMarketplace.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/NftMarketplace.graphql.swift; sourceTree = ""; }; - 608B07304ED1387B5AEAA3BB /* ApplicationContract.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ApplicationContract.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/ApplicationContract.graphql.swift; sourceTree = ""; }; 62CEA9F2D5176D20A6402A3E /* Pods-Uniswap.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.beta.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.beta.xcconfig"; sourceTree = ""; }; - 63ACFF2A1D5AE3498731AC69 /* NftCollection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollection.graphql.swift; sourceTree = ""; }; - 647933A5DC35BDFEEF3620A1 /* Token.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Token.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Token.graphql.swift; sourceTree = ""; }; + 62DDC1F42E7172783EAF3776 /* NftBalanceConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalanceConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftBalanceConnection.graphql.swift; sourceTree = ""; }; 649A7A762D9AE70B00B53589 /* KeychainConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainConstants.swift; sourceTree = ""; }; 649A7A772D9AE70B00B53589 /* KeychainUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtils.swift; sourceTree = ""; }; - 69F2FFD11FB5E462CC454A15 /* TransactionType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TransactionType.graphql.swift; sourceTree = ""; }; - 6A84A192F750A0F1BC8D7A69 /* OnRampServiceProvider.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampServiceProvider.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OnRampServiceProvider.graphql.swift; sourceTree = ""; }; + 64D494D944670E557175A694 /* TransactionDirection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionDirection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TransactionDirection.graphql.swift; sourceTree = ""; }; + 65AD13BFAD4EBA18AA6205E3 /* TokenBalanceParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalanceParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBalanceParts.graphql.swift; sourceTree = ""; }; + 67C0D95F7EA9E7ACA9B18692 /* NftOrder.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftOrder.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftOrder.graphql.swift; sourceTree = ""; }; + 696F6C5220D0072E380E198F /* NftBalanceAssetInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalanceAssetInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftBalanceAssetInput.graphql.swift; sourceTree = ""; }; 6BC7D07B2B5FF02400617C95 /* ScantasticEncryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScantasticEncryption.m; sourceTree = ""; }; 6BC7D07C2B5FF02400617C95 /* ScantasticEncryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScantasticEncryption.swift; sourceTree = ""; }; 6BC7D07D2B5FF02400617C95 /* EncryptionUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionUtils.swift; sourceTree = ""; }; @@ -669,26 +664,28 @@ 6CA91BDE2A95226200C4063E /* RNCloudStorageBackupsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCloudStorageBackupsManager.m; sourceTree = ""; }; 6CA91BDF2A95226200C4063E /* EncryptionHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionHelper.swift; sourceTree = ""; }; 6CA91BE02A95226200C4063E /* RNCloudStorageBackupsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RNCloudStorageBackupsManager.swift; sourceTree = ""; }; - 6DF7C8888BE6AFA2F22889FE /* NftAssetEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAssetEdge.graphql.swift; sourceTree = ""; }; - 6E3A78386B3B779B688021FF /* NftOrder.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftOrder.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftOrder.graphql.swift; sourceTree = ""; }; + 6D4C849A7805FCCEE8C8C3E6 /* AssetChange.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssetChange.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Unions/AssetChange.graphql.swift; sourceTree = ""; }; + 6E4DEE7FC8D7BCD030CFA4D3 /* ConvertQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConvertQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/ConvertQuery.graphql.swift; sourceTree = ""; }; + 6F0A91F56B1CA2E71B479BAD /* WidgetTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WidgetTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/WidgetTokensQuery.graphql.swift; sourceTree = ""; }; 6F33E8069B7B40AFB313B8B0 /* Basel-Grotesk-Book.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Basel-Grotesk-Book.otf"; path = "../src/assets/fonts/Basel-Grotesk-Book.otf"; sourceTree = ""; }; 6F3DC921A65D749C0852B10C /* Pods-Uniswap-UniswapTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap-UniswapTests.debug.xcconfig"; path = "Target Support Files/Pods-Uniswap-UniswapTests/Pods-Uniswap-UniswapTests.debug.xcconfig"; sourceTree = ""; }; 6F7814C6D40D9C348EA1F1C7 /* Pods-OneSignalNotificationServiceExtension.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.dev.xcconfig"; path = "Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.dev.xcconfig"; sourceTree = ""; }; - 6FF8B660E9C2C0D8DDF7588A /* TokenProjectDescriptionQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectDescriptionQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenProjectDescriptionQuery.graphql.swift; sourceTree = ""; }; - 70851E3D1A4B296A1CE22A20 /* NftAssetTrait.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetTrait.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAssetTrait.graphql.swift; sourceTree = ""; }; - 730CA356D6E1D498CC0C1472 /* NftsTabQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftsTabQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NftsTabQuery.graphql.swift; sourceTree = ""; }; - 74885865ED8CFEA875E40916 /* TokenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenQuery.graphql.swift; sourceTree = ""; }; + 74AD16E33DC518A42C386FE2 /* NftTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftTransfer.graphql.swift; sourceTree = ""; }; + 764C4757EB194D13A8857DFA /* Dimensions.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Dimensions.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Dimensions.graphql.swift; sourceTree = ""; }; 7A7637BBC9B3A68E0338D96E /* Pods-WidgetIntentExtension.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetIntentExtension.beta.xcconfig"; path = "Target Support Files/Pods-WidgetIntentExtension/Pods-WidgetIntentExtension.beta.xcconfig"; sourceTree = ""; }; - 7BD3F0794908635CDFA7DAB6 /* ConvertQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConvertQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/ConvertQuery.graphql.swift; sourceTree = ""; }; - 7E1F288B7EDDE906C4C00C46 /* DescriptionTranslations.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DescriptionTranslations.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/DescriptionTranslations.graphql.swift; sourceTree = ""; }; + 7AFDF569E43F173C2F5A07AF /* SwapOrderType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwapOrderType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/SwapOrderType.graphql.swift; sourceTree = ""; }; + 7DBCFBC330BF97BA2F630908 /* Chain.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Chain.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/Chain.graphql.swift; sourceTree = ""; }; 82C9871585F60F92D079FB95 /* Pods-Widgets.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Widgets.release.xcconfig"; path = "Target Support Files/Pods-Widgets/Pods-Widgets.release.xcconfig"; sourceTree = ""; }; - 84997376A0B436ECC0A996C5 /* ExploreSearchQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExploreSearchQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/ExploreSearchQuery.graphql.swift; sourceTree = ""; }; + 835D9DF76533D22EDF0E9D67 /* TokenProtectionInfoParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProtectionInfoParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenProtectionInfoParts.graphql.swift; sourceTree = ""; }; + 83FB27D93A9468E6DB47231C /* NftMarketplace.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftMarketplace.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/NftMarketplace.graphql.swift; sourceTree = ""; }; + 85B43D489CC5ACF1EA34E9EB /* NftAssetTrait.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetTrait.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAssetTrait.graphql.swift; sourceTree = ""; }; 8719E5872CC41AB64503E903 /* Pods-WidgetIntentExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetIntentExtension.debug.xcconfig"; path = "Target Support Files/Pods-WidgetIntentExtension/Pods-WidgetIntentExtension.debug.xcconfig"; sourceTree = ""; }; - 8A4EB7BAD2C90E61FD6C30AF /* ActivityDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ActivityDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Unions/ActivityDetails.graphql.swift; sourceTree = ""; }; - 8A5342DEF1410E0E81E97F40 /* SearchTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SearchTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/SearchTokensQuery.graphql.swift; sourceTree = ""; }; - 8B477EBEA1EDA3914C77E8A6 /* NftAssetsFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetsFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftAssetsFilterInput.graphql.swift; sourceTree = ""; }; - 8BEADEB4AE05B860CF2232D1 /* NetworkFee.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkFee.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NetworkFee.graphql.swift; sourceTree = ""; }; - 8DA4E7C052A6C4AC3A9DCDA6 /* TokenTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenTransfer.graphql.swift; sourceTree = ""; }; + 87442A651C068E552BEA89C6 /* MobileSchema.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MobileSchema.graphql.swift; path = WidgetsCore/MobileSchema/MobileSchema.graphql.swift; sourceTree = ""; }; + 87CF9B03AE29A19C5B0F4E35 /* OnRampTransactionDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampTransactionDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OnRampTransactionDetails.graphql.swift; sourceTree = ""; }; + 8856DFFC7FDC9BF105B0B4E3 /* TokenProject.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProject.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenProject.graphql.swift; sourceTree = ""; }; + 8B2A92162EB3E78E00990413 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Uniswap/AppDelegate.swift; sourceTree = ""; }; + 8B2A92182EB3E79500990413 /* Uniswap-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Uniswap-Bridging-Header.h"; path = "Uniswap/Uniswap-Bridging-Header.h"; sourceTree = ""; }; + 8C9047B981FA322A727947F7 /* Image.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Image.graphql.swift; sourceTree = ""; }; 8E89C3A62AB8AAA400C84DE5 /* MnemonicConfirmationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MnemonicConfirmationView.swift; sourceTree = ""; }; 8E89C3A72AB8AAA400C84DE5 /* MnemonicDisplayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MnemonicDisplayView.swift; sourceTree = ""; }; 8E89C3A92AB8AAA400C84DE5 /* MnemonicConfirmationWordBankView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MnemonicConfirmationWordBankView.swift; sourceTree = ""; }; @@ -719,11 +716,14 @@ 91D5016E2CDBEAE700B09B7F /* TopTokenParts.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopTokenParts.graphql.swift; sourceTree = ""; }; 91D5016F2CDBEAE700B09B7F /* TokenFeeDataParts.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenFeeDataParts.graphql.swift; sourceTree = ""; }; 91D5017C2CDBEAF600B09B7F /* HomeScreenTokenParts.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenTokenParts.graphql.swift; sourceTree = ""; }; - 92DE390311705FF1EE65C0E6 /* NftCollectionConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollectionConnection.graphql.swift; sourceTree = ""; }; - 985DDC8324B872DD44E87781 /* PortfolioBalancesQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PortfolioBalancesQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/PortfolioBalancesQuery.graphql.swift; sourceTree = ""; }; - 98F596250F55A3D6E9E1F499 /* TokenParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenParts.graphql.swift; sourceTree = ""; }; - 9DFBD29C0BDFDB4464B8189C /* TokenPriceHistoryQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenPriceHistoryQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenPriceHistoryQuery.graphql.swift; sourceTree = ""; }; - 9E975BEDBED5FE51D0DC6E96 /* TokenApproval.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenApproval.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenApproval.graphql.swift; sourceTree = ""; }; + 945EF0A3BD6F19DA14E40AE1 /* TransactionStatus.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionStatus.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TransactionStatus.graphql.swift; sourceTree = ""; }; + 95283AC560844B50E95934E4 /* TransactionListQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionListQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TransactionListQuery.graphql.swift; sourceTree = ""; }; + 957A35D2DE7A140BA198A0F1 /* TokenDetailsScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenDetailsScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenDetailsScreenQuery.graphql.swift; sourceTree = ""; }; + 9602E2A290179F8741EDD35A /* TokenPriceHistoryQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenPriceHistoryQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenPriceHistoryQuery.graphql.swift; sourceTree = ""; }; + 97FBBE677B56FFE07052EF1D /* PageInfo.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PageInfo.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/PageInfo.graphql.swift; sourceTree = ""; }; + 98F489175099FE697721272D /* NftOrderConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftOrderConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftOrderConnection.graphql.swift; sourceTree = ""; }; + 9BEBBC668E69EE3A1FC6AB05 /* TokenTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenTransfer.graphql.swift; sourceTree = ""; }; + 9EFADDBB38BB9261933C1349 /* FeedTransactionListQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FeedTransactionListQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/FeedTransactionListQuery.graphql.swift; sourceTree = ""; }; 9F00A4392B33894C0088A0D0 /* ApplicationContract.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationContract.graphql.swift; sourceTree = ""; }; 9F29D4EC2B47126D004D003A /* NftBalanceAssetInput.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NftBalanceAssetInput.graphql.swift; sourceTree = ""; }; 9F3500812A8AA5890077BFC5 /* EXSplashScreen.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = EXSplashScreen.xcframework; path = "../../../node_modules/expo-splash-screen/ios/EXSplashScreen.xcframework"; sourceTree = ""; }; @@ -737,74 +737,75 @@ 9FCEBEFF2A95A8E00079EDDB /* RNWalletConnect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RNWalletConnect.swift; path = Uniswap/WalletConnect/RNWalletConnect.swift; sourceTree = ""; }; 9FCEBF022A95A99B0079EDDB /* RCTThemeModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTThemeModule.h; path = Appearance/RCTThemeModule.h; sourceTree = ""; }; 9FCEBF032A95A99B0079EDDB /* RCTThemeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTThemeModule.m; path = Appearance/RCTThemeModule.m; sourceTree = ""; }; - 9FEC9B8A2A858CF1003CD019 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Uniswap/AppDelegate.m; sourceTree = ""; }; A32F9FBC272343C8002CFCDB /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; A3F0A5B0272B1DFA00895B25 /* KeychainSwiftDistrib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainSwiftDistrib.swift; sourceTree = ""; }; - A4E0C448C5D45BF1071FA458 /* MobileSchema.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MobileSchema.graphql.swift; path = WidgetsCore/MobileSchema/MobileSchema.graphql.swift; sourceTree = ""; }; A70E4DD32C25DA0A002D6D86 /* NetworkFee.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkFee.graphql.swift; path = MobileSchema/Schema/Objects/NetworkFee.graphql.swift; sourceTree = ""; }; A70E4DD52C260416002D6D86 /* SwapOrderType.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwapOrderType.graphql.swift; path = MobileSchema/Schema/Enums/SwapOrderType.graphql.swift; sourceTree = ""; }; A70E4DD62C260416002D6D86 /* SwapOrderStatus.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwapOrderStatus.graphql.swift; path = MobileSchema/Schema/Enums/SwapOrderStatus.graphql.swift; sourceTree = ""; }; - A7A33933670CCAD9E62A1A80 /* TokenDetailsScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenDetailsScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenDetailsScreenQuery.graphql.swift; sourceTree = ""; }; A7B8EFCA2BF68F0D00CA4A1C /* FeeData.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeeData.graphql.swift; path = MobileSchema/Schema/Objects/FeeData.graphql.swift; sourceTree = ""; }; A7C9F415D0E128A43003E071 /* Pods-Uniswap.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.debug.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.debug.xcconfig"; sourceTree = ""; }; - A9030AC59702756C39396FFE /* NftsQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftsQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NftsQuery.graphql.swift; sourceTree = ""; }; - ABE51731EB853137302A7B0D /* TransactionDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TransactionDetails.graphql.swift; sourceTree = ""; }; + A98CEFE7189D63880FD5A702 /* TokenProjectsQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectsQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenProjectsQuery.graphql.swift; sourceTree = ""; }; + AAB6B8C64F7525D0D0E978C6 /* ProtectionResult.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProtectionResult.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/ProtectionResult.graphql.swift; sourceTree = ""; }; + AB772B265A35BFBC3A7761AD /* HomeScreenTokenParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeScreenTokenParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/HomeScreenTokenParts.graphql.swift; sourceTree = ""; }; AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Uniswap/PrivacyInfo.xcprivacy; sourceTree = ""; }; AC2794442C51541E00F9AF68 /* sourcemaps-datadog.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "sourcemaps-datadog.sh"; sourceTree = ""; }; AC2EF4022C914B1600EEEFDB /* fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; name = fonts; path = ../src/assets/fonts; sourceTree = ""; }; - AF3D7A923E5E1FB504E1F64D /* TransactionHistoryUpdaterQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionHistoryUpdaterQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TransactionHistoryUpdaterQuery.graphql.swift; sourceTree = ""; }; - B033206DC974BCB6AF8CC613 /* SelectWalletScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectWalletScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/SelectWalletScreenQuery.graphql.swift; sourceTree = ""; }; + AD452B34344D670BA23EA331 /* TokenBalanceMainParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalanceMainParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBalanceMainParts.graphql.swift; sourceTree = ""; }; + AD86EFAA0D6B8FBECCC4828B /* NftAssetConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAssetConnection.graphql.swift; sourceTree = ""; }; + AE2A9FBFB7F718246B668A60 /* NftProfile.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftProfile.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftProfile.graphql.swift; sourceTree = ""; }; + AED050A208757F137DBDA57D /* ContractInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ContractInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/ContractInput.graphql.swift; sourceTree = ""; }; B0DA4D39B1A6D74A1D05B99F /* Pods-WidgetsCore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCore.debug.xcconfig"; path = "Target Support Files/Pods-WidgetsCore/Pods-WidgetsCore.debug.xcconfig"; sourceTree = ""; }; - B0DB67D6C438F623976727E9 /* TimestampedAmount.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TimestampedAmount.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TimestampedAmount.graphql.swift; sourceTree = ""; }; - B3C3F0403FAEFDD632AC6CCE /* TokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokensQuery.graphql.swift; sourceTree = ""; }; - B7E03AA02B39A7A90C96AA76 /* HomeScreenTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HomeScreenTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/HomeScreenTokensQuery.graphql.swift; sourceTree = ""; }; + B25620D6E0E297C56198F190 /* NftCollection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollection.graphql.swift; sourceTree = ""; }; + B3CC3D9DE64A39897BA580B3 /* OnRampTransactionsAuth.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnRampTransactionsAuth.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/OnRampTransactionsAuth.graphql.swift; sourceTree = ""; }; + B3F4D5E8B9ED6E0FE29EA779 /* ProtectionAttackType.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProtectionAttackType.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/ProtectionAttackType.graphql.swift; sourceTree = ""; }; + B5C2E70E43A02405C5CEAD7C /* TokenProjectMarketsParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectMarketsParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenProjectMarketsParts.graphql.swift; sourceTree = ""; }; + BA3BCD49511DE84892B34ED0 /* NftsTabQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftsTabQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NftsTabQuery.graphql.swift; sourceTree = ""; }; BA869E362D56B0B600D7A718 /* WidgetTokensQuery.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetTokensQuery.graphql.swift; sourceTree = ""; }; + BB76D1207400E308430A837F /* TokenParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenParts.graphql.swift; sourceTree = ""; }; BCB2A43E5FB0D7B69CA02312 /* Pods-WidgetsCore.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCore.dev.xcconfig"; path = "Target Support Files/Pods-WidgetsCore/Pods-WidgetsCore.dev.xcconfig"; sourceTree = ""; }; - BD0FC2D534CB82B151C7F9B7 /* ProtectionResult.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProtectionResult.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/ProtectionResult.graphql.swift; sourceTree = ""; }; - BD35300F81746B2D4A23065A /* BlockaidFees.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BlockaidFees.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/BlockaidFees.graphql.swift; sourceTree = ""; }; BF9176E944C84910B1C0B057 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Uniswap/SplashScreen.storyboard; sourceTree = ""; }; - BFC36D963A2A52D1E7CA77E4 /* TokenMarketParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenMarketParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenMarketParts.graphql.swift; sourceTree = ""; }; + BFFD4C1E5A4B30DE3CE02DB4 /* NftAssetEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAssetEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAssetEdge.graphql.swift; sourceTree = ""; }; + C0359DDEA2E2E17759FCC5CF /* SwapOrderStatus.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwapOrderStatus.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/SwapOrderStatus.graphql.swift; sourceTree = ""; }; C26D739993D5C939C6FBB58A /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Uniswap-UniswapTests/ExpoModulesProvider.swift"; sourceTree = ""; }; - C4790C2A43570CAC086B936E /* NftCollectionsFilterInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionsFilterInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/NftCollectionsFilterInput.graphql.swift; sourceTree = ""; }; - C65195A1CC9894C6341385C3 /* SchemaMetadata.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SchemaMetadata.graphql.swift; path = WidgetsCore/MobileSchema/Schema/SchemaMetadata.graphql.swift; sourceTree = ""; }; + C4CA32B2B2997D73A0185635 /* OffRampTransactionDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OffRampTransactionDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OffRampTransactionDetails.graphql.swift; sourceTree = ""; }; C89238E3ED9F3AC98876B573 /* Pods-WidgetsCoreTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCoreTests.release.xcconfig"; path = "Target Support Files/Pods-WidgetsCoreTests/Pods-WidgetsCoreTests.release.xcconfig"; sourceTree = ""; }; - C955CE2592785712A11892DE /* SwapOrderDetails.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwapOrderDetails.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/SwapOrderDetails.graphql.swift; sourceTree = ""; }; - C9D5E6A07F404974045BD525 /* OffRampTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OffRampTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/OffRampTransfer.graphql.swift; sourceTree = ""; }; + C8E5B09464C768590127BFA6 /* TokenMarket.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenMarket.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenMarket.graphql.swift; sourceTree = ""; }; + CA3BFDC64C5E0A058877A348 /* NftCollectionScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NftCollectionScreenQuery.graphql.swift; sourceTree = ""; }; + CAC62CDEEBE5712219A457C7 /* BridgedWithdrawalInfo.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BridgedWithdrawalInfo.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/BridgedWithdrawalInfo.graphql.swift; sourceTree = ""; }; CB29AC0C0907A833F23D2C30 /* Pods_WidgetsCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WidgetsCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - CC055C3EDFDE2966FCA83C9E /* TokenProjectMarketsParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectMarketsParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenProjectMarketsParts.graphql.swift; sourceTree = ""; }; - CC2FBE9F353D3753FB94B8BF /* TransactionDirection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransactionDirection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TransactionDirection.graphql.swift; sourceTree = ""; }; - D20E9461A6C9C1CD6A24BEED /* Amount.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Amount.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Amount.graphql.swift; sourceTree = ""; }; - D574AB5194929038A54B4D4B /* SwapOrderStatus.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwapOrderStatus.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/SwapOrderStatus.graphql.swift; sourceTree = ""; }; + CBE0699A65BEEEE6E9A6B03F /* NftActivityConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftActivityConnection.graphql.swift; sourceTree = ""; }; + CD0CA37FB709C06F123EBF74 /* Currency.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Currency.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/Currency.graphql.swift; sourceTree = ""; }; + CF4115ACC8D8F0C7529AABA7 /* SchemaConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SchemaConfiguration.swift; path = WidgetsCore/MobileSchema/Schema/SchemaConfiguration.swift; sourceTree = ""; }; + D13E592852F52B2855F05A5E /* TokenBasicInfoParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBasicInfoParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBasicInfoParts.graphql.swift; sourceTree = ""; }; + D34AC1057AD14362BB91E88B /* TokenStandard.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenStandard.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TokenStandard.graphql.swift; sourceTree = ""; }; + D4C8174A3A9EB3244AA844F4 /* TokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokensQuery.graphql.swift; sourceTree = ""; }; D79B717BEAEA7857469D770A /* Pods-Widgets.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Widgets.debug.xcconfig"; path = "Target Support Files/Pods-Widgets/Pods-Widgets.debug.xcconfig"; sourceTree = ""; }; - DB35E518AD7FBFEBEA9CAB95 /* NftProfile.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftProfile.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftProfile.graphql.swift; sourceTree = ""; }; - DBA33683D2BA6BBE3251D67C /* WidgetTokensQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WidgetTokensQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/WidgetTokensQuery.graphql.swift; sourceTree = ""; }; - E4624A41EC1468712BD77653 /* AssetActivity.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssetActivity.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/AssetActivity.graphql.swift; sourceTree = ""; }; - E475EBABF7EA310435E2F19C /* PageInfo.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PageInfo.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/PageInfo.graphql.swift; sourceTree = ""; }; - E63EA0C75D8011BCC182492C /* AmountChange.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AmountChange.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/AmountChange.graphql.swift; sourceTree = ""; }; - E8A49D011C60137D4034CAC4 /* TokenProjectsQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenProjectsQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/TokenProjectsQuery.graphql.swift; sourceTree = ""; }; - EBD833F6580F9B82A68D4A98 /* TokenMarket.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenMarket.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenMarket.graphql.swift; sourceTree = ""; }; - ED157D3DB73302C8A1B9522E /* Query.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Query.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Query.graphql.swift; sourceTree = ""; }; + D91432EF6C35F5A6301340A2 /* DescriptionTranslations.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DescriptionTranslations.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/DescriptionTranslations.graphql.swift; sourceTree = ""; }; + DB2B02717B1A87BD41920151 /* NftBalance.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftBalance.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftBalance.graphql.swift; sourceTree = ""; }; + DD111A711D83D1D82E4025A9 /* NftApproval.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftApproval.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftApproval.graphql.swift; sourceTree = ""; }; + DD12FD368B3A96F323444D8E /* ProtectionInfo.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProtectionInfo.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/ProtectionInfo.graphql.swift; sourceTree = ""; }; + DD56CBC543897BE8E37CC2C1 /* TokenApproval.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenApproval.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenApproval.graphql.swift; sourceTree = ""; }; + DE735ED4766E65C0D6808F9A /* NftsQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftsQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NftsQuery.graphql.swift; sourceTree = ""; }; + E09D12605BAA868AB51573BE /* PortfolioValueModifier.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PortfolioValueModifier.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/PortfolioValueModifier.graphql.swift; sourceTree = ""; }; + E0A53C3A09AD259B9A8EDBDD /* PortfolioBalancesQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PortfolioBalancesQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/PortfolioBalancesQuery.graphql.swift; sourceTree = ""; }; + EB9CACD175D6C20B6D4707C8 /* SelectWalletScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SelectWalletScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/SelectWalletScreenQuery.graphql.swift; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - EE0973BB445AAD8FAA25757F /* NftCollectionScreenQuery.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionScreenQuery.graphql.swift; path = WidgetsCore/MobileSchema/Operations/Queries/NftCollectionScreenQuery.graphql.swift; sourceTree = ""; }; - F0C34E94906CB9F0A016D566 /* TokenBalanceParts.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalanceParts.graphql.swift; path = WidgetsCore/MobileSchema/Fragments/TokenBalanceParts.graphql.swift; sourceTree = ""; }; - F1A24D364C0D262A14BB7182 /* NftActivity.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivity.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftActivity.graphql.swift; sourceTree = ""; }; F1A3F4DDD7E40DA9E4BBAAD1 /* Pods-Uniswap-UniswapTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap-UniswapTests.release.xcconfig"; path = "Target Support Files/Pods-Uniswap-UniswapTests/Pods-Uniswap-UniswapTests.release.xcconfig"; sourceTree = ""; }; - F2932B486DD7070DF226A27B /* NftAsset.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftAsset.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftAsset.graphql.swift; sourceTree = ""; }; - F3317EBD4E3B11E2F2A1CE58 /* NftTransfer.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftTransfer.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftTransfer.graphql.swift; sourceTree = ""; }; + F286C514E849B29F5235310A /* NftCollectionMarket.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionMarket.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollectionMarket.graphql.swift; sourceTree = ""; }; F35AFD3627EE49230011A725 /* Uniswap.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Uniswap.entitlements; path = Uniswap/Uniswap.entitlements; sourceTree = ""; }; F35AFD3B27EE49990011A725 /* OneSignalNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OneSignalNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; F35AFD3D27EE49990011A725 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; F35AFD3F27EE49990011A725 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F35AFD4727EE4B400011A725 /* OneSignalNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OneSignalNotificationServiceExtension.entitlements; sourceTree = ""; }; - F4B149117182C6CC58DD570C /* TokenBalance.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenBalance.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/TokenBalance.graphql.swift; sourceTree = ""; }; + F4C3461E088654B80ABE3DE7 /* Amount.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Amount.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/Amount.graphql.swift; sourceTree = ""; }; F56CC08FBB20FAC0DF6B93DA /* Pods-OneSignalNotificationServiceExtension.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.beta.xcconfig"; path = "Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.beta.xcconfig"; sourceTree = ""; }; - F5BCBB5D46312050E4A5BD64 /* NftApproveForAll.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftApproveForAll.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftApproveForAll.graphql.swift; sourceTree = ""; }; - F7B3C6DF5DED22E892851B10 /* ContractInput.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ContractInput.graphql.swift; path = WidgetsCore/MobileSchema/Schema/InputObjects/ContractInput.graphql.swift; sourceTree = ""; }; - F912AB9AB67808B740246798 /* NftActivityConnection.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftActivityConnection.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftActivityConnection.graphql.swift; sourceTree = ""; }; - FC419570F5A025E80AB10A72 /* TokenStandard.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TokenStandard.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Enums/TokenStandard.graphql.swift; sourceTree = ""; }; + F70593F94C8B315F5304B9BE /* NftCollectionEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftCollectionEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftCollectionEdge.graphql.swift; sourceTree = ""; }; + FD158681F2E84A5E49656B11 /* NftOrderEdge.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftOrderEdge.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftOrderEdge.graphql.swift; sourceTree = ""; }; FD54D51C296C79A4007A37E9 /* GoogleServiceInfo */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GoogleServiceInfo; sourceTree = SOURCE_ROOT; }; FD7304CD28A364FC0085BDEA /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Colors.xcassets; path = Uniswap/Colors.xcassets; sourceTree = ""; }; FD7304CF28A3650A0085BDEA /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Colors.swift; path = Uniswap/Colors.swift; sourceTree = ""; }; + FEE1CB436B299B0349BA9957 /* FeeData.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FeeData.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/FeeData.graphql.swift; sourceTree = ""; }; + FF1B893C0275DAAA156B0108 /* NftApproveForAll.graphql.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NftApproveForAll.graphql.swift; path = WidgetsCore/MobileSchema/Schema/Objects/NftApproveForAll.graphql.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1204,8 +1205,7 @@ 07B067692A7D6EC8001DD9B9 /* Widget */, 03DD298C2A4CE34B00E3E0F5 /* Appearance */, F35AFD3627EE49230011A725 /* Uniswap.entitlements */, - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 9FEC9B8A2A858CF1003CD019 /* AppDelegate.m */, + 45FFF7DD2E8C2A3A00362570 /* Notifications */, 6C84F055283D83CF0071FA2E /* Onboarding */, 6CE631B928186D4500716D29 /* WalletConnect */, A32F9FBC272343C8002CFCDB /* GoogleService-Info.plist */, @@ -1213,14 +1213,34 @@ 6CA91BDD2A95226200C4063E /* RNCloudBackupsManager */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB71A68108700A75B9A /* main.m */, BF9176E944C84910B1C0B057 /* SplashScreen.storyboard */, FD7304CD28A364FC0085BDEA /* Colors.xcassets */, FD7304CF28A3650A0085BDEA /* Colors.swift */, + 8B2A92162EB3E78E00990413 /* AppDelegate.swift */, + 8B2A92182EB3E79500990413 /* Uniswap-Bridging-Header.h */, ); name = Uniswap; sourceTree = ""; }; + 157C3B73F4388F6F1274463F /* Operations */ = { + isa = PBXGroup; + children = ( + 5AF8BB7C2143FF285A912E6A /* Queries */, + ); + name = Operations; + sourceTree = ""; + }; + 281687A9A85A9EF49839548D /* MobileSchema */ = { + isa = PBXGroup; + children = ( + 87442A651C068E552BEA89C6 /* MobileSchema.graphql.swift */, + 308DDBB1BED0BB4E93F1AD8C /* Fragments */, + 157C3B73F4388F6F1274463F /* Operations */, + 4C36556DE2985BABC27C13AD /* Schema */, + ); + name = MobileSchema; + sourceTree = ""; + }; 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1240,6 +1260,36 @@ name = Frameworks; sourceTree = ""; }; + 308DDBB1BED0BB4E93F1AD8C /* Fragments */ = { + isa = PBXGroup; + children = ( + AB772B265A35BFBC3A7761AD /* HomeScreenTokenParts.graphql.swift */, + AD452B34344D670BA23EA331 /* TokenBalanceMainParts.graphql.swift */, + 65AD13BFAD4EBA18AA6205E3 /* TokenBalanceParts.graphql.swift */, + 5B0A1F5F6FC3E1141E228B7D /* TokenBalanceQuantityParts.graphql.swift */, + D13E592852F52B2855F05A5E /* TokenBasicInfoParts.graphql.swift */, + 2E8286984864AD69A0FAE3BC /* TokenBasicProjectParts.graphql.swift */, + 1166529407CA13E4D4F24F12 /* TokenFeeDataParts.graphql.swift */, + 55081ADF188C967FC1ECD289 /* TokenMarketParts.graphql.swift */, + BB76D1207400E308430A837F /* TokenParts.graphql.swift */, + B5C2E70E43A02405C5CEAD7C /* TokenProjectMarketsParts.graphql.swift */, + 190523DBBA423868C74FF80D /* TokenProjectUrlsParts.graphql.swift */, + 835D9DF76533D22EDF0E9D67 /* TokenProtectionInfoParts.graphql.swift */, + 07F0E504152E22110917353E /* TopTokenParts.graphql.swift */, + ); + name = Fragments; + sourceTree = ""; + }; + 45FFF7DD2E8C2A3A00362570 /* Notifications */ = { + isa = PBXGroup; + children = ( + 45FFF7E02E8C2E6100362570 /* SilentPushEventEmitter.swift */, + 45FFF7DE2E8C2A6400362570 /* SilentPushEventEmitter.m */, + ); + name = Notifications; + path = Uniswap/Notifications; + sourceTree = ""; + }; 47914D9EE3A4DE926EFC5089 /* UniswapTests */ = { isa = PBXGroup; children = ( @@ -1248,27 +1298,18 @@ name = UniswapTests; sourceTree = ""; }; - 4EEA950E66FC2881D331F823 /* Enums */ = { + 4C36556DE2985BABC27C13AD /* Schema */ = { isa = PBXGroup; children = ( - 4A8990A17F489F284E0DB1B3 /* Chain.graphql.swift */, - 25B4C7C7FEF32EAF10D030F9 /* Currency.graphql.swift */, - 3BF42F88002431BFDC9FCA9F /* HistoryDuration.graphql.swift */, - 2D95FE36D2667E070345CDD0 /* NftActivityType.graphql.swift */, - 600250B00B6C02AAB063818B /* NftMarketplace.graphql.swift */, - 4B308EC3DB7C3C1DD5DDE03D /* NftStandard.graphql.swift */, - 40E034582FBD2C374CEF808E /* ProtectionAttackType.graphql.swift */, - BD0FC2D534CB82B151C7F9B7 /* ProtectionResult.graphql.swift */, - 4ACFA5B4204741038C986C5D /* SafetyLevel.graphql.swift */, - D574AB5194929038A54B4D4B /* SwapOrderStatus.graphql.swift */, - 148617FD73F2CD7117960DBA /* SwapOrderType.graphql.swift */, - 32012AC135EBD991C8F02F92 /* TokenSortableField.graphql.swift */, - FC419570F5A025E80AB10A72 /* TokenStandard.graphql.swift */, - CC2FBE9F353D3753FB94B8BF /* TransactionDirection.graphql.swift */, - 2ABA47847A73130D356EB6BF /* TransactionStatus.graphql.swift */, - 69F2FFD11FB5E462CC454A15 /* TransactionType.graphql.swift */, + CF4115ACC8D8F0C7529AABA7 /* SchemaConfiguration.swift */, + 522135E54BDB258B45B5A694 /* SchemaMetadata.graphql.swift */, + F054FF4C0D351C1EAF0012D5 /* Enums */, + F498443CD9DA8803A092F3D9 /* InputObjects */, + C28876A002BC02672EE6A673 /* Interfaces */, + B83DA94147128EE9CC2DB246 /* Objects */, + 971EB6FCC042316AEFF90697 /* Unions */, ); - name = Enums; + name = Schema; sourceTree = ""; }; 5754C6A1B51170788A63F6F3 /* ExpoModulesProviders */ = { @@ -1283,11 +1324,39 @@ 5841D897B122046172ACD989 /* WidgetsCore */ = { isa = PBXGroup; children = ( - 8D57A3CBC005A567F7303961 /* MobileSchema */, + 281687A9A85A9EF49839548D /* MobileSchema */, ); name = WidgetsCore; sourceTree = ""; }; + 5AF8BB7C2143FF285A912E6A /* Queries */ = { + isa = PBXGroup; + children = ( + 6E4DEE7FC8D7BCD030CFA4D3 /* ConvertQuery.graphql.swift */, + 3B03A0A6AB0957A512A2D225 /* FavoriteTokenCardQuery.graphql.swift */, + 9EFADDBB38BB9261933C1349 /* FeedTransactionListQuery.graphql.swift */, + 0E7FF51A507BF788E64E3EFE /* HomeScreenTokensQuery.graphql.swift */, + 595699514939A2CE780AF0CF /* MultiplePortfolioBalancesQuery.graphql.swift */, + 03AB6B21A1BFB3A18982B47E /* NFTItemScreenQuery.graphql.swift */, + CA3BFDC64C5E0A058877A348 /* NftCollectionScreenQuery.graphql.swift */, + DE735ED4766E65C0D6808F9A /* NftsQuery.graphql.swift */, + BA3BCD49511DE84892B34ED0 /* NftsTabQuery.graphql.swift */, + E0A53C3A09AD259B9A8EDBDD /* PortfolioBalancesQuery.graphql.swift */, + EB9CACD175D6C20B6D4707C8 /* SelectWalletScreenQuery.graphql.swift */, + 957A35D2DE7A140BA198A0F1 /* TokenDetailsScreenQuery.graphql.swift */, + 9602E2A290179F8741EDD35A /* TokenPriceHistoryQuery.graphql.swift */, + 1276D33B06E840B8E6839F39 /* TokenProjectDescriptionQuery.graphql.swift */, + A98CEFE7189D63880FD5A702 /* TokenProjectsQuery.graphql.swift */, + 0914ECF70C274E2B064BCD42 /* TokenQuery.graphql.swift */, + D4C8174A3A9EB3244AA844F4 /* TokensQuery.graphql.swift */, + 243576FAC8D2801F9D99ECC9 /* TopTokensQuery.graphql.swift */, + 559CBC7F568C769C48E7C6F4 /* TransactionHistoryUpdaterQuery.graphql.swift */, + 95283AC560844B50E95934E4 /* TransactionListQuery.graphql.swift */, + 6F0A91F56B1CA2E71B479BAD /* WidgetTokensQuery.graphql.swift */, + ); + name = Queries; + sourceTree = ""; + }; 5B4398EB2DD3B22C00F6BE08 /* PrivateKeyDisplay */ = { isa = PBXGroup; children = ( @@ -1403,17 +1472,6 @@ name = Products; sourceTree = ""; }; - 8D57A3CBC005A567F7303961 /* MobileSchema */ = { - isa = PBXGroup; - children = ( - A4E0C448C5D45BF1071FA458 /* MobileSchema.graphql.swift */, - CA8FF4CDF9B448F7BE77CA07 /* Fragments */, - F79593090BE522E6D2A3330F /* Operations */, - 9723B33A3716C9068F5FD2B1 /* Schema */, - ); - name = MobileSchema; - sourceTree = ""; - }; 8E566D9F2AA1095000D4AA76 /* Components */ = { isa = PBXGroup; children = ( @@ -1462,18 +1520,13 @@ path = Uniswap/Icons; sourceTree = ""; }; - 9723B33A3716C9068F5FD2B1 /* Schema */ = { + 971EB6FCC042316AEFF90697 /* Unions */ = { isa = PBXGroup; children = ( - 2E49E724432C70A2551FB2C7 /* SchemaConfiguration.swift */, - C65195A1CC9894C6341385C3 /* SchemaMetadata.graphql.swift */, - 4EEA950E66FC2881D331F823 /* Enums */, - B741FF3580D33B5968CB9841 /* InputObjects */, - A60630E00A276B08BE85275D /* Interfaces */, - AB0BCF231B03A3F4E547DFF5 /* Objects */, - F23767E14A7943D5969AFDC1 /* Unions */, + 075E4C1DFAA00EE5B3CE792D /* ActivityDetails.graphql.swift */, + 6D4C849A7805FCCEE8C8C3E6 /* AssetChange.graphql.swift */, ); - name = Schema; + name = Unions; sourceTree = ""; }; 9759A762F61D6B2F01C79DBF /* Uniswap */ = { @@ -1484,87 +1537,72 @@ name = Uniswap; sourceTree = ""; }; - A60630E00A276B08BE85275D /* Interfaces */ = { + B83DA94147128EE9CC2DB246 /* Objects */ = { isa = PBXGroup; children = ( - 03AD8C2B1226EBE1524F2573 /* IAmount.graphql.swift */, - 5E0D68CC8D1F13D864F069BB /* IContract.graphql.swift */, - ); - name = Interfaces; - sourceTree = ""; - }; - AB0BCF231B03A3F4E547DFF5 /* Objects */ = { - isa = PBXGroup; - children = ( - D20E9461A6C9C1CD6A24BEED /* Amount.graphql.swift */, - E63EA0C75D8011BCC182492C /* AmountChange.graphql.swift */, - 608B07304ED1387B5AEAA3BB /* ApplicationContract.graphql.swift */, - E4624A41EC1468712BD77653 /* AssetActivity.graphql.swift */, - BD35300F81746B2D4A23065A /* BlockaidFees.graphql.swift */, - 7E1F288B7EDDE906C4C00C46 /* DescriptionTranslations.graphql.swift */, - 54B1C71AED738D17898103A6 /* Dimensions.graphql.swift */, - 17819A49DE69723EC60D9D72 /* FeeData.graphql.swift */, - 31ED613D63FE9C56D9270517 /* Image.graphql.swift */, - 8BEADEB4AE05B860CF2232D1 /* NetworkFee.graphql.swift */, - F1A24D364C0D262A14BB7182 /* NftActivity.graphql.swift */, - F912AB9AB67808B740246798 /* NftActivityConnection.graphql.swift */, - 4DB4379B5E222207CA7328D0 /* NftActivityEdge.graphql.swift */, - 4E32BBB65A6DD9CFF608C8E8 /* NftApproval.graphql.swift */, - F5BCBB5D46312050E4A5BD64 /* NftApproveForAll.graphql.swift */, - F2932B486DD7070DF226A27B /* NftAsset.graphql.swift */, - 218B8AB05C80D919671D3970 /* NftAssetConnection.graphql.swift */, - 6DF7C8888BE6AFA2F22889FE /* NftAssetEdge.graphql.swift */, - 70851E3D1A4B296A1CE22A20 /* NftAssetTrait.graphql.swift */, - 05BE2F49DD1FB9A692E21C13 /* NftBalance.graphql.swift */, - 1FC7B80EC0E0D2134B67575B /* NftBalanceConnection.graphql.swift */, - 0B132543F80E3C9191219E6D /* NftBalanceEdge.graphql.swift */, - 63ACFF2A1D5AE3498731AC69 /* NftCollection.graphql.swift */, - 92DE390311705FF1EE65C0E6 /* NftCollectionConnection.graphql.swift */, - 37038CCEB11E70E2027EF4EA /* NftCollectionEdge.graphql.swift */, - 4F4F47D22B749A438B5BF674 /* NftCollectionMarket.graphql.swift */, - 18B9EB43501127BE1B14F1A5 /* NftContract.graphql.swift */, - 6E3A78386B3B779B688021FF /* NftOrder.graphql.swift */, - 39FC2A13550FA6754EC1A7FC /* NftOrderConnection.graphql.swift */, - 49BD6C776029FAF4FC711DE7 /* NftOrderEdge.graphql.swift */, - DB35E518AD7FBFEBEA9CAB95 /* NftProfile.graphql.swift */, - F3317EBD4E3B11E2F2A1CE58 /* NftTransfer.graphql.swift */, - 396B6AB3BB41898B56EB3828 /* OffRampTransactionDetails.graphql.swift */, - C9D5E6A07F404974045BD525 /* OffRampTransfer.graphql.swift */, - 6A84A192F750A0F1BC8D7A69 /* OnRampServiceProvider.graphql.swift */, - 12D3AB6C2FC9513E9F224B7C /* OnRampTransactionDetails.graphql.swift */, - 2139ABF56EA067ED792A48C9 /* OnRampTransfer.graphql.swift */, - E475EBABF7EA310435E2F19C /* PageInfo.graphql.swift */, - 1849039FD4A210DA990363C8 /* Portfolio.graphql.swift */, - 0702203B17BDD858CED28F8B /* ProtectionInfo.graphql.swift */, - ED157D3DB73302C8A1B9522E /* Query.graphql.swift */, - C955CE2592785712A11892DE /* SwapOrderDetails.graphql.swift */, - B0DB67D6C438F623976727E9 /* TimestampedAmount.graphql.swift */, - 647933A5DC35BDFEEF3620A1 /* Token.graphql.swift */, - 9E975BEDBED5FE51D0DC6E96 /* TokenApproval.graphql.swift */, - F4B149117182C6CC58DD570C /* TokenBalance.graphql.swift */, - EBD833F6580F9B82A68D4A98 /* TokenMarket.graphql.swift */, - 4981A14906A35E8A0B7F9457 /* TokenProject.graphql.swift */, - 3B9A505509D9A85BE1DB7D05 /* TokenProjectMarket.graphql.swift */, - 8DA4E7C052A6C4AC3A9DCDA6 /* TokenTransfer.graphql.swift */, - ABE51731EB853137302A7B0D /* TransactionDetails.graphql.swift */, + F4C3461E088654B80ABE3DE7 /* Amount.graphql.swift */, + 066BBBD815CF2DE8803338BC /* AmountChange.graphql.swift */, + 59DD4D834D7B42A6D02F4F12 /* ApplicationContract.graphql.swift */, + 05C66C49AF2B78703FCE4C7C /* AssetActivity.graphql.swift */, + 3EF9AEC00868BFD7CEBF202C /* BlockaidFees.graphql.swift */, + CAC62CDEEBE5712219A457C7 /* BridgedWithdrawalInfo.graphql.swift */, + D91432EF6C35F5A6301340A2 /* DescriptionTranslations.graphql.swift */, + 764C4757EB194D13A8857DFA /* Dimensions.graphql.swift */, + FEE1CB436B299B0349BA9957 /* FeeData.graphql.swift */, + 8C9047B981FA322A727947F7 /* Image.graphql.swift */, + 09BE4B220A4F8465C3D01BA5 /* NetworkFee.graphql.swift */, + 55C67E53A2C8AEB6AE92E33F /* NftActivity.graphql.swift */, + CBE0699A65BEEEE6E9A6B03F /* NftActivityConnection.graphql.swift */, + 043C7D80D6722AFCF1715D07 /* NftActivityEdge.graphql.swift */, + DD111A711D83D1D82E4025A9 /* NftApproval.graphql.swift */, + FF1B893C0275DAAA156B0108 /* NftApproveForAll.graphql.swift */, + 5C9F3B31FC6AF8CE30CD7DA2 /* NftAsset.graphql.swift */, + AD86EFAA0D6B8FBECCC4828B /* NftAssetConnection.graphql.swift */, + BFFD4C1E5A4B30DE3CE02DB4 /* NftAssetEdge.graphql.swift */, + 85B43D489CC5ACF1EA34E9EB /* NftAssetTrait.graphql.swift */, + DB2B02717B1A87BD41920151 /* NftBalance.graphql.swift */, + 62DDC1F42E7172783EAF3776 /* NftBalanceConnection.graphql.swift */, + 4DD55DFFC717D37DD960B82E /* NftBalanceEdge.graphql.swift */, + B25620D6E0E297C56198F190 /* NftCollection.graphql.swift */, + 0D09FFE2B5E56CDCAB2F9455 /* NftCollectionConnection.graphql.swift */, + F70593F94C8B315F5304B9BE /* NftCollectionEdge.graphql.swift */, + F286C514E849B29F5235310A /* NftCollectionMarket.graphql.swift */, + 55A243177E9FA92A0524A222 /* NftContract.graphql.swift */, + 67C0D95F7EA9E7ACA9B18692 /* NftOrder.graphql.swift */, + 98F489175099FE697721272D /* NftOrderConnection.graphql.swift */, + FD158681F2E84A5E49656B11 /* NftOrderEdge.graphql.swift */, + AE2A9FBFB7F718246B668A60 /* NftProfile.graphql.swift */, + 74AD16E33DC518A42C386FE2 /* NftTransfer.graphql.swift */, + C4CA32B2B2997D73A0185635 /* OffRampTransactionDetails.graphql.swift */, + 166A9AE7203545C397E683E8 /* OffRampTransfer.graphql.swift */, + 5BED80DB034487FCA1450EDB /* OnRampServiceProvider.graphql.swift */, + 87CF9B03AE29A19C5B0F4E35 /* OnRampTransactionDetails.graphql.swift */, + 3E5D1A04B42B25218C53C107 /* OnRampTransfer.graphql.swift */, + 97FBBE677B56FFE07052EF1D /* PageInfo.graphql.swift */, + 5397E77A577E9952D2477020 /* Portfolio.graphql.swift */, + DD12FD368B3A96F323444D8E /* ProtectionInfo.graphql.swift */, + 5956ECBE73216D266D8D2E86 /* Query.graphql.swift */, + 53F4E5E9341BE66984935185 /* SwapOrderDetails.graphql.swift */, + 31CBA4397C44A04E218AEF28 /* TimestampedAmount.graphql.swift */, + 2FB1590D18A58D37C750D9AC /* Token.graphql.swift */, + DD56CBC543897BE8E37CC2C1 /* TokenApproval.graphql.swift */, + 181209B4CDE80BDC80891CFD /* TokenBalance.graphql.swift */, + C8E5B09464C768590127BFA6 /* TokenMarket.graphql.swift */, + 8856DFFC7FDC9BF105B0B4E3 /* TokenProject.graphql.swift */, + 0B0505DB30407C221B6A8491 /* TokenProjectMarket.graphql.swift */, + 9BEBBC668E69EE3A1FC6AB05 /* TokenTransfer.graphql.swift */, + 55240C84512D3851C8B9F027 /* TransactionDetails.graphql.swift */, ); name = Objects; sourceTree = ""; }; - B741FF3580D33B5968CB9841 /* InputObjects */ = { + C28876A002BC02672EE6A673 /* Interfaces */ = { isa = PBXGroup; children = ( - F7B3C6DF5DED22E892851B10 /* ContractInput.graphql.swift */, - 43E2ECCEFBC3BE8D13BE6CAB /* NftActivityFilterInput.graphql.swift */, - 556339705BD103365D4ED07B /* NftAssetTraitInput.graphql.swift */, - 8B477EBEA1EDA3914C77E8A6 /* NftAssetsFilterInput.graphql.swift */, - 4E5FA43C5E31035775917C4E /* NftBalanceAssetInput.graphql.swift */, - 41BA2129FFE961C8549C8A30 /* NftBalancesFilterInput.graphql.swift */, - C4790C2A43570CAC086B936E /* NftCollectionsFilterInput.graphql.swift */, - 315B8A905FA2C60C053F138B /* OnRampTransactionsAuth.graphql.swift */, - 41003491EF939043CF9D0F4E /* PortfolioValueModifier.graphql.swift */, + 37376E4CF3A4B76E9DAE150C /* IAmount.graphql.swift */, + 1D9189C818AFF8E988F861D0 /* IContract.graphql.swift */, ); - name = InputObjects; + name = Interfaces; sourceTree = ""; }; C2C18ECBEF5A4489BF3A314C /* Resources */ = { @@ -1577,56 +1615,6 @@ name = Resources; sourceTree = ""; }; - CA8FF4CDF9B448F7BE77CA07 /* Fragments */ = { - isa = PBXGroup; - children = ( - 27F8B915042BD8D67D9627EA /* HomeScreenTokenParts.graphql.swift */, - 137696908D703632CEE3AB28 /* TokenBalanceMainParts.graphql.swift */, - F0C34E94906CB9F0A016D566 /* TokenBalanceParts.graphql.swift */, - 2370BAD675DC8064C00AC037 /* TokenBalanceQuantityParts.graphql.swift */, - 3377F7BB38BD5A45AE31909D /* TokenBasicInfoParts.graphql.swift */, - 2C2F4BE3080D4129D4478528 /* TokenBasicProjectParts.graphql.swift */, - 018603D0FA4D05DBF16F1441 /* TokenFeeDataParts.graphql.swift */, - BFC36D963A2A52D1E7CA77E4 /* TokenMarketParts.graphql.swift */, - 98F596250F55A3D6E9E1F499 /* TokenParts.graphql.swift */, - CC055C3EDFDE2966FCA83C9E /* TokenProjectMarketsParts.graphql.swift */, - 236126979349098B98C70F27 /* TokenProjectUrlsParts.graphql.swift */, - 0BB4A40E30E1D985519DB3CF /* TokenProtectionInfoParts.graphql.swift */, - 5640432978873FB030467750 /* TopTokenParts.graphql.swift */, - ); - name = Fragments; - sourceTree = ""; - }; - D9DDAE0E99B4703AF96A81AB /* Queries */ = { - isa = PBXGroup; - children = ( - 7BD3F0794908635CDFA7DAB6 /* ConvertQuery.graphql.swift */, - 84997376A0B436ECC0A996C5 /* ExploreSearchQuery.graphql.swift */, - 3F0EEAC1DADE827AE38EB8A6 /* FavoriteTokenCardQuery.graphql.swift */, - 2BA0ACE5B9B2C41187CD41F3 /* FeedTransactionListQuery.graphql.swift */, - B7E03AA02B39A7A90C96AA76 /* HomeScreenTokensQuery.graphql.swift */, - 4429CA6DA254ECC24A65DA14 /* MultiplePortfolioBalancesQuery.graphql.swift */, - 207F4DF7664454C31DE069F8 /* NFTItemScreenQuery.graphql.swift */, - EE0973BB445AAD8FAA25757F /* NftCollectionScreenQuery.graphql.swift */, - A9030AC59702756C39396FFE /* NftsQuery.graphql.swift */, - 730CA356D6E1D498CC0C1472 /* NftsTabQuery.graphql.swift */, - 985DDC8324B872DD44E87781 /* PortfolioBalancesQuery.graphql.swift */, - 8A5342DEF1410E0E81E97F40 /* SearchTokensQuery.graphql.swift */, - B033206DC974BCB6AF8CC613 /* SelectWalletScreenQuery.graphql.swift */, - A7A33933670CCAD9E62A1A80 /* TokenDetailsScreenQuery.graphql.swift */, - 9DFBD29C0BDFDB4464B8189C /* TokenPriceHistoryQuery.graphql.swift */, - 6FF8B660E9C2C0D8DDF7588A /* TokenProjectDescriptionQuery.graphql.swift */, - E8A49D011C60137D4034CAC4 /* TokenProjectsQuery.graphql.swift */, - 74885865ED8CFEA875E40916 /* TokenQuery.graphql.swift */, - B3C3F0403FAEFDD632AC6CCE /* TokensQuery.graphql.swift */, - 04E5C2900254E7BA7F10CE51 /* TopTokensQuery.graphql.swift */, - AF3D7A923E5E1FB504E1F64D /* TransactionHistoryUpdaterQuery.graphql.swift */, - 224CA82F1871016F4173F69E /* TransactionListQuery.graphql.swift */, - DBA33683D2BA6BBE3251D67C /* WidgetTokensQuery.graphql.swift */, - ); - name = Queries; - sourceTree = ""; - }; E233CBF5F47BEE60B243DCF8 /* Pods */ = { isa = PBXGroup; children = ( @@ -1662,13 +1650,27 @@ path = Pods; sourceTree = ""; }; - F23767E14A7943D5969AFDC1 /* Unions */ = { + F054FF4C0D351C1EAF0012D5 /* Enums */ = { isa = PBXGroup; children = ( - 8A4EB7BAD2C90E61FD6C30AF /* ActivityDetails.graphql.swift */, - 39F60B2B54796DC978765C99 /* AssetChange.graphql.swift */, + 7DBCFBC330BF97BA2F630908 /* Chain.graphql.swift */, + CD0CA37FB709C06F123EBF74 /* Currency.graphql.swift */, + 1053483279043EED7612BE90 /* HistoryDuration.graphql.swift */, + 1CDAC035887C3908C3BD1A77 /* NftActivityType.graphql.swift */, + 83FB27D93A9468E6DB47231C /* NftMarketplace.graphql.swift */, + 0373C61A7AF015058A502CE2 /* NftStandard.graphql.swift */, + B3F4D5E8B9ED6E0FE29EA779 /* ProtectionAttackType.graphql.swift */, + AAB6B8C64F7525D0D0E978C6 /* ProtectionResult.graphql.swift */, + 22F8093091A9E5FC0D6284BA /* SafetyLevel.graphql.swift */, + C0359DDEA2E2E17759FCC5CF /* SwapOrderStatus.graphql.swift */, + 7AFDF569E43F173C2F5A07AF /* SwapOrderType.graphql.swift */, + 51F133663B435829DE47E3CE /* TokenSortableField.graphql.swift */, + D34AC1057AD14362BB91E88B /* TokenStandard.graphql.swift */, + 64D494D944670E557175A694 /* TransactionDirection.graphql.swift */, + 945EF0A3BD6F19DA14E40AE1 /* TransactionStatus.graphql.swift */, + 18D48B25E03122149B0D1304 /* TransactionType.graphql.swift */, ); - name = Unions; + name = Enums; sourceTree = ""; }; F35AFD3C27EE49990011A725 /* OneSignalNotificationServiceExtension */ = { @@ -1682,12 +1684,19 @@ path = OneSignalNotificationServiceExtension; sourceTree = ""; }; - F79593090BE522E6D2A3330F /* Operations */ = { + F498443CD9DA8803A092F3D9 /* InputObjects */ = { isa = PBXGroup; children = ( - D9DDAE0E99B4703AF96A81AB /* Queries */, + AED050A208757F137DBDA57D /* ContractInput.graphql.swift */, + 50C3E075E850CEC1714BE52C /* NftActivityFilterInput.graphql.swift */, + 1F1731869775D16A9EAB475C /* NftAssetTraitInput.graphql.swift */, + 310373063D8A11E9E02314FE /* NftAssetsFilterInput.graphql.swift */, + 696F6C5220D0072E380E198F /* NftBalanceAssetInput.graphql.swift */, + 1810C60B56CC74150C868801 /* NftBalancesFilterInput.graphql.swift */, + B3CC3D9DE64A39897BA580B3 /* OnRampTransactionsAuth.graphql.swift */, + E09D12605BAA868AB51573BE /* PortfolioValueModifier.graphql.swift */, ); - name = Operations; + name = InputObjects; sourceTree = ""; }; /* End PBXGroup section */ @@ -1822,7 +1831,7 @@ 9F7898182A819D62004D5A98 /* Embed Frameworks */, 163678CCBB906C7B12421609 /* [CP] Embed Pods Frameworks */, 0487071ABBC71F28EF79F4AA /* [CP] Copy Pods Resources */, - A98B71284330BF95E7CAB037 /* [CP-User] [RNFB] Core Configuration */, + 868B6279F959D6931E7A870E /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -1991,7 +2000,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export PROJECT_ROOT=$PWD/..\nexport EXTRA_PACKAGER_ARGS=\"--sourcemap-output $PROJECT_ROOT/main.jsbundle.map\"\n\nset -e\nif [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env.local\"\nfi\nexport CONFIG_CMD=\"dummy-workaround-value\"\nexport CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@rnef/cli/package.json')) + '/dist/src/bin.js'\")\"\n\nWITH_ENVIRONMENT=\"../../../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../../../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli')\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n"; }; 0487071ABBC71F28EF79F4AA /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; @@ -2149,6 +2158,7 @@ "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/ApplicationContract.graphql.swift", "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/AssetActivity.graphql.swift", "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/BlockaidFees.graphql.swift", + "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/BridgedWithdrawalInfo.graphql.swift", "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/DescriptionTranslations.graphql.swift", "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/Dimensions.graphql.swift", "$(SRCROOT)/WidgetsCore/MobileSchema/Schema/Objects/FeeData.graphql.swift", @@ -2387,7 +2397,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - A98B71284330BF95E7CAB037 /* [CP-User] [RNFB] Core Configuration */ = { + 868B6279F959D6931E7A870E /* [CP-User] [RNFB] Core Configuration */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -2416,7 +2426,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\nexport SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map\n\nset -e\nif [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env.local\"\nfi\nexport CONFIG_CMD=\"dummy-workaround-value\"\nexport CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@rnef/cli/package.json')) + '/dist/src/bin.js'\")\"\n\nWITH_ENVIRONMENT=\"../../../node_modules/react-native/scripts/xcode/with-environment.sh\"\nDATADOG_XCODE=\"./sourcemaps-datadog.sh\"\n\nif [[ -n \"$DATADOG_API_KEY\" ]]; then\n # JS source maps\n /bin/sh -c \"$WITH_ENVIRONMENT $DATADOG_XCODE\"\n # iOS dSYM\n ../../../node_modules/.bin/datadog-ci dsyms upload $DWARF_DSYM_FOLDER_PATH\nelse\n echo \"Ignoring upload step for local, API key is missing.\"\nfi\n"; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../../../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"./sourcemaps-datadog.sh\"\n\nexport SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map\n\nif [[ -n \"$DATADOG_API_KEY\" && \"$SKIP_DATADOG_UPLOAD\" != \"true\" ]]; then\n echo \"warning: Starting Datadog Uploads\"\n echo \"warning: Build Configuration: $CONFIGURATION\"\n echo \"warning: Product Name: $PRODUCT_NAME\"\n echo \"warning: Source Map File: $SOURCEMAP_FILE\"\n echo \"warning: dSYM Path: $DWARF_DSYM_FOLDER_PATH\"\n echo \"\"\n\n # JS source maps\n echo \"warning: Uploading JS source maps...\"\n \n /bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n\n echo \"warning: \"\n\n # iOS dSYM\n echo \"warning: Uploading iOS dSYMs...\"\n echo \"warning: dSYM command: datadog-ci dsyms upload $DWARF_DSYM_FOLDER_PATH\"\n\n DSYM_LOG=$(mktemp)\n if ../../../node_modules/.bin/datadog-ci dsyms upload $DWARF_DSYM_FOLDER_PATH 2>&1 | tee \"$DSYM_LOG\"; then\n echo \"warning: dSYM upload completed successfully\"\n rm -f \"$DSYM_LOG\"\n else\n DSYM_EXIT_CODE=$?\n echo \"warning: \"\n echo \"warning: dSYM Upload Failed\"\n echo \"warning: Exit Code: $DSYM_EXIT_CODE\"\n echo \"warning: \"\n echo \"warning:Full Error Output:\"\n echo \"warning: ---\"\n echo \"warning: $(cat \"$DSYM_LOG\")\"\n echo \"warning: ---\"\n echo \"warning: \"\n echo \"warning: Debug Information:\"\n echo \"warning: - datadog-ci version: $(../../../node_modules/.bin/datadog-ci version 2>&1 || echo 'Failed to get version')\"\n echo \"warning: - dSYM folder exists: $([ -d \\\"$DWARF_DSYM_FOLDER_PATH\\\" ] && echo 'Yes' || echo 'No')\"\n echo \"warning: - dSYM contents: $(ls -la \\\"$DWARF_DSYM_FOLDER_PATH\\\" 2>&1 || echo 'Failed to list')\"\n echo \"warning: - DATADOG_API_KEY set: $([ -n \\\"$DATADOG_API_KEY\\\" ] && echo 'Yes (${#DATADOG_API_KEY} chars)' || echo 'No')\"\n echo \"warning: - Working directory: $(pwd)\"\n echo \"warning: \"\n echo \"warning: This is non-critical. Build will continue.\"\n rm -f \"$DSYM_LOG\"\n fi\n\n echo \"warning: \"\n echo \"warning: Datadog upload phase completed (build continues regardless of upload status)\"\nelse\n echo \"warning: Skipping Datadog upload (DATADOG_API_KEY not set or SKIP_DATADOG_UPLOAD=true)\"\nfi\n\n# Always exit 0 to not fail the build\nexit 0\n"; }; F5C7F44CBF58F052A43EB4AA /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -2473,7 +2483,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\nexport RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > `$NODE_BINARY --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/.packager.env'\"`\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open `$NODE_BINARY --print \"require('path').dirname(require.resolve('expo/package.json')) + '/scripts/launchPackager.command'\"` || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; FD54D51B296C780A007A37E9 /* Copy configuration-specific GoogleServices-Info.plist */ = { @@ -2640,122 +2650,123 @@ 0743221A2A83E3CA00F8518D /* TokenMarket.graphql.swift in Sources */, 074322322A83E3CA00F8518D /* NftBalance.graphql.swift in Sources */, 9F813EA02AA8FB7500438D89 /* TransactionDetails.graphql.swift in Sources */, - 30872BFB39EEC66944E3EFD7 /* MobileSchema.graphql.swift in Sources */, - 9822D243ED2F5C350404A375 /* HomeScreenTokenParts.graphql.swift in Sources */, - 61988A922656857278F7CBF9 /* TokenBalanceMainParts.graphql.swift in Sources */, - C8402101FF510BAA358DCA46 /* TokenBalanceParts.graphql.swift in Sources */, - FA8EB6A63AB3F56194C6CFB8 /* TokenBalanceQuantityParts.graphql.swift in Sources */, - A974633048E27D5D23420F34 /* TokenBasicInfoParts.graphql.swift in Sources */, - C8338C5BE951EF9111C48217 /* TokenBasicProjectParts.graphql.swift in Sources */, - 09F9DEB33392F3051BEA8D52 /* TokenFeeDataParts.graphql.swift in Sources */, - D7E3642851C618D369D26B82 /* TokenMarketParts.graphql.swift in Sources */, - 302E24504C4FCE674CF95984 /* TokenParts.graphql.swift in Sources */, - F53121FB1B070DB9A81347DF /* TokenProjectMarketsParts.graphql.swift in Sources */, - E0F63BA3A99DB10FA1CCAB5E /* TokenProjectUrlsParts.graphql.swift in Sources */, - 2B12DFE797E2CBF314565D81 /* TokenProtectionInfoParts.graphql.swift in Sources */, - 71CF37F19F1C138B57CC135C /* TopTokenParts.graphql.swift in Sources */, - 93566FBDE94E1A2D8CC5AB62 /* ConvertQuery.graphql.swift in Sources */, - 63BDDE4499AC6B2375C9F795 /* FavoriteTokenCardQuery.graphql.swift in Sources */, - FD4E55146E046C7F5983A379 /* FeedTransactionListQuery.graphql.swift in Sources */, - F0D6A92BB4FCE19CDFA5BBE1 /* HomeScreenTokensQuery.graphql.swift in Sources */, - D660CA59775C3634324037ED /* MultiplePortfolioBalancesQuery.graphql.swift in Sources */, - 2B422D705C68BB51FBDA9895 /* NFTItemScreenQuery.graphql.swift in Sources */, - EF3D92CA76DEE66090F18D0C /* NftCollectionScreenQuery.graphql.swift in Sources */, - AC70FF8207ED26561B634E30 /* NftsQuery.graphql.swift in Sources */, - 1C3559D557BC09C709104802 /* NftsTabQuery.graphql.swift in Sources */, - A104024A1861354EC1DD53C1 /* PortfolioBalancesQuery.graphql.swift in Sources */, - F772B09295DABC5E8895C1C5 /* SelectWalletScreenQuery.graphql.swift in Sources */, - 9381B5EA5839DA17D07A45F0 /* TokenDetailsScreenQuery.graphql.swift in Sources */, - DC0641C5870F91D26C914F1D /* TokenPriceHistoryQuery.graphql.swift in Sources */, - 93AEECDBDB160B5E0C9E3149 /* TokenProjectDescriptionQuery.graphql.swift in Sources */, - 8CEA6459A5B739C3A0382000 /* TokenProjectsQuery.graphql.swift in Sources */, - 677FC15A05D9BD23930FAC95 /* TokenQuery.graphql.swift in Sources */, - 62B32E9623DA0E939F062033 /* TokensQuery.graphql.swift in Sources */, - 171DD1C966C7FBF9DD68CFAC /* TopTokensQuery.graphql.swift in Sources */, - 257C7A9F2C4AC3C99C6B7348 /* TransactionHistoryUpdaterQuery.graphql.swift in Sources */, - 3361FE5837059AEF4AA877BE /* TransactionListQuery.graphql.swift in Sources */, - 8410B98A8D7974A941AAF299 /* WidgetTokensQuery.graphql.swift in Sources */, - 9AF0D1FDF2BFB17FB732C5FD /* SchemaConfiguration.swift in Sources */, - 8950F8354810AA673E1E35DA /* SchemaMetadata.graphql.swift in Sources */, - 4917B04DB81579CF4243A1DA /* Chain.graphql.swift in Sources */, - 614FCC3D8E9AA8175E950726 /* Currency.graphql.swift in Sources */, - 326CFFDBB89D95FA4CB95EBB /* HistoryDuration.graphql.swift in Sources */, - 143C638C2CCAC689371BFC93 /* NftActivityType.graphql.swift in Sources */, - 648B919F00D069DE1F0040F6 /* NftMarketplace.graphql.swift in Sources */, - 7BE97D20155AE69FF36C4CB4 /* NftStandard.graphql.swift in Sources */, - 1BC3A49161EB906542F8E23B /* ProtectionAttackType.graphql.swift in Sources */, - 8FE0F30936E893297558F467 /* ProtectionResult.graphql.swift in Sources */, - 66D2765A00D8CE3136D28F1F /* SafetyLevel.graphql.swift in Sources */, - 85ADD03923353DB3D6CD7301 /* SwapOrderStatus.graphql.swift in Sources */, - C791C5505DBB3036B9D5066D /* SwapOrderType.graphql.swift in Sources */, - 0C6FC49E0FC9BCB2DF5B627E /* TokenSortableField.graphql.swift in Sources */, - FD84AA379C4562598807843D /* TokenStandard.graphql.swift in Sources */, - 2E05037B51AF526A47701B09 /* TransactionDirection.graphql.swift in Sources */, - CCBC45FD4310D8153D859361 /* TransactionStatus.graphql.swift in Sources */, - 5D06BAB366D14A5AFE2355E8 /* TransactionType.graphql.swift in Sources */, - FBE634E2AEC7A62B89863366 /* ContractInput.graphql.swift in Sources */, - 3E338E14E33C721DAB708664 /* NftActivityFilterInput.graphql.swift in Sources */, - 50C89DFAF2DBC80D6343B164 /* NftAssetTraitInput.graphql.swift in Sources */, - D680C4844F2C5DACF5D0892E /* NftAssetsFilterInput.graphql.swift in Sources */, - AA3AE4E5C2AAC3F9460A3637 /* NftBalanceAssetInput.graphql.swift in Sources */, - C7ABAADA504107D152A52FD4 /* NftBalancesFilterInput.graphql.swift in Sources */, - 09E8C497051C37FE604FD40E /* OnRampTransactionsAuth.graphql.swift in Sources */, - C1FC1F8B4A2725A195F66D60 /* PortfolioValueModifier.graphql.swift in Sources */, - E091F379106E92D3181103CB /* IAmount.graphql.swift in Sources */, - EF05F69E61EA8A16C6A66D53 /* IContract.graphql.swift in Sources */, - CE1DF567D58943ACCBB8360C /* Amount.graphql.swift in Sources */, - 1CE49305114BF4792290DDC6 /* AmountChange.graphql.swift in Sources */, - F5B577CA1201CB6FF04A9A58 /* ApplicationContract.graphql.swift in Sources */, - 9A3E861F5D7B0F2CB0EFA7A6 /* AssetActivity.graphql.swift in Sources */, - 8ADDD2E2AB1FD9D1D9AF5627 /* BlockaidFees.graphql.swift in Sources */, - 03291E0DA448AF438F97EA5D /* DescriptionTranslations.graphql.swift in Sources */, - 4C187202229BDC4D8898E067 /* Dimensions.graphql.swift in Sources */, - 5C3958DA046B5A84FE90C1BD /* FeeData.graphql.swift in Sources */, - 0CEBEB8BE3AB95C3F584F6CE /* Image.graphql.swift in Sources */, - B377DB0418EA15695F208D9F /* NetworkFee.graphql.swift in Sources */, - BD59A9C8414D6A4BFE9C9A2E /* NftActivity.graphql.swift in Sources */, - B746C09DA19B4C7F9C700989 /* NftActivityConnection.graphql.swift in Sources */, - FE9CE5C3B5A456B249FC34C5 /* NftActivityEdge.graphql.swift in Sources */, - 1DC34AE3E11264475E8B9F62 /* NftApproval.graphql.swift in Sources */, - FC7C117CEA5EA63460E6A2C6 /* NftApproveForAll.graphql.swift in Sources */, - BD1FF76A8E50EFEA0720CC04 /* NftAsset.graphql.swift in Sources */, - 8CC06A8205186D0640F0BC55 /* NftAssetConnection.graphql.swift in Sources */, - 5804A1B1BB144D9EFBBE177D /* NftAssetEdge.graphql.swift in Sources */, - 126CC4BC99F3F15CC1E2F1C3 /* NftAssetTrait.graphql.swift in Sources */, - CF36F741187285B430EFE557 /* NftBalance.graphql.swift in Sources */, - 33928A7366AFE7F892CDC89F /* NftBalanceConnection.graphql.swift in Sources */, - CA0EDABCA1B6C2B0936BF5CF /* NftBalanceEdge.graphql.swift in Sources */, - 252BD40CB9B46FE47B2440B1 /* NftCollection.graphql.swift in Sources */, - D81BAFFCC105668B88B607FC /* NftCollectionConnection.graphql.swift in Sources */, - A3318C676D7FBF78A1583B16 /* NftCollectionEdge.graphql.swift in Sources */, - 85D0E81B798D0F2D841386E9 /* NftCollectionMarket.graphql.swift in Sources */, - 3FE25F715DFFD1D03DCDA57D /* NftContract.graphql.swift in Sources */, - 1B59968CF49C45B127E9C768 /* NftOrder.graphql.swift in Sources */, - 9B6C88F7D8D542AAC353A3EA /* NftOrderConnection.graphql.swift in Sources */, - 4BB9E218D89B8AF5E4E27CCB /* NftOrderEdge.graphql.swift in Sources */, - 6A60BDC9D46A710D871DEC6E /* NftProfile.graphql.swift in Sources */, - E54093DC857B8C634804D42B /* NftTransfer.graphql.swift in Sources */, - 0F282C26344F1AE3622232B0 /* OffRampTransactionDetails.graphql.swift in Sources */, - 1E2AF2C38C8FBEB2A95B644E /* OffRampTransfer.graphql.swift in Sources */, - CF2BAECCF9A2EC43AC3EC583 /* OnRampServiceProvider.graphql.swift in Sources */, - B61E182CF4937B5169885C95 /* OnRampTransactionDetails.graphql.swift in Sources */, - 553E6B467BEE49C9984691C5 /* OnRampTransfer.graphql.swift in Sources */, - 35B8176433A98BA798BBEE79 /* PageInfo.graphql.swift in Sources */, - 4FED6AAF896BA371C56D88B1 /* Portfolio.graphql.swift in Sources */, - 818179F5724AA35E1B1D5A9C /* ProtectionInfo.graphql.swift in Sources */, - ADE104A101B3DFFBDB308189 /* Query.graphql.swift in Sources */, - 03E3515E38E247F459218CAA /* SwapOrderDetails.graphql.swift in Sources */, - 31661D70B58410EA030E2C53 /* TimestampedAmount.graphql.swift in Sources */, - F2EB62621E64B57B07DE8B13 /* Token.graphql.swift in Sources */, - 9301D18644F3DFA9ABB8F0BE /* TokenApproval.graphql.swift in Sources */, - E24ED8688E9B18BBD543F8F0 /* TokenBalance.graphql.swift in Sources */, - AF83E7713BB625D787DD1A1D /* TokenMarket.graphql.swift in Sources */, - 0094F4FBBC1C0A2FFABF7157 /* TokenProject.graphql.swift in Sources */, - 869F3639FBC6D8156FFE3BD3 /* TokenProjectMarket.graphql.swift in Sources */, - 4EF8D293BB1EBCFBFC65A330 /* TokenTransfer.graphql.swift in Sources */, - 97CA219E1FFD832D8FA02C20 /* TransactionDetails.graphql.swift in Sources */, - BA83003638263D702D03C3C1 /* ActivityDetails.graphql.swift in Sources */, - 36E601F269D40A67FC353947 /* AssetChange.graphql.swift in Sources */, + 0D87DBFD19D5A6DF176C0AB7 /* MobileSchema.graphql.swift in Sources */, + A250CF455F6CEFB64ABFC277 /* HomeScreenTokenParts.graphql.swift in Sources */, + 0AC6A2E1C5837AAD902742DA /* TokenBalanceMainParts.graphql.swift in Sources */, + F77F1A182DCA1D8DFE46ACC8 /* TokenBalanceParts.graphql.swift in Sources */, + 02B7B534DFEC9DA0F7F06B8D /* TokenBalanceQuantityParts.graphql.swift in Sources */, + 6882C4768011FDD4D746BDDB /* TokenBasicInfoParts.graphql.swift in Sources */, + F09E3F15A36A9050B028B3E3 /* TokenBasicProjectParts.graphql.swift in Sources */, + 5551DDD6A8B9B28E03459816 /* TokenFeeDataParts.graphql.swift in Sources */, + 06C9F16E22B8B64855980A69 /* TokenMarketParts.graphql.swift in Sources */, + 2B2738BAB9E906B561E0D4D6 /* TokenParts.graphql.swift in Sources */, + 9D638326CD705ABE549C8CA7 /* TokenProjectMarketsParts.graphql.swift in Sources */, + FA318FB37223FAF04A5C887B /* TokenProjectUrlsParts.graphql.swift in Sources */, + 47797825D18637A2FE57AC1F /* TokenProtectionInfoParts.graphql.swift in Sources */, + CE8C365D91CADE7A5A8F4D52 /* TopTokenParts.graphql.swift in Sources */, + D6149AE9ED70F546DF5841BD /* ConvertQuery.graphql.swift in Sources */, + ED606CD83873CC9DFCCA44F1 /* FavoriteTokenCardQuery.graphql.swift in Sources */, + 147F4DFD43CE86324A066003 /* FeedTransactionListQuery.graphql.swift in Sources */, + 6250D4ACD5696D845FD83DFD /* HomeScreenTokensQuery.graphql.swift in Sources */, + CEE9E9912D5621F6E8F819B7 /* MultiplePortfolioBalancesQuery.graphql.swift in Sources */, + 70CD2D0665E5AC6A0DF6F697 /* NFTItemScreenQuery.graphql.swift in Sources */, + 206E0025B8FCE0EA96AC744A /* NftCollectionScreenQuery.graphql.swift in Sources */, + 7C886F9D4C02EF4E5F67B011 /* NftsQuery.graphql.swift in Sources */, + 092E5F0FE7DE6113285AF5E3 /* NftsTabQuery.graphql.swift in Sources */, + 57ECD348EA564F0CBF715E9E /* PortfolioBalancesQuery.graphql.swift in Sources */, + C0BBEE1CAEA4B2F778426BDE /* SelectWalletScreenQuery.graphql.swift in Sources */, + 81E55E8D3056E2378A332B31 /* TokenDetailsScreenQuery.graphql.swift in Sources */, + 34610491FF9A452F4F806157 /* TokenPriceHistoryQuery.graphql.swift in Sources */, + 1C84E14C7F52CF6C9A6D2930 /* TokenProjectDescriptionQuery.graphql.swift in Sources */, + DC4AA6DE28B9F40A3D7600F1 /* TokenProjectsQuery.graphql.swift in Sources */, + D0B9B6DEC559290B7F64B24F /* TokenQuery.graphql.swift in Sources */, + E654760CA0FF19139A85472A /* TokensQuery.graphql.swift in Sources */, + 8E6DA65AC5CAE9F9A67F9E38 /* TopTokensQuery.graphql.swift in Sources */, + FC61EDE606C85346CE069C5D /* TransactionHistoryUpdaterQuery.graphql.swift in Sources */, + E1009979B48ACF5017C12F09 /* TransactionListQuery.graphql.swift in Sources */, + EE4B861AE191A1BF70CF3784 /* WidgetTokensQuery.graphql.swift in Sources */, + AD6036D32C44048781B728E1 /* SchemaConfiguration.swift in Sources */, + 9EBC41A90B1A80653960045E /* SchemaMetadata.graphql.swift in Sources */, + F814C0144D90ADB4A8E0A34E /* Chain.graphql.swift in Sources */, + 9260D04A891FEDE2BF511707 /* Currency.graphql.swift in Sources */, + 23B195B262495EABE4A6CDF4 /* HistoryDuration.graphql.swift in Sources */, + D5F36D3EDC206DF15EC368AC /* NftActivityType.graphql.swift in Sources */, + 4DB6B64CFF3B00FCF0258336 /* NftMarketplace.graphql.swift in Sources */, + 91DBDA9B4F4006350AEB8E4B /* NftStandard.graphql.swift in Sources */, + F86D86FC31BBDA7246C50392 /* ProtectionAttackType.graphql.swift in Sources */, + CA966CBD02A7B16BC55F1B8E /* ProtectionResult.graphql.swift in Sources */, + 15193A0A3CE80FF72AAB54B4 /* SafetyLevel.graphql.swift in Sources */, + 3720F641F397A2819500B1B1 /* SwapOrderStatus.graphql.swift in Sources */, + E7D4A29333634717D099F80E /* SwapOrderType.graphql.swift in Sources */, + D48D1B7A4469BAAA22608058 /* TokenSortableField.graphql.swift in Sources */, + D1FB4E293EC152C12A495ACD /* TokenStandard.graphql.swift in Sources */, + 252A28057D1D481D14D2F5E5 /* TransactionDirection.graphql.swift in Sources */, + BFB114718A0D262E3AEAEDC0 /* TransactionStatus.graphql.swift in Sources */, + AAB837C01239A62D00068853 /* TransactionType.graphql.swift in Sources */, + 8BCFF1F648887F78894F1071 /* ContractInput.graphql.swift in Sources */, + C8F5AF75BDB439143FE1C173 /* NftActivityFilterInput.graphql.swift in Sources */, + 4FD78AA88D2EB8E22144BCDF /* NftAssetTraitInput.graphql.swift in Sources */, + 997BFD5324E4BDE2422FEFED /* NftAssetsFilterInput.graphql.swift in Sources */, + 0764D0BF05C44615BDD9AD65 /* NftBalanceAssetInput.graphql.swift in Sources */, + 51362EC6F0E6D8928C067F5E /* NftBalancesFilterInput.graphql.swift in Sources */, + BB28AF35DC102F8DE2A4BE9D /* OnRampTransactionsAuth.graphql.swift in Sources */, + 9EC9E4AB5C3FD254EDB28D7A /* PortfolioValueModifier.graphql.swift in Sources */, + A949039A6A9EB3584B5644F3 /* IAmount.graphql.swift in Sources */, + 2FC53D1C58218F0BF1D4E5F3 /* IContract.graphql.swift in Sources */, + 845327D60EFBB850189D6AB3 /* Amount.graphql.swift in Sources */, + ACA7AE75760B76F7B30DCD43 /* AmountChange.graphql.swift in Sources */, + 4DB88A2CBFF5FF356CA757F0 /* ApplicationContract.graphql.swift in Sources */, + 0AD9B4E176D2E9FE86E6ED21 /* AssetActivity.graphql.swift in Sources */, + 391FD815120230BDAF53F6F0 /* BlockaidFees.graphql.swift in Sources */, + 24B4011ADC672F470EC515AC /* BridgedWithdrawalInfo.graphql.swift in Sources */, + E2C528F617A9675690171D54 /* DescriptionTranslations.graphql.swift in Sources */, + BAB54489223FD0027F7A8DBE /* Dimensions.graphql.swift in Sources */, + 15A72E27235628C56431EC98 /* FeeData.graphql.swift in Sources */, + 86038259E487A108DDC2948B /* Image.graphql.swift in Sources */, + C064037906B259088B6B78B2 /* NetworkFee.graphql.swift in Sources */, + BE0DBD01CF4DCA6D3CDDA369 /* NftActivity.graphql.swift in Sources */, + 19BC2371F6BE6CAD5218AA53 /* NftActivityConnection.graphql.swift in Sources */, + 71954B20E4341CEC36203F87 /* NftActivityEdge.graphql.swift in Sources */, + 3389D45727F15B8E4F59B99F /* NftApproval.graphql.swift in Sources */, + 39DEA445993253BCC1201199 /* NftApproveForAll.graphql.swift in Sources */, + BED0DD22C0D9E09C85DF010D /* NftAsset.graphql.swift in Sources */, + E7B6F1CA0E30585C949C9D9A /* NftAssetConnection.graphql.swift in Sources */, + 7700BED7CD7F52C83A3FF430 /* NftAssetEdge.graphql.swift in Sources */, + D8A0C6D04FF53BA4F50543FE /* NftAssetTrait.graphql.swift in Sources */, + DB752E8F664505726ABDBCD3 /* NftBalance.graphql.swift in Sources */, + B64BD6DEB100AEB723DB640D /* NftBalanceConnection.graphql.swift in Sources */, + E475BA358DD8BE30A6FDC051 /* NftBalanceEdge.graphql.swift in Sources */, + 1CA252C75CD5291E2A0B308B /* NftCollection.graphql.swift in Sources */, + 8141B38CCDB09F91180C0EBD /* NftCollectionConnection.graphql.swift in Sources */, + 5676CCD265609B71D3B1DB7C /* NftCollectionEdge.graphql.swift in Sources */, + 31A4EC91F1924E25AECA2F4E /* NftCollectionMarket.graphql.swift in Sources */, + 438115E2759B1194751A8021 /* NftContract.graphql.swift in Sources */, + E7EDBB8CDF65D5D6602BF8FC /* NftOrder.graphql.swift in Sources */, + A9AF7B483E9666E88CD6253E /* NftOrderConnection.graphql.swift in Sources */, + 9E7EC26AC45301198E280DC7 /* NftOrderEdge.graphql.swift in Sources */, + 9061ACE5AF99CEDE7BA51C94 /* NftProfile.graphql.swift in Sources */, + EB0A75424F8EEF6612D28D52 /* NftTransfer.graphql.swift in Sources */, + 9A9B39BC22F3BDFCD0917B66 /* OffRampTransactionDetails.graphql.swift in Sources */, + F1193089AE71CA3C4C101EA5 /* OffRampTransfer.graphql.swift in Sources */, + A88A26642AC25B022F428953 /* OnRampServiceProvider.graphql.swift in Sources */, + 03EEEDA1A3D5EF23C0D14B08 /* OnRampTransactionDetails.graphql.swift in Sources */, + 4260B9F719F8E25DFBF99D73 /* OnRampTransfer.graphql.swift in Sources */, + 7E3412EB776E1F43D6904BA0 /* PageInfo.graphql.swift in Sources */, + 7B557C3224F990851430DBD2 /* Portfolio.graphql.swift in Sources */, + 2C9935142A9582467B5B5FC5 /* ProtectionInfo.graphql.swift in Sources */, + CBEB9122D993BCB0E9A604B7 /* Query.graphql.swift in Sources */, + 03AE019583139330A3E408AB /* SwapOrderDetails.graphql.swift in Sources */, + 16506815CDEE17670D3DD363 /* TimestampedAmount.graphql.swift in Sources */, + F49C1C4E9175DFE7EEF9FB51 /* Token.graphql.swift in Sources */, + 2D73D2D80BC8C455DB35CE57 /* TokenApproval.graphql.swift in Sources */, + 8117BFB02DCF0F315FD83E67 /* TokenBalance.graphql.swift in Sources */, + E4985BA9090E013F2C9FC190 /* TokenMarket.graphql.swift in Sources */, + 8971E73E041A6283E2684481 /* TokenProject.graphql.swift in Sources */, + 4DB6D0FB611F6C68EADFB948 /* TokenProjectMarket.graphql.swift in Sources */, + 15F6E43DA2BD9A8A681EC70C /* TokenTransfer.graphql.swift in Sources */, + 2925C6E0262B95C5EA0C2AE7 /* TransactionDetails.graphql.swift in Sources */, + 63FBF9C23ED568816590F093 /* ActivityDetails.graphql.swift in Sources */, + 8D85F349FEF81A52FB93EAAB /* AssetChange.graphql.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2796,11 +2807,12 @@ 8E89C3B22AB8AAA400C84DE5 /* MnemonicTextField.swift in Sources */, FD7304D028A3650A0085BDEA /* Colors.swift in Sources */, 8E89C3AF2AB8AAA400C84DE5 /* MnemonicDisplayView.swift in Sources */, - 9FEC9B8B2A858CF1003CD019 /* AppDelegate.m in Sources */, + 45FFF7E12E8C2E6900362570 /* SilentPushEventEmitter.swift in Sources */, 6BC7D0802B5FF02400617C95 /* EncryptionUtils.swift in Sources */, 03C788232C10E7390011E5DC /* ActionButtons.swift in Sources */, 8EA8AB3B2AB7ED3C004E7EF3 /* SeedPhraseInputManager.m in Sources */, 03D2F3182C218D390030D987 /* RelativeOffsetView.swift in Sources */, + 45FFF7DF2E8C2A8100362570 /* SilentPushEventEmitter.m in Sources */, 6CA91BDB2A95223C00C4063E /* RNEthersRS.swift in Sources */, 8EA8AB3C2AB7ED3C004E7EF3 /* SeedPhraseInputViewModel.swift in Sources */, 072F6C2E2A44A32F00DA720A /* TokenPriceWidget.intentdefinition in Sources */, @@ -2810,6 +2822,7 @@ 6BC7D07F2B5FF02400617C95 /* ScantasticEncryption.swift in Sources */, 07B0676C2A7D6EC8001DD9B9 /* RNWidgets.swift in Sources */, 8E89C3AE2AB8AAA400C84DE5 /* MnemonicConfirmationView.swift in Sources */, + 8B2A92172EB3E78E00990413 /* AppDelegate.swift in Sources */, 5B4398EC2DD3B22C00F6BE08 /* PrivateKeyDisplayManager.m in Sources */, 5B4398ED2DD3B22C00F6BE08 /* PrivateKeyDisplayManager.swift in Sources */, 5B4398EE2DD3B22C00F6BE08 /* PrivateKeyDisplayView.swift in Sources */, @@ -2819,7 +2832,6 @@ 649A7A782D9AE70B00B53589 /* KeychainUtils.swift in Sources */, 649A7A792D9AE70B00B53589 /* KeychainConstants.swift in Sources */, 9FCEBF002A95A8E00079EDDB /* RNWalletConnect.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, 6CA91BE32A95226200C4063E /* RNCloudStorageBackupsManager.swift in Sources */, 9FCEBF042A95A99C0079EDDB /* RCTThemeModule.m in Sources */, 9FCEBF012A95A8E00079EDDB /* RNWalletConnect.swift in Sources */, @@ -2985,7 +2997,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3038,7 +3050,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore; @@ -3091,7 +3103,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore; @@ -3144,7 +3156,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore; @@ -3182,7 +3194,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3218,7 +3230,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests; @@ -3253,7 +3265,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests; @@ -3288,7 +3300,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests; @@ -3335,7 +3347,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3381,7 +3393,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets; @@ -3427,7 +3439,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets; @@ -3473,7 +3485,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets; @@ -3515,7 +3527,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3558,7 +3570,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension; @@ -3601,7 +3613,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension; @@ -3644,7 +3656,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension; @@ -3664,7 +3676,7 @@ baseConfigurationReference = A7C9F415D0E128A43003E071 /* Pods-Uniswap.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; - ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon${BUNDLE_ID_SUFFIX}"; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon$(BUNDLE_ID_SUFFIX)"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = ""; BUNDLE_ID_SUFFIX = .dev; CLANG_ENABLE_MODULES = YES; @@ -3680,14 +3692,14 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; - PRODUCT_BUNDLE_IDENTIFIER = "com.uniswap.mobile${BUNDLE_ID_SUFFIX}"; + PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev; PRODUCT_NAME = Uniswap; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Uniswap/RNEthersRs/RNEthersRS-Bridging-Header.h"; @@ -3702,7 +3714,7 @@ baseConfigurationReference = 178644A78AB62609EFDB66B3 /* Pods-Uniswap.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; - ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon${BUNDLE_ID_SUFFIX}"; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon$(BUNDLE_ID_SUFFIX)"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = ""; BUNDLE_ID_SUFFIX = ""; CLANG_ENABLE_MODULES = YES; @@ -3718,14 +3730,14 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; - PRODUCT_BUNDLE_IDENTIFIER = "com.uniswap.mobile${BUNDLE_ID_SUFFIX}"; + PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile; PRODUCT_NAME = Uniswap; PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.uniswap.mobile"; SWIFT_OBJC_BRIDGING_HEADER = "Uniswap/RNEthersRs/RNEthersRS-Bridging-Header.h"; @@ -3813,6 +3825,7 @@ REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + SWIFT_VERSION = 5.0; USE_HERMES = true; }; name = Debug; @@ -3887,6 +3900,7 @@ OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; USE_HERMES = true; VALIDATE_PRODUCT = YES; }; @@ -3920,7 +3934,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3965,7 +3979,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension; @@ -4050,6 +4064,7 @@ OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; USE_HERMES = true; VALIDATE_PRODUCT = YES; }; @@ -4060,7 +4075,7 @@ baseConfigurationReference = 62CEA9F2D5176D20A6402A3E /* Pods-Uniswap.beta.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; - ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon${BUNDLE_ID_SUFFIX}"; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon$(BUNDLE_ID_SUFFIX)"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = ""; BUNDLE_ID_SUFFIX = .beta; CLANG_ENABLE_MODULES = YES; @@ -4076,14 +4091,14 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; - PRODUCT_BUNDLE_IDENTIFIER = "com.uniswap.mobile${BUNDLE_ID_SUFFIX}"; + PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta; PRODUCT_NAME = Uniswap; PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.uniswap.mobile.beta"; SWIFT_OBJC_BRIDGING_HEADER = "Uniswap/RNEthersRs/RNEthersRS-Bridging-Header.h"; @@ -4148,7 +4163,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension; @@ -4233,6 +4248,7 @@ OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; USE_HERMES = true; VALIDATE_PRODUCT = YES; }; @@ -4243,7 +4259,7 @@ baseConfigurationReference = 56FE9C9AF785221B7E3F4C04 /* Pods-Uniswap.dev.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; - ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon${BUNDLE_ID_SUFFIX}"; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon$(BUNDLE_ID_SUFFIX)"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = ""; BUNDLE_ID_SUFFIX = .dev; CLANG_ENABLE_MODULES = YES; @@ -4259,14 +4275,14 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; - PRODUCT_BUNDLE_IDENTIFIER = "com.uniswap.mobile${BUNDLE_ID_SUFFIX}"; + PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev; PRODUCT_NAME = Uniswap; PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.uniswap.mobile.dev"; SWIFT_OBJC_BRIDGING_HEADER = "Uniswap/RNEthersRs/RNEthersRS-Bridging-Header.h"; @@ -4331,7 +4347,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.61; + MARKETING_VERSION = 1.64.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension; diff --git a/apps/mobile/ios/Uniswap/AppDelegate.h b/apps/mobile/ios/Uniswap/AppDelegate.h deleted file mode 100644 index 3151f6de6ed..00000000000 --- a/apps/mobile/ios/Uniswap/AppDelegate.h +++ /dev/null @@ -1,7 +0,0 @@ -#import -#import -#import - -@interface AppDelegate : RCTAppDelegate - -@end diff --git a/apps/mobile/ios/Uniswap/AppDelegate.m b/apps/mobile/ios/Uniswap/AppDelegate.m deleted file mode 100644 index 28b0f7e1310..00000000000 --- a/apps/mobile/ios/Uniswap/AppDelegate.m +++ /dev/null @@ -1,131 +0,0 @@ -#import "AppDelegate.h" - -#import - -#import "Uniswap-Swift.h" - -#import -#import -#import -#import -#import - -@implementation AppDelegate - -static NSString *const hasLaunchedOnceKey = @"HasLaunchedOnce"; - -/** - * Handles keychain cleanup on first run of the app. - * A migration flag is persisted in the keychain to avoid clearing the keychain for existing users, while the first run flag is saved in NSUserDefaults, which is cleared every install. - */ -- (void)handleKeychainCleanup { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - BOOL isFirstRun = ![defaults boolForKey:hasLaunchedOnceKey]; - BOOL canClearKeychainOnReinstall = [KeychainUtils getCanClearKeychainOnReinstall]; - - if (canClearKeychainOnReinstall && isFirstRun) { - [KeychainUtils clearKeychain]; - } - - if (!canClearKeychainOnReinstall || isFirstRun) { - [defaults setBool:YES forKey:hasLaunchedOnceKey]; - [KeychainUtils setCanClearKeychainOnReinstall]; - } -} - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - // Must be first line in startup routine - [ReactNativePerformance onAppStarted]; - - [self handleKeychainCleanup]; - - [FIRApp configure]; - - // This is needed so universal links opened from OneSignal notifications navigate to the proper page. - // More details here: - // https://documentation.onesignal.com/v7.0/docs/react-native-sdk in the deep linking warning section. - NSMutableDictionary *newLaunchOptions = [NSMutableDictionary dictionaryWithDictionary:launchOptions]; - if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) { - NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; - if (remoteNotif[@"custom"] && remoteNotif[@"custom"][@"u"]) { - NSString *initialURL = remoteNotif[@"custom"][@"u"]; - if (!launchOptions[UIApplicationLaunchOptionsURLKey]) { - newLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL]; - } - } - } - - self.moduleName = @"Uniswap"; - self.dependencyProvider = [RCTAppDependencyProvider new]; - self.initialProps = @{}; - - [self.window makeKeyAndVisible]; - - if (@available(iOS 13.0, *)) { - self.window.rootViewController.view.backgroundColor = [UIColor systemBackgroundColor]; - } else { - self.window.rootViewController.view.backgroundColor = [UIColor whiteColor]; - } - - [super application:application didFinishLaunchingWithOptions:newLaunchOptions]; - - [[RCTI18nUtil sharedInstance] allowRTL:NO]; - [RNBootSplash initWithStoryboard:@"SplashScreen" rootView:self.window.rootViewController.view]; - - return YES; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ - return [self bundleURL]; -} - -- (NSURL *)bundleURL -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. -/// -/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html -/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. -- (BOOL)concurrentRootEnabled -{ - return true; -} - -// Enable deep linking -- (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - options:(NSDictionary *)options -{ - return [RCTLinkingManager application:application openURL:url options:options]; -} - -// Enable universal links -- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity - restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler -{ - return [RCTLinkingManager application:application - continueUserActivity:userActivity - restorationHandler:restorationHandler]; -} - -// Disable 3rd party keyboard --(BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(NSString *)extensionPointIdentifier -{ - if (extensionPointIdentifier == UIApplicationKeyboardExtensionPointIdentifier) - { - return NO; - } - - return YES; -} - -@end diff --git a/apps/mobile/ios/Uniswap/AppDelegate.swift b/apps/mobile/ios/Uniswap/AppDelegate.swift new file mode 100644 index 00000000000..ca105e69da3 --- /dev/null +++ b/apps/mobile/ios/Uniswap/AppDelegate.swift @@ -0,0 +1,163 @@ +import UIKit +import Expo +import ExpoModulesCore +import React +import ReactAppDependencyProvider +import Firebase +import ReactNativePerformance +import RNBootSplash +import UserNotifications + +@main +class AppDelegate: ExpoAppDelegate { + + static let hasLaunchedOnceKey = "HasLaunchedOnce" + + var window: UIWindow? + var reactNativeDelegate: ExpoReactNativeFactoryDelegate? + var reactNativeFactory: ExpoReactNativeFactory? + + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + print("🚀 AppDelegate: Starting initialization") + + // Must be first line in startup routine + ReactNativePerformance.onAppStarted() + print("📊 ReactNativePerformance started") + + // Handle keychain cleanup on first launch + handleKeychainCleanup() + print("🔐 Keychain cleanup completed") + + // Configure Firebase + FirebaseApp.configure() + print("🔥 Firebase configured") + + // Handle OneSignal deep linking + var newLaunchOptions = launchOptions ?? [:] + if let remoteNotif = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [String: Any], + let custom = remoteNotif["custom"] as? [String: Any], + let initialURL = custom["u"] as? String, + launchOptions?[UIApplication.LaunchOptionsKey.url] == nil { + newLaunchOptions[UIApplication.LaunchOptionsKey.url] = URL(string: initialURL) + print("🔗 OneSignal deep link processed") + } + + // Set up Expo React Native factory + let delegate = ReactNativeDelegate() + let factory = ExpoReactNativeFactory(delegate: delegate) + delegate.dependencyProvider = RCTAppDependencyProvider() + + reactNativeDelegate = delegate + reactNativeFactory = factory + bindReactNativeFactory(factory) + + window = UIWindow(frame: UIScreen.main.bounds) + factory.startReactNative( + withModuleName: "Uniswap", + in: window, + launchOptions: newLaunchOptions + ) + + let result = super.application(application, didFinishLaunchingWithOptions: newLaunchOptions) + + print("🏁 AppDelegate initialization complete") + return result + } + + // MARK: - Keychain Cleanup + private func handleKeychainCleanup() { + let defaults = UserDefaults.standard + let isFirstRun = !defaults.bool(forKey: AppDelegate.hasLaunchedOnceKey) + let canClearKeychainOnReinstall = KeychainUtils.getCanClearKeychainOnReinstall() + + if canClearKeychainOnReinstall && isFirstRun { + KeychainUtils.clearKeychain() + } + + if !canClearKeychainOnReinstall || isFirstRun { + defaults.set(true, forKey: AppDelegate.hasLaunchedOnceKey) + KeychainUtils.setCanClearKeychainOnReinstall() + } + } + + // MARK: - Deep Linking + override func application( + _ app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any] = [:] + ) -> Bool { + return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options) + } + + // Universal Links + override func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void + ) -> Bool { + let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler) + return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result + } + + // MARK: - Push Notifications + override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + // Handle device token registration + // OneSignal and other services will handle this via swizzling + } + + override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { + // Handle registration failure + print("Failed to register for remote notifications: \(error)") + } + + override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + if let aps = userInfo["aps"] as? [String: Any] { + let contentAvailable = aps["content-available"] ?? aps["content_available"] + + if let contentNumber = contentAvailable as? NSNumber, contentNumber.intValue == 1 { + // Convert obj-c payload to SilentPushEventEmitter + let payload = userInfo.reduce(into: [String: Any]()) { result, entry in + if let key = entry.key as? String { + result[key] = entry.value + } + } + + SilentPushEventEmitter.emitEvent(with: payload) + } + } + completionHandler(.noData) + } + + // MARK: - Security + @objc(application:shouldAllowExtensionPointIdentifier:) + func application(_ application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier: UIApplication.ExtensionPointIdentifier) -> Bool { + // Disable 3rd party keyboards + if extensionPointIdentifier == .keyboard { + return false + } + return true + } +} + +class ReactNativeDelegate: ExpoReactNativeFactoryDelegate { + override func sourceURL(for bridge: RCTBridge) -> URL? { + bridge.bundleURL ?? bundleURL() + } + + override func bundleURL() -> URL? { + #if DEBUG + return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry") + #else + return Bundle.main.url(forResource: "main", withExtension: "jsbundle") + #endif + } + + // Override customize to initialize RNBootSplash BEFORE the window becomes visible + public override func customize(_ rootView: UIView) { + super.customize(rootView) + RNBootSplash.initWithStoryboard("SplashScreen", rootView: rootView) + } +} diff --git a/apps/mobile/ios/Uniswap/Notifications/SilentPushEventEmitter.m b/apps/mobile/ios/Uniswap/Notifications/SilentPushEventEmitter.m new file mode 100644 index 00000000000..343b5119cf9 --- /dev/null +++ b/apps/mobile/ios/Uniswap/Notifications/SilentPushEventEmitter.m @@ -0,0 +1,18 @@ +// +// SilentPushEventEmitter.m +// Uniswap +// +// Created by John Short on 9/29/25. +// + +#import +#import +#import + +@interface RCT_EXTERN_MODULE(SilentPushEventEmitter, RCTEventEmitter) + +RCT_EXTERN_METHOD(supportedEvents) +RCT_EXTERN_METHOD(addListener:(NSString *)eventName) +RCT_EXTERN_METHOD(removeListeners:(nonnull NSNumber *)count) + +@end diff --git a/apps/mobile/ios/Uniswap/Notifications/SilentPushEventEmitter.swift b/apps/mobile/ios/Uniswap/Notifications/SilentPushEventEmitter.swift new file mode 100644 index 00000000000..c79a3fb051a --- /dev/null +++ b/apps/mobile/ios/Uniswap/Notifications/SilentPushEventEmitter.swift @@ -0,0 +1,31 @@ +// +// SilentPushEventEmitter.swift +// Uniswap +// +// Created by John Short on 9/29/25. +// + +import React + +@objc(SilentPushEventEmitter) +open class SilentPushEventEmitter: RCTEventEmitter { + + public static weak var emitter: RCTEventEmitter? + + override init() { + super.init() + SilentPushEventEmitter.emitter = self + } + + open override func supportedEvents() -> [String] { + ["SilentPushReceived"] + } + + @objc(emitEventWithPayload:) + public static func emitEvent(with payload: [String: Any]) { + guard let emitter = emitter else { + return + } + emitter.sendEvent(withName: "SilentPushReceived", body: payload) + } +} diff --git a/apps/mobile/ios/Uniswap/Uniswap-Bridging-Header.h b/apps/mobile/ios/Uniswap/Uniswap-Bridging-Header.h new file mode 100644 index 00000000000..8a24474f8ac --- /dev/null +++ b/apps/mobile/ios/Uniswap/Uniswap-Bridging-Header.h @@ -0,0 +1,23 @@ +// +// Uniswap-Bridging-Header.h +// Uniswap +// +// Bridging header for Swift/Objective-C interoperability +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import "libethers_ffi.h" + +// Import any other Objective-C headers that need to be accessible from Swift \ No newline at end of file diff --git a/apps/mobile/ios/Uniswap/main.m b/apps/mobile/ios/Uniswap/main.m deleted file mode 100644 index b1df44b953e..00000000000 --- a/apps/mobile/ios/Uniswap/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/apps/mobile/ios/WidgetsCore/.mobileschema_fingerprint b/apps/mobile/ios/WidgetsCore/.mobileschema_fingerprint index dcdbb6388ae..bdbfc2b1a8b 100644 --- a/apps/mobile/ios/WidgetsCore/.mobileschema_fingerprint +++ b/apps/mobile/ios/WidgetsCore/.mobileschema_fingerprint @@ -1 +1 @@ -7a03144e3423ea77bd883f7e79a0e3d6a3c54ea3e98424ac288e5f817f88931c \ No newline at end of file +6ac99a17ab7586621527349b19aa9fee726de2187db2a35e940d94747d2b4cf8 \ No newline at end of file diff --git a/apps/mobile/ios/sourcemaps-datadog.sh b/apps/mobile/ios/sourcemaps-datadog.sh index b182975a380..0bf96a9cb4a 100755 --- a/apps/mobile/ios/sourcemaps-datadog.sh +++ b/apps/mobile/ios/sourcemaps-datadog.sh @@ -1,6 +1,64 @@ #!/bin/sh +# Note: Not using 'set -e' because we want to handle errors gracefully -REACT_NATIVE_XCODE="../../../node_modules/react-native/scripts/react-native-xcode.sh" -DATADOG_XCODE="../../../node_modules/.bin/datadog-ci react-native xcode" +# Fix invalid paths for --entry-file and --assets-dest params, +# needed for react-native/scripts/bundle.js script. +export ENTRY_FILE="apps/mobile/index.js" +export DEST="ios/Uniswap.app" -/bin/sh -c "$DATADOG_XCODE $REACT_NATIVE_XCODE" +# Store the starting directory, and if we're in an `ios` dir, move up to parent +START_DIR=$(pwd) +BASENAME=$(basename "$START_DIR") +if [ "$BASENAME" = "ios" ]; then + cd .. +fi + +DATADOG_XCODE="../../node_modules/.bin/datadog-ci react-native xcode" +REACT_NATIVE_XCODE="../../node_modules/react-native/scripts/react-native-xcode.sh" + +# Create a temporary file for capturing output. +TEMP_LOG=$(mktemp) + +# As Xcode doesn't show echo messages by default, we enforce printing logs with the warning label. +echo "warning: Starting Datadog source map generation and upload..." +echo "warning: Command: $DATADOG_XCODE $REACT_NATIVE_XCODE" +echo "warning: SOURCEMAP_FILE: $SOURCEMAP_FILE" +echo "warning: Configuration: $CONFIGURATION" +echo "" + +# Run the datadog-ci command and capture both stdout and stderr +# Use pipefail to catch the exit code of the datadog command, not tee +set -o pipefail +if /bin/sh -c "$DATADOG_XCODE $REACT_NATIVE_XCODE" 2>&1 | tee "$TEMP_LOG"; then + set +o pipefail + echo "warning: Datadog source map upload completed successfully" + rm -f "$TEMP_LOG" + exit 0 +else + set +o pipefail + EXIT_CODE=$? + echo "error: " + echo "error: Datadog Source Map Upload Failed" + echo "error: Exit Code: $EXIT_CODE" + echo "error: " + echo "error: Full Error Output:" + echo "error: ---" + echo "error: $(cat "$TEMP_LOG")" + echo "error: ---" + echo "error: " + echo "error: Debug Information:" + echo "error: - datadog-ci version: $(../../../node_modules/.bin/datadog-ci version 2>&1 || echo 'Failed to get version')" + echo "error: - Node version: $(node --version 2>&1 || echo 'Node not found')" + echo "error: - React Native CLI: $(../../../node_modules/.bin/react-native --version 2>&1 || echo 'RN CLI not found')" + echo "error: - Working directory: $(pwd)" + echo "error: - DATADOG_API_KEY set: $([ -n "$DATADOG_API_KEY" ] && echo 'Yes' || echo 'No')" + echo "error: - Bundle file exists: $([ -f "$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/main.jsbundle" ] && echo 'Yes' || echo 'No')" + echo "error: - Source map exists: $([ -f "$SOURCEMAP_FILE" ] && echo "Yes ($SOURCEMAP_FILE)" || echo "No ($SOURCEMAP_FILE)")" + echo "error: " + echo "error: This is non-critical. Build will continue." + echo "error: Please report this error for investigation." + + rm -f "$TEMP_LOG" + # Exit with 0 to not fail the build + exit 0 +fi diff --git a/apps/mobile/jest-setup.js b/apps/mobile/jest-setup.js index 979c7cc8709..cb00bb1d297 100644 --- a/apps/mobile/jest-setup.js +++ b/apps/mobile/jest-setup.js @@ -1,6 +1,7 @@ -// Setups and mocks can go here -// For example: https://reactnavigation.org/docs/testing/ - +// From https://reactnavigation.org/docs/testing/#setting-up-jest +import 'react-native-gesture-handler/jestSetup'; +import { setUpTests } from 'react-native-reanimated'; +// Other import 'core-js' // necessary so setImmediate works in tests import 'utilities/jest-package-mocks' import 'uniswap/jest-package-mocks' @@ -11,6 +12,11 @@ import 'uniswap/src/i18n' // Uses real translations for tests import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock.js' +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('@walletconnect/react-native-compat', () => ({})) @@ -65,6 +71,22 @@ jest.mock('@react-native-community/netinfo', () => ({ ...mockRNCNetInfo, NetInfo jest.mock('react-native', () => { const RN = jest.requireActual('react-native') // use original implementation, which comes with mocks out of the box + // Mock Linking module within React Native + RN.Linking = { + openURL: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + canOpenURL: jest.fn(), + getInitialURL: jest.fn(), + } + + // Mock Share module within React Native + RN.Share = { + share: jest.fn(), + sharedAction: 'sharedAction', + dismissedAction: 'dismissedAction', + } + return RN }) @@ -74,22 +96,10 @@ jest.mock('@react-navigation/elements', () => ({ require('react-native-reanimated').setUpTests() -jest.mock('react-native/Libraries/Share/Share', () => ({ - share: jest.fn(), -})) - jest.mock('@react-native-firebase/auth', () => () => ({ signInAnonymously: jest.fn(), })) -jest.mock('react-native/Libraries/Linking/Linking', () => ({ - openURL: jest.fn(), - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - canOpenURL: jest.fn(), - getInitialURL: jest.fn(), -})) - jest.mock("react-native-bootsplash", () => { return { hide: jest.fn().mockResolvedValue(), diff --git a/apps/mobile/jest.config.js b/apps/mobile/jest.config.js index 6b32f9ca40d..6f5da1044a5 100644 --- a/apps/mobile/jest.config.js +++ b/apps/mobile/jest.config.js @@ -23,6 +23,5 @@ module.exports = { setupFiles: [ '../../config/jest-presets/jest/setup.js', './jest-setup.js', - '../../node_modules/react-native-gesture-handler/jestSetup.js', ], } diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index 26513961f50..d3215f0097a 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -1,61 +1,38 @@ -/** - * Metro configuration for React Native with support for SVG files - * https://github.com/react-native-svg/react-native-svg#use-with-svg-files - * - * @format - */ -const { getMetroAndroidAssetsResolutionFix } = require('react-native-monorepo-tools') -const androidAssetsResolutionFix = getMetroAndroidAssetsResolutionFix() +const withStorybook = require('@storybook/react-native/metro/withStorybook'); +const { mergeConfig } = require('@react-native/metro-config'); +const { getDefaultConfig: getExpoDefaultConfig } = require('expo/metro-config'); -const withStorybook = require('@storybook/react-native/metro/withStorybook') - -const path = require('path') -const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config') - -const mobileRoot = path.resolve(__dirname) -const workspaceRoot = path.resolve(mobileRoot, '../..') - -const watchFolders = [mobileRoot, `${workspaceRoot}/node_modules`, `${workspaceRoot}/packages`] - - -const defaultConfig = getDefaultConfig(__dirname) +const defaultConfig = getExpoDefaultConfig(__dirname); const { resolver: { sourceExts, assetExts }, -} = defaultConfig +} = defaultConfig; -const config = { +// Only customize necessary fields for SVG and Storybook support +const customConfig = { resolver: { - nodeModulesPaths: [`${workspaceRoot}/node_modules`], assetExts: assetExts.filter((ext) => ext !== 'svg'), sourceExts: [...sourceExts, 'svg', 'cjs'], }, transformer: { + babelTransformerPath: require.resolve('react-native-svg-transformer'), getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: true, }, }), - babelTransformerPath: require.resolve('react-native-svg-transformer'), - publicPath: androidAssetsResolutionFix.publicPath, - }, - server: { - enhanceMiddleware: (middleware) => { - return androidAssetsResolutionFix.applyMiddleware(middleware) - }, }, - watchFolders, -} - -const IS_STORYBOOK_ENABLED = process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test' - -// Checkout more useful options in the docs: https://github.com/storybookjs/react-native?tab=readme-ov-file#options -module.exports = withStorybook(mergeConfig(defaultConfig, config), { - // Set to false to remove storybook specific options - // you can also use a env variable to set this - enabled: IS_STORYBOOK_ENABLED, - onDisabledRemoveStorybook: true, - // Path to your storybook config - configPath: path.resolve(__dirname, './.storybook'), -}) +}; + +const IS_STORYBOOK_ENABLED = + process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test'; + +module.exports = withStorybook( + mergeConfig(getExpoDefaultConfig(__dirname), defaultConfig, customConfig), + { + enabled: IS_STORYBOOK_ENABLED, + onDisabledRemoveStorybook: true, + configPath: require('path').resolve(__dirname, './.storybook'), + } +); diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 853a8cb2918..74e1b431ded 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -3,6 +3,7 @@ "version": "0.0.1", "private": true, "license": "GPL-3.0-or-later", + "main": "./index.js", "scripts": { "android": "nx android mobile", "android:release": "nx android:release mobile", @@ -66,11 +67,12 @@ }, "dependencies": { "@amplitude/analytics-react-native": "1.4.11", - "@apollo/client": "3.10.4", - "@datadog/mobile-react-native": "2.8.2", - "@datadog/mobile-react-navigation": "2.8.2", + "@apollo/client": "3.11.10", + "@datadog/mobile-react-native": "2.12.2", + "@datadog/mobile-react-navigation": "2.12.2", "@ethersproject/bignumber": "5.7.0", "@ethersproject/shims": "5.6.0", + "@expo/fingerprint": "0.15.3", "@formatjs/intl-datetimeformat": "4.5.1", "@formatjs/intl-getcanonicallocales": "1.9.0", "@formatjs/intl-locale": "2.4.44", @@ -79,42 +81,39 @@ "@formatjs/intl-relativetimeformat": "11.1.2", "@gorhom/bottom-sheet": "4.6.4", "@legendapp/list": "1.1.4", - "@react-native-async-storage/async-storage": "1.23.1", + "@react-native-async-storage/async-storage": "2.1.2", "@react-native-community/netinfo": "11.4.1", "@react-native-firebase/app": "21.0.0", "@react-native-firebase/auth": "21.0.0", "@react-native-firebase/firestore": "21.0.0", "@react-native-masked-view/masked-view": "0.3.2", - "@react-native/metro-config": "0.77.2", + "@react-native/metro-config": "0.79.5", "@react-navigation/bottom-tabs": "6.6.1", "@react-navigation/core": "7.9.2", "@react-navigation/native": "7.1.9", "@react-navigation/native-stack": "7.3.13", "@react-navigation/stack": "7.3.2", "@reduxjs/toolkit": "1.9.3", - "@reown/walletkit": "1.2.8", - "@rnef/cli": "0.7.18", - "@rnef/platform-android": "0.7.18", - "@rnef/platform-ios": "0.7.18", - "@rnef/plugin-metro": "0.7.18", - "@rnef/provider-github": "0.7.18", - "@shopify/flash-list": "1.7.3", + "@reown/walletkit": "1.4.1", + "@shopify/flash-list": "1.7.6", "@shopify/react-native-performance": "4.1.2", "@shopify/react-native-performance-navigation": "3.0.0", - "@shopify/react-native-skia": "1.12.4", + "@shopify/react-native-skia": "2.2.4", "@sparkfabrik/react-native-idfa-aaid": "1.2.0", "@tanstack/react-query": "5.77.2", - "@testing-library/react": "16.1.0", + "@testing-library/react": "16.3.0", "@uniswap/analytics": "1.7.2", "@uniswap/analytics-events": "2.43.0", "@uniswap/client-explore": "0.0.17", "@uniswap/ethers-rs-mobile": "0.0.5", - "@uniswap/sdk-core": "7.7.2", + "@uniswap/sdk-core": "7.9.0", "@universe/api": "workspace:^", - "@walletconnect/core": "2.21.4", - "@walletconnect/react-native-compat": "2.21.4", - "@walletconnect/types": "2.21.4", - "@walletconnect/utils": "2.21.4", + "@universe/gating": "workspace:^", + "@universe/sessions": "workspace:^", + "@walletconnect/core": "2.23.0", + "@walletconnect/react-native-compat": "2.23.0", + "@walletconnect/types": "2.23.0", + "@walletconnect/utils": "2.23.0", "apollo3-cache-persist": "0.14.1", "babel-plugin-transform-inline-environment-variables": "0.4.4", "babel-plugin-transform-remove-console": "6.9.4", @@ -122,34 +121,36 @@ "d3-shape": "3.2.0", "dayjs": "1.11.7", "dotenv": "16.0.3", + "eas-build-cache-provider": "16.4.2", "eslint-plugin-rulesdir": "0.2.2", "ethers": "5.7.2", - "expo": "52.0.46", - "expo-blur": "14.0.3", - "expo-camera": "16.0.18", - "expo-clipboard": "7.0.1", - "expo-linear-gradient": "14.0.2", - "expo-linking": "7.0.5", - "expo-local-authentication": "15.0.2", - "expo-localization": "16.0.1", - "expo-screen-capture": "7.0.1", + "expo": "53.0.22", + "expo-blur": "14.1.5", + "expo-camera": "16.1.11", + "expo-clipboard": "7.1.5", + "expo-dev-client": "5.2.4", + "expo-linear-gradient": "14.1.5", + "expo-linking": "7.1.7", + "expo-local-authentication": "16.0.5", + "expo-localization": "16.1.6", + "expo-screen-capture": "7.2.0", "expo-secure-store": "14.0.1", - "expo-store-review": "8.0.1", - "expo-web-browser": "14.0.2", + "expo-store-review": "8.1.5", + "expo-web-browser": "14.2.0", "fuse.js": "6.5.3", "i18next": "23.10.0", "lodash": "4.17.21", - "react": "18.3.1", + "react": "19.0.0", "react-freeze": "1.0.3", "react-i18next": "14.1.0", - "react-native": "0.77.2", + "react-native": "0.79.5", "react-native-appsflyer": "6.13.1", - "react-native-bootsplash": "6.3.1", + "react-native-bootsplash": "6.3.10", "react-native-context-menu-view": "1.15.0", "react-native-device-info": "10.11.0", "react-native-dotenv": "3.2.0", "react-native-fast-image": "8.6.3", - "react-native-gesture-handler": "2.22.1", + "react-native-gesture-handler": "2.24.0", "react-native-get-random-values": "1.11.0", "react-native-image-colors": "1.5.2", "react-native-image-picker": "7.1.0", @@ -158,15 +159,15 @@ "react-native-markdown-display": "7.0.0-alpha.2", "react-native-mmkv": "2.10.1", "react-native-onesignal": "5.2.9", - "react-native-pager-view": "6.5.1", + "react-native-pager-view": "6.7.1", "react-native-passkey": "3.1.0", "react-native-permissions": "4.1.5", - "react-native-reanimated": "3.16.7", + "react-native-reanimated": "3.19.3", "react-native-restart": "0.0.27", - "react-native-safe-area-context": "5.1.0", - "react-native-screens": "4.11.0", + "react-native-safe-area-context": "5.4.0", + "react-native-screens": "4.11.1", "react-native-sortables": "1.7.1", - "react-native-svg": "15.11.2", + "react-native-svg": "15.13.0", "react-native-tab-view": "3.5.2", "react-native-url-polyfill": "1.3.0", "react-native-video": "6.13.0", @@ -195,61 +196,48 @@ "@babel/plugin-proposal-numeric-separator": "7.16.7", "@babel/runtime": "7.26.0", "@datadog/datadog-ci": "2.48.0", - "@react-native-community/datetimepicker": "8.2.0", - "@react-native-community/slider": "4.5.5", + "@react-native-community/cli": "18.0.0", + "@react-native-community/cli-platform-android": "18.0.0", + "@react-native-community/cli-platform-ios": "18.0.0", + "@react-native-community/datetimepicker": "8.4.1", + "@react-native-community/slider": "4.5.6", "@storybook/addon-ondevice-controls": "8.5.2", "@storybook/react": "8.5.2", "@storybook/react-native": "8.5.2", - "@tamagui/babel-plugin": "1.125.17", - "@testing-library/react-native": "13.0.0", + "@tamagui/babel-plugin": "1.136.1", + "@testing-library/react-native": "13.3.3", "@types/inquirer": "9.0.8", "@types/jest": "29.5.14", "@types/node": "22.13.1", - "@types/react": "18.3.18", + "@types/react": "19.0.10", "@types/redux-mock-store": "1.0.6", "@uniswap/eslint-config": "workspace:^", - "@welldone-software/why-did-you-render": "8.0.1", + "@welldone-software/why-did-you-render": "10.0.1", "babel-loader": "8.2.3", "babel-plugin-module-resolver": "5.0.0", "babel-plugin-react-native-web": "0.17.5", - "babel-preset-expo": "12.0.6", + "babel-preset-expo": "13.0.0", "core-js": "2.6.12", "esbuild": "0.25.9", - "eslint": "8.44.0", - "expo-modules-core": "2.2.3", + "eslint": "8.57.1", + "expo-modules-core": "2.5.0", "inquirer": "8.2.6", "jest": "29.7.0", - "jest-expo": "52.0.3", + "jest-expo": "53.0.10", "jest-extended": "4.0.2", "jest-transformer-svg": "2.0.0", "madge": "6.1.0", "mockdate": "3.0.5", "postinstall-postinstall": "2.1.0", - "react-dom": "18.3.1", + "react-dom": "19.0.0", "react-native-asset": "2.1.1", "react-native-clean-project": "4.0.1", - "react-native-monorepo-tools": "1.2.1", "react-native-svg-transformer": "1.3.0", - "react-test-renderer": "18.3.1", - "reactotron-react-native": "5.1.10", - "reactotron-react-native-mmkv": "0.2.7", - "reactotron-redux": "3.1.10", + "react-test-renderer": "19.0.0", + "reactotron-react-native": "5.1.15", + "reactotron-react-native-mmkv": "0.2.8", + "reactotron-redux": "3.2.0", "redux-saga-test-plan": "4.0.4", - "typescript": "5.3.3" - }, - "expo": { - "install": { - "exclude": [ - "react-native@~0.76.9", - "react-native-reanimated@~3.16.7", - "react-native-gesture-handler@~2.20.0", - "react-native-screens@~4.4.0", - "react-native-safe-area-context@~4.12.0", - "react-native-webview@~13.12.5" - ] - }, - "autolinking": { - "exclude": ["expo-constants"] - } + "typescript": "5.8.3" } } diff --git a/apps/mobile/project.json b/apps/mobile/project.json index 64583483260..78f2b753000 100644 --- a/apps/mobile/project.json +++ b/apps/mobile/project.json @@ -4,37 +4,37 @@ "executor": "nx:noop" }, "android": { - "command": "rnef run:android --variant=devDebug --app-id-suffix=dev && bun run start", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile.dev/com.uniswap.MainActivity' bunx expo run:android --variant=devDebug --app-id=com.uniswap.mobile.dev", "options": { "cwd": "{projectRoot}" } }, "android:release": { - "command": "rnef run:android --variant=devRelease --app-id-suffix=dev", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile.dev/com.uniswap.MainActivity' bunx expo run:android --variant=devRelease --app-id=com.uniswap.mobile.dev", "options": { "cwd": "{projectRoot}" } }, "android:beta": { - "command": "rnef run:android --variant=betaDebug --app-id-suffix=beta", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile.beta/com.uniswap.MainActivity' bunx expo run:android --variant=betaDebug --app-id=com.uniswap.mobile.beta", "options": { "cwd": "{projectRoot}" } }, "android:beta:release": { - "command": "rnef run:android --variant=betaRelease --app-id-suffix=beta", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile.beta/com.uniswap.MainActivity' bunx expo run:android --variant=betaRelease --app-id=com.uniswap.mobile.beta", "options": { "cwd": "{projectRoot}" } }, "android:prod": { - "command": "rnef run:android --variant=prodDebug", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile/com.uniswap.MainActivity' bunx expo run:android --variant=prodDebug", "options": { "cwd": "{projectRoot}" } }, "android:prod:release": { - "command": "rnef run:android --variant=prodRelease", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile/com.uniswap.MainActivity' bunx expo run:android --variant=prodRelease", "options": { "cwd": "{projectRoot}" } @@ -196,7 +196,7 @@ } }, "ios": { - "command": "rnef run:ios --scheme Uniswap --configuration Debug && bun run start", + "command": "bunx expo run:ios --scheme Uniswap --configuration Debug", "options": { "cwd": "{projectRoot}" } @@ -208,31 +208,31 @@ } }, "ios:smol": { - "command": "rnef run:ios --device=\"iPhone SE (3rd generation)\"", + "command": "bunx expo run:ios --device=\"iPhone SE (3rd generation)\"", "options": { "cwd": "{projectRoot}" } }, "ios:dev:release": { - "command": "rnef run:ios --configuration Dev", + "command": "bunx expo run:ios --configuration Dev", "options": { "cwd": "{projectRoot}" } }, "ios:beta": { - "command": "rnef run:ios --configuration Beta", + "command": "bunx expo run:ios --configuration Beta", "options": { "cwd": "{projectRoot}" } }, "ios:bundle": { - "command": "rnef bundle --entry-file='index.js' --dev false --bundle-output='./ios/main.jsbundle' --sourcemap-output ./ios/main.jsbundle.map --dev=false --platform='ios' --assets-dest='./ios'", + "command": "bunx react-native bundle --entry-file apps/mobile/index.js --platform ios --dev false --bundle-output ./ios/main.jsbundle --assets-dest ./ios --sourcemap-output ./ios/main.jsbundle.map", "options": { "cwd": "{projectRoot}" } }, "ios:release": { - "command": "rnef run:ios --configuration Release", + "command": "bunx expo run:ios --configuration Release", "options": { "cwd": "{projectRoot}" } @@ -244,22 +244,25 @@ "lint:eslint": {}, "lint:eslint:fix": {}, "start": { - "command": "NODE_ENV=development rnef start --client-logs", + "command": "EXPO_ANDROID_LAUNCH_ACTIVITY='com.uniswap.mobile.dev/com.uniswap.MainActivity' EXPO_BUILD_CONFIGURATION=Debug NODE_ENV=development bunx expo start --scheme uniswap", "options": { "cwd": "{projectRoot}" - } + }, + "dependsOn": ["pod:ensure", "android:ensure"] }, "start:e2e": { - "command": "NODE_ENV=development IS_E2E_TEST=true rnef start --client-logs", + "command": "NODE_ENV=development IS_E2E_TEST=true bunx expo start", "options": { "cwd": "{projectRoot}" - } + }, + "dependsOn": ["pod:ensure", "android:ensure"] }, "start:production": { - "command": "NODE_ENV=production rnef start --reset-cache", + "command": "NODE_ENV=production bunx expo start --reset-cache", "options": { "cwd": "{projectRoot}" - } + }, + "dependsOn": ["pod:ensure", "android:ensure"] }, "test": { "command": "node --max-old-space-size=8912 ../../node_modules/.bin/jest", @@ -286,6 +289,27 @@ "cwd": "{projectRoot}" } }, + "pod:ensure": { + "command": "./scripts/check-podfile.sh", + "options": { + "cwd": "{projectRoot}" + }, + "inputs": ["{projectRoot}/ios/Podfile", "{projectRoot}/ios/Podfile.lock"], + "cache": true + }, + "android:ensure": { + "command": "./scripts/check-android-gradle.sh", + "options": { + "cwd": "{projectRoot}" + }, + "inputs": [ + "{projectRoot}/android/build.gradle", + "{projectRoot}/android/app/build.gradle", + "{projectRoot}/android/settings.gradle", + "{projectRoot}/android/gradle.properties" + ], + "cache": true + }, "pod:update": { "command": "./scripts/podinstall.sh -u", "options": { diff --git a/apps/mobile/rnef.config.mjs b/apps/mobile/rnef.config.mjs deleted file mode 100644 index b513fc0efc3..00000000000 --- a/apps/mobile/rnef.config.mjs +++ /dev/null @@ -1,36 +0,0 @@ -import { platformAndroid } from '@rnef/platform-android' -import { platformIOS } from '@rnef/platform-ios' -import { pluginMetro } from '@rnef/plugin-metro' -import { providerGitHub } from '@rnef/provider-github' -import { config } from 'dotenv' -config({ path: '../../.env.defaults.local' }) - -const isGitHubAction = process.env.GITHUB_ACTIONS === 'true' - -export default { - plugins: [pluginMetro()], - platforms: { - ios: platformIOS(), - android: platformAndroid(), - }, - remoteCacheProvider: isGitHubAction - ? 'github-actions' - : providerGitHub({ - owner: 'uniswap', - repository: 'universe', - token: process.env.GH_TOKEN_RN_CLI, - }), - fingerprint: { - ignorePaths: [ - // Files generated by [GraphQL] Apollo Generate Swift script phase in Xcode, making fingerprint unstable when installing pods vs not - 'ios/OneSignalNotificationServiceExtension/Env.swift', - 'ios/WidgetsCore/Env.swift', - 'ios/WidgetsCore/MobileSchema/MobileSchema.graphql.swift', - 'ios/WidgetsCore/MobileSchema/Fragments/**/*', - 'ios/WidgetsCore/MobileSchema/Operations/**/*', - 'ios/WidgetsCore/MobileSchema/Schema/**/*', - // There's a setup script in Podfile that changes the podspec in node_modules, making fingerprint unstable when installing pods vs not - '../../node_modules/react-native-permissions/RNPermissions.podspec', - ], - }, -} diff --git a/apps/mobile/scripts/check-android-gradle.sh b/apps/mobile/scripts/check-android-gradle.sh new file mode 100755 index 00000000000..775576a1897 --- /dev/null +++ b/apps/mobile/scripts/check-android-gradle.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# This script warns users if they need to sync Gradle +# It's designed to be used with NX caching - NX will only run this +# when Android Gradle files change + +# Detect if we're in a workspace (monorepo) by checking for workspace root +# Script runs from apps/mobile, so check if ../../nx.json exists (workspace root) +if [ -f "../../nx.json" ]; then + # We're in a workspace, user likely runs commands from root + ANDROID_CMD="bun mobile android" +else + # We're not in a workspace, user runs commands from mobile dir + ANDROID_CMD="bun android" +fi + +echo "⚠️ Warning: Android Gradle files have changed since last build" +echo "" +echo "You may encounter issues when running the Android app." +echo "To fix this, run one of the following:" +echo " • $ANDROID_CMD (build Android app, which will sync Gradle automatically)" +echo "" +echo "Metro bundler will continue starting, but you should build the Android app before" +echo "attempting to run it to ensure Gradle dependencies are synced." + diff --git a/apps/mobile/scripts/check-podfile.sh b/apps/mobile/scripts/check-podfile.sh new file mode 100755 index 00000000000..4aec05ffce3 --- /dev/null +++ b/apps/mobile/scripts/check-podfile.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# This script warns users if they need to run pod install +# It's designed to be used with NX caching - NX will only run this +# when Podfile or Podfile.lock changes + +# Detect if we're in a workspace (monorepo) by checking for workspace root +# Script runs from apps/mobile, so check if ../../nx.json exists (workspace root) +if [ -f "../../nx.json" ]; then + # We're in a workspace, user likely runs commands from root + IOS_CMD="bun mobile ios" +else + # We're not in a workspace, user runs commands from mobile dir + IOS_CMD="bun ios" +fi + +echo "⚠️ Warning: Podfile or Podfile.lock has changed since last pod install" +echo "" +echo "You may encounter issues when running the iOS app." +echo "To fix this, run:" +echo " • $IOS_CMD (build iOS app, which will install pods automatically)" +echo "" +echo "Metro bundler will continue starting, but you should build the iOS app before" +echo "attempting to run it to ensure pods are installed." + diff --git a/apps/mobile/scripts/checkBundleSize.sh b/apps/mobile/scripts/checkBundleSize.sh index b9a4e5a57da..e72af794fa6 100755 --- a/apps/mobile/scripts/checkBundleSize.sh +++ b/apps/mobile/scripts/checkBundleSize.sh @@ -1,5 +1,5 @@ #!/bin/bash -MAX_SIZE=24.60 +MAX_SIZE=24.50 MAX_BUFFER=0.5 # Check OS type and use appropriate stat command diff --git a/apps/mobile/scripts/getFingerprintForRadonIDE.ts b/apps/mobile/scripts/getFingerprintForRadonIDE.ts new file mode 100644 index 00000000000..61d1e332131 --- /dev/null +++ b/apps/mobile/scripts/getFingerprintForRadonIDE.ts @@ -0,0 +1,22 @@ +#!/usr/bin/env bun +import { createProjectHashAsync } from '@expo/fingerprint' + +async function main(): Promise { + try { + const projectRoot = process.cwd() + const hash = await createProjectHashAsync(projectRoot, { + silent: true, + }) + console.log(hash) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + console.error(`Failed to generate fingerprint: ${errorMessage}`) + process.exit(1) + } +} + +main().catch((error) => { + const errorMessage = error instanceof Error ? error.message : String(error) + console.error(`Fatal error: ${errorMessage}`) + process.exit(1) +}) diff --git a/apps/mobile/scripts/ios-build-interactive/main.ts b/apps/mobile/scripts/ios-build-interactive/main.ts index db54dae187a..131f4c54749 100755 --- a/apps/mobile/scripts/ios-build-interactive/main.ts +++ b/apps/mobile/scripts/ios-build-interactive/main.ts @@ -219,11 +219,11 @@ const resetMetroCache = async (): Promise => { const buildForSimulator = async (config: BuildConfig): Promise => { printBuildInfo(config, 'iOS Simulator') - const args = ['rnef', 'run:ios', '--scheme', 'Uniswap', '--configuration', config.configuration] + const args = ['expo', 'run:ios', '--scheme', 'Uniswap', '--configuration', config.configuration] if (config.simulator) { const simulatorName = config.simulator.split('(')[0]?.trim() - args.push(`--device="${simulatorName}"`) + args.push(`--device=${simulatorName}`) } log.info(`Command: bun run ${args.join(' ')}\n`) @@ -241,13 +241,13 @@ const buildForDevice = async (config: BuildConfig): Promise => { printBuildInfo(config, 'iOS Device') const args = [ - 'rnef', + 'expo', 'run:ios', '--scheme', config.scheme, '--configuration', config.configuration, - '--destination', + '--device', 'device', ] diff --git a/apps/mobile/src/app/App.tsx b/apps/mobile/src/app/App.tsx index 7554650723a..f846bf04019 100644 --- a/apps/mobile/src/app/App.tsx +++ b/apps/mobile/src/app/App.tsx @@ -3,6 +3,26 @@ import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev' import { DdRum, RumActionType } from '@datadog/mobile-react-native' import { BottomSheetModalProvider } from '@gorhom/bottom-sheet' import { PerformanceProfiler, RenderPassReport } from '@shopify/react-native-performance' +import { ApiInit, getEntryGatewayUrl, provideSessionService } from '@universe/api' +import { + DatadogSessionSampleRateKey, + DynamicConfigs, + Experiments, + getDynamicConfigValue, + getIsSessionServiceEnabled, + getIsSessionUpgradeAutoEnabled, + getStatsigClient, + StatsigCustomAppValue, + StatsigUser, + Storage, + useIsSessionServiceEnabled, + WALLET_FEATURE_FLAG_NAMES, +} from '@universe/gating' +import { + createChallengeSolverService, + createSessionInitializationService, + SessionInitializationService, +} from '@universe/sessions' import { MMKVWrapper } from 'apollo3-cache-persist' import { default as React, StrictMode, useCallback, useEffect, useMemo, useRef } from 'react' import { I18nextProvider } from 'react-i18next' @@ -55,13 +75,7 @@ import { BlankUrlProvider } from 'uniswap/src/contexts/UrlContext' import { initializePortfolioQueryOverrides } from 'uniswap/src/data/rest/portfolioBalanceOverrides' import { selectFavoriteTokens } from 'uniswap/src/features/favorites/selectors' import { useAppFiatCurrencyInfo } from 'uniswap/src/features/fiatCurrency/hooks' -import { DatadogSessionSampleRateKey, DynamicConfigs } from 'uniswap/src/features/gating/configs' -import { StatsigCustomAppValue } from 'uniswap/src/features/gating/constants' -import { Experiments } from 'uniswap/src/features/gating/experiments' -import { WALLET_FEATURE_FLAG_NAMES } from 'uniswap/src/features/gating/flags' -import { getDynamicConfigValue } from 'uniswap/src/features/gating/hooks' import { StatsigProviderWrapper } from 'uniswap/src/features/gating/StatsigProviderWrapper' -import { getStatsigClient, StatsigUser, Storage } from 'uniswap/src/features/gating/sdk/statsig' import { useCurrentLanguageInfo } from 'uniswap/src/features/language/hooks' import { LocalizationContextProvider } from 'uniswap/src/features/language/LocalizationContext' import { clearNotificationQueue } from 'uniswap/src/features/notifications/slice/slice' @@ -121,6 +135,19 @@ initDynamicIntlPolyfills() initOneSignal() initAppsFlyer() +initializePortfolioQueryOverrides({ store }) + +const provideSessionInitializationService = (): SessionInitializationService => + createSessionInitializationService({ + getSessionService: () => + provideSessionService({ + getBaseUrl: getEntryGatewayUrl, + getIsSessionServiceEnabled, + }), + challengeSolverService: createChallengeSolverService(), + getIsSessionUpgradeAutoEnabled, + }) + function App(): JSX.Element | null { useEffect(() => { if (!__DEV__) { @@ -240,12 +267,6 @@ function AppOuter(): JSX.Element | null { } }, []) - useEffect(() => { - if (client) { - initializePortfolioQueryOverrides({ store, apolloClient: client }) - } - }, [client]) - if (!client) { return null } @@ -341,6 +362,7 @@ function DataUpdaters(): JSX.Element { const { locale } = useCurrentLanguageInfo() const { code } = useAppFiatCurrencyInfo() const finishedOnboarding = useSelector(selectFinishedOnboarding) + const isSessionServiceEnabled = useIsSessionServiceEnabled() useDatadogUserAttributesTracking({ isOnboarded: !!finishedOnboarding }) useHeartbeatReporter({ isOnboarded: !!finishedOnboarding }) @@ -365,6 +387,10 @@ function DataUpdaters(): JSX.Element { return ( <> + ) diff --git a/apps/mobile/src/app/MobileWalletNavigationProvider.tsx b/apps/mobile/src/app/MobileWalletNavigationProvider.tsx index 9e692d3fe02..6f2319b4364 100644 --- a/apps/mobile/src/app/MobileWalletNavigationProvider.tsx +++ b/apps/mobile/src/app/MobileWalletNavigationProvider.tsx @@ -1,4 +1,5 @@ import { StackActions } from '@react-navigation/native' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { PropsWithChildren, useCallback } from 'react' import { Share } from 'react-native' import { useDispatch } from 'react-redux' @@ -15,8 +16,6 @@ import { useFiatOnRampAggregatorGetCountryQuery, } from 'uniswap/src/features/fiatOnRamp/api' import { RampDirection } from 'uniswap/src/features/fiatOnRamp/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ModalName, WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { TransactionState } from 'uniswap/src/features/transactions/types/transactionState' diff --git a/apps/mobile/src/app/migrations.test.ts b/apps/mobile/src/app/migrations.test.ts index 55b7a04a610..76b64ac21ae 100644 --- a/apps/mobile/src/app/migrations.test.ts +++ b/apps/mobile/src/app/migrations.test.ts @@ -96,6 +96,8 @@ import { v90Schema, v91Schema, v92Schema, + v93Schema, + v95Schema, } from 'src/app/schema' import { persistConfig } from 'src/app/store' import { initialBiometricsSettingsState } from 'src/features/biometricsSettings/slice' @@ -105,6 +107,7 @@ import { initialPushNotificationsState } from 'src/features/notifications/slice' import { initialTweaksState } from 'src/features/tweaks/slice' import { initialWalletConnectState } from 'src/features/walletConnect/walletConnectSlice' import { ScannerModalState } from 'uniswap/src/components/ReceiveQRCode/constants' +import { USDC } from 'uniswap/src/constants/tokens' import { AccountType } from 'uniswap/src/features/accounts/types' import { initialUniswapBehaviorHistoryState } from 'uniswap/src/features/behaviorHistory/slice' import { UniverseChainId } from 'uniswap/src/features/chains/types' @@ -114,11 +117,16 @@ import { initialNotificationsState } from 'uniswap/src/features/notifications/sl import { initialSearchHistoryState } from 'uniswap/src/features/search/searchHistorySlice' import { initialUserSettingsState } from 'uniswap/src/features/settings/slice' import { ModalName } from 'uniswap/src/features/telemetry/constants' -import { initialTokensState } from 'uniswap/src/features/tokens/slice/slice' +import { initialTokensState } from 'uniswap/src/features/tokens/warnings/slice/slice' import { initialTransactionsState } from 'uniswap/src/features/transactions/slice' import { TransactionStatus, TransactionType } from 'uniswap/src/features/transactions/types/transactionDetails' import { initialVisibilityState } from 'uniswap/src/features/visibility/slice' -import { testMigrateSearchHistory, testRemoveTHBFromCurrency } from 'uniswap/src/state/uniswapMigrationTests' +import { + testAddActivityVisibility, + testMigrateDismissedTokenWarnings, + testMigrateSearchHistory, + testRemoveTHBFromCurrency, +} from 'uniswap/src/state/uniswapMigrationTests' import { transactionDetails } from 'uniswap/src/test/fixtures' import { DappRequestType } from 'uniswap/src/types/walletConnect' import { getAllKeysOfNestedObject } from 'utilities/src/primitives/objects' @@ -1784,4 +1792,24 @@ describe('Redux state migrations', () => { it('migrates from v92 to v93', () => { testMigrateSearchHistory(migrations[93], v92Schema) }) + + it('migrates from v93 to v95', () => { + testAddActivityVisibility(migrations[95], v93Schema) + }) + + it('migrates from v95 to v96', () => { + testMigrateDismissedTokenWarnings(migrations[96], { + ...v95Schema, + tokens: { + dismissedTokenWarnings: { + [UniverseChainId.Mainnet]: { + [USDC.address]: { + chainId: UniverseChainId.Mainnet, + address: USDC.address, + }, + }, + }, + }, + }) + }) }) diff --git a/apps/mobile/src/app/migrations.ts b/apps/mobile/src/app/migrations.ts index 779ea388446..8017c636abf 100644 --- a/apps/mobile/src/app/migrations.ts +++ b/apps/mobile/src/app/migrations.ts @@ -18,7 +18,9 @@ import { TransactionType, } from 'uniswap/src/features/transactions/types/transactionDetails' import { + addActivityVisibility, addDismissedBridgedAndCompatibleWarnings, + migrateDismissedTokenWarnings, migrateSearchHistory, removeThaiBahtFromFiatCurrency, unchecksumDismissedTokenWarningKeys, @@ -649,7 +651,8 @@ export const migrations = { const newNftKey = nftKey && tokenId && getNFTAssetKey(nftKey, tokenId) const accountNftsData = nftsData[accountAddress] - if (newNftKey && accountNftsData) { + + if (newNftKey) { accountNftsData[newNftKey] = { isHidden: true } } } @@ -1084,6 +1087,8 @@ export const migrations = { 93: migrateSearchHistory, 94: addDismissedBridgedAndCompatibleWarnings, + 95: addActivityVisibility, + 96: migrateDismissedTokenWarnings, } -export const MOBILE_STATE_VERSION = 94 +export const MOBILE_STATE_VERSION = 96 diff --git a/apps/mobile/src/app/modals/AccountSwitcherModal.tsx b/apps/mobile/src/app/modals/AccountSwitcherModal.tsx index de176e99439..aa6cb7b89f3 100644 --- a/apps/mobile/src/app/modals/AccountSwitcherModal.tsx +++ b/apps/mobile/src/app/modals/AccountSwitcherModal.tsx @@ -1,4 +1,5 @@ import { useIsFocused } from '@react-navigation/core' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -12,9 +13,13 @@ import { Button, Flex, Text, TouchableArea, useSporeColors } from 'ui/src' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { spacing } from 'ui/src/theme' import { AddressDisplay } from 'uniswap/src/components/accounts/AddressDisplay' +import { buildWrappedUrl } from 'uniswap/src/components/banners/shared/utils' +import { UniswapWrapped2025Card } from 'uniswap/src/components/banners/UniswapWrapped2025Card/UniswapWrapped2025Card' import { ActionSheetModal, MenuItemProp } from 'uniswap/src/components/modals/ActionSheetModal' import { Modal } from 'uniswap/src/components/modals/Modal' -import { AccountType, DisplayNameType } from 'uniswap/src/features/accounts/types' +import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls' +import { AccountType } from 'uniswap/src/features/accounts/types' +import { setHasDismissedUniswapWrapped2025Banner } from 'uniswap/src/features/behaviorHistory/slice' import { Platform } from 'uniswap/src/features/platforms/types/Platform' import { ElementName, ModalName, WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' @@ -23,13 +28,15 @@ import { TestID } from 'uniswap/src/test/fixtures/testIDs' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobile' import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { openUri } from 'uniswap/src/utils/linking' +import { logger } from 'utilities/src/logger/logger' import { isAndroid } from 'utilities/src/platform' import { PlusCircle } from 'wallet/src/components/icons/PlusCircle' import { createOnboardingAccount } from 'wallet/src/features/onboarding/createOnboardingAccount' import { BackupType } from 'wallet/src/features/wallet/accounts/types' import { hasBackup } from 'wallet/src/features/wallet/accounts/utils' import { createAccountsActions } from 'wallet/src/features/wallet/create/createAccountsSaga' -import { useActiveAccountAddress, useDisplayName, useNativeAccountExists } from 'wallet/src/features/wallet/hooks' +import { useActiveAccountAddress, useNativeAccountExists } from 'wallet/src/features/wallet/hooks' import { selectAllAccountsSorted, selectSortedSignerMnemonicAccounts } from 'wallet/src/features/wallet/selectors' import { setAccountAsActive } from 'wallet/src/features/wallet/slice' @@ -59,9 +66,8 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme const hasImportedSeedPhrase = useNativeAccountExists() const isModalOpen = useIsFocused() const { openWalletRestoreModal, walletRestoreType } = useWalletRestore() - const displayName = useDisplayName(activeAccountAddress) - const activeAccountHasENS = displayName?.type === DisplayNameType.ENS + const isWrappedBannerEnabled = useFeatureFlag(FeatureFlags.UniswapWrapped2025) const sortedMnemonicAccounts = useSelector(selectSortedSignerMnemonicAccounts) @@ -99,6 +105,21 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme }) } + const onPressWrappedCard = useCallback(async () => { + if (!activeAccountAddress) { + return + } + + try { + const url = buildWrappedUrl(UNISWAP_WEB_URL, activeAccountAddress) + await openUri({ uri: url, openExternalBrowser: true }) + onClose() + dispatch(setHasDismissedUniswapWrapped2025Banner(true)) + } catch (error) { + logger.error(error, { tags: { file: 'AccountSwitcherModal', function: 'onPressWrappedCard' } }) + } + }, [activeAccountAddress, onClose, dispatch]) + const addWalletOptions = useMemo(() => { const createAdditionalAccount = async (): Promise => { // Generate new account @@ -270,19 +291,22 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme size={spacing.spacing60 - spacing.spacing4} variant="subheading1" /> - {!activeAccountHasENS && ( + {isWrappedBannerEnabled && ( - + )} + + + @@ -513,16 +564,9 @@ exports[`AccountSwitcher renders correctly 1`] = ` line-height-disabled="true" maxFontSizeMultiplier={1.2} numberOfLines={1} - onBlur={[Function]} - onFocus={[Function]} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 17, "fontWeight": "500", @@ -586,11 +630,35 @@ exports[`AccountSwitcher renders correctly 1`] = ` collapsable={false} focusVisibleStyle={{}} forwardedRef={[Function]} + jestAnimatedProps={ + { + "value": {}, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "marginTop": 16, + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + ] + } onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -603,27 +671,28 @@ exports[`AccountSwitcher renders correctly 1`] = ` onStartShouldSetResponder={[Function]} role="button" style={ - { - "backgroundColor": "transparent", - "borderBottomLeftRadius": 12, - "borderBottomRightRadius": 12, - "borderTopLeftRadius": 12, - "borderTopRightRadius": 12, - "flexDirection": "column", - "marginTop": 16, - "opacity": 1, - "transform": [ - { - "scale": 1, - }, - ], - } + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "marginTop": 16, + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + {}, + ] } testID="account-switcher-add-wallet" > = { reducer: executeSwapReducer, actions: executeSwapActions, }, - [swapSagaName]: { - name: swapSagaName, - wrappedSaga: swapSaga, - reducer: swapReducer, - actions: swapActions, - }, - [tokenWrapSagaName]: { - name: tokenWrapSagaName, - wrappedSaga: tokenWrapSaga, - reducer: tokenWrapReducer, - actions: tokenWrapActions, - }, [removeDelegationSagaName]: { name: removeDelegationSagaName, wrappedSaga: removeDelegationSaga, diff --git a/apps/mobile/src/app/navigation/NavigationContainer.tsx b/apps/mobile/src/app/navigation/NavigationContainer.tsx index 3300418d114..42ecef4f228 100644 --- a/apps/mobile/src/app/navigation/NavigationContainer.tsx +++ b/apps/mobile/src/app/navigation/NavigationContainer.tsx @@ -91,7 +91,7 @@ export const NavigationContainer: FC> = ({ children, on const useManageDeepLinks = (): void => { const dispatch = useDispatch() - const urlListener = useRef() + const urlListener = useRef(undefined) const deepLinkMutation = useMutation({ mutationFn: async () => { diff --git a/apps/mobile/src/app/navigation/constants.ts b/apps/mobile/src/app/navigation/constants.ts new file mode 100644 index 00000000000..778a6d63580 --- /dev/null +++ b/apps/mobile/src/app/navigation/constants.ts @@ -0,0 +1,2 @@ +// Some pages in react native navigation require a delay before the modal is opened +export const MODAL_OPEN_WAIT_TIME = 300 diff --git a/apps/mobile/src/app/navigation/navigation.tsx b/apps/mobile/src/app/navigation/navigation.tsx index d59a3a4d5d1..889fbf8ea67 100644 --- a/apps/mobile/src/app/navigation/navigation.tsx +++ b/apps/mobile/src/app/navigation/navigation.tsx @@ -1,6 +1,7 @@ import { NavigationContainer, NavigationIndependentTree } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' import { createStackNavigator, TransitionPresets } from '@react-navigation/stack' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { useEffect } from 'react' import { DevSettings } from 'react-native' import { INCLUDE_PROTOTYPE_FEATURES, IS_E2E_TEST } from 'react-native-dotenv' @@ -39,6 +40,8 @@ import { PasskeyHelpModalScreen } from 'src/components/modals/ReactNavigationMod import { PasskeyManagementModalScreen } from 'src/components/modals/ReactNavigationModals/PasskeyManagementModalScreen' import { PermissionsSettingsScreen } from 'src/components/modals/ReactNavigationModals/PermissionsSettingsScreen' import { PortfolioBalanceSettingsScreen } from 'src/components/modals/ReactNavigationModals/PortfolioBalanceSettingsScreen' +import { ReportTokenDataModalScreen } from 'src/components/modals/ReactNavigationModals/ReportTokenDataModalScreen' +import { ReportTokenIssueModalScreen } from 'src/components/modals/ReactNavigationModals/ReportTokenIssueModalScreen' import { SmartWalletEnabledModalScreen } from 'src/components/modals/ReactNavigationModals/SmartWalletEnabledModalScreen' import { SmartWalletNudgeScreen } from 'src/components/modals/ReactNavigationModals/SmartWalletNudgeScreen' import { TestnetModeModalScreen } from 'src/components/modals/ReactNavigationModals/TestnetModeModalScreen' @@ -110,8 +113,6 @@ import { ViewPrivateKeysScreen } from 'src/screens/ViewPrivateKeys/ViewPrivateKe import { WebViewScreen } from 'src/screens/WebViewScreen' import { useSporeColors } from 'ui/src' import { spacing } from 'ui/src/theme' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ModalName } from 'uniswap/src/features/telemetry/constants' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' import { OnboardingEntryPoint } from 'uniswap/src/types/onboarding' @@ -420,6 +421,8 @@ export function AppStackNavigator(): JSX.Element { + + diff --git a/apps/mobile/src/app/navigation/tabs/CustomTabBar/CustomTabBar.tsx b/apps/mobile/src/app/navigation/tabs/CustomTabBar/CustomTabBar.tsx index dc6727df6f5..d1b9cddd9bc 100644 --- a/apps/mobile/src/app/navigation/tabs/CustomTabBar/CustomTabBar.tsx +++ b/apps/mobile/src/app/navigation/tabs/CustomTabBar/CustomTabBar.tsx @@ -4,7 +4,7 @@ import type { LayoutChangeEvent } from 'react-native' import { useAnimatedStyle, useDerivedValue, withTiming } from 'react-native-reanimated' import { TAB_BAR_ANIMATION_DURATION, TAB_ITEMS } from 'src/app/navigation/tabs/CustomTabBar/constants' import { SwapButton } from 'src/app/navigation/tabs/SwapButton' -import { SwapLongPressModal } from 'src/app/navigation/tabs/SwapLongPressModal' +import { SwapLongPressOverlay } from 'src/app/navigation/tabs/SwapLongPressOverlay' import { Flex, TouchableArea, useIsDarkMode, useSporeColors } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { iconSizes, spacing } from 'ui/src/theme' @@ -37,6 +37,7 @@ const TabItem = ({ tab, index, isFocused, onPress, colors }: TabItemProps): JSX. return ( - (null) + const longPressTimerRef = useRef(null) const hasTriggeredLongPressHaptic = useRef(false) const activeAccountAddress = useActiveAccountAddressWithThrow() diff --git a/apps/mobile/src/app/navigation/tabs/SwapLongPressModal.tsx b/apps/mobile/src/app/navigation/tabs/SwapLongPressOverlay.tsx similarity index 53% rename from apps/mobile/src/app/navigation/tabs/SwapLongPressModal.tsx rename to apps/mobile/src/app/navigation/tabs/SwapLongPressOverlay.tsx index ef44031e018..9b36790f87a 100644 --- a/apps/mobile/src/app/navigation/tabs/SwapLongPressModal.tsx +++ b/apps/mobile/src/app/navigation/tabs/SwapLongPressOverlay.tsx @@ -1,38 +1,76 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { BlurView } from 'expo-blur' import { type ReactNode, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Modal, StyleSheet } from 'react-native' +import { StyleSheet, type ViewStyle } from 'react-native' +import Animated, { FadeIn, FadeOut } from 'react-native-reanimated' import { useDispatch } from 'react-redux' import { navigate } from 'src/app/navigation/rootNavigation' import { ESTIMATED_BOTTOM_TABS_HEIGHT } from 'src/app/navigation/tabs/CustomTabBar/constants' import { SwapButton } from 'src/app/navigation/tabs/SwapButton' import { useOpenReceiveModal } from 'src/features/modals/hooks/useOpenReceiveModal' import { openModal } from 'src/features/modals/modalSlice' -import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src' +import { Flex, Text, TouchableArea, useIsDarkMode, useSporeColors } from 'ui/src' import { Bank, Buy, ReceiveAlt, SendAction } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' import { isAndroid } from 'utilities/src/platform' import { useEvent } from 'utilities/src/react/hooks' +const ANIMATION_DURATION = 200 +const BASE_DELAY = 40 + +const AnimatedBlurView = Animated.createAnimatedComponent(BlurView) + +function AnimatedContainer({ + enteringDelay, + exitingDelay, + children, + style, + exitingDuration = ANIMATION_DURATION, +}: { + enteringDelay: number + exitingDelay: number + children: ReactNode + style?: ViewStyle + exitingDuration?: number +}): JSX.Element { + return ( + + {children} + + ) +} + const styles = StyleSheet.create({ blurView: { - flex: 1, + height: '100%', + left: 0, + position: 'absolute', + top: 0, + width: '100%', }, }) -interface SwapLongPressModalProps { +interface SwapLongPressOverlayProps { isVisible: boolean onClose: () => void onSwapLongPress: () => void } -export function SwapLongPressModal({ isVisible, onClose, onSwapLongPress }: SwapLongPressModalProps): JSX.Element { +export function SwapLongPressOverlay({ + isVisible, + onClose, + onSwapLongPress, +}: SwapLongPressOverlayProps): JSX.Element | null { const colors = useSporeColors() + const isDarkMode = useIsDarkMode() const insets = useAppInsets() const dispatch = useDispatch() const { t } = useTranslation() @@ -126,37 +164,72 @@ export function SwapLongPressModal({ isVisible, onClose, onSwapLongPress }: Swap [t, onReceivePress, onSendPress, onBuyPress, onSellPress, colors.accent1.val], ) + const NUM_OF_SWAP_MENU_ITEMS = swapMenuItems.length + const TOTAL_DELAY_FOR_EXIT_FROM_MENU_ITEMS = NUM_OF_SWAP_MENU_ITEMS * BASE_DELAY + + // Used for main container and SwapButton + const DELAY_FOR_MAIN_EXIT = TOTAL_DELAY_FOR_EXIT_FROM_MENU_ITEMS + ANIMATION_DURATION / 2 + + // We want to start animating the text out just before the main animation starts + const DELAY_FOR_SWAP_TEXT_EXIT = DELAY_FOR_MAIN_EXIT - ANIMATION_DURATION / 4 + + if (!isVisible) { + return null + } + return ( - - - - - {swapMenuItems.map((item) => ( - - ))} - - {/* Swap Button as last item in the column */} - + + + + {swapMenuItems.map((item, i) => { + const length = NUM_OF_SWAP_MENU_ITEMS + + // Wait for initial animation to complete, then animate each item in sequentially with a delay based on its position in the array. + const enteringDelay = ANIMATION_DURATION + (length - i) * BASE_DELAY + + // We want to start animating before the main animation starts + const exitingDelay = i * BASE_DELAY + + return ( + + + + ) + })} + + {/* Swap Button as last item in the column */} + + {/* We want to delay animating the text entering so the Swap button shows first, then the text animates in, followed by the actions + + Text animates out at the same time as the Swap button, so it looks like the text is animating in while the Swap button is animating out. + */} + {t('common.button.swap')} + + {/* Swap Button doesn't animate in so it feels like the same button that was long pressed on HomeScreen is still there. It is the final thing to animate out (along with the Text above). */} + - + - - - + + + ) } diff --git a/apps/mobile/src/app/navigation/types.ts b/apps/mobile/src/app/navigation/types.ts index c4d0cf03be1..c0ed40ff6cd 100644 --- a/apps/mobile/src/app/navigation/types.ts +++ b/apps/mobile/src/app/navigation/types.ts @@ -21,6 +21,8 @@ import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState' import { ViewPrivateKeysScreenState } from 'src/screens/ViewPrivateKeys/ViewPrivateKeysScreenState' import { BridgedAssetModalProps } from 'uniswap/src/components/BridgedAsset/BridgedAssetModal' import { WormholeModalProps } from 'uniswap/src/components/BridgedAsset/WormholeModal' +import { ReportTokenDataModalProps } from 'uniswap/src/components/reporting/ReportTokenDataModal' +import { ReportTokenModalProps } from 'uniswap/src/components/reporting/ReportTokenIssueModal' import { UniverseChainId } from 'uniswap/src/features/chains/types' import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' import { NFTItem } from 'uniswap/src/features/nfts/types' @@ -221,6 +223,8 @@ export type AppStackParamList = { [ModalName.ConfirmDisableSmartWalletScreen]: undefined [ModalName.BridgedAsset]: BridgedAssetModalProps [ModalName.Wormhole]: WormholeModalProps + [ModalName.ReportTokenIssue]: ReportTokenModalProps + [ModalName.ReportTokenData]: ReportTokenDataModalProps } export type AppStackNavigationProp = NativeStackNavigationProp diff --git a/apps/mobile/src/app/schema.ts b/apps/mobile/src/app/schema.ts index b396fd537ad..8f8a78eecc1 100644 --- a/apps/mobile/src/app/schema.ts +++ b/apps/mobile/src/app/schema.ts @@ -721,8 +721,18 @@ delete v92SchemaIntermediate.cloudBackup export const v92Schema = v92SchemaIntermediate -const v93Schema = v92Schema +export const v93Schema = v92Schema + +export const v95Schema = { + ...v93Schema, + visibility: { + ...v93Schema.visibility, + activity: {}, + }, +} + +const v96Schema = v95Schema // TODO: [MOB-201] use function with typed output when API reducers are removed from rootReducer // export const getSchema = (): RootState => v0Schema -export const getSchema = (): typeof v93Schema => v93Schema +export const getSchema = (): typeof v96Schema => v96Schema diff --git a/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx b/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx index 0018779f299..6bc7ac3a944 100644 --- a/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx +++ b/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx @@ -14,6 +14,7 @@ import { PriceNumberOfDigits, TokenSpotData, useTokenPriceHistory } from 'src/co import { useTokenDetailsContext } from 'src/components/TokenDetails/TokenDetailsContext' import { useIsScreenNavigationReady } from 'src/utils/useIsScreenNavigationReady' import { Flex, SegmentedControl, Text } from 'ui/src' +import { useLayoutAnimationOnChange } from 'ui/src/animations' import GraphCurve from 'ui/src/assets/backgrounds/graph-curve.svg' import { spacing } from 'ui/src/theme' import { isLowVarianceRange } from 'uniswap/src/components/charts/utils' @@ -32,15 +33,20 @@ const LOW_VARIANCE_Y_PADDING = 100 type PriceTextProps = { loading: boolean - relativeChange?: SharedValue + relativeChange?: SharedValue numberOfDigits: PriceNumberOfDigits spotPrice?: SharedValue + startingPrice?: number + shouldTreatAsStablecoin?: boolean } const PriceTextSection = memo(function PriceTextSection({ loading, numberOfDigits, + relativeChange, spotPrice, + startingPrice, + shouldTreatAsStablecoin, }: PriceTextProps): JSX.Element { const price = useLineChartPrice(spotPrice) const currency = useAppFiatCurrencyInfo() @@ -62,9 +68,14 @@ const PriceTextSection = memo(function PriceTextSection({ We want both the animated number skeleton and the relative change skeleton to hide at the exact same time. When multiple skeletons hide in different order, it gives the feeling of things being slower than they actually are. */} - - + + ) }) @@ -136,6 +147,8 @@ const PriceExplorerInner = memo(function _PriceExplorerInner(): JSX.Element { return { lastPricePoint: priceHistory.length - 1, convertedPriceHistory: priceHistory } }, [data, conversionRate]) + useLayoutAnimationOnChange(convertedPriceHistory.length) + const convertedSpotValue = useDerivedValue(() => conversionRate * (data?.spot?.value.value ?? 0)) const convertedSpot = useMemo((): TokenSpotData | undefined => { return ( @@ -144,7 +157,7 @@ const PriceExplorerInner = memo(function _PriceExplorerInner(): JSX.Element { value: convertedSpotValue, } ) - }, [data, convertedSpotValue]) + }, [data]) // Zoom out y-axis for low variance assets const shouldZoomOut = useMemo(() => { @@ -177,6 +190,9 @@ const PriceExplorerInner = memo(function _PriceExplorerInner(): JSX.Element { return } + // Get the starting price for fiat delta calculation + const startingPrice = convertedPriceHistory[0]?.value + return ( @@ -185,6 +201,8 @@ const PriceExplorerInner = memo(function _PriceExplorerInner(): JSX.Element { numberOfDigits={numberOfDigits} relativeChange={convertedSpot?.relativeChange} spotPrice={convertedSpot?.value} + startingPrice={startingPrice} + shouldTreatAsStablecoin={shouldZoomOut} /> diff --git a/apps/mobile/src/components/PriceExplorer/Text.tsx b/apps/mobile/src/components/PriceExplorer/Text.tsx index 0da167fcdcd..7970a5a3096 100644 --- a/apps/mobile/src/components/PriceExplorer/Text.tsx +++ b/apps/mobile/src/components/PriceExplorer/Text.tsx @@ -1,9 +1,11 @@ import React from 'react' -import { useAnimatedStyle } from 'react-native-reanimated' -import { useLineChartDatetime } from 'react-native-wagmi-charts' +import { SharedValue, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated' +import { useLineChart, useLineChartDatetime } from 'react-native-wagmi-charts' import { AnimatedDecimalNumber } from 'src/components/PriceExplorer/AnimatedDecimalNumber' +import { useLineChartFiatDelta } from 'src/components/PriceExplorer/useFiatDelta' import { useLineChartPrice, useLineChartRelativeChange } from 'src/components/PriceExplorer/usePrice' import { AnimatedText } from 'src/components/text/AnimatedText' +import { numberToPercentWorklet } from 'src/utils/reanimated' import { Flex, Text, useSporeColors } from 'ui/src' import { AnimatedCaretChange } from 'ui/src/components/icons' import { FiatCurrency } from 'uniswap/src/features/fiatCurrency/constants' @@ -39,19 +41,79 @@ export function PriceText({ maxWidth }: { loading: boolean; maxWidth?: number }) ) } -export function RelativeChangeText({ loading }: { loading: boolean }): JSX.Element { +export function RelativeChangeText({ + loading, + spotRelativeChange, + startingPrice, + shouldTreatAsStablecoin = false, +}: { + loading: boolean + /** Price change for selected duration (used when not scrubbing chart) */ + spotRelativeChange?: SharedValue + startingPrice?: number + shouldTreatAsStablecoin?: boolean +}): JSX.Element { const colors = useSporeColors() + const { isActive } = useLineChart() - const relativeChange = useLineChartRelativeChange() + // Calculate relative change from chart data (used when scrubbing) + const calculatedRelativeChange = useLineChartRelativeChange() + + const fiatDelta = useLineChartFiatDelta({ + startingPrice, + shouldTreatAsStablecoin, + }) + + // Decide which source to use: API's 24hr when idle, chart's when scrubbing + // This ensures the color shows immediately with correct API data + const hasSpotData = !!spotRelativeChange + const shouldUseSpotData = useDerivedValue(() => !isActive.value && hasSpotData) + + const relativeChange = useDerivedValue(() => { + return shouldUseSpotData.value + ? (spotRelativeChange?.value ?? calculatedRelativeChange.value.value) + : calculatedRelativeChange.value.value + }) + + const relativeChangeFormatted = useDerivedValue(() => { + if (shouldUseSpotData.value) { + return spotRelativeChange + ? numberToPercentWorklet(spotRelativeChange.value, { precision: 2, absolute: true }) + : calculatedRelativeChange.formatted.value + } + return calculatedRelativeChange.formatted.value + }) + + const changeColor = useDerivedValue(() => { + // Round the range to 2 decimal places to check if is equal to 0 + const absRelativeChange = Math.round(Math.abs(relativeChange.value) * 100) + if (absRelativeChange === 0) { + return colors.neutral3.val + } + return relativeChange.value > 0 ? colors.statusSuccess.val : colors.statusCritical.val + }) const styles = useAnimatedStyle(() => ({ - color: relativeChange.value.value >= 0 ? colors.statusSuccess.val : colors.statusCritical.val, + color: changeColor.value, })) const caretStyle = useAnimatedStyle(() => ({ - color: relativeChange.value.value >= 0 ? colors.statusSuccess.val : colors.statusCritical.val, - transform: [{ rotate: relativeChange.value.value >= 0 ? '180deg' : '0deg' }], + color: changeColor.value, + transform: [ + { rotate: relativeChange.value >= 0 ? '180deg' : '0deg' }, + // fix vertical centering + { translateY: relativeChange.value >= 0 ? -1 : 1 }, + ], })) + // Combine fiat delta and percentage in a derived value + const combinedText = useDerivedValue(() => { + const delta = fiatDelta.formatted.value + if (delta) { + return `${delta} (${relativeChangeFormatted.value})` + } + return relativeChangeFormatted.value + }) + return ( - {loading ? ( + {loading && ( // We use `no-shimmer` here to speed up the first render and so that this skeleton renders // at the exact same time as the animated number skeleton. // TODO(WALL-5215): we can remove `no-shimmer` once we have a better Skeleton component. - ) : ( - <> - = 0 ? -1 : 1 }, - ]} - /> - - )} + {/* Must always mount this component to avoid stale values on initial render */} + + + + ) } @@ -93,8 +147,8 @@ export function DatetimeText({ loading }: { loading: boolean }): JSX.Element | n } return ( - - + + ) } diff --git a/apps/mobile/src/components/PriceExplorer/__snapshots__/Text.test.tsx.snap b/apps/mobile/src/components/PriceExplorer/__snapshots__/Text.test.tsx.snap index 98694cc5409..219e745507e 100644 --- a/apps/mobile/src/components/PriceExplorer/__snapshots__/Text.test.tsx.snap +++ b/apps/mobile/src/components/PriceExplorer/__snapshots__/Text.test.tsx.snap @@ -6,10 +6,9 @@ exports[`DatetimeText renders without error 1`] = ` @@ -17,26 +16,48 @@ exports[`DatetimeText renders without error 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": "Thursday, November 1st, 2023", + }, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "color": "rgba(19, 19, 19, 0.63)", + "fontFamily": "Basel Grotesk", + "fontSize": 15, + "fontWeight": "400", + "lineHeight": 19.5, + }, + undefined, + ] + } maxFontSizeMultiplier={1.4} style={ - { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.65)", - "light": "rgba(19, 19, 19, 0.63)", - }, + [ + { + "padding": 0, }, - "fontFamily": "Basel Grotesk", - "fontSize": 19, - "fontWeight": "400", - "lineHeight": 24.7, - "padding": 0, - } + { + "color": "rgba(19, 19, 19, 0.63)", + "fontFamily": "Basel Grotesk", + "fontSize": 15, + "fontWeight": "400", + "lineHeight": 19.5, + }, + undefined, + ] } text="Thursday, November 1st, 2023" underlineColorAndroid="transparent" @@ -58,6 +79,13 @@ exports[`PriceText renders loading state 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": "-", + }, + } + } jestAnimatedStyle={ { "value": { @@ -65,16 +93,43 @@ exports[`PriceText renders loading state 1`] = ` }, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + [ + { + "color": "#131313", + }, + ], + ] + } maxFontSizeMultiplier={1.2} style={ - { - "color": "#131313", - "fontFamily": "Basel Grotesk", - "fontSize": 106, - "fontWeight": "400", - "lineHeight": 50.879999999999995, - "padding": 0, - } + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + { + "color": "#131313", + }, + { + "fontSize": 106, + }, + ] } testID="wholePart" text="-" @@ -97,6 +152,13 @@ exports[`PriceText renders without error 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": "$55", + }, + } + } jestAnimatedStyle={ { "value": { @@ -104,16 +166,43 @@ exports[`PriceText renders without error 1`] = ` }, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + [ + { + "color": "#131313", + }, + ], + ] + } maxFontSizeMultiplier={1.2} style={ - { - "color": "#131313", - "fontFamily": "Basel Grotesk", - "fontSize": 106, - "fontWeight": "400", - "lineHeight": 50.879999999999995, - "padding": 0, - } + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + { + "color": "#131313", + }, + { + "fontSize": 106, + }, + ] } testID="wholePart" text="$55" @@ -124,6 +213,13 @@ exports[`PriceText renders without error 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": ".00", + }, + } + } jestAnimatedStyle={ { "value": { @@ -132,16 +228,39 @@ exports[`PriceText renders without error 1`] = ` }, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + [], + ] + } maxFontSizeMultiplier={1.2} style={ - { - "color": "rgba(19, 19, 19, 0.35)", - "fontFamily": "Basel Grotesk", - "fontSize": 106, - "fontWeight": "400", - "lineHeight": 50.879999999999995, - "padding": 0, - } + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + { + "color": "rgba(19, 19, 19, 0.35)", + }, + { + "fontSize": 106, + }, + ] } testID="decimalPart" text=".00" @@ -164,6 +283,13 @@ exports[`PriceText renders without error less than a dollar 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": "$0", + }, + } + } jestAnimatedStyle={ { "value": { @@ -171,16 +297,43 @@ exports[`PriceText renders without error less than a dollar 1`] = ` }, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + [ + { + "color": "#131313", + }, + ], + ] + } maxFontSizeMultiplier={1.2} style={ - { - "color": "#131313", - "fontFamily": "Basel Grotesk", - "fontSize": 106, - "fontWeight": "400", - "lineHeight": 50.879999999999995, - "padding": 0, - } + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + { + "color": "#131313", + }, + { + "fontSize": 106, + }, + ] } testID="wholePart" text="$0" @@ -191,6 +344,13 @@ exports[`PriceText renders without error less than a dollar 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": ".0500", + }, + } + } jestAnimatedStyle={ { "value": { @@ -199,16 +359,39 @@ exports[`PriceText renders without error less than a dollar 1`] = ` }, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + [], + ] + } maxFontSizeMultiplier={1.2} style={ - { - "color": "#131313", - "fontFamily": "Basel Grotesk", - "fontSize": 106, - "fontWeight": "400", - "lineHeight": 50.879999999999995, - "padding": 0, - } + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 53, + "fontWeight": "400", + "lineHeight": 50.879999999999995, + }, + { + "color": "#131313", + }, + { + "fontSize": 106, + }, + ] } testID="decimalPart" text=".0500" @@ -257,12 +440,7 @@ exports[`RelativeChangeText renders loading state 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "transparent", - "light": "transparent", - }, - }, + "color": "transparent", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -278,12 +456,7 @@ exports[`RelativeChangeText renders loading state 1`] = ` + + + + + + + + `; @@ -314,88 +613,131 @@ exports[`RelativeChangeText renders without error 1`] = ` } testID="relative-price-change" > - - - - - - + + + + + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 19, + "fontWeight": "400", + "lineHeight": 24.7, + }, + ] + } + maxFontSizeMultiplier={1.4} + style={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 19, + "fontWeight": "400", + "lineHeight": 24.7, + }, + { + "color": "#E10F0F", + }, + ] + } + testID="relative-change-text" + text="10.00%" + underlineColorAndroid="transparent" + value="10.00%" + /> + `; diff --git a/apps/mobile/src/components/PriceExplorer/useFiatDelta.tsx b/apps/mobile/src/components/PriceExplorer/useFiatDelta.tsx new file mode 100644 index 00000000000..ff0024319e8 --- /dev/null +++ b/apps/mobile/src/components/PriceExplorer/useFiatDelta.tsx @@ -0,0 +1,116 @@ +import { useCallback, useMemo } from 'react' +import { runOnJS, SharedValue, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated' +import { useLineChart } from 'react-native-wagmi-charts' +import { useFormatChartFiatDelta } from 'uniswap/src/features/fiatCurrency/hooks/useFormatChartFiatDelta' +import { useLocalizationContext } from 'uniswap/src/features/language/LocalizationContext' + +interface UseFiatDeltaParams { + startingPrice?: number + shouldTreatAsStablecoin?: boolean +} + +interface FiatDeltaResult { + formatted: SharedValue +} + +/** + * Hook to calculate and format fiat delta for price charts. + * Optimized to only calculate deltas on-demand during scrubbing, reducing memory usage. + */ +export function useLineChartFiatDelta({ + startingPrice, + shouldTreatAsStablecoin = false, +}: UseFiatDeltaParams): FiatDeltaResult { + const { currentIndex, data, isActive } = useLineChart() + const { conversionRate } = useLocalizationContext() + const { formatChartFiatDelta } = useFormatChartFiatDelta() + + // Shared value for the current scrubbing delta + const scrubbingDeltaSharedValue = useSharedValue('') + + // Pre-calculate only the last point's delta (for non-scrubbing state) + const lastPointDelta = useMemo(() => { + if (!startingPrice || !data || !conversionRate || data.length === 0) { + return '' + } + + const convertedStartPrice = startingPrice * conversionRate + const lastPoint = data[data.length - 1] + if (!lastPoint) { + return '' + } + const convertedEndPrice = lastPoint.value * conversionRate + + const delta = formatChartFiatDelta({ + startingPrice: convertedStartPrice, + endingPrice: convertedEndPrice, + isStablecoin: shouldTreatAsStablecoin, + }) + + return delta.formatted + }, [startingPrice, data, conversionRate, formatChartFiatDelta, shouldTreatAsStablecoin]) + + // Calculate delta for current scrubbing position + const calculateCurrentDelta = useMemo(() => { + return (index: number) => { + if (!startingPrice || !data || !conversionRate) { + return '' + } + + const currentPoint = data[index] + if (!currentPoint) { + return '' + } + + const convertedStartPrice = startingPrice * conversionRate + const convertedEndPrice = currentPoint.value * conversionRate + + const delta = formatChartFiatDelta({ + startingPrice: convertedStartPrice, + endingPrice: convertedEndPrice, + isStablecoin: shouldTreatAsStablecoin, + }) + + return delta.formatted + } + }, [startingPrice, data, conversionRate, formatChartFiatDelta, shouldTreatAsStablecoin]) + + // Callback for updating the scrubbing delta from the UI thread + const updateScrubbingDelta = useCallback( + (index: number) => { + scrubbingDeltaSharedValue.value = calculateCurrentDelta(index) + }, + [calculateCurrentDelta], + ) + + // Track current index changes with useAnimatedReaction + useAnimatedReaction( + () => { + return currentIndex.value + }, + (currentIndexValue) => { + if (data && data.length > 0) { + const safeIndex = Math.min(Math.max(0, Math.round(currentIndexValue)), data.length - 1) + runOnJS(updateScrubbingDelta)(safeIndex) + } + }, + [data, updateScrubbingDelta], + ) + + // Create a derived value that decides which delta to show + const formatted = useDerivedValue(() => { + if (!data || data.length === 0) { + return '' + } + + // When scrubbing, use the current scrubbing delta + if (isActive.value) { + return scrubbingDeltaSharedValue.value + } + + // When not scrubbing, use the pre-calculated last point delta + return lastPointDelta + }) + + return { formatted } +} diff --git a/apps/mobile/src/components/PriceExplorer/usePrice.tsx b/apps/mobile/src/components/PriceExplorer/usePrice.tsx index 3b8197599c2..0e23b3d110b 100644 --- a/apps/mobile/src/components/PriceExplorer/usePrice.tsx +++ b/apps/mobile/src/components/PriceExplorer/usePrice.tsx @@ -68,7 +68,7 @@ export function useLineChartPrice(currentSpot?: SharedValue): ValueAndFo formatted: priceFormatted, shouldAnimate, }), - [price, priceFormatted, shouldAnimate], + [], ) } diff --git a/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts b/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts index 4d2669249db..1c1c44a6e83 100644 --- a/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts +++ b/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts @@ -3,6 +3,7 @@ import { GraphQLApi } from '@universe/api' import { act } from 'react-test-renderer' import { useTokenPriceHistory } from 'src/components/PriceExplorer/usePriceHistory' import { renderHookWithProviders } from 'src/test/render' +import { USDC, USDC_ARBITRUM, USDC_BASE, USDC_OPTIMISM, USDC_POLYGON } from 'uniswap/src/constants/tokens' import { getLatestPrice, priceHistory, @@ -34,6 +35,24 @@ const mockTokenProjectsQuery = (historyPrices: number[]) => (): GraphQLApi.Token const formatPriceHistory = (history: GraphQLApi.TimestampedAmount[]): Omit[] => history.map(({ timestamp, value }) => ({ value, timestamp: timestamp * 1000 })) +/** + * Creates a USDC token project with matching priceHistory for both the aggregated market + * and the Ethereum token's market. This ensures the hook returns the expected data since + * it prefers per-chain price history over aggregated price history. + */ +const createUsdcTokenProjectWithMatchingPriceHistory = ( + history: (GraphQLApi.TimestampedAmount | undefined)[], +): GraphQLApi.TokenProject => ({ + ...usdcTokenProject({ priceHistory: history }), + tokens: [ + token({ sdkToken: USDC, market: tokenMarket({ priceHistory: history }) }), + token({ sdkToken: USDC_POLYGON }), + token({ sdkToken: USDC_ARBITRUM }), + token({ sdkToken: USDC_BASE, market: tokenMarket() }), + token({ sdkToken: USDC_OPTIMISM }), + ], +}) + describe(useTokenPriceHistory, () => { it('returns correct initial values', async () => { const { result } = renderHookWithProviders(() => useTokenPriceHistory({ currencyId: SAMPLE_CURRENCY_ID_1 })) @@ -59,7 +78,13 @@ describe(useTokenPriceHistory, () => { it('returns on-chain spot price if off-chain spot price is not available', async () => { const market = tokenMarket() const { resolvers } = queryResolvers({ - tokenProjects: () => [usdcTokenProject({ markets: undefined, tokens: [token({ market })] })], + tokenProjects: () => [ + usdcTokenProject({ + markets: undefined, + // Ensure token has the correct chain to match SAMPLE_CURRENCY_ID_1 (Ethereum) + tokens: [token({ market, chain: GraphQLApi.Chain.Ethereum })], + }), + ], }) const { result } = renderHookWithProviders(() => useTokenPriceHistory({ currencyId: SAMPLE_CURRENCY_ID_1 }), { resolvers, @@ -80,6 +105,35 @@ describe(useTokenPriceHistory, () => { }) }) + it('handles gracefully when no token matches the currencyId chain', async () => { + const aggregatedMarket = tokenProjectMarket() + const { resolvers } = queryResolvers({ + tokenProjects: () => [ + usdcTokenProject({ + markets: [aggregatedMarket], + // Provide tokens for different chains, but none matching SAMPLE_CURRENCY_ID_1 (Ethereum) + tokens: [token({ chain: GraphQLApi.Chain.Polygon }), token({ chain: GraphQLApi.Chain.Arbitrum })], + }), + ], + }) + const { result } = renderHookWithProviders(() => useTokenPriceHistory({ currencyId: SAMPLE_CURRENCY_ID_1 }), { + resolvers, + }) + + await waitFor(() => { + expect(result.current.loading).toBe(false) + expect(result.current.error).toBe(false) + }) + + // Should fall back to aggregated market data when no chain-specific token is found + await waitFor(() => { + expect(result.current.data?.spot).toEqual({ + value: expect.objectContaining({ value: aggregatedMarket.price.value }), + relativeChange: expect.objectContaining({ value: aggregatedMarket.pricePercentChange24h.value }), + }) + }) + }) + describe('correct number of digits', () => { it('for max price greater than 1', async () => { const { resolvers } = queryResolvers({ @@ -141,7 +195,7 @@ describe(useTokenPriceHistory, () => { it('properly formats price history entries', async () => { const history = priceHistory() const { resolvers } = queryResolvers({ - tokenProjects: () => [usdcTokenProject({ priceHistory: history })], + tokenProjects: () => [createUsdcTokenProjectWithMatchingPriceHistory(history)], }) const { result } = renderHookWithProviders(() => useTokenPriceHistory({ currencyId: SAMPLE_CURRENCY_ID_1 }), { resolvers, @@ -156,12 +210,9 @@ describe(useTokenPriceHistory, () => { }) it('filters out invalid price history entries', async () => { + const invalidHistory = [undefined, timestampedAmount({ value: 1 }), undefined, timestampedAmount({ value: 2 })] const { resolvers } = queryResolvers({ - tokenProjects: () => [ - usdcTokenProject({ - priceHistory: [undefined, timestampedAmount({ value: 1 }), undefined, timestampedAmount({ value: 2 })], - }), - ], + tokenProjects: () => [createUsdcTokenProjectWithMatchingPriceHistory(invalidHistory)], }) const { result } = renderHookWithProviders(() => useTokenPriceHistory({ currencyId: SAMPLE_CURRENCY_ID_1 }), { resolvers, @@ -191,10 +242,10 @@ describe(useTokenPriceHistory, () => { const monthPriceHistory = priceHistory({ duration: GraphQLApi.HistoryDuration.Month }) const yearPriceHistory = priceHistory({ duration: GraphQLApi.HistoryDuration.Year }) - const dayTokenProject = usdcTokenProject({ priceHistory: dayPriceHistory }) - const weekTokenProject = usdcTokenProject({ priceHistory: weekPriceHistory }) - const monthTokenProject = usdcTokenProject({ priceHistory: monthPriceHistory }) - const yearTokenProject = usdcTokenProject({ priceHistory: yearPriceHistory }) + const dayTokenProject = createUsdcTokenProjectWithMatchingPriceHistory(dayPriceHistory) + const weekTokenProject = createUsdcTokenProjectWithMatchingPriceHistory(weekPriceHistory) + const monthTokenProject = createUsdcTokenProjectWithMatchingPriceHistory(monthPriceHistory) + const yearTokenProject = createUsdcTokenProjectWithMatchingPriceHistory(yearPriceHistory) const { resolvers } = queryResolvers({ // eslint-disable-next-line max-params @@ -239,10 +290,11 @@ describe(useTokenPriceHistory, () => { }) await waitFor(() => { + const ethereumToken = dayTokenProject.tokens.find((t) => t.chain === GraphQLApi.Chain.Ethereum) expect(result.current.data?.spot).toEqual({ - value: expect.objectContaining({ value: dayTokenProject.markets[0]?.price.value }), + value: expect.objectContaining({ value: ethereumToken?.market?.price?.value }), relativeChange: expect.objectContaining({ - value: dayTokenProject.markets[0]?.pricePercentChange24h.value, + value: dayTokenProject.markets?.[0]?.pricePercentChange24h?.value, }), }) }) @@ -273,7 +325,7 @@ describe(useTokenPriceHistory, () => { }) }) - it('returns correct spot price', async () => { + it('returns correct spot price with calculated percentage change', async () => { const { result } = renderHookWithProviders( () => useTokenPriceHistory({ @@ -283,10 +335,16 @@ describe(useTokenPriceHistory, () => { { resolvers }, ) await waitFor(() => { + const ethereumToken = yearTokenProject.tokens.find((t) => t.chain === GraphQLApi.Chain.Ethereum) + // For non-Day durations, relativeChange is calculated from price history + const openPrice = yearPriceHistory[0]?.value ?? 0 + const closePrice = yearPriceHistory[yearPriceHistory.length - 1]?.value ?? 0 + const calculatedChange = openPrice > 0 ? ((closePrice - openPrice) / openPrice) * 100 : 0 + expect(result.current.data?.spot).toEqual({ - value: expect.objectContaining({ value: yearTokenProject.markets[0]?.price?.value }), + value: expect.objectContaining({ value: ethereumToken?.market?.price?.value }), relativeChange: expect.objectContaining({ - value: yearTokenProject.markets[0]?.pricePercentChange24h?.value, + value: calculatedChange, }), }) }) @@ -294,18 +352,20 @@ describe(useTokenPriceHistory, () => { }) describe('when duration is changed', () => { - it('returns new price history and spot price', async () => { + it('returns new price history and spot price with correct percentage change calculation', async () => { const { result } = renderHookWithProviders(() => useTokenPriceHistory({ currencyId: SAMPLE_CURRENCY_ID_1 }), { resolvers, }) await waitFor(() => { + const ethereumToken = dayTokenProject.tokens.find((t) => t.chain === GraphQLApi.Chain.Ethereum) + // For Day duration, should use API's 24hr value expect(result.current.data).toEqual({ priceHistory: formatPriceHistory(dayPriceHistory), spot: { - value: expect.objectContaining({ value: dayTokenProject.markets[0]?.price.value }), + value: expect.objectContaining({ value: ethereumToken?.market?.price?.value }), relativeChange: expect.objectContaining({ - value: dayTokenProject.markets[0]?.pricePercentChange24h.value, + value: dayTokenProject.markets?.[0]?.pricePercentChange24h?.value, }), }, }) @@ -317,12 +377,18 @@ describe(useTokenPriceHistory, () => { }) await waitFor(() => { + const ethereumToken = weekTokenProject.tokens.find((t) => t.chain === GraphQLApi.Chain.Ethereum) + // For Week duration, should calculate from price history + const openPrice = weekPriceHistory[0]?.value ?? 0 + const closePrice = weekPriceHistory[weekPriceHistory.length - 1]?.value ?? 0 + const calculatedChange = openPrice > 0 ? ((closePrice - openPrice) / openPrice) * 100 : 0 + expect(result.current.data).toEqual({ priceHistory: formatPriceHistory(weekPriceHistory), spot: { - value: expect.objectContaining({ value: weekTokenProject.markets[0]?.price?.value }), + value: expect.objectContaining({ value: ethereumToken?.market?.price?.value }), relativeChange: expect.objectContaining({ - value: weekTokenProject.markets[0]?.pricePercentChange24h?.value, + value: calculatedChange, }), }, }) diff --git a/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts b/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts index 5f1222aaeda..b472493f045 100644 --- a/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts +++ b/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts @@ -4,12 +4,15 @@ import { type Dispatch, type SetStateAction, useCallback, useMemo, useRef, useSt import { type SharedValue, useDerivedValue } from 'react-native-reanimated' import { type TLineChartData } from 'react-native-wagmi-charts' import { PollingInterval } from 'uniswap/src/constants/misc' +import { UniverseChainId } from 'uniswap/src/features/chains/types' +import { toGraphQLChain } from 'uniswap/src/features/chains/utils' import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils/currencyIdToContractInput' import { useLocalizationContext } from 'uniswap/src/features/language/LocalizationContext' +import { currencyIdToChain } from 'uniswap/src/utils/currencyId' export type TokenSpotData = { value: SharedValue - relativeChange: SharedValue + relativeChange: SharedValue } export type PriceNumberOfDigits = { @@ -63,18 +66,48 @@ export function useTokenPriceHistory({ skip, }) + // Data source strategy for multi-chain tokens: + // - Use PER-CHAIN data (token.market) for price and price history to show the correct chain-specific view + // - Fallback to AGGREGATED data (project.markets) when per-chain history is unavailable + // - Continue using aggregated 24hr change for consistency across platforms + // Note: TokenProjectMarket is aggregated across chains, TokenMarket is per-chain const offChainData = priceData?.tokenProjects?.[0]?.markets?.[0] - const onChainData = priceData?.tokenProjects?.[0]?.tokens[0]?.market - const price = offChainData?.price?.value ?? onChainData?.price?.value ?? lastPrice.current + // We need to find the specific token for the chain we're viewing + const currentChain = toGraphQLChain(currencyIdToChain(currencyId) ?? UniverseChainId.Mainnet) + const currentChainToken = priceData?.tokenProjects?.[0]?.tokens.find((token) => token.chain === currentChain) + const onChainData = currentChainToken?.market + + // Use per-chain price to ensure correct price on each chain (e.g., USDC on Ethereum vs Polygon) + const price = onChainData?.price?.value ?? offChainData?.price?.value ?? lastPrice.current lastPrice.current = price - const priceHistory = offChainData?.priceHistory ?? onChainData?.priceHistory + + // Prefer per-chain price history so multi-chain tokens render the correct chart for the selected chain + const priceHistory = onChainData?.priceHistory ?? offChainData?.priceHistory + const pricePercentChange24h = offChainData?.pricePercentChange24h?.value ?? onChainData?.pricePercentChange24h?.value ?? 0 + // Calculate percentage change from price history for the selected duration + const calculatedPriceChange = useMemo(() => { + if (!priceHistory || priceHistory.length === 0) { + return undefined + } + const openPrice = priceHistory[0]?.value + const closePrice = priceHistory[priceHistory.length - 1]?.value + if (openPrice === undefined || closePrice === undefined || openPrice === 0) { + return undefined + } + return ((closePrice - openPrice) / openPrice) * 100 + }, [priceHistory]) + + // Use API's 24hr change for 1d, calculated change for other durations + const priceChange = duration === GraphQLApi.HistoryDuration.Day ? pricePercentChange24h : calculatedPriceChange + const spotValue = useDerivedValue(() => price ?? 0) - const spotRelativeChange = useDerivedValue(() => pricePercentChange24h) + const spotRelativeChange = useDerivedValue(() => priceChange) + // biome-ignore lint/correctness/useExhaustiveDependencies: ensure spot updates when price changes const spot = useMemo( () => price !== undefined @@ -83,7 +116,7 @@ export function useTokenPriceHistory({ relativeChange: spotRelativeChange, } : undefined, - [price, spotValue, spotRelativeChange], + [price, priceChange, spotValue, spotRelativeChange], ) const formattedPriceHistory = useMemo(() => { diff --git a/apps/mobile/src/components/Requests/ConnectedDapps/DappConnectionItem.tsx b/apps/mobile/src/components/Requests/ConnectedDapps/DappConnectionItem.tsx index 2f7d54b1a79..f854756545e 100644 --- a/apps/mobile/src/components/Requests/ConnectedDapps/DappConnectionItem.tsx +++ b/apps/mobile/src/components/Requests/ConnectedDapps/DappConnectionItem.tsx @@ -4,11 +4,11 @@ import { NativeSyntheticEvent, StyleSheet } from 'react-native' import ContextMenu, { ContextMenuOnPressNativeEvent } from 'react-native-context-menu-view' import 'react-native-reanimated' import { FadeIn, FadeOut } from 'react-native-reanimated' -import { DappHeaderIcon } from 'src/components/Requests/DappHeaderIcon' import { WalletConnectSession } from 'src/features/walletConnect/walletConnectSlice' import { AnimatedTouchableArea, Flex, Text } from 'ui/src' import { iconSizes, spacing } from 'ui/src/theme' import { noop } from 'utilities/src/react/noop' +import { DappHeaderIcon } from 'wallet/src/components/dappRequests/DappHeaderIcon' export function DappConnectionItem({ session, @@ -74,7 +74,7 @@ export function DappConnectionItem({ )} - + {dappRequestInfo.name || dappRequestInfo.url} diff --git a/apps/mobile/src/components/Requests/ModalWithOverlay/ModalWithOverlay.tsx b/apps/mobile/src/components/Requests/ModalWithOverlay/ModalWithOverlay.tsx index e86b139e12b..39392c5ebd2 100644 --- a/apps/mobile/src/components/Requests/ModalWithOverlay/ModalWithOverlay.tsx +++ b/apps/mobile/src/components/Requests/ModalWithOverlay/ModalWithOverlay.tsx @@ -1,4 +1,5 @@ import { BottomSheetFooter, BottomSheetScrollView, useBottomSheetInternal } from '@gorhom/bottom-sheet' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { PropsWithChildren, useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { @@ -17,8 +18,6 @@ import { Button, ButtonProps, Flex } from 'ui/src' import { spacing } from 'ui/src/theme' import { Modal } from 'uniswap/src/components/modals/Modal' import { ModalProps } from 'uniswap/src/components/modals/ModalProps' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' import { TestID } from 'uniswap/src/test/fixtures/testIDs' @@ -59,7 +58,7 @@ export function ModalWithOverlay({ }: ModalWithOverlayProps): JSX.Element { const scrollViewRef = useRef(null) const contentViewRef = useRef(null) - const measureLayoutTimeoutRef = useRef() + const measureLayoutTimeoutRef = useRef(undefined) const startedScrollingRef = useRef(false) const [showOverlay, setShowOverlay] = useState(false) @@ -127,7 +126,6 @@ export function ModalWithOverlay({ contentContainerStyle={ contentContainerStyle ?? { paddingHorizontal: spacing.spacing24, - paddingTop: spacing.spacing12, } } showsVerticalScrollIndicator={false} diff --git a/apps/mobile/src/components/Requests/RequestModal/ClientDetails.tsx b/apps/mobile/src/components/Requests/RequestModal/ClientDetails.tsx index 69120a67ad8..2fb263be61e 100644 --- a/apps/mobile/src/components/Requests/RequestModal/ClientDetails.tsx +++ b/apps/mobile/src/components/Requests/RequestModal/ClientDetails.tsx @@ -1,12 +1,9 @@ import React from 'react' -import { DappHeaderIcon } from 'src/components/Requests/DappHeaderIcon' import { HeaderText } from 'src/components/Requests/RequestModal/HeaderText' import { WalletConnectSigningRequest } from 'src/features/walletConnect/walletConnectSlice' -import { Flex, useSporeColors } from 'ui/src' -import { iconSizes } from 'ui/src/theme' import { useCurrencyInfo } from 'uniswap/src/features/tokens/useCurrencyInfo' -import { formatDappURL } from 'utilities/src/format/urls' -import { LinkButton } from 'wallet/src/components/buttons/LinkButton' +import { DappHeaderIcon } from 'wallet/src/components/dappRequests/DappHeaderIcon' +import { DappRequestHeader } from 'wallet/src/components/dappRequests/DappRequestHeader' export interface PermitInfo { currencyId: string @@ -21,26 +18,11 @@ export function ClientDetails({ permitInfo?: PermitInfo }): JSX.Element { const { dappRequestInfo } = request - const colors = useSporeColors() - const permitCurrencyInfo = useCurrencyInfo(permitInfo?.currencyId) - - return ( - - - - - + const headerIcon = + const title = ( + ) + + return } diff --git a/apps/mobile/src/components/Requests/RequestModal/HeaderText.tsx b/apps/mobile/src/components/Requests/RequestModal/HeaderText.tsx index 3a03d374293..5fe9b474ce5 100644 --- a/apps/mobile/src/components/Requests/RequestModal/HeaderText.tsx +++ b/apps/mobile/src/components/Requests/RequestModal/HeaderText.tsx @@ -26,7 +26,7 @@ export function HeaderText({ })?.toExact() return readablePermitAmount ? ( - + }} @@ -39,7 +39,7 @@ export function HeaderText({ /> ) : ( - + }} @@ -70,9 +70,5 @@ export function HeaderText({ return } - return ( - - {getReadableMethodName(method, dappRequestInfo.name || dappRequestInfo.url)} - - ) + return {getReadableMethodName(method, dappRequestInfo.name || dappRequestInfo.url)} } diff --git a/apps/mobile/src/components/Requests/RequestModal/RequestDetails.tsx b/apps/mobile/src/components/Requests/RequestModal/RequestDetails.tsx index de2feb1b953..46767665dce 100644 --- a/apps/mobile/src/components/Requests/RequestModal/RequestDetails.tsx +++ b/apps/mobile/src/components/Requests/RequestModal/RequestDetails.tsx @@ -6,6 +6,7 @@ import { ScrollView } from 'react-native-gesture-handler' import { PermitInfo } from 'src/components/Requests/RequestModal/ClientDetails' import { isBatchedTransactionRequest, + isPersonalSignRequest, isTransactionRequest, SignRequest, WalletConnectSigningRequest, @@ -44,7 +45,7 @@ const requestMessageStyle: StyleProp = { } const getStrMessage = (request: WalletConnectSigningRequest): string => { - if (request.type === EthMethod.PersonalSign || request.type === EthMethod.EthSign) { + if (isPersonalSignRequest(request)) { return request.message || request.rawMessage } diff --git a/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModal.tsx b/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModal.tsx index daf85557387..a4e7f666d49 100644 --- a/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModal.tsx +++ b/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModal.tsx @@ -1,7 +1,8 @@ import { useNetInfo } from '@react-native-community/netinfo' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { getSdkError } from '@walletconnect/utils' import { providers } from 'ethers' -import React, { useMemo, useRef } from 'react' +import React, { useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { ModalWithOverlay } from 'src/components/Requests/ModalWithOverlay/ModalWithOverlay' @@ -27,20 +28,20 @@ import { } from 'src/features/walletConnect/walletConnectSlice' import { spacing } from 'ui/src/theme' import { EthMethod } from 'uniswap/src/features/dappRequests/types' -import { isSignTypedDataRequest } from 'uniswap/src/features/dappRequests/utils' +import { isSelfCallWithData, isSignTypedDataRequest } from 'uniswap/src/features/dappRequests/utils' import { useTransactionGasFee } from 'uniswap/src/features/gas/hooks' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { Platform } from 'uniswap/src/features/platforms/types/Platform' import { useHasAccountMismatchCallback } from 'uniswap/src/features/smartWallet/mismatch/hooks' import { MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import { useIsBlocked } from 'uniswap/src/features/trm/hooks' import { DappRequestType, UwULinkMethod, WCEventType, WCRequestOutcome } from 'uniswap/src/types/walletConnect' import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { useBooleanState } from 'utilities/src/react/useBooleanState' +import { TransactionRiskLevel } from 'wallet/src/features/dappRequests/types' +import { shouldDisableConfirm } from 'wallet/src/features/dappRequests/utils/riskUtils' import { formatExternalTxnWithGasEstimates } from 'wallet/src/features/gas/formatExternalTxnWithGasEstimates' -import { useIsBlockedActiveAddress } from 'wallet/src/features/trm/hooks' -import { useSignerAccounts } from 'wallet/src/features/wallet/hooks' +import { useLiveAccountDelegationDetails } from 'wallet/src/features/smartWallet/hooks/useLiveAccountDelegationDetails' +import { useHasSmartWalletConsent, useSignerAccounts } from 'wallet/src/features/wallet/hooks' interface Props { onClose: () => void @@ -62,8 +63,14 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem const netInfo = useNetInfo() const didOpenFromDeepLink = useSelector(selectDidOpenFromDeepLink) const chainId = request.chainId + // Initialize with null to indicate scan hasn't completed yet + const [riskLevel, setRiskLevel] = useState(null) + const { value: confirmedRisk, setValue: setConfirmedRisk } = useBooleanState(false) const enablePermitMismatchUx = useFeatureFlag(FeatureFlags.EnablePermitMismatchUX) + const enableEip5792Methods = useFeatureFlag(FeatureFlags.Eip5792Methods) + const hasSmartWalletConsent = useHasSmartWalletConsent() + const blockaidTransactionScanning = useFeatureFlag(FeatureFlags.BlockaidTransactionScanning) const tx: providers.TransactionRequest | undefined = useMemo(() => { if (isTransactionRequest(request)) { @@ -83,7 +90,33 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem addressInput2: { address: request.account, platform: Platform.EVM }, }), ) - const gasFee = useTransactionGasFee({ tx }) + const delegationData = useLiveAccountDelegationDetails({ + address: request.account, + chainId, + }) + // Check if this is a self-transaction (to === from) with data + // This is required for delegation to occur + // Note: chainId is required for correct address comparison + const isSelfTransaction = useMemo( + () => + isSelfCallWithData({ + from: request.account, + to: tx?.to, + data: tx?.data ? String(tx.data) : undefined, + chainId, + }), + [request.account, tx?.to, tx?.data, chainId], + ) + const shouldDelegate = Boolean( + delegationData?.needsDelegation && enableEip5792Methods && hasSmartWalletConsent && isSelfTransaction, + ) + const smartContractDelegationAddress = shouldDelegate + ? delegationData?.contractAddress // latest Uniswap delegation address + : delegationData?.currentDelegationAddress + const gasFee = useTransactionGasFee({ + tx, + ...(smartContractDelegationAddress && { smartContractDelegationAddress }), + }) const hasSufficientFunds = useHasSufficientFunds({ account: request.account, @@ -92,12 +125,6 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem value: tx?.value?.toString(), }) - const { isBlocked: isSenderBlocked, isBlockedLoading: isSenderBlockedLoading } = useIsBlockedActiveAddress() - const { isBlocked: isRecipientBlocked, isBlockedLoading: isRecipientBlockedLoading } = useIsBlocked(tx?.to) - - const isBlocked = isSenderBlocked || isRecipientBlocked - const isBlockedLoading = isSenderBlockedLoading || isRecipientBlockedLoading - const getHasMismatch = useHasAccountMismatchCallback() const hasMismatch = getHasMismatch(chainId) // When link mode is active we can sign messages through universal links on device @@ -112,8 +139,11 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem return false } - if (isBlocked || isBlockedLoading) { - return false + // If Blockaid scanning is enabled, disable confirm based on risk level and confirmation state + if (blockaidTransactionScanning) { + if (shouldDisableConfirm({ riskLevel, confirmedRisk })) { + return false + } } if (getDoesMethodCostGas(request)) { @@ -282,7 +312,7 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem ) diff --git a/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModalContent.tsx b/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModalContent.tsx index 86c391031a3..593d0dc401a 100644 --- a/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModalContent.tsx +++ b/apps/mobile/src/components/Requests/RequestModal/WalletConnectRequestModalContent.tsx @@ -1,5 +1,6 @@ import { useBottomSheetInternal } from '@gorhom/bottom-sheet' import { useNetInfo } from '@react-native-community/netinfo' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useTranslation } from 'react-i18next' import Animated, { useAnimatedStyle } from 'react-native-reanimated' import { ClientDetails, PermitInfo } from 'src/components/Requests/RequestModal/ClientDetails' @@ -12,15 +13,19 @@ import { import { Flex, Text } from 'ui/src' import { AlertTriangleFilled } from 'ui/src/components/icons' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' -import { nativeOnChain } from 'uniswap/src/constants/tokens' +import { getChainInfo } from 'uniswap/src/features/chains/chainInfo' import { EthMethod } from 'uniswap/src/features/dappRequests/types' import { GasFeeResult } from 'uniswap/src/features/gas/types' -import { BlockedAddressWarning } from 'uniswap/src/features/transactions/modals/BlockedAddressWarning' -import { isPrimaryTypePermit } from 'uniswap/src/types/walletConnect' +import { isPrimaryTypePermit, UwULinkMethod } from 'uniswap/src/types/walletConnect' import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' import { MAX_HIDDEN_CALLS_BY_DEFAULT } from 'wallet/src/components/BatchedTransactions/BatchedTransactionDetails' +import { DappPersonalSignContent } from 'wallet/src/components/dappRequests/DappPersonalSignContent' +import { DappSendCallsScanningContent } from 'wallet/src/components/dappRequests/DappSendCallsScanningContent' +import { DappSignTypedDataContent } from 'wallet/src/components/dappRequests/DappSignTypedDataContent' +import { DappTransactionScanningContent } from 'wallet/src/components/dappRequests/DappTransactionScanningContent' import { WarningBox } from 'wallet/src/components/WarningBox/WarningBox' +import { TransactionRiskLevel } from 'wallet/src/features/dappRequests/types' import { AddressFooter } from 'wallet/src/features/transactions/TransactionRequest/AddressFooter' import { NetworkFeeFooter } from 'wallet/src/features/transactions/TransactionRequest/NetworkFeeFooter' @@ -58,21 +63,27 @@ type WalletConnectRequestModalContentProps = { gasFee: GasFeeResult hasSufficientFunds: boolean request: WalletConnectSigningRequest - isBlocked: boolean + showSmartWalletActivation?: boolean + confirmedRisk: boolean + onConfirmRisk: (confirmed: boolean) => void + onRiskLevelChange: (riskLevel: TransactionRiskLevel) => void } export function WalletConnectRequestModalContent({ request, hasSufficientFunds, - isBlocked, gasFee, + showSmartWalletActivation, + confirmedRisk, + onConfirmRisk, + onRiskLevelChange, }: WalletConnectRequestModalContentProps): JSX.Element { const chainId = request.chainId const permitInfo = getPermitInfo(request) - const nativeCurrency = nativeOnChain(chainId) + const nativeCurrency = getChainInfo(chainId).nativeCurrency - const { t } = useTranslation() const { animatedFooterHeight } = useBottomSheetInternal() + const blockaidTransactionScanning = useFeatureFlag(FeatureFlags.BlockaidTransactionScanning) const netInfo = useNetInfo() @@ -87,56 +98,111 @@ export function WalletConnectRequestModalContent({ return ( <> - + - - - - - - - {!hasSufficientFunds && ( - - - {t('walletConnect.request.error.insufficientFunds', { - currencySymbol: nativeCurrency.symbol ?? '', - })} - + {/* Show Blockaid scanning UI for supported request types */} + {blockaidTransactionScanning ? ( + <> + + + + - )} - - {!netInfo.isInternetReachable && !suppressOfflineWarning ? ( - } - textColor="$statusWarning" - title={t('walletConnect.request.error.network')} - /> - ) : ( - - )} - - + + + ) : ( + <> + {/* Fallback to original UI for non-scanning requests */} + + + + + + + + + + + + )} + + ) +} + +function RequestWarnings({ + request, + hasSufficientFunds, + isNetworkReachable, + suppressOfflineWarning, + nativeCurrencySymbol, +}: { + request: WalletConnectSigningRequest + hasSufficientFunds: boolean + isNetworkReachable: boolean + suppressOfflineWarning: boolean + nativeCurrencySymbol: string +}): JSX.Element { + const { t } = useTranslation() + + return ( + <> + {!hasSufficientFunds && ( + + + {t('walletConnect.request.error.insufficientFunds', { + currencySymbol: nativeCurrencySymbol, + })} + + + )} + + {!isNetworkReachable && !suppressOfflineWarning ? ( + } + textColor="$statusWarning" + title={t('walletConnect.request.error.network')} + /> + ) : ( + + )} ) } @@ -144,22 +210,16 @@ export function WalletConnectRequestModalContent({ function WarningSection({ request, showUnsafeWarning, - isBlockedAddress, }: { request: WalletConnectSigningRequest showUnsafeWarning: boolean - isBlockedAddress: boolean }): JSX.Element | null { const { t } = useTranslation() - if (!showUnsafeWarning && !isBlockedAddress) { + if (!showUnsafeWarning) { return null } - if (isBlockedAddress) { - return - } - if (isBatchedTransactionRequest(request)) { if (request.calls.length <= 1) { return null @@ -175,3 +235,97 @@ function WarningSection({ return null } + +/** Helper component to render appropriate scanning content based on request type */ +function ScanningContent({ + request, + chainId, + gasFee, + showSmartWalletActivation, + confirmedRisk, + onConfirmRisk, + onRiskLevelChange, +}: { + request: WalletConnectSigningRequest + chainId: number + gasFee: GasFeeResult + showSmartWalletActivation?: boolean + confirmedRisk: boolean + onConfirmRisk: (confirmed: boolean) => void + onRiskLevelChange: (riskLevel: TransactionRiskLevel) => void +}): JSX.Element { + switch (request.type) { + case EthMethod.EthSendTransaction: + case UwULinkMethod.Erc20Send: + return ( + + ) + + case EthMethod.PersonalSign: + case EthMethod.EthSign: + return ( + + ) + + case EthMethod.WalletSendCalls: + return ( + + ) + + case EthMethod.SignTypedData: + case EthMethod.SignTypedDataV4: + return ( + + ) + } +} diff --git a/apps/mobile/src/components/Requests/ScanSheet/PendingConnectionModal.tsx b/apps/mobile/src/components/Requests/ScanSheet/PendingConnectionModal.tsx index ba8b4b13594..6f64a0317be 100644 --- a/apps/mobile/src/components/Requests/ScanSheet/PendingConnectionModal.tsx +++ b/apps/mobile/src/components/Requests/ScanSheet/PendingConnectionModal.tsx @@ -1,15 +1,13 @@ import { useBottomSheetInternal } from '@gorhom/bottom-sheet' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { getSdkError } from '@walletconnect/utils' import React, { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Animated, { useAnimatedStyle } from 'react-native-reanimated' import { useDispatch, useSelector } from 'react-redux' -import { DappHeaderIcon } from 'src/components/Requests/DappHeaderIcon' import { ModalWithOverlay, ModalWithOverlayProps } from 'src/components/Requests/ModalWithOverlay/ModalWithOverlay' -import { AccountSelectPopover } from 'src/components/Requests/ScanSheet/AccountSelectPopover' -import { SitePermissions } from 'src/components/Requests/ScanSheet/SitePermissions' import { selectDidOpenFromDeepLink } from 'src/features/walletConnect/selectors' -import { getSessionNamespaces } from 'src/features/walletConnect/utils' +import { convertCapabilitiesToScopedProperties, getSessionNamespaces } from 'src/features/walletConnect/utils' import { returnToPreviousApp } from 'src/features/walletConnect/WalletConnect' import { wcWeb3Wallet } from 'src/features/walletConnect/walletConnectClient' import { @@ -17,21 +15,28 @@ import { removePendingSession, setDidOpenFromDeepLink, WalletConnectPendingSession, - WalletConnectVerifyStatus, } from 'src/features/walletConnect/walletConnectSlice' -import { Flex, Text, useSporeColors } from 'ui/src' -import { Verified } from 'ui/src/components/icons' +import { Flex } from 'ui/src' import { AccountType } from 'uniswap/src/features/accounts/types' import { pushNotification } from 'uniswap/src/features/notifications/slice/slice' import { AppNotificationType } from 'uniswap/src/features/notifications/slice/types' import { MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { DappRequestType, WalletConnectEvent, WCEventType, WCRequestOutcome } from 'uniswap/src/types/walletConnect' -import { formatDappURL } from 'utilities/src/format/urls' import { useEvent } from 'utilities/src/react/hooks' import { ONE_SECOND_MS } from 'utilities/src/time/time' -import { LinkButton } from 'wallet/src/components/buttons/LinkButton' -import { useActiveAccountWithThrow, useSignerAccounts } from 'wallet/src/features/wallet/hooks' +import { DappConnectionContent } from 'wallet/src/components/dappRequests/DappConnectionContent' +import { DappRequestHeader } from 'wallet/src/components/dappRequests/DappRequestHeader' +import { getCapabilitiesCore } from 'wallet/src/features/batchedTransactions/utils' +import { useBlockaidVerification } from 'wallet/src/features/dappRequests/hooks/useBlockaidVerification' +import { useDappConnectionConfirmation } from 'wallet/src/features/dappRequests/hooks/useDappConnectionConfirmation' +import { DappConnectionInfo, DappVerificationStatus } from 'wallet/src/features/dappRequests/types' +import { mergeVerificationStatuses } from 'wallet/src/features/dappRequests/verification' +import { + useActiveAccountWithThrow, + useHasSmartWalletConsent, + useSignerAccounts, +} from 'wallet/src/features/wallet/hooks' type Props = { pendingSession: WalletConnectPendingSession @@ -47,12 +52,20 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. const isViewOnly = activeAccount.type === AccountType.Readonly const didOpenFromDeepLink = useSelector(selectDidOpenFromDeepLink) + const hasSmartWalletConsent = useHasSmartWalletConsent() + const eip5792MethodsEnabled = useFeatureFlag(FeatureFlags.Eip5792Methods) - const [confirmedWarning, setConfirmedWarning] = useState(false) const [isConnecting, setIsConnecting] = useState(false) - const isThreat = pendingSession.verifyStatus === WalletConnectVerifyStatus.Threat - const disableConfirm = (isThreat && !confirmedWarning) || isViewOnly || isConnecting + // Merge WalletConnect verification with Blockaid verification + const { verificationStatus: blockaidStatus } = useBlockaidVerification(pendingSession.dappRequestInfo.url) + const finalVerificationStatus = mergeVerificationStatuses(pendingSession.verifyStatus, blockaidStatus) + + const { confirmedWarning, setConfirmedWarning, disableConfirm } = useDappConnectionConfirmation({ + verificationStatus: finalVerificationStatus, + isViewOnly, + isLoading: isConnecting, + }) const signerAccounts = useSignerAccounts() const defaultSelectedAccountAddresses = useMemo(() => { @@ -103,10 +116,18 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. // Handle WC 2.0 session request if (approved) { const namespaces = getSessionNamespaces(orderedSelectedAccountAddresses, pendingSession.proposalNamespaces) + const capabilities = await getCapabilitiesCore({ + address: activeAddress, + chainIds: pendingSession.chains, + hasSmartWalletConsent: hasSmartWalletConsent ?? false, + }) + + const scopedProperties = convertCapabilitiesToScopedProperties(capabilities) const session = await wcWeb3Wallet.approveSession({ id: Number(pendingSession.id), namespaces, + ...(eip5792MethodsEnabled ? { scopedProperties } : {}), }) dispatch( @@ -122,6 +143,7 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. chains: pendingSession.chains, namespaces, activeAccount: activeAddress, + ...(eip5792MethodsEnabled ? { capabilities } : {}), }, }), ) @@ -156,8 +178,7 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. } }) - const dappName = pendingSession.dappRequestInfo.name || pendingSession.dappRequestInfo.url || '' - + const isThreat = finalVerificationStatus === DappVerificationStatus.Threat const isThreatProps: Partial = isThreat ? { cancelButtonText: t('walletConnect.pending.button.reject'), @@ -188,9 +209,8 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. > void - dappName: string pendingSession: WalletConnectPendingSession - verifyStatus: WalletConnectVerifyStatus + verifyStatus: DappVerificationStatus isViewOnly: boolean onConfirmWarning: (confirmed: boolean) => void confirmedWarning: boolean @@ -218,7 +237,6 @@ function PendingConnectionModalContent({ allAccountAddresses, selectedAccountAddresses, setSelectedAccountAddresses, - dappName, pendingSession, verifyStatus, isViewOnly, @@ -226,73 +244,37 @@ function PendingConnectionModalContent({ confirmedWarning, }: PendingConnectionModalContentProps): JSX.Element { const { t } = useTranslation() - const colors = useSporeColors() - const { animatedFooterHeight } = useBottomSheetInternal() const bottomSpacerStyle = useAnimatedStyle(() => ({ height: animatedFooterHeight.value, })) + const dappInfo: DappConnectionInfo = { + name: pendingSession.dappRequestInfo.name, + url: pendingSession.dappRequestInfo.url, + icon: pendingSession.dappRequestInfo.icon, + } + return ( <> - - - - {t('walletConnect.pending.title', { - dappName, - })} - - - - {verifyStatus === WalletConnectVerifyStatus.Verified && ( - - )} - + + - } onConfirmWarning={onConfirmWarning} /> - {!isViewOnly && ( - - - - )} - {isViewOnly && ( - - - {t('home.warning.viewOnly')} - - - )} - ) } diff --git a/apps/mobile/src/components/Requests/ScanSheet/WalletConnectModal.tsx b/apps/mobile/src/components/Requests/ScanSheet/WalletConnectModal.tsx index b3d79b23c5c..d3bd8eb86a0 100644 --- a/apps/mobile/src/components/Requests/ScanSheet/WalletConnectModal.tsx +++ b/apps/mobile/src/components/Requests/ScanSheet/WalletConnectModal.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Alert } from 'react-native' import 'react-native-reanimated' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useDispatch } from 'react-redux' import { useEagerExternalProfileRootNavigation } from 'src/app/navigation/hooks' import { BackButtonView } from 'src/components/layout/BackButtonView' @@ -24,8 +25,6 @@ import { Modal } from 'uniswap/src/components/modals/Modal' import { ScannerModalState } from 'uniswap/src/components/ReceiveQRCode/constants' import { ReceiveQRCode } from 'uniswap/src/components/ReceiveQRCode/ReceiveQRCode' import { AccountType } from 'uniswap/src/features/accounts/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import Trace from 'uniswap/src/features/telemetry/Trace' import { TestID } from 'uniswap/src/test/fixtures/testIDs' diff --git a/apps/mobile/src/components/Requests/ScanSheet/util.test.ts b/apps/mobile/src/components/Requests/ScanSheet/util.test.ts index 441ed9a01a7..76993011636 100644 --- a/apps/mobile/src/components/Requests/ScanSheet/util.test.ts +++ b/apps/mobile/src/components/Requests/ScanSheet/util.test.ts @@ -1,5 +1,9 @@ import * as wcUtils from '@walletconnect/utils' import { CUSTOM_UNI_QR_CODE_PREFIX, getSupportedURI, URIType } from 'src/components/Requests/ScanSheet/util' +import { + UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM, + UNISWAP_WALLETCONNECT_URL, +} from 'src/features/deepLinking/constants' import { wcAsParamInUniwapScheme, wcInUniwapScheme, @@ -121,4 +125,84 @@ describe('getSupportedURI', () => { it('should return undefined for invalid metamask address', async () => { expect(await getSupportedURI('ethereum:invalid_address')).toBeUndefined() }) + + describe('URL and HTML encoded URIs', () => { + it('should handle percent-encoded WalletConnect v2 URI', async () => { + // Simulate a URI that has been percent-encoded (& becomes %26) + const encodedUri = encodeURIComponent(VALID_WC_V2_URI) + const result = await getSupportedURI(encodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle HTML entity-encoded ampersands in WalletConnect v2 URI', async () => { + // Simulate a URI with HTML-encoded ampersands (& becomes &) + const htmlEncodedUri = VALID_WC_V2_URI.replace(/&/g, '&') + const result = await getSupportedURI(htmlEncodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle both percent-encoded and HTML entity-encoded URI', async () => { + // First apply HTML encoding, then percent encoding + const htmlEncodedUri = VALID_WC_V2_URI.replace(/&/g, '&') + const doubleEncodedUri = encodeURIComponent(htmlEncodedUri) + const result = await getSupportedURI(doubleEncodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle percent-encoded uniswap scheme URI with query param', async () => { + const fullUri = UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM + VALID_WC_V2_URI + const encodedUri = encodeURIComponent(fullUri) + const result = await getSupportedURI(encodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle HTML entity-encoded uniswap scheme URI with query param', async () => { + const fullUri = UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM + VALID_WC_V2_URI + const htmlEncodedUri = fullUri.replace(/&/g, '&') + const result = await getSupportedURI(htmlEncodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle percent-encoded uniswap app URL', async () => { + const fullUri = UNISWAP_WALLETCONNECT_URL + VALID_WC_V2_URI + const encodedUri = encodeURIComponent(fullUri) + const result = await getSupportedURI(encodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle HTML entity-encoded uniswap app URL', async () => { + const fullUri = UNISWAP_WALLETCONNECT_URL + VALID_WC_V2_URI + const htmlEncodedUri = fullUri.replace(/&/g, '&') + const result = await getSupportedURI(htmlEncodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle percent-encoded hello_uniwallet prefix', async () => { + const fullUri = CUSTOM_UNI_QR_CODE_PREFIX + VALID_WC_V2_URI + const encodedUri = encodeURIComponent(fullUri) + const result = await getSupportedURI(encodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle HTML entity-encoded hello_uniwallet prefix', async () => { + const fullUri = CUSTOM_UNI_QR_CODE_PREFIX + VALID_WC_V2_URI + const htmlEncodedUri = fullUri.replace(/&/g, '&') + const result = await getSupportedURI(htmlEncodedUri) + expect(result).toEqual({ type: URIType.WalletConnectV2URL, value: VALID_WC_V2_URI }) + }) + + it('should handle malformed percent-encoded URI without crashing', async () => { + // Malformed URI with invalid percent encoding (% not followed by valid hex) + const malformedUri = 'uniswap://wc?uri=%E0%A4%A' + // Should not throw an error, even with malformed encoding + await expect(getSupportedURI(malformedUri)).resolves.not.toThrow() + }) + + it('should handle URI with standalone percent sign', async () => { + // URI with a standalone % which is invalid for decodeURIComponent + const malformedUri = 'hello_uniwallet:' + VALID_WC_V2_URI + '%' + // Should not throw an error, even with malformed encoding + await expect(getSupportedURI(malformedUri)).resolves.not.toThrow() + }) + }) }) diff --git a/apps/mobile/src/components/Requests/ScanSheet/util.ts b/apps/mobile/src/components/Requests/ScanSheet/util.ts index 140882c7d38..f8f07334473 100644 --- a/apps/mobile/src/components/Requests/ScanSheet/util.ts +++ b/apps/mobile/src/components/Requests/ScanSheet/util.ts @@ -46,6 +46,9 @@ export async function getSupportedURI( return undefined } + // Decode URI in case it's encoded (handles both percent encoding and HTML ampersand) + uri = safeDecodeURIComponent(uri).replace(/&/g, '&') + const maybeAddress = getValidAddress({ address: uri, platform: Platform.EVM, @@ -72,7 +75,7 @@ export async function getSupportedURI( (await getWcUriWithCustomPrefix(uri, CUSTOM_UNI_QR_CODE_PREFIX)) || (await getWcUriWithCustomPrefix(uri, UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM)) || (await getWcUriWithCustomPrefix(uri, UNISWAP_URL_SCHEME)) || - (await getWcUriWithCustomPrefix(decodeURIComponent(uri), UNISWAP_WALLETCONNECT_URL)) || + (await getWcUriWithCustomPrefix(uri, UNISWAP_WALLETCONNECT_URL)) || {} if (maybeCustomWcUri && type) { @@ -147,6 +150,22 @@ export function getScantasticQueryParams(uri: string): Nullable { return uriParts[1] || null } +function safeDecodeURIComponent(value: string): string { + try { + return decodeURIComponent(value) + } catch (e) { + logger.error(new Error('Failed to decode URI component'), { + tags: { + file: 'util.ts', + function: 'safeDecodeURIComponent', + }, + extra: { value, error: e }, + }) + // If decoding fails, return the original value + return value + } +} + const PARAM_PUB_KEY = 'pubKey' const PARAM_UUID = 'uuid' const PARAM_VENDOR = 'vendor' @@ -181,10 +200,10 @@ export function parseScantasticParams(uri: string): ScantasticParams | undefined try { return ScantasticParamsSchema.parse({ publicKey: publicKey ? JSON.parse(publicKey) : undefined, - uuid: uuid ? decodeURIComponent(uuid) : undefined, - vendor: vendor ? decodeURIComponent(vendor) : undefined, - model: model ? decodeURIComponent(model) : undefined, - browser: browser ? decodeURIComponent(browser) : undefined, + uuid: uuid ? safeDecodeURIComponent(uuid) : undefined, + vendor: vendor ? safeDecodeURIComponent(vendor) : undefined, + model: model ? safeDecodeURIComponent(model) : undefined, + browser: browser ? safeDecodeURIComponent(browser) : undefined, }) } catch (e) { const wrappedError = new Error('Invalid scantastic params', { cause: e }) diff --git a/apps/mobile/src/components/Requests/Uwulink/utils.ts b/apps/mobile/src/components/Requests/Uwulink/utils.ts index a600453c6dc..f1d138acde1 100644 --- a/apps/mobile/src/components/Requests/Uwulink/utils.ts +++ b/apps/mobile/src/components/Requests/Uwulink/utils.ts @@ -1,15 +1,15 @@ -import { parseEther } from 'ethers/lib/utils' -import { WalletConnectSigningRequest } from 'src/features/walletConnect/walletConnectSlice' -import { AssetType } from 'uniswap/src/entities/assets' -import { SignerMnemonicAccountMeta } from 'uniswap/src/features/accounts/types' -import { EthMethod } from 'uniswap/src/features/dappRequests/types' import { DynamicConfigs, UwULinkAllowlist, UwULinkAllowlistItem, UwuLinkConfigKey, -} from 'uniswap/src/features/gating/configs' -import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' + useDynamicConfigValue, +} from '@universe/gating' +import { parseEther } from 'ethers/lib/utils' +import { WalletConnectSigningRequest } from 'src/features/walletConnect/walletConnectSlice' +import { AssetType } from 'uniswap/src/entities/assets' +import { SignerMnemonicAccountMeta } from 'uniswap/src/features/accounts/types' +import { EthMethod } from 'uniswap/src/features/dappRequests/types' import { isUwULinkAllowlistType } from 'uniswap/src/features/gating/typeGuards' import { DappRequestType, @@ -147,6 +147,7 @@ export async function getFormattedUwuLinkTxnRequest({ dappRequestInfo: { name: '', url: '', + icon: null, ...request.dapp, requestType: DappRequestType.UwULink, chain_id: request.chainId, diff --git a/apps/mobile/src/components/RestoreWalletModal/__snapshots__/PrivateKeySpeedBumpModal.test.tsx.snap b/apps/mobile/src/components/RestoreWalletModal/__snapshots__/PrivateKeySpeedBumpModal.test.tsx.snap index 031dd009b31..a7a02717069 100644 --- a/apps/mobile/src/components/RestoreWalletModal/__snapshots__/PrivateKeySpeedBumpModal.test.tsx.snap +++ b/apps/mobile/src/components/RestoreWalletModal/__snapshots__/PrivateKeySpeedBumpModal.test.tsx.snap @@ -3,15 +3,7 @@ exports[`PrivateKeySpeedBumpModal renders correctly 1`] = ` @@ -509,16 +544,9 @@ exports[`PrivateKeySpeedBumpModal renders correctly 1`] = ` line-height-disabled="false" maxFontSizeMultiplier={1.2} numberOfLines={1} - onBlur={[Function]} - onFocus={[Function]} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#FFFFFF", - }, - }, + "color": "#FFFFFF", "fontFamily": "Basel Grotesk", "fontSize": 17, "fontWeight": "500", diff --git a/apps/mobile/src/components/Settings/EditWalletModal/EditLabelSettingsModal.tsx b/apps/mobile/src/components/Settings/EditWalletModal/EditLabelSettingsModal.tsx index 67233dc41e3..4aff5cbda2b 100644 --- a/apps/mobile/src/components/Settings/EditWalletModal/EditLabelSettingsModal.tsx +++ b/apps/mobile/src/components/Settings/EditWalletModal/EditLabelSettingsModal.tsx @@ -105,9 +105,7 @@ export function EditLabelSettingsModal({ py="$spacing12" returnKeyType="done" value={nickname} - placeholder={ - sanitizeAddressText(shortenAddress({ address, chars: 6 })) ?? t('settings.setting.wallet.label') - } + placeholder={sanitizeAddressText(shortenAddress({ address })) ?? t('settings.setting.wallet.label')} onBlur={onFinishEditing} onChangeText={setNickname} onSubmitEditing={onFinishEditing} diff --git a/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx b/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx index bc6d6057e9c..7b350cdee9d 100644 --- a/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx +++ b/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx @@ -279,6 +279,13 @@ const TokenBalanceItemRow = memo(function TokenBalanceItemRow({ item }: { item: + navigate(ModalName.ReportTokenIssue, { + currency: portfolioBalance.currencyInfo.currency, + isMarkedSpam: portfolioBalance.currencyInfo.isSpam, + source: 'portfolio', + }) + } onPressToken={handlePressToken} > {tokenBalanceItem} diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx index fb6b9f9ea56..75e72429278 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx @@ -10,6 +10,7 @@ import { TokenList } from 'uniswap/src/features/dataApi/types' import { ElementName, MobileEventName, SectionName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import Trace from 'uniswap/src/features/telemetry/Trace' +import { useShouldShowAztecWarning } from 'uniswap/src/hooks/useShouldShowAztecWarning' import { TestID, TestIDType } from 'uniswap/src/test/fixtures/testIDs' import { useBooleanState } from 'utilities/src/react/useBooleanState' @@ -66,10 +67,13 @@ export function TokenDetailsActionButtons({ }): JSX.Element { const { currencyInfo, isChainEnabled, tokenColor } = useTokenDetailsContext() const { value: actionMenuOpen, setFalse: closeActionMenu, toggle: toggleActionMenu } = useBooleanState(false) + const showAztecWarning = useShouldShowAztecWarning( + currencyInfo?.currency.isToken ? currencyInfo.currency.address : '', + ) const isBlocked = currencyInfo?.safetyInfo?.tokenList === TokenList.Blocked - const disabled = isBlocked || !isChainEnabled + const disabled = isBlocked || showAztecWarning || !isChainEnabled const validTokenColor = validColor(tokenColor) const lightTokenColor = validTokenColor ? opacify(12, validTokenColor) : undefined diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsBridgedAssetSection.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsBridgedAssetSection.tsx index 840d9c10050..cd3aecd60b1 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsBridgedAssetSection.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsBridgedAssetSection.tsx @@ -1,8 +1,6 @@ -import { useMemo } from 'react' import { navigate } from 'src/app/navigation/rootNavigation' import { useTokenDetailsContext } from 'src/components/TokenDetails/TokenDetailsContext' import { BridgedAssetTDPSection } from 'uniswap/src/components/BridgedAsset/BridgedAssetTDPSection' -import { checkIsBridgedAsset } from 'uniswap/src/components/BridgedAsset/utils' import { ModalName } from 'uniswap/src/features/telemetry/constants' import { useCurrencyInfo } from 'uniswap/src/features/tokens/useCurrencyInfo' import { CurrencyField } from 'uniswap/src/types/currency' @@ -29,7 +27,7 @@ export function TokenDetailsBridgedAssetSection(): JSX.Element | null { }, }) }) - const isBridgedAsset = useMemo(() => currencyInfo && checkIsBridgedAsset(currencyInfo), [currencyInfo]) + const isBridgedAsset = Boolean(currencyInfo?.isBridged) if (!isBridgedAsset || !currencyInfo) { return null } diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsContext.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsContext.tsx index 9f6d231631a..194ccaf059f 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsContext.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsContext.tsx @@ -14,6 +14,7 @@ import { CurrencyField } from 'uniswap/src/types/currency' import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { setClipboard } from 'uniswap/src/utils/clipboard' import { currencyIdToAddress, currencyIdToChain } from 'uniswap/src/utils/currencyId' +import { useBooleanState } from 'utilities/src/react/useBooleanState' type TokenDetailsContextState = { currencyId: string @@ -32,6 +33,9 @@ type TokenDetailsContextState = { isContractAddressExplainerModalOpen: boolean openContractAddressExplainerModal: () => void closeContractAddressExplainerModal: (markViewed: boolean) => void + isAztecWarningModalOpen: boolean + openAztecWarningModal: () => void + closeAztecWarningModal: () => void copyAddressToClipboard: (address: string) => Promise error: unknown | undefined setError: (error: unknown | undefined) => void @@ -64,6 +68,12 @@ export function TokenDetailsContextProvider({ [dispatch], ) + const { + value: isAztecWarningModalOpen, + setTrue: openAztecWarningModal, + setFalse: closeAztecWarningModal, + } = useBooleanState(false) + const copyAddressToClipboard = useCallback( async (address: string): Promise => { await setClipboard(address) @@ -113,6 +123,9 @@ export function TokenDetailsContextProvider({ isContractAddressExplainerModalOpen, openContractAddressExplainerModal, closeContractAddressExplainerModal, + isAztecWarningModalOpen, + openAztecWarningModal, + closeAztecWarningModal, copyAddressToClipboard, error, setError, @@ -121,13 +134,16 @@ export function TokenDetailsContextProvider({ activeTransactionType, closeTokenWarningModal, closeContractAddressExplainerModal, + closeAztecWarningModal, currencyId, currencyInfo, enabledChains, error, + isAztecWarningModalOpen, isContractAddressExplainerModalOpen, isTokenWarningModalOpen, navigation, + openAztecWarningModal, openContractAddressExplainerModal, openTokenWarningModal, tokenColor, diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx index bc5f3c6e691..ac629a8be05 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx @@ -1,11 +1,11 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FlatList } from 'react-native-gesture-handler' -import { getBlockExplorerIcon } from 'src/components/icons/BlockExplorerIcon' import { LinkButton, type LinkButtonProps, LinkButtonType } from 'src/components/TokenDetails/LinkButton' import { useTokenDetailsContext } from 'src/components/TokenDetails/TokenDetailsContext' import { Flex, Text } from 'ui/src' import { GlobeFilled, XTwitter } from 'ui/src/components/icons' +import { getBlockExplorerIcon } from 'uniswap/src/components/chains/BlockExplorerIcon' import { useTokenProjectUrlsPartsFragment } from 'uniswap/src/data/graphql/uniswap-data-api/fragments' import { getChainInfo } from 'uniswap/src/features/chains/chainInfo' import { chainIdToPlatform } from 'uniswap/src/features/platforms/utils/chains' diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx index be50bcd450a..5f0ccd4bbec 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx @@ -10,9 +10,8 @@ import { DEP_accentColors, validColor } from 'ui/src/theme' import { useTokenBasicInfoPartsFragment, useTokenBasicProjectPartsFragment, - useTokenMarketPartsFragment, - useTokenProjectMarketsPartsFragment, } from 'uniswap/src/data/graphql/uniswap-data-api/fragments' +import { useTokenMarketStats } from 'uniswap/src/features/dataApi/tokenDetails/useTokenDetailsData' import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils/currencyIdToContractInput' import { Language } from 'uniswap/src/features/language/constants' import { useCurrentLanguage, useCurrentLanguageInfo } from 'uniswap/src/features/language/hooks' @@ -52,21 +51,8 @@ const TokenDetailsMarketData = memo(function _TokenDetailsMarketData(): JSX.Elem const { currencyId, tokenColor } = useTokenDetailsContext() - const tokenMarket = useTokenMarketPartsFragment({ currencyId }).data.market - const projectMarkets = useTokenProjectMarketsPartsFragment({ currencyId }).data.project?.markets - - const price = projectMarkets?.[0]?.price?.value || tokenMarket?.price?.value || undefined - const marketCap = projectMarkets?.[0]?.marketCap?.value - const volume = tokenMarket?.volume?.value - const rawPriceHigh52W = projectMarkets?.[0]?.priceHigh52W?.value || tokenMarket?.priceHigh52W?.value || undefined - const rawPriceLow52W = projectMarkets?.[0]?.priceLow52W?.value || tokenMarket?.priceLow52W?.value || undefined - - // Use current price for 52w high/low if it exceeds the bounds - const priceHight52W = - price !== undefined && rawPriceHigh52W !== undefined ? Math.max(price, rawPriceHigh52W) : rawPriceHigh52W - const priceLow52W = - price !== undefined && rawPriceLow52W !== undefined ? Math.min(price, rawPriceLow52W) : rawPriceLow52W - const fullyDilutedValuation = projectMarkets?.[0]?.fullyDilutedValuation?.value + // Use shared hook for unified data fetching (CoinGecko-first strategy) + const { marketCap, fdv, volume, high52w, low52w } = useTokenMarketStats(currencyId) return ( @@ -84,7 +70,7 @@ const TokenDetailsMarketData = memo(function _TokenDetailsMarketData(): JSX.Elem statsIcon={} > - {convertFiatAmountFormatted(fullyDilutedValuation, NumberType.FiatTokenStats)} + {convertFiatAmountFormatted(fdv, NumberType.FiatTokenStats)} @@ -102,7 +88,7 @@ const TokenDetailsMarketData = memo(function _TokenDetailsMarketData(): JSX.Elem statsIcon={} > - {convertFiatAmountFormatted(priceHight52W, NumberType.FiatTokenDetails)} + {convertFiatAmountFormatted(high52w, NumberType.FiatTokenDetails)} @@ -111,7 +97,7 @@ const TokenDetailsMarketData = memo(function _TokenDetailsMarketData(): JSX.Elem statsIcon={} > - {convertFiatAmountFormatted(priceLow52W, NumberType.FiatTokenDetails)} + {convertFiatAmountFormatted(low52w, NumberType.FiatTokenDetails)} diff --git a/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx b/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx index adaca05b633..6563707edd9 100644 --- a/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx +++ b/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx @@ -10,7 +10,8 @@ import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { FiatOnRampCurrency, FORCurrencyOrBalance } from 'uniswap/src/features/fiatOnRamp/types' import { getUnsupportedFORTokensWithBalance, isSupportedFORCurrency } from 'uniswap/src/features/fiatOnRamp/utils' import { useLocalizationContext } from 'uniswap/src/features/language/LocalizationContext' -import { useDismissedTokenWarnings } from 'uniswap/src/features/tokens/slice/hooks' +import { getTokenProtectionWarning } from 'uniswap/src/features/tokens/warnings/safetyUtils' +import { useDismissedTokenWarnings } from 'uniswap/src/features/tokens/warnings/slice/hooks' import { ListSeparatorToggle } from 'uniswap/src/features/transactions/TransactionDetails/ListSeparatorToggle' import { CurrencyId } from 'uniswap/src/types/currency' import { NumberType } from 'utilities/src/format/types' @@ -51,7 +52,8 @@ function TokenOptionItemWrapper({ [currencyInfo, balanceUSD, quantity, isUnsupported], ) const onPress = useCallback(() => onSelectCurrency(currency), [currency, onSelectCurrency]) - const { tokenWarningDismissed } = useDismissedTokenWarnings(currencyInfo?.currency) + const tokenProtectionWarning = getTokenProtectionWarning(currencyInfo) + const { tokenWarningDismissed } = useDismissedTokenWarnings(currencyInfo?.currency, tokenProtectionWarning) const { convertFiatAmountFormatted, formatNumberOrString } = useLocalizationContext() if (!option) { diff --git a/apps/mobile/src/components/accounts/AccountCardItem.tsx b/apps/mobile/src/components/accounts/AccountCardItem.tsx index 7a0547713e1..9d5cbdc115c 100644 --- a/apps/mobile/src/components/accounts/AccountCardItem.tsx +++ b/apps/mobile/src/components/accounts/AccountCardItem.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import ContextMenu from 'react-native-context-menu-view' import { useDispatch } from 'react-redux' +import { MODAL_OPEN_WAIT_TIME } from 'src/app/navigation/constants' import { navigate } from 'src/app/navigation/rootNavigation' import { NotificationBadge } from 'src/components/notifications/Badge' import { Flex, Text, TouchableArea } from 'ui/src' @@ -24,8 +25,6 @@ import { noop } from 'utilities/src/react/noop' import { useAccountListData } from 'wallet/src/features/accounts/useAccountListData' import { useAccounts } from 'wallet/src/features/wallet/hooks' -const MODAL_CLOSE_WAIT_TIME = 300 - type AccountCardItemProps = { address: Address isViewOnly: boolean @@ -130,7 +129,7 @@ export function AccountCardItem({ navigate(ModalName.ConnectionsDappListModal, { address, }) - }, MODAL_CLOSE_WAIT_TIME) + }, MODAL_OPEN_WAIT_TIME) }, [address, onClose]) const onPressRemoveWallet = useCallback(() => { diff --git a/apps/mobile/src/components/accounts/AccountHeader.tsx b/apps/mobile/src/components/accounts/AccountHeader.tsx index 040684b16b8..5b29965af69 100644 --- a/apps/mobile/src/components/accounts/AccountHeader.tsx +++ b/apps/mobile/src/components/accounts/AccountHeader.tsx @@ -1,4 +1,5 @@ import { SharedEventName } from '@uniswap/analytics-events' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { useCallback, useEffect } from 'react' import { Gesture, GestureDetector, State } from 'react-native-gesture-handler' import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withDelay, withTiming } from 'react-native-reanimated' @@ -11,8 +12,6 @@ import { CopyAlt, ScanHome, SettingsHome } from 'ui/src/components/icons' import { ScannerModalState } from 'uniswap/src/components/ReceiveQRCode/constants' import { AccountIcon } from 'uniswap/src/features/accounts/AccountIcon' import { AccountType, DisplayNameType } from 'uniswap/src/features/accounts/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { pushNotification } from 'uniswap/src/features/notifications/slice/slice' import { AppNotificationType, CopyNotificationType } from 'uniswap/src/features/notifications/slice/types' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' @@ -39,7 +38,7 @@ const RotatingSettingsIcon = ({ onPressSettings }: { onPressSettings(): void }): if (isScreenFocused) { pressProgress.value = withDelay(50, withTiming(0)) } - }, [isScreenFocused, pressProgress]) + }, [isScreenFocused]) const tap = Gesture.Tap() .withTestId(TestID.AccountHeaderSettings) diff --git a/apps/mobile/src/components/accounts/__snapshots__/AccountCardItem.test.tsx.snap b/apps/mobile/src/components/accounts/__snapshots__/AccountCardItem.test.tsx.snap index 5727f60c85c..15e2ee463fd 100644 --- a/apps/mobile/src/components/accounts/__snapshots__/AccountCardItem.test.tsx.snap +++ b/apps/mobile/src/components/accounts/__snapshots__/AccountCardItem.test.tsx.snap @@ -17,11 +17,38 @@ exports[`AccountCardItem renders correctly 1`] = ` collapsable={false} focusVisibleStyle={{}} forwardedRef={[Function]} + jestAnimatedProps={ + { + "value": {}, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "paddingBottom": 12, + "paddingLeft": 24, + "paddingRight": 24, + "paddingTop": 8, + "transform": [ + { + "scale": 1, + }, + ], + }, + ] + } onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -34,29 +61,30 @@ exports[`AccountCardItem renders correctly 1`] = ` onStartShouldSetResponder={[Function]} role="button" style={ - { - "backgroundColor": "transparent", - "borderBottomLeftRadius": 12, - "borderBottomRightRadius": 12, - "borderTopLeftRadius": 12, - "borderTopRightRadius": 12, - "flexDirection": "column", - "opacity": 1, - "paddingBottom": 12, - "paddingLeft": 24, - "paddingRight": 24, - "paddingTop": 8, - "transform": [ - { - "scale": 1, - }, - ], - } + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "paddingBottom": 12, + "paddingLeft": 24, + "paddingRight": 24, + "paddingTop": 8, + "transform": [ + { + "scale": 1, + }, + ], + }, + {}, + ] } > diff --git a/apps/mobile/src/components/accounts/__snapshots__/AccountList.test.tsx.snap b/apps/mobile/src/components/accounts/__snapshots__/AccountList.test.tsx.snap index 732b9a75f9f..51ede447887 100644 --- a/apps/mobile/src/components/accounts/__snapshots__/AccountList.test.tsx.snap +++ b/apps/mobile/src/components/accounts/__snapshots__/AccountList.test.tsx.snap @@ -113,8 +113,6 @@ exports[`AccountList renders without error 1`] = ` } > , TabProps>(function _ActivityTab( @@ -55,13 +56,22 @@ export const ActivityContent = memo( const { onContentSizeChange, adaptiveFooter } = useAdaptiveFooter(containerProps?.contentContainerStyle) - const onPressReceive = (): void => { + const onPressReceive = useEvent((): void => { // in case we received a pending session from a previous scan after closing modal dispatch(removePendingSession()) dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) - } + }) - const { maybeEmptyComponent, renderActivityItem, sectionData, keyExtractor } = useActivityDataWallet({ + const { + maybeEmptyComponent, + renderActivityItem, + sectionData, + keyExtractor, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + refetch, + } = useActivityDataWallet({ evmOwner: owner, authTrigger: requiresBiometrics ? biometricsTrigger : undefined, isExternalProfile, @@ -71,6 +81,20 @@ export const ActivityContent = memo( usePerformanceLogger(DDRumManualTiming.RenderActivityTabList, []) + const [isRefreshing, setIsRefreshing] = useState(false) + + const handleRefresh = useEvent(async () => { + setIsRefreshing(true) + try { + onRefresh?.() + await refetch() + } finally { + setIsRefreshing(false) + } + }) + + const refreshingAll = refreshing ?? isRefreshing + const refreshControl = useMemo(() => { const progressViewOffset = isBottomTabsEnabled ? undefined @@ -79,12 +103,12 @@ export const ActivityContent = memo( return ( ) - }, [isBottomTabsEnabled, insets.top, headerHeight, refreshing, colors.neutral3, onRefresh]) + }, [isBottomTabsEnabled, insets.top, headerHeight, refreshingAll, colors.neutral3, handleRefresh]) const List = renderedInModal ? AnimatedBottomSheetFlatList : AnimatedFlatList @@ -103,8 +127,20 @@ export const ActivityContent = memo( estimatedItemSize={ESTIMATED_ITEM_SIZE} drawDistance={ESTIMATED_ITEM_SIZE * AMOUNT_TO_DRAW} ListEmptyComponent={maybeEmptyComponent} - ListFooterComponent={isExternalProfile ? null : adaptiveFooter} + ListFooterComponent={ + isExternalProfile ? null : ( + + {isFetchingNextPage && } + {adaptiveFooter} + + ) + } contentContainerStyle={containerProps?.contentContainerStyle} + refreshControl={refreshControl} + refreshing={refreshingAll} + onContentSizeChange={onContentSizeChange} + onEndReached={hasNextPage && !isFetchingNextPage ? fetchNextPage : undefined} + onEndReachedThreshold={ON_END_REACHED_THRESHOLD} /> ) : ( + {isFetchingNextPage && } + {adaptiveFooter} + + ) + } onScroll={scrollHandler} onContentSizeChange={onContentSizeChange} + onEndReached={hasNextPage && !isFetchingNextPage ? fetchNextPage : undefined} + onEndReachedThreshold={ON_END_REACHED_THRESHOLD} {...containerProps} /> )} diff --git a/apps/mobile/src/components/buttons/__snapshots__/BackButton.test.tsx.snap b/apps/mobile/src/components/buttons/__snapshots__/BackButton.test.tsx.snap index a65d4699cc6..d217b47b4b1 100644 --- a/apps/mobile/src/components/buttons/__snapshots__/BackButton.test.tsx.snap +++ b/apps/mobile/src/components/buttons/__snapshots__/BackButton.test.tsx.snap @@ -7,11 +7,35 @@ exports[`BackButton renders without error 1`] = ` focusVisibleStyle={{}} forwardedRef={[Function]} hitSlop={24} + jestAnimatedProps={ + { + "value": {}, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "alignItems": "center", + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + ] + } onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -24,27 +48,28 @@ exports[`BackButton renders without error 1`] = ` onStartShouldSetResponder={[Function]} role="button" style={ - { - "alignItems": "center", - "backgroundColor": "transparent", - "borderBottomLeftRadius": 12, - "borderBottomRightRadius": 12, - "borderTopLeftRadius": 12, - "borderTopRightRadius": 12, - "flexDirection": "column", - "opacity": 1, - "transform": [ - { - "scale": 1, - }, - ], - } + [ + { + "alignItems": "center", + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + {}, + ] } testID="back" > diff --git a/apps/mobile/src/components/carousel/Carousel.tsx b/apps/mobile/src/components/carousel/Carousel.tsx index 285baf051ca..793ca80fc86 100644 --- a/apps/mobile/src/components/carousel/Carousel.tsx +++ b/apps/mobile/src/components/carousel/Carousel.tsx @@ -35,14 +35,14 @@ export function Carousel({ slides, ...flatListProps }: CarouselProps): JSX.Eleme myRef.current?._listRef._scrollRef.scrollTo({ x: Math.ceil(scroll.value / fullWidth + 0.5) * fullWidth, }) - }, [fullWidth, scroll]) + }, [fullWidth]) const goToPrev = useCallback(() => { // @ts-expect-error https://github.com/software-mansion/react-native-reanimated/issues/2976 myRef.current?._listRef._scrollRef.scrollTo({ x: Math.floor(scroll.value / fullWidth - 0.5) * fullWidth, }) - }, [fullWidth, scroll]) + }, [fullWidth]) return ( diff --git a/apps/mobile/src/components/education/SeedPhrase.tsx b/apps/mobile/src/components/education/SeedPhrase.tsx index 91ccad3d526..a0d70799b98 100644 --- a/apps/mobile/src/components/education/SeedPhrase.tsx +++ b/apps/mobile/src/components/education/SeedPhrase.tsx @@ -62,25 +62,60 @@ function Page({ text, params }: { text: ReactNode; params: OnboardingStackBasePa ) } -export const SeedPhraseEducationContent = (params: OnboardingStackBaseParams): JSX.Element[] => { - const cloudProviderName = getCloudProviderName() - const highlightComponent = +const highlightComponent = + +const cloudProviderName = getCloudProviderName() - const pageContentList = [ - // biome-ignore-start lint/correctness/useJsxKeyInIterable: Static array items don't need keys - , - , - , - , - , - , - // biome-ignore-end lint/correctness/useJsxKeyInIterable: Static array items don't need keys - ] +const pageContentList = [ + , + , + , + , + , + , + , + , + , +] +export const SeedPhraseEducationContent = (params: OnboardingStackBaseParams): JSX.Element[] => { return pageContentList.map((content, i) => ( {content}} /> )) diff --git a/apps/mobile/src/components/explore/ExploreSections/ExploreSections.tsx b/apps/mobile/src/components/explore/ExploreSections/ExploreSections.tsx index 26d19b1252f..5dff1928fec 100644 --- a/apps/mobile/src/components/explore/ExploreSections/ExploreSections.tsx +++ b/apps/mobile/src/components/explore/ExploreSections/ExploreSections.tsx @@ -6,6 +6,7 @@ import { TokenStats, } from '@uniswap/client-explore/dist/uniswap/explore/v1/service_pb' import { ALL_NETWORKS_ARG } from '@universe/api' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { @@ -29,13 +30,12 @@ import { TokenItemData } from 'src/components/explore/TokenItemData' import { getTokenMetadataDisplayType } from 'src/features/explore/utils' import { Flex, Loader, Text } from 'ui/src' import { AnimatedBottomSheetFlashList } from 'ui/src/components/AnimatedFlashList/AnimatedFlashList' +import { NoTokens } from 'ui/src/components/icons' import { spacing } from 'ui/src/theme' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' import { useTokenRankingsQuery } from 'uniswap/src/data/rest/tokenRankings' import type { UniverseChainId } from 'uniswap/src/features/chains/types' import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' @@ -155,10 +155,15 @@ function _ExploreSections({ }, [insets.bottom]) const dataWithBottomTabs = useMemo( - () => (showFullScreenLoadingState ? [] : (topTokenItems ?? [])), + () => (showFullScreenLoadingState ? [] : topTokenItems), [showFullScreenLoadingState, topTokenItems], ) + const listEmptyComponent = useMemo( + () => , + [showFullScreenLoadingState], + ) + if (!hasAllData && error) { return ( @@ -177,7 +182,7 @@ function _ExploreSections({ { +): Partial> { if (!tokenRankings) { - return {} as Record + return {} as const } const result: Record = {} @@ -322,14 +327,11 @@ function processTokenRankings( return result } -function useTokenItems( - data: TokenRankingsResponse | undefined, - orderBy: ExploreOrderBy, -): TokenItemDataWithMetadata[] | undefined { +function useTokenItems(data: TokenRankingsResponse | undefined, orderBy: ExploreOrderBy): TokenItemDataWithMetadata[] { // process all the token rankings into a map of orderBy to token items (only do this once) const allTokenItemsByOrderBy = useMemo(() => processTokenRankings(data?.tokenRankings), [data]) - // return the token items for the given orderBy - return useMemo(() => allTokenItemsByOrderBy[orderBy], [allTokenItemsByOrderBy, orderBy]) + // return the token items for the given orderBy, or empty array if the orderBy key doesn't exist + return useMemo(() => allTokenItemsByOrderBy[orderBy] ?? [], [allTokenItemsByOrderBy, orderBy]) } type ListHeaderProps = { @@ -387,11 +389,31 @@ const ListHeaderComponent = ({ ) } -const ListEmptyComponent = (): JSX.Element => ( - - - -) +const TokenListEmptyComponent = memo(function TokenListEmptyComponent({ + isLoading, +}: { + isLoading: boolean +}): JSX.Element { + const { t } = useTranslation() + + if (isLoading) { + return ( + + + + ) + } + + return ( + + } + title={t('explore.tokens.empty.title')} + /> + + ) +}) function useOrderBy(): { uiOrderBy: ExploreOrderBy diff --git a/apps/mobile/src/components/explore/ExploreSections/NetworkPillsRow.tsx b/apps/mobile/src/components/explore/ExploreSections/NetworkPillsRow.tsx index b0383bd3525..425f19a2438 100644 --- a/apps/mobile/src/components/explore/ExploreSections/NetworkPillsRow.tsx +++ b/apps/mobile/src/components/explore/ExploreSections/NetworkPillsRow.tsx @@ -1,3 +1,4 @@ +import { useTheme } from '@react-navigation/core' import { memo, useCallback, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { ViewStyle } from 'react-native' @@ -58,6 +59,7 @@ const NetworkPillsRow = memo(function NetworkPillsRow({ onSelectNetwork: (chainId: UniverseChainId | null) => void }): JSX.Element { const colors = useSporeColors() + const theme = useTheme() const { chains } = useEnabledChains() const flatListRef = useRef>(null) @@ -68,6 +70,7 @@ const NetworkPillsRow = memo(function NetworkPillsRow({ items: chains, }) + // biome-ignore lint/correctness/useExhaustiveDependencies: need theme dep for foregroundColor to change on theme change const renderItemNetworkPills = useCallback( ({ item }: { item: UniverseChainId }) => { return ( @@ -90,7 +93,7 @@ const NetworkPillsRow = memo(function NetworkPillsRow({ ) }, - [colors.neutral1.val, onSelectNetwork, selectedNetwork], + [colors.neutral1.val, onSelectNetwork, selectedNetwork, theme], ) const ListHeaderComponent = useMemo(() => { diff --git a/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap index 2217f92f48c..da2b061869e 100644 --- a/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap +++ b/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap @@ -20,12 +20,7 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.65)", - "light": "rgba(19, 19, 19, 0.63)", - }, - }, + "color": "rgba(19, 19, 19, 0.63)", "fontFamily": "Basel Grotesk", "fontSize": 17, "fontWeight": "400", @@ -41,11 +36,34 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = ` focusVisibleStyle={{}} forwardedRef={[Function]} hitSlop={16} + jestAnimatedProps={ + { + "value": {}, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + ] + } onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -58,35 +76,31 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = ` onStartShouldSetResponder={[Function]} role="button" style={ - { - "backgroundColor": "transparent", - "borderBottomLeftRadius": 12, - "borderBottomRightRadius": 12, - "borderTopLeftRadius": 12, - "borderTopRightRadius": 12, - "flexDirection": "column", - "opacity": 1, - "transform": [ - { - "scale": 1, - }, - ], - } + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + {}, + ] } > diff --git a/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap index f6d96f05640..41fc51fc1c9 100644 --- a/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap +++ b/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap @@ -38,11 +38,51 @@ exports[`FavoriteTokenCard renders without error 1`] = ` collapsable={false} focusVisibleStyle={{}} forwardedRef={[Function]} + jestAnimatedProps={ + { + "value": {}, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "backgroundColor": "#FFFFFF", + "borderBottomColor": "rgba(19, 19, 19, 0.08)", + "borderBottomLeftRadius": 16, + "borderBottomRightRadius": 16, + "borderBottomWidth": 1, + "borderLeftColor": "rgba(19, 19, 19, 0.08)", + "borderLeftWidth": 1, + "borderRightColor": "rgba(19, 19, 19, 0.08)", + "borderRightWidth": 1, + "borderStyle": "solid", + "borderTopColor": "rgba(19, 19, 19, 0.08)", + "borderTopLeftRadius": 16, + "borderTopRightRadius": 16, + "borderTopWidth": 1, + "flexDirection": "column", + "opacity": 1, + "overflow": "hidden", + "shadowColor": "rgb(0,0,0)", + "shadowOffset": { + "height": 1, + "width": 0, + }, + "shadowOpacity": 0.0196078431372549, + "shadowRadius": 6, + "transform": [ + { + "scale": 1, + }, + ], + }, + ] + } onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -55,43 +95,44 @@ exports[`FavoriteTokenCard renders without error 1`] = ` onStartShouldSetResponder={[Function]} role="button" style={ - { - "backgroundColor": "#FFFFFF", - "borderBottomColor": "rgba(19, 19, 19, 0.08)", - "borderBottomLeftRadius": 16, - "borderBottomRightRadius": 16, - "borderBottomWidth": 1, - "borderLeftColor": "rgba(19, 19, 19, 0.08)", - "borderLeftWidth": 1, - "borderRightColor": "rgba(19, 19, 19, 0.08)", - "borderRightWidth": 1, - "borderStyle": "solid", - "borderTopColor": "rgba(19, 19, 19, 0.08)", - "borderTopLeftRadius": 16, - "borderTopRightRadius": 16, - "borderTopWidth": 1, - "flexDirection": "column", - "opacity": 1, - "overflow": "hidden", - "shadowColor": "rgb(0,0,0)", - "shadowOffset": { - "height": 1, - "width": 0, - }, - "shadowOpacity": 0.0196078431372549, - "shadowRadius": 6, - "transform": [ - { - "scale": 1, + [ + { + "backgroundColor": "#FFFFFF", + "borderBottomColor": "rgba(19, 19, 19, 0.08)", + "borderBottomLeftRadius": 16, + "borderBottomRightRadius": 16, + "borderBottomWidth": 1, + "borderLeftColor": "rgba(19, 19, 19, 0.08)", + "borderLeftWidth": 1, + "borderRightColor": "rgba(19, 19, 19, 0.08)", + "borderRightWidth": 1, + "borderStyle": "solid", + "borderTopColor": "rgba(19, 19, 19, 0.08)", + "borderTopLeftRadius": 16, + "borderTopRightRadius": 16, + "borderTopWidth": 1, + "flexDirection": "column", + "opacity": 1, + "overflow": "hidden", + "shadowColor": "rgb(0,0,0)", + "shadowOffset": { + "height": 1, + "width": 0, }, - ], - } + "shadowOpacity": 0.0196078431372549, + "shadowRadius": 6, + "transform": [ + { + "scale": 1, + }, + ], + }, + {}, + ] } testID="favorite-token-card-undefined" > { - flatListRef: React.RefObject> + flatListRef: React.RefObject | null> selectedItem: T | null items: T[] scrollDelay?: number @@ -20,7 +20,7 @@ export function useFlatListAutoScroll(options: UseFlatListAutoScrollOptions { - let timeoutId: NodeJS.Timeout | undefined + let timeoutId: NodeJS.Timeout | number | undefined if (flatListRef.current) { // If selectedItem is null (All/First option), scroll to the beginning diff --git a/apps/mobile/src/components/explore/search/ExploreScreenSearchResultsList.tsx b/apps/mobile/src/components/explore/search/ExploreScreenSearchResultsList.tsx index 1ae943b09fb..49db351bbc5 100644 --- a/apps/mobile/src/components/explore/search/ExploreScreenSearchResultsList.tsx +++ b/apps/mobile/src/components/explore/search/ExploreScreenSearchResultsList.tsx @@ -1,3 +1,4 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { KeyboardAvoidingView } from 'react-native-keyboard-controller' @@ -5,8 +6,6 @@ import { ESTIMATED_BOTTOM_TABS_HEIGHT } from 'src/app/navigation/tabs/CustomTabB import { Flex, flexStyles, Text, TouchableArea } from 'ui/src' import { spacing } from 'ui/src/theme' import type { UniverseChainId } from 'uniswap/src/features/chains/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { SearchModalNoQueryList } from 'uniswap/src/features/search/SearchModal/SearchModalNoQueryList' import { SearchModalResultsList } from 'uniswap/src/features/search/SearchModal/SearchModalResultsList' import { MOBILE_SEARCH_TABS, SearchTab } from 'uniswap/src/features/search/SearchModal/types' diff --git a/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.graphql b/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.graphql deleted file mode 100644 index 61455310ac7..00000000000 --- a/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.graphql +++ /dev/null @@ -1,21 +0,0 @@ -query SearchPopularNFTCollections { - topCollections(chains: [ETHEREUM], orderBy: VOLUME, duration: DAY, first: 2) { - edges { - node { - id - name - collectionId - isVerified - nftContracts { - id - chain - address - } - image { - id - url - } - } - } - } -} diff --git a/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx b/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx index b6285af3438..a4f042361c5 100644 --- a/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx +++ b/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx @@ -1,7 +1,7 @@ import React from 'react' import { useTranslation } from 'react-i18next' import { Button, SpinningLoader, useIsShortMobileDevice } from 'ui/src' -import { InfoCircleFilled } from 'ui/src/components/icons' +import { InfoCircleFilled } from 'ui/src/components/icons/InfoCircleFilled' interface FiatOnRampCtaButtonProps { onPress: () => void diff --git a/apps/mobile/src/components/home/HomeExploreTab.tsx b/apps/mobile/src/components/home/HomeExploreTab.tsx index a52b9881913..899a6fddaed 100644 --- a/apps/mobile/src/components/home/HomeExploreTab.tsx +++ b/apps/mobile/src/components/home/HomeExploreTab.tsx @@ -1,5 +1,6 @@ import { ReactNavigationPerformanceView } from '@shopify/react-native-performance-navigation' import { GraphQLApi } from '@universe/api' +import { DynamicConfigs, HomeScreenExploreTokensConfigKey, useDynamicConfigValue } from '@universe/gating' import { ForwardedRef, forwardRef, memo, useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { FlatList, LayoutRectangle, RefreshControl } from 'react-native' @@ -15,8 +16,6 @@ import { SwirlyArrowDown } from 'ui/src/components/icons' import { spacing, zIndexes } from 'ui/src/theme' import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { useAppFiatCurrency } from 'uniswap/src/features/fiatCurrency/hooks' -import { DynamicConfigs, HomeScreenExploreTokensConfigKey } from 'uniswap/src/features/gating/configs' -import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' import { isContractInputArrayType } from 'uniswap/src/features/gating/typeGuards' import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' diff --git a/apps/mobile/src/components/home/TokensTab.tsx b/apps/mobile/src/components/home/TokensTab.tsx index 9b41db908a6..b5da1f62cb5 100644 --- a/apps/mobile/src/components/home/TokensTab.tsx +++ b/apps/mobile/src/components/home/TokensTab.tsx @@ -1,4 +1,5 @@ import { useStartProfiler } from '@shopify/react-native-performance' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { forwardRef, memo, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FlatList } from 'react-native' @@ -14,8 +15,6 @@ import { NoTokens } from 'ui/src/components/icons' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' import { PortfolioEmptyState } from 'uniswap/src/components/portfolio/PortfolioEmptyState' import { ScannerModalState } from 'uniswap/src/components/ReceiveQRCode/constants' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { TokenBalanceListRow } from 'uniswap/src/features/portfolio/types' import { ModalName } from 'uniswap/src/features/telemetry/constants' import { CurrencyId } from 'uniswap/src/types/currency' diff --git a/apps/mobile/src/components/home/hooks.tsx b/apps/mobile/src/components/home/hooks.tsx index c216431ac85..e5b8826f637 100644 --- a/apps/mobile/src/components/home/hooks.tsx +++ b/apps/mobile/src/components/home/hooks.tsx @@ -1,6 +1,8 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useCallback, useEffect, useMemo } from 'react' import { StyleProp, ViewStyle } from 'react-native' import Animated, { SharedValue, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' +import { ESTIMATED_BOTTOM_TABS_HEIGHT } from 'src/app/navigation/tabs/CustomTabBar/constants' import { TAB_BAR_HEIGHT } from 'src/components/layout/TabHelpers' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' @@ -13,6 +15,9 @@ export function useAdaptiveFooter(contentContainerStyle?: StyleProp): } { const { fullHeight } = useDeviceDimensions() const insets = useAppInsets() + const isBottomTabsEnabled = useFeatureFlag(FeatureFlags.BottomTabs) + + const HEIGHT_TO_SUBTRACT = isBottomTabsEnabled ? ESTIMATED_BOTTOM_TABS_HEIGHT : TAB_BAR_HEIGHT // Content is rendered under the navigation bar but not under the status bar const maxContentHeight = fullHeight - insets.top // Use maxContentHeight as the initial value to properly position the TabBar @@ -28,7 +33,7 @@ export function useAdaptiveFooter(contentContainerStyle?: StyleProp): } // The height of the footer added to the list can be calculated from // the following equation (for collapsed tab bar): - // maxContentHeight = TAB_BAR_HEIGHT + + footerHeight + paddingBottom + // maxContentHeight = HEIGHT_TO_SUBTRACT + + footerHeight + paddingBottom // // To get the we need to subtract padding already // added to the content container style and the footer if it's already @@ -36,17 +41,17 @@ export function useAdaptiveFooter(contentContainerStyle?: StyleProp): // = contentHeight - paddingTop - paddingBottom - footerHeight // // The resulting equation is: - // footerHeight = maxContentHeight - - TAB_BAR_HEIGHT - paddingBottom - // = maxContentHeight - (contentHeight - paddingTop - paddingBottom - footerHeight) - TAB_BAR_HEIGHT - paddingBottom - // = maxContentHeight + paddingTop + footerHeight - (contentHeight + TAB_BAR_HEIGHT) + // footerHeight = maxContentHeight - - HEIGHT_TO_SUBTRACT - paddingBottom + // = maxContentHeight - (contentHeight - paddingTop - paddingBottom - footerHeight) - HEIGHT_TO_SUBTRACT - paddingBottom + // = maxContentHeight + paddingTop + footerHeight - (contentHeight + HEIGHT_TO_SUBTRACT) const paddingTopProp = (contentContainerStyle as ViewStyle).paddingTop const paddingTop = typeof paddingTopProp === 'number' ? paddingTopProp : 0 const calculatedFooterHeight = - maxContentHeight + paddingTop + footerHeight.value - (contentHeight + TAB_BAR_HEIGHT) + maxContentHeight + paddingTop + footerHeight.value - (contentHeight + HEIGHT_TO_SUBTRACT) footerHeight.value = Math.max(0, calculatedFooterHeight) }, - [footerHeight, contentContainerStyle, maxContentHeight], + [contentContainerStyle, maxContentHeight, HEIGHT_TO_SUBTRACT], ) // biome-ignore lint/correctness/useExhaustiveDependencies: we want to recalculate this when activeAccount changes diff --git a/apps/mobile/src/components/home/introCards/FundWalletModal.tsx b/apps/mobile/src/components/home/introCards/FundWalletModal.tsx index 67565c140cf..35c592bf7ee 100644 --- a/apps/mobile/src/components/home/introCards/FundWalletModal.tsx +++ b/apps/mobile/src/components/home/introCards/FundWalletModal.tsx @@ -1,3 +1,4 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { PropsWithChildren, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FlatList } from 'react-native' @@ -13,8 +14,6 @@ import { ActionCard, ActionCardItem } from 'uniswap/src/components/misc/ActionCa import { Modal } from 'uniswap/src/components/modals/Modal' import { ImageUri } from 'uniswap/src/components/nfts/images/ImageUri' import { useCexTransferProviders } from 'uniswap/src/features/fiatOnRamp/useCexTransferProviders' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import { usePortfolioEmptyStateBackground } from 'wallet/src/components/portfolio/empty' diff --git a/apps/mobile/src/components/home/introCards/OnboardingIntroCardStack.tsx b/apps/mobile/src/components/home/introCards/OnboardingIntroCardStack.tsx index a03ea0a749d..73f8117bb61 100644 --- a/apps/mobile/src/components/home/introCards/OnboardingIntroCardStack.tsx +++ b/apps/mobile/src/components/home/introCards/OnboardingIntroCardStack.tsx @@ -1,19 +1,25 @@ import { SharedEventName } from '@uniswap/analytics-events' -import React, { useCallback, useMemo } from 'react' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' +import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { navigate } from 'src/app/navigation/rootNavigation' +import { useAppStackNavigation } from 'src/app/navigation/types' import { NotificationPermission, useNotificationOSPermissionsEnabled, } from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled' -import { Flex } from 'ui/src' -import { BRIDGED_ASSETS_CARD_BANNER, PUSH_NOTIFICATIONS_CARD_BANNER } from 'ui/src/assets' +import { Flex, useIsDarkMode } from 'ui/src' +import { + BRIDGED_ASSETS_CARD_BANNER, + BRIDGED_ASSETS_V2_CARD_BANNER_DARK, + BRIDGED_ASSETS_V2_CARD_BANNER_LIGHT, + PUSH_NOTIFICATIONS_CARD_BANNER, +} from 'ui/src/assets' import { Buy } from 'ui/src/components/icons' +import { MonadAnnouncementModal } from 'uniswap/src/components/notifications/MonadAnnouncementModal' import { AccountType } from 'uniswap/src/features/accounts/types' import { UniverseChainId } from 'uniswap/src/features/chains/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName, ModalName, WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { OnboardingCardLoggingName } from 'uniswap/src/features/telemetry/types' @@ -31,9 +37,14 @@ import { useSharedIntroCards } from 'wallet/src/components/introCards/useSharedI import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { selectHasViewedBridgedAssetsCard, + selectHasViewedBridgedAssetsV2Card, selectHasViewedNotificationsCard, } from 'wallet/src/features/behaviorHistory/selectors' -import { setHasViewedBridgedAssetsCard, setHasViewedNotificationsCard } from 'wallet/src/features/behaviorHistory/slice' +import { + setHasViewedBridgedAssetsCard, + setHasViewedBridgedAssetsV2Card, + setHasViewedNotificationsCard, +} from 'wallet/src/features/behaviorHistory/slice' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' type OnboardingIntroCardStackProps = { @@ -45,10 +56,12 @@ export function OnboardingIntroCardStack({ isLoading = false, }: OnboardingIntroCardStackProps): JSX.Element | null { const { t } = useTranslation() + const isDarkMode = useIsDarkMode() const dispatch = useDispatch() const activeAccount = useActiveAccountWithThrow() const address = activeAccount.address const isSignerAccount = activeAccount.type === AccountType.SignerMnemonic + const [isMonadModalOpen, setIsMonadModalOpen] = useState(false) const { notificationPermissionsEnabled } = useNotificationOSPermissionsEnabled() const notificationOnboardingCardEnabled = useFeatureFlag(FeatureFlags.NotificationOnboardingCard) @@ -61,7 +74,12 @@ export function OnboardingIntroCardStack({ const hasViewedBridgedAssetCard = useSelector(selectHasViewedBridgedAssetsCard) const shouldShowBridgedAssetCard = useFeatureFlag(FeatureFlags.BridgedAssetsBanner) && !hasViewedBridgedAssetCard + const hasViewedBridgedAssetsV2Card = useSelector(selectHasViewedBridgedAssetsV2Card) + const shouldShowBridgedAssetsV2Card = + useFeatureFlag(FeatureFlags.BridgedAssetsBannerV2) && !hasViewedBridgedAssetsV2Card + const { navigateToSwapFlow } = useWalletNavigation() + const navigation = useAppStackNavigation() const navigateToUnitagClaim = useCallback(() => { navigate(MobileScreens.UnitagStack, { @@ -94,10 +112,21 @@ export function OnboardingIntroCardStack({ navigateToSwapFlow({ openTokenSelector: CurrencyField.OUTPUT, inputChainId: UniverseChainId.Unichain }) }, [navigateToSwapFlow]) + const handleMonadExplorePress = useCallback(() => { + navigation.navigate(ModalName.Explore, { + screen: MobileScreens.Explore, + params: { + chainId: UniverseChainId.Monad, + }, + }) + setIsMonadModalOpen(false) + }, [navigation]) + const { cards: sharedCards } = useSharedIntroCards({ navigateToUnitagClaim, navigateToUnitagIntro, navigateToBackupFlow, + onMonadAnnouncementPress: () => setIsMonadModalOpen(true), }) const cards = useMemo((): IntroCardProps[] => { @@ -152,6 +181,26 @@ export function OnboardingIntroCardStack({ }) } + if (shouldShowBridgedAssetsV2Card) { + output.push({ + loggingName: OnboardingCardLoggingName.BridgedAsset, + graphic: { + type: IntroCardGraphicType.Image, + image: isDarkMode ? BRIDGED_ASSETS_V2_CARD_BANNER_DARK : BRIDGED_ASSETS_V2_CARD_BANNER_LIGHT, + }, + title: t('onboarding.home.intro.bridgedAssets.title'), + description: t('onboarding.home.intro.bridgedAssets.description.v2'), + cardType: CardType.Dismissible, + onPress: () => { + navigateToBridgedAssetSwap() + dispatch(setHasViewedBridgedAssetsV2Card(true)) + }, + onClose: () => { + dispatch(setHasViewedBridgedAssetsV2Card(true)) + }, + }) + } + if (shouldShowBridgedAssetCard) { output.push({ loggingName: OnboardingCardLoggingName.BridgedAsset, @@ -178,9 +227,11 @@ export function OnboardingIntroCardStack({ isSignerAccount, sharedCards, t, + isDarkMode, dispatch, navigateToBridgedAssetSwap, shouldShowBridgedAssetCard, + shouldShowBridgedAssetsV2Card, showEnableNotificationsCard, ]) @@ -196,13 +247,24 @@ export function OnboardingIntroCardStack({ [cards], ) - if (cards.length) { - return ( - - {isLoading ? : } - - ) - } - - return null + return ( + <> + {!!cards.length && ( + + {isLoading ? ( + + ) : ( + + )} + + )} + {isMonadModalOpen && ( + setIsMonadModalOpen(false)} + onExplorePress={handleMonadExplorePress} + /> + )} + + ) } diff --git a/apps/mobile/src/components/icons/TripleDot.tsx b/apps/mobile/src/components/icons/TripleDot.tsx deleted file mode 100644 index 32488c129fe..00000000000 --- a/apps/mobile/src/components/icons/TripleDot.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { memo } from 'react' -import { ColorTokens, Flex } from 'ui/src' - -type Props = { - size?: number - color?: ColorTokens -} - -export const TripleDot = memo(function _TripleDot({ size = 5, color = '$neutral2' }: Props) { - return ( - - - - - - ) -}) diff --git a/apps/mobile/src/components/input/__snapshots__/SelectionCircle.test.tsx.snap b/apps/mobile/src/components/input/__snapshots__/SelectionCircle.test.tsx.snap index f1d13ae2006..b29ce1681af 100644 --- a/apps/mobile/src/components/input/__snapshots__/SelectionCircle.test.tsx.snap +++ b/apps/mobile/src/components/input/__snapshots__/SelectionCircle.test.tsx.snap @@ -5,36 +5,16 @@ exports[`renders selection circle 1`] = ` style={ { "alignItems": "center", - "borderBottomColor": { - "dynamic": { - "dark": "#FF37C7", - "light": "#FF37C7", - }, - }, + "borderBottomColor": "#FF37C7", "borderBottomLeftRadius": 999999, "borderBottomRightRadius": 999999, "borderBottomWidth": 1, - "borderLeftColor": { - "dynamic": { - "dark": "#FF37C7", - "light": "#FF37C7", - }, - }, + "borderLeftColor": "#FF37C7", "borderLeftWidth": 1, - "borderRightColor": { - "dynamic": { - "dark": "#FF37C7", - "light": "#FF37C7", - }, - }, + "borderRightColor": "#FF37C7", "borderRightWidth": 1, "borderStyle": "solid", - "borderTopColor": { - "dynamic": { - "dark": "#FF37C7", - "light": "#FF37C7", - }, - }, + "borderTopColor": "#FF37C7", "borderTopLeftRadius": 999999, "borderTopRightRadius": 999999, "borderTopWidth": 1, @@ -48,12 +28,7 @@ exports[`renders selection circle 1`] = ` | RefObject> + list: RefObject | RefObject | null> position: Animated.SharedValue index: number } diff --git a/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx b/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx index 3aac1094369..955b828c3c2 100644 --- a/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx +++ b/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx @@ -19,8 +19,6 @@ const EDGES: Edge[] = ['top', 'left', 'right'] type HeaderScrollScreenProps = { centerElement?: JSX.Element rightElement?: JSX.Element - alwaysShowCenterElement?: boolean - fullScreen?: boolean // Expand to device edges renderedInModal?: boolean // Apply styling to display within bottom sheet modal showHandleBar?: boolean // add handlebar element to top of view backgroundColor?: ColorTokens @@ -30,8 +28,6 @@ type HeaderScrollScreenProps = { export function HeaderScrollScreen({ centerElement, rightElement = , - alwaysShowCenterElement, - fullScreen = false, renderedInModal = false, showHandleBar = false, backgroundColor = '$surface1', @@ -63,14 +59,12 @@ export function HeaderScrollScreen({ ) return ( - + {showHandleBar ? : null} showHeaderScrollYDistance: number + fullScreen?: boolean // hard to type // biome-ignore lint/suspicious/noExplicitAny: Ref type varies based on list component used listRef: React.MutableRefObject centerElement?: JSX.Element rightElement?: JSX.Element - alwaysShowCenterElement?: boolean - fullScreen?: boolean // Expand to device edges backgroundColor?: ColorTokens backButtonColor?: ColorTokens } @@ -36,7 +35,6 @@ export function ScrollHeader({ showHeaderScrollYDistance, centerElement, rightElement = , - alwaysShowCenterElement, fullScreen = false, backgroundColor, backButtonColor, @@ -74,11 +72,7 @@ export function ScrollHeader({ > - {alwaysShowCenterElement ? ( - centerElement - ) : ( - {centerElement} - )} + {centerElement} {rightElement} diff --git a/apps/mobile/src/components/loading/parts/WaveLoader.tsx b/apps/mobile/src/components/loading/parts/WaveLoader.tsx index 8eb6baa7881..202eb9da63b 100644 --- a/apps/mobile/src/components/loading/parts/WaveLoader.tsx +++ b/apps/mobile/src/components/loading/parts/WaveLoader.tsx @@ -19,7 +19,6 @@ export function WaveLoader(): JSX.Element { const yPosition = useSharedValue(0) const { chartHeight } = useChartDimensions() - // biome-ignore lint/correctness/useExhaustiveDependencies: only want to do this once on mount useEffect(() => { yPosition.value = withRepeat(withTiming(1, { duration: WAVE_DURATION }), Infinity, false) }, []) diff --git a/apps/mobile/src/components/modals/ReactNavigationModals/ReactNavigationModal.tsx b/apps/mobile/src/components/modals/ReactNavigationModals/ReactNavigationModal.tsx index 64cbdf8aac4..7e8b9ad958b 100644 --- a/apps/mobile/src/components/modals/ReactNavigationModals/ReactNavigationModal.tsx +++ b/apps/mobile/src/components/modals/ReactNavigationModals/ReactNavigationModal.tsx @@ -4,6 +4,8 @@ import { useReactNavigationModal } from 'src/components/modals/useReactNavigatio import type { GetProps } from 'ui/src' import { BridgedAssetModal } from 'uniswap/src/components/BridgedAsset/BridgedAssetModal' import { WormholeModal } from 'uniswap/src/components/BridgedAsset/WormholeModal' +import { ReportTokenDataModal } from 'uniswap/src/components/reporting/ReportTokenDataModal' +import { ReportTokenIssueModal } from 'uniswap/src/components/reporting/ReportTokenIssueModal' import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal' import { PasskeysHelpModal } from 'uniswap/src/features/passkey/PasskeysHelpModal' import { ModalName } from 'uniswap/src/features/telemetry/constants' @@ -31,6 +33,8 @@ type ValidModalNames = keyof Pick< | typeof ModalName.LanguageSelector | typeof ModalName.BridgedAsset | typeof ModalName.Wormhole + | typeof ModalName.ReportTokenIssue + | typeof ModalName.ReportTokenData > type ModalNameWithComponentProps = { @@ -46,6 +50,8 @@ type ModalNameWithComponentProps = { [ModalName.LanguageSelector]: GetProps [ModalName.BridgedAsset]: GetProps [ModalName.Wormhole]: GetProps + [ModalName.ReportTokenIssue]: GetProps + [ModalName.ReportTokenData]: GetProps } type NavigationModalProps = { diff --git a/apps/mobile/src/components/modals/ReactNavigationModals/ReportTokenDataModalScreen.tsx b/apps/mobile/src/components/modals/ReactNavigationModals/ReportTokenDataModalScreen.tsx new file mode 100644 index 00000000000..0cf6770c2b4 --- /dev/null +++ b/apps/mobile/src/components/modals/ReactNavigationModals/ReportTokenDataModalScreen.tsx @@ -0,0 +1,10 @@ +import { AppStackScreenProp } from 'src/app/navigation/types' +import { ReactNavigationModal } from 'src/components/modals/ReactNavigationModals/ReactNavigationModal' +import { ReportTokenDataModal } from 'uniswap/src/components/reporting/ReportTokenDataModal' +import { ModalName } from 'uniswap/src/features/telemetry/constants' + +export const ReportTokenDataModalScreen = ( + props: AppStackScreenProp, +): JSX.Element => { + return +} diff --git a/apps/mobile/src/components/modals/ReactNavigationModals/ReportTokenIssueModalScreen.tsx b/apps/mobile/src/components/modals/ReactNavigationModals/ReportTokenIssueModalScreen.tsx new file mode 100644 index 00000000000..aba9bc3f71a --- /dev/null +++ b/apps/mobile/src/components/modals/ReactNavigationModals/ReportTokenIssueModalScreen.tsx @@ -0,0 +1,10 @@ +import { AppStackScreenProp } from 'src/app/navigation/types' +import { ReactNavigationModal } from 'src/components/modals/ReactNavigationModals/ReactNavigationModal' +import { ReportTokenIssueModal } from 'uniswap/src/components/reporting/ReportTokenIssueModal' +import { ModalName } from 'uniswap/src/features/telemetry/constants' + +export const ReportTokenIssueModalScreen = ( + props: AppStackScreenProp, +): JSX.Element => { + return +} diff --git a/apps/mobile/src/components/text/LongMarkdownText.tsx b/apps/mobile/src/components/text/LongMarkdownText.tsx index 27f4f51fdd5..d58333fc546 100644 --- a/apps/mobile/src/components/text/LongMarkdownText.tsx +++ b/apps/mobile/src/components/text/LongMarkdownText.tsx @@ -35,7 +35,7 @@ export function LongMarkdownText(props: LongMarkdownTextProps): JSX.Element { const [expanded, toggleExpanded] = useReducer((isExpanded) => !isExpanded, true) const [textLengthExceedsLimit, setTextLengthExceedsLimit] = useState(false) const [textLineHeight, setTextLineHeight] = useState(fonts[variant].lineHeight) - const initialContentHeightRef = useRef() + const initialContentHeightRef = useRef(undefined) const maxVisibleHeight = textLineHeight * initialDisplayedLines const onMarkdownLayout = useCallback( diff --git a/apps/mobile/src/components/text/__snapshots__/AnimatedText.test.tsx.snap b/apps/mobile/src/components/text/__snapshots__/AnimatedText.test.tsx.snap index eeceb546c5f..a95e4887071 100644 --- a/apps/mobile/src/components/text/__snapshots__/AnimatedText.test.tsx.snap +++ b/apps/mobile/src/components/text/__snapshots__/AnimatedText.test.tsx.snap @@ -5,20 +5,46 @@ exports[`AnimatedText renders without error 1`] = ` allowFontScaling={true} collapsable={false} editable={false} + jestAnimatedProps={ + { + "value": { + "text": "Rendered", + }, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 17, + "fontWeight": "400", + "lineHeight": 22.1, + }, + undefined, + ] + } maxFontSizeMultiplier={1.4} style={ - { - "fontFamily": "Basel Grotesk", - "fontSize": 17, - "fontWeight": "400", - "lineHeight": 22.1, - "padding": 0, - } + [ + { + "padding": 0, + }, + { + "fontFamily": "Basel Grotesk", + "fontSize": 17, + "fontWeight": "400", + "lineHeight": 22.1, + }, + undefined, + ] } text="Rendered" underlineColorAndroid="transparent" diff --git a/apps/mobile/src/components/text/__snapshots__/DecimalNumber.test.tsx.snap b/apps/mobile/src/components/text/__snapshots__/DecimalNumber.test.tsx.snap index 9e4ebfa46c2..7def4eb8b01 100644 --- a/apps/mobile/src/components/text/__snapshots__/DecimalNumber.test.tsx.snap +++ b/apps/mobile/src/components/text/__snapshots__/DecimalNumber.test.tsx.snap @@ -6,12 +6,7 @@ exports[`renders a DecimalNumber 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -26,12 +21,7 @@ exports[`renders a DecimalNumber 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -52,12 +42,7 @@ exports[`renders a DecimalNumber without a comma separator 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -72,12 +57,7 @@ exports[`renders a DecimalNumber without a comma separator 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -98,12 +78,7 @@ exports[`renders a DecimalNumber without a decimal part 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", diff --git a/apps/mobile/src/components/text/__snapshots__/TextWithFuseMatches.test.tsx.snap b/apps/mobile/src/components/text/__snapshots__/TextWithFuseMatches.test.tsx.snap index ea3c13571bf..f5750a3f748 100644 --- a/apps/mobile/src/components/text/__snapshots__/TextWithFuseMatches.test.tsx.snap +++ b/apps/mobile/src/components/text/__snapshots__/TextWithFuseMatches.test.tsx.snap @@ -14,12 +14,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -35,12 +30,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -56,12 +46,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -77,12 +62,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -98,12 +78,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -119,12 +94,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -140,12 +110,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -161,12 +126,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -182,12 +142,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -203,12 +158,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -224,12 +174,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -245,12 +190,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -266,12 +206,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -287,12 +222,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -308,12 +238,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -329,12 +254,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -350,12 +270,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -371,12 +286,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -392,12 +302,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -413,12 +318,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -434,12 +334,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -455,12 +350,7 @@ exports[`renders text with few matches 1`] = ` maxFontSizeMultiplier={1.4} style={ { - "color": { - "dynamic": { - "dark": "rgba(255, 255, 255, 0.38)", - "light": "rgba(19, 19, 19, 0.35)", - }, - }, + "color": "rgba(19, 19, 19, 0.35)", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", @@ -481,12 +371,7 @@ exports[`renders text without matches 1`] = ` numberOfLines={1} style={ { - "color": { - "dynamic": { - "dark": "#FFFFFF", - "light": "#131313", - }, - }, + "color": "#131313", "fontFamily": "Basel Grotesk", "fontSize": 19, "fontWeight": "400", diff --git a/apps/mobile/src/features/biometrics/biometrics-utils.test.ts b/apps/mobile/src/features/biometrics/biometrics-utils.test.ts index 12959fa4481..a75f42ca282 100644 --- a/apps/mobile/src/features/biometrics/biometrics-utils.test.ts +++ b/apps/mobile/src/features/biometrics/biometrics-utils.test.ts @@ -19,7 +19,7 @@ describe(tryLocalAuthenticate, () => { it('checks enrollement', async () => { mockedHasHardwareAsync.mockResolvedValue(true) mockedIsEnrolledAsync.mockResolvedValue(false) - mockedAuthenticateAsync.mockResolvedValue({ success: false, error: '' }) + mockedAuthenticateAsync.mockResolvedValue({ success: false, error: 'unknown' }) const status = await tryLocalAuthenticate() @@ -29,7 +29,7 @@ describe(tryLocalAuthenticate, () => { it('fails to authenticate when user rejects', async () => { mockedHasHardwareAsync.mockResolvedValue(true) mockedIsEnrolledAsync.mockResolvedValue(true) - mockedAuthenticateAsync.mockResolvedValue({ success: false, error: '' }) + mockedAuthenticateAsync.mockResolvedValue({ success: false, error: 'unknown' }) const status = await tryLocalAuthenticate() diff --git a/apps/mobile/src/features/biometrics/biometricsSaga.ts b/apps/mobile/src/features/biometrics/biometricsSaga.ts index 9416bbaa5eb..2f36a784766 100644 --- a/apps/mobile/src/features/biometrics/biometricsSaga.ts +++ b/apps/mobile/src/features/biometrics/biometricsSaga.ts @@ -35,7 +35,7 @@ export function* biometricsSaga(): SagaIterator { // -------------------------------------------------------------------------------------------- const authTask: Task = yield* fork(function* watchAuthenticationTriggers(): SagaIterator { while (true) { - const action = yield* take(triggerAuthentication.type) + const action = yield* take(triggerAuthentication) yield* call(handleAuthentication, action) } }) diff --git a/apps/mobile/src/features/datadog/DatadogProviderWrapper.tsx b/apps/mobile/src/features/datadog/DatadogProviderWrapper.tsx index b307753f982..33cc7e78ee6 100644 --- a/apps/mobile/src/features/datadog/DatadogProviderWrapper.tsx +++ b/apps/mobile/src/features/datadog/DatadogProviderWrapper.tsx @@ -8,15 +8,15 @@ import { UploadFrequency, } from '@datadog/mobile-react-native' import { ErrorEventMapper } from '@datadog/mobile-react-native/lib/typescript/rum/eventMappers/errorEventMapper' -import { PropsWithChildren, default as React, useEffect, useState } from 'react' -import { DatadogContext } from 'src/features/datadog/DatadogContext' -import { config } from 'uniswap/src/config' import { DatadogIgnoredErrorsConfigKey, DatadogIgnoredErrorsValType, DynamicConfigs, -} from 'uniswap/src/features/gating/configs' -import { getDynamicConfigValue } from 'uniswap/src/features/gating/hooks' + getDynamicConfigValue, +} from '@universe/gating' +import { PropsWithChildren, default as React, useEffect, useState } from 'react' +import { DatadogContext } from 'src/features/datadog/DatadogContext' +import { config } from 'uniswap/src/config' import { datadogEnabledBuild, isTestRun, localDevDatadogEnabled } from 'utilities/src/environment/constants' import { setAttributesToDatadog } from 'utilities/src/logger/datadog/Datadog' import { getDatadogEnvironment } from 'utilities/src/logger/datadog/env' diff --git a/apps/mobile/src/features/deepLinking/configUtils.ts b/apps/mobile/src/features/deepLinking/configUtils.ts index f6047a337dd..e182bed83f6 100644 --- a/apps/mobile/src/features/deepLinking/configUtils.ts +++ b/apps/mobile/src/features/deepLinking/configUtils.ts @@ -1,25 +1,6 @@ -import { - DeepLinkUrlAllowlist, - DeepLinkUrlAllowlistConfigKey, - DynamicConfigs, - UwULinkAllowlist, - UwuLinkConfigKey, -} from 'uniswap/src/features/gating/configs' -import { getDynamicConfigValue } from 'uniswap/src/features/gating/hooks' +import { DynamicConfigs, getDynamicConfigValue, UwULinkAllowlist, UwuLinkConfigKey } from '@universe/gating' import { isUwULinkAllowlistType } from 'uniswap/src/features/gating/typeGuards' -/** - * Gets the in-app browser allowlist from dynamic config. - * This function wraps getDynamicConfigValue for easier testing. - */ -export function getInAppBrowserAllowlist(): DeepLinkUrlAllowlist { - return getDynamicConfigValue({ - config: DynamicConfigs.DeepLinkUrlAllowlist, - key: DeepLinkUrlAllowlistConfigKey.AllowedUrls, - defaultValue: { allowedUrls: [] }, - }) -} - /** * Gets the UwuLink allowlist from dynamic config. * This function wraps getDynamicConfigValue for easier testing. diff --git a/apps/mobile/src/features/deepLinking/deepLinkUtils.test.ts b/apps/mobile/src/features/deepLinking/deepLinkUtils.test.ts index d1b329914e6..0cc51ce831d 100644 --- a/apps/mobile/src/features/deepLinking/deepLinkUtils.test.ts +++ b/apps/mobile/src/features/deepLinking/deepLinkUtils.test.ts @@ -1,10 +1,4 @@ import { DeepLinkAction, parseDeepLinkUrl } from 'src/features/deepLinking/deepLinkUtils' -import { logger } from 'utilities/src/logger/logger' - -// Mock the config utils -jest.mock('src/features/deepLinking/configUtils', () => ({ - getInAppBrowserAllowlist: jest.fn(() => ({ allowedUrls: [] })), -})) // Mock the logger jest.mock('utilities/src/logger/logger', () => ({ @@ -13,11 +7,6 @@ jest.mock('utilities/src/logger/logger', () => ({ }, })) -const mockGetInAppBrowserAllowlist = jest.mocked( - require('src/features/deepLinking/configUtils').getInAppBrowserAllowlist, -) -const mockLogger = jest.mocked(logger) - describe('getDeepLinkAction', () => { it.each` url | expected @@ -38,84 +27,11 @@ describe('getDeepLinkAction', () => { ${'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} `('url=$url should return expected=$expected', ({ url, expected }) => { expect(parseDeepLinkUrl(url).action).toEqual(expected) }) }) - -describe('parseDeepLinkUrl allowlist behavior', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - describe('when allowlist is empty', () => { - beforeEach(() => { - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: [] }) - }) - - it('should return Unknown action and log appropriate error message', () => { - const url = 'https://example.com/test' - const result = parseDeepLinkUrl(url) - - expect(result.action).toBe(DeepLinkAction.Unknown) - expect(mockLogger.error).toHaveBeenCalledWith( - `No allowlist configured for browser opening, rejecting URL: ${url}`, - { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }, - ) - expect(mockLogger.error).toHaveBeenCalledWith(`Unknown deep link action for url=${url}`, { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }) - }) - }) - - describe('when allowlist is non-empty but URL is not allowlisted', () => { - beforeEach(() => { - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: ['https://trusted.com'] }) - }) - - it('should return Unknown action and log URL not allowlisted error', () => { - const url = 'https://untrusted.com/test' - const result = parseDeepLinkUrl(url) - - expect(result.action).toBe(DeepLinkAction.Unknown) - expect(mockLogger.error).toHaveBeenCalledWith(`URL not allowlisted for browser opening: ${url}`, { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }) - expect(mockLogger.error).toHaveBeenCalledWith(`Unknown deep link action for url=${url}`, { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }) - }) - }) - - describe('when URL is allowlisted', () => { - beforeEach(() => { - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: [{ url: 'https://example.com', openInApp: true }] }) - }) - - it('should return InAppBrowser action without logging errors', () => { - const url = 'https://example.com/test' - const result = parseDeepLinkUrl(url) - - expect(result.action).toBe(DeepLinkAction.InAppBrowser) - if (result.action === DeepLinkAction.InAppBrowser) { - expect(result.data.targetUrl).toBe(url) - expect(result.data.openInApp).toBe(true) - } - expect(mockLogger.error).not.toHaveBeenCalled() - }) - - it('should respect openInApp configuration', () => { - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: [{ url: 'https://example.com', openInApp: false }] }) - - const url = 'https://example.com/test' - const result = parseDeepLinkUrl(url) - - expect(result.action).toBe(DeepLinkAction.InAppBrowser) - if (result.action === DeepLinkAction.InAppBrowser) { - expect(result.data.openInApp).toBe(false) - } - }) - }) -}) diff --git a/apps/mobile/src/features/deepLinking/deepLinkUtils.ts b/apps/mobile/src/features/deepLinking/deepLinkUtils.ts index a25138e2060..6bcb8ba2605 100644 --- a/apps/mobile/src/features/deepLinking/deepLinkUtils.ts +++ b/apps/mobile/src/features/deepLinking/deepLinkUtils.ts @@ -1,6 +1,5 @@ import { getScantasticQueryParams } from 'src/components/Requests/ScanSheet/util' import { UNISWAP_URL_SCHEME_UWU_LINK } from 'src/components/Requests/Uwulink/utils' -import { getInAppBrowserAllowlist } from 'src/features/deepLinking/configUtils' import { UNISWAP_URL_SCHEME, UNISWAP_URL_SCHEME_SCANTASTIC, @@ -8,7 +7,6 @@ import { UNISWAP_WALLETCONNECT_URL, } from 'src/features/deepLinking/constants' import { UNISWAP_WEB_HOSTNAME } from 'uniswap/src/constants/urls' -import { DeepLinkUrlAllowlist } from 'uniswap/src/features/gating/configs' import { isCurrencyIdValid } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' @@ -17,6 +15,7 @@ const WALLETCONNECT_URI_SCHEME = 'wc:' // https://eips.ethereum.org/EIPS/eip-132 export enum DeepLinkAction { UniswapWebLink = 'uniswapWebLink', + UniswapExternalBrowserLink = 'uniswapExternalBrowserLink', UniswapWalletConnect = 'uniswapWalletConnect', WalletConnectAsParam = 'walletConnectAsParam', UniswapWidget = 'uniswapWidget', @@ -29,7 +28,6 @@ export enum DeepLinkAction { SkipNonWalletConnect = 'skipNonWalletConnect', UniversalWalletConnectLink = 'universalWalletConnectLink', WalletConnect = 'walletConnect', - InAppBrowser = 'inAppBrowser', Error = 'error', Unknown = 'unknown', TokenDetails = 'tokenDetails', @@ -85,6 +83,7 @@ export type PayloadWithFiatOnRampParams = BasePayload & { export type DeepLinkActionResult = | { action: DeepLinkAction.UniswapWebLink; data: BasePayload & { urlPath: string } } + | { action: DeepLinkAction.UniswapExternalBrowserLink; data: BasePayload & { urlPath: string } } | { action: DeepLinkAction.WalletConnectAsParam; data: PayloadWithWcUri } | { action: DeepLinkAction.UniswapWalletConnect; data: PayloadWithWcUri } | { action: DeepLinkAction.UniswapWidget; data: BasePayload } @@ -97,7 +96,6 @@ export type DeepLinkActionResult = | { action: DeepLinkAction.SkipNonWalletConnect; data: BasePayload } | { action: DeepLinkAction.UniversalWalletConnectLink; data: PayloadWithWcUri } | { action: DeepLinkAction.WalletConnect; data: BasePayload & { wcUri: string } } - | { action: DeepLinkAction.InAppBrowser; data: BasePayload & { targetUrl: string; openInApp: boolean } } | { action: DeepLinkAction.TokenDetails; data: BasePayload & { currencyId: string } } | { action: DeepLinkAction.FiatOnRampScreen; data: PayloadWithFiatOnRampParams } | { action: DeepLinkAction.Error; data: BasePayload } @@ -105,56 +103,6 @@ export type DeepLinkActionResult = type DeepLinkHandler = (url: URL, data: BasePayload) => DeepLinkActionResult -/** - * Checks if a URL is allowlisted for browser opening and returns the configuration. - * This function should be called with the dynamic config value. - * - * @param urlString - The URL to check. - * @param allowList - Allowlist from dynamic config. - * @returns Object with isAllowed and openInApp flags, or null if not allowlisted. - */ -function getUrlAllowlistConfig( - urlString: string, - allowList: DeepLinkUrlAllowlist, -): { isAllowed: boolean; openInApp: boolean } { - try { - const url = new URL(urlString) - - // Only allow HTTPS protocol - if (url.protocol !== 'https:') { - return { isAllowed: false, openInApp: false } - } - - const urlToCheck = `${url.protocol}//${url.hostname}${url.pathname}` - - for (const allowedItem of allowList.allowedUrls) { - const allowedUrl = typeof allowedItem === 'string' ? allowedItem : allowedItem.url - const openInApp = typeof allowedItem === 'string' ? true : (allowedItem.openInApp ?? true) // Default to in-app - - try { - // Support both exact matches and hostname matches - if (allowedUrl === urlString || allowedUrl === urlToCheck) { - return { isAllowed: true, openInApp } - } - - // Support hostname-only matches (e.g., "example.com" matches "https://example.com/any/path") - // Always use HTTPS for allowed URL validation - const allowedUrlObj = new URL(allowedUrl.startsWith('https://') ? allowedUrl : `https://${allowedUrl}`) - if (url.hostname === allowedUrlObj.hostname) { - return { isAllowed: true, openInApp } - } - } catch { - // If allowedUrl is not a valid URL, reject it for security - continue - } - } - - return { isAllowed: false, openInApp: false } - } catch { - return { isAllowed: false, openInApp: false } - } -} - /** * Parses a deep link URL and returns the action to be taken as well as * any additional data that may be needed to handle the deep link. @@ -174,6 +122,10 @@ export function parseDeepLinkUrl(urlString: string): DeepLinkActionResult { } } + if (isValidUniswapExternalWebLink(urlString)) { + return { action: DeepLinkAction.UniswapExternalBrowserLink, data: { ...data, urlPath: url.pathname } } + } + const urlPath = url.pathname const userAddress = url.searchParams.get('userAddress') ?? undefined const fiatOnRamp = url.searchParams.get('fiatOnRamp') === 'true' @@ -290,35 +242,8 @@ export function parseDeepLinkUrl(urlString: string): DeepLinkActionResult { return { action: DeepLinkAction.WalletConnect, data: { ...data, wcUri } } } - // Check if URL is allowlisted for browser opening - const inAppBrowserAllowlist = getInAppBrowserAllowlist() - - // Always perform allowlist check for consistent behavior and logging - const allowlistConfig = getUrlAllowlistConfig(urlString, inAppBrowserAllowlist) - if (allowlistConfig.isAllowed) { - return { - action: DeepLinkAction.InAppBrowser, - data: { ...data, targetUrl: urlString, openInApp: allowlistConfig.openInApp }, - } - } - - // Log appropriate message based on allowlist state - if (inAppBrowserAllowlist.allowedUrls.length === 0) { - logger.error(`No allowlist configured for browser opening, rejecting URL: ${urlString}`, { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }) - } else { - logger.error(`URL not allowlisted for browser opening: ${urlString}`, { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }) - } - - logger.error(`Unknown deep link action for url=${urlString}`, { - tags: { file: 'deepLinkUtils', function: 'parseDeepLinkUrl' }, - }) return { action: DeepLinkAction.Unknown, data } } - const handlers: Record = { [UNISWAP_WEB_HOSTNAME]: (url, data) => { const urlParts = url.href.split(`${UNISWAP_WEB_HOSTNAME}/`) @@ -368,6 +293,17 @@ const handlers: Record = { }), } +const UNISWAP_EXTERNAL_WEB_LINK_VALID_REGEXES = [ + // eslint-disable-next-line security/detect-unsafe-regex + /^https:\/\/([a-zA-Z0-9-]+)\.uniswap\.org(\/.*)?$/, + // eslint-disable-next-line security/detect-unsafe-regex + /^https:\/\/cryptothegame\.com(\/.*)?$/, +] + +function isValidUniswapExternalWebLink(urlString: string): boolean { + return UNISWAP_EXTERNAL_WEB_LINK_VALID_REGEXES.some((regex) => regex.test(urlString)) +} + /** * Extracts the WalletConnect URI from a URL string. * diff --git a/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.test.ts b/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.test.ts index 170a0955994..f3133c385fd 100644 --- a/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.test.ts +++ b/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.test.ts @@ -10,7 +10,6 @@ import { ONRAMP_DEEPLINK_DELAY, parseAndValidateUserAddress, } from 'src/features/deepLinking/handleDeepLinkSaga' -import { handleInAppBrowser } from 'src/features/deepLinking/handleInAppBrowserSaga' import { handleOnRampReturnLink } from 'src/features/deepLinking/handleOnRampReturnLinkSaga' import { handleTransactionLink } from 'src/features/deepLinking/handleTransactionLinkSaga' import { handleUniswapAppDeepLink } from 'src/features/deepLinking/handleUniswapAppDeepLink' @@ -42,26 +41,14 @@ jest.mock('expo-web-browser', () => ({ FULL_SCREEN: 'fullScreen', }, })) -jest.mock('uniswap/src/features/gating/sdk/statsig', () => ({ +jest.mock('@universe/gating', () => ({ + ...jest.requireActual('@universe/gating'), getStatsigClient: jest.fn(() => ({ checkGate: jest.fn(() => false), // Always return false to avoid Korea gate redirects })), -})) - -jest.mock('uniswap/src/features/gating/hooks', () => ({ getFeatureFlag: jest.fn(() => false), // Default to false for feature flags })) -jest.mock('src/features/deepLinking/configUtils', () => ({ - getInAppBrowserAllowlist: jest.fn(() => ({ allowedUrls: [] })), // Default to empty allowlist - getUwuLinkAllowlist: jest.fn(() => ({ contracts: [], tokenRecipients: [] })), // Default to empty allowlist -})) - -// Get the mocked functions for proper typing -const mockGetInAppBrowserAllowlist = jest.mocked( - require('src/features/deepLinking/configUtils').getInAppBrowserAllowlist, -) - 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` @@ -660,363 +647,4 @@ describe(handleDeepLink, () => { .returns(undefined) .silentRun() }) - - describe('In-app browser functionality', () => { - const testUrl = 'https://example.com/test' - const testUrlPayload = { url: testUrl, coldStart: false } - - beforeEach(() => { - // Reset the mock before each test - mockGetInAppBrowserAllowlist.mockClear() - }) - - it('Handles allowlisted URL with openInApp=true (default)', () => { - const mockAllowlist = [{ url: 'https://example.com', openInApp: true }] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, testUrl, true) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - it('Handles allowlisted URL with openInApp=false (external browser)', () => { - const mockAllowlist = [{ url: 'https://example.com', openInApp: false }] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, testUrl, false) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - it('Handles allowlisted URL with string format (defaults to openInApp=true)', () => { - const mockAllowlist = ['https://example.com'] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, testUrl, true) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - it('Handles hostname matching with openInApp configuration', () => { - const mockAllowlist = [{ url: 'example.com', openInApp: false }] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, testUrl, false) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - it('Handles allowlisted URL without active account', () => { - const mockAllowlist = [{ url: 'https://example.com', openInApp: true }] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState({ - wallet: { - accounts: {}, - activeAccountAddress: null, - }, - }) - .call(handleInAppBrowser, testUrl, true) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - it('Rejects non-allowlisted URL and logs error', () => { - const mockAllowlist = [{ url: 'https://trusted.com', openInApp: true }] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.Unknown, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - .finally(() => { - consoleSpy.mockRestore() - }) - }) - - it('Handles mixed allowlist with different URL formats and openInApp settings', () => { - const testUrl2 = 'https://docs.example.com/help' - const testUrl2Payload = { url: testUrl2, coldStart: false } - - const mockAllowlist = [ - 'https://example.com', // String format - defaults to openInApp=true - { url: 'https://docs.example.com', openInApp: false }, // Object format - external browser - { url: 'trusted-site.com', openInApp: true }, // Hostname matching - in-app browser - ] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: testUrl2Payload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, testUrl2, false) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: testUrl2, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - it('Handles empty allowlist', () => { - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: [] }) - - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined) - - return expectSaga(handleDeepLink, { payload: testUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.Unknown, - url: testUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - .finally(() => { - consoleSpy.mockRestore() - }) - }) - - it('Handles URL with query parameters and fragments', () => { - const complexUrl = 'https://example.com/path?param=value#section' - const complexUrlPayload = { url: complexUrl, coldStart: false } - - const mockAllowlist = [{ url: 'https://example.com', openInApp: true }] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - return expectSaga(handleDeepLink, { payload: complexUrlPayload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, complexUrl, true) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: complexUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - }) - - // Security tests for URL validation - describe('URL validation security', () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined) - - afterEach(() => { - consoleSpy.mockClear() - }) - - afterAll(() => { - consoleSpy.mockRestore() - }) - - it('Rejects malicious URLs that would match allowlist substrings', () => { - // Test case: allowlist contains "example.com" but malicious URL contains it as substring - const mockAllowlist = ['example.com'] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - const maliciousUrls = [ - 'https://malicious-example.com/steal-data', - 'https://notexample.com/example.com', - 'https://example.com.evil.com/phishing', - 'https://sub.example.com.attacker.com/fake', - ] - - const testMaliciousUrl = (maliciousUrl: string): Promise => { - const payload = { url: maliciousUrl, coldStart: false } - return expectSaga(handleDeepLink, { payload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.Unknown, - url: maliciousUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - } - - const promises = maliciousUrls.map(testMaliciousUrl) - return Promise.all(promises) - }) - - it('Rejects URLs with invalid allowlist entries that would previously use includes() fallback', () => { - // Test case: allowlist contains invalid URL strings that would trigger the dangerous fallback - const mockAllowlist = [ - 'invalid-url-format', // This is not a valid URL - '://malformed-url', - 'just-a-string', - ] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - const invalidTestUrls = [ - 'https://malicious-invalid-url-format.com/attack', - 'https://evil.com/invalid-url-format', - 'https://attacker.com/path?param=invalid-url-format', - ] - - const testInvalidUrl = (invalidTestUrl: string): Promise => { - const payload = { url: invalidTestUrl, coldStart: false } - return expectSaga(handleDeepLink, { payload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.Unknown, - url: invalidTestUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - } - - const promises = invalidTestUrls.map(testInvalidUrl) - return Promise.all(promises) - }) - - it('Still allows legitimate URLs that match hostname exactly', () => { - const mockAllowlist = ['example.com'] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - const legitimateUrls = [ - 'https://example.com/', - 'https://example.com/path', - 'https://example.com/path?param=value', - 'https://example.com/path?param=value#fragment', - ] - - const testLegitimateUrl = (legitimateUrl: string): Promise => { - const payload = { url: legitimateUrl, coldStart: false } - return expectSaga(handleDeepLink, { payload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(handleInAppBrowser, legitimateUrl, true) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.InAppBrowser, - url: legitimateUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - } - - const promises = legitimateUrls.map(testLegitimateUrl) - return Promise.all(promises) - }) - - it('Rejects non-HTTPS URLs even if they match allowlist', () => { - const mockAllowlist = ['example.com'] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - const insecureUrls = ['http://example.com/', 'ftp://example.com/', 'file://example.com/'] - - const testInsecureUrl = (insecureUrl: string): Promise => { - const payload = { url: insecureUrl, coldStart: false } - return expectSaga(handleDeepLink, { payload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.Unknown, - url: insecureUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - } - - const promises = insecureUrls.map(testInsecureUrl) - return Promise.all(promises) - }) - - it('Handles edge cases with subdomain attacks', () => { - const mockAllowlist = ['trusted.com'] - mockGetInAppBrowserAllowlist.mockReturnValue({ allowedUrls: mockAllowlist }) - - // These should be rejected - they are different hostnames - const subdomainAttackUrls = [ - 'https://evil.trusted.com.attacker.com/', - 'https://trusted.com.evil.com/', - 'https://anytrusted.com/', - 'https://trusted.com.fake/', - ] - - const testAttackUrl = (attackUrl: string): Promise => { - const payload = { url: attackUrl, coldStart: false } - return expectSaga(handleDeepLink, { payload, type: '' }) - .withState(stateWithActiveAccountAddress) - .call(sendAnalyticsEvent, MobileEventName.DeepLinkOpened, { - action: DeepLinkAction.Unknown, - url: attackUrl, - screen: 'other', - is_cold_start: false, - source: 'unknown', - }) - .returns(undefined) - .silentRun() - } - - const promises = subdomainAttackUrls.map(testAttackUrl) - return Promise.all(promises) - }) - }) - }) }) diff --git a/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts b/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts index a9afe70a1a0..ffa647d65d9 100644 --- a/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts +++ b/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts @@ -1,4 +1,5 @@ import { createAction } from '@reduxjs/toolkit' +import { FeatureFlags, getFeatureFlagName, getStatsigClient } from '@universe/gating' import { parseUri } from '@walletconnect/utils' import { Alert } from 'react-native' import { navigate } from 'src/app/navigation/rootNavigation' @@ -15,7 +16,6 @@ import { PayloadWithFiatOnRampParams, parseDeepLinkUrl, } from 'src/features/deepLinking/deepLinkUtils' -import { handleInAppBrowser } from 'src/features/deepLinking/handleInAppBrowserSaga' import { handleOffRampReturnLink } from 'src/features/deepLinking/handleOffRampReturnLinkSaga' import { handleOnRampReturnLink } from 'src/features/deepLinking/handleOnRampReturnLinkSaga' import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga' @@ -29,8 +29,6 @@ import { waitForWcWeb3WalletIsReady } from 'src/features/walletConnect/walletCon import { addRequest, setDidOpenFromDeepLink } from 'src/features/walletConnect/walletConnectSlice' import { call, delay, put, select, takeLatest } from 'typed-redux-saga' import { AccountType } from 'uniswap/src/features/accounts/types' -import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags' -import { getStatsigClient } from 'uniswap/src/features/gating/sdk/statsig' import { MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import i18n from 'uniswap/src/i18n' @@ -67,8 +65,6 @@ export function* handleDeepLink(action: ReturnType) { if (!activeAccount) { if (deepLinkAction.action === DeepLinkAction.UniswapWebLink) { yield* call(openUri, { uri: deepLinkAction.data.url.toString(), openExternalBrowser: true }) - } else if (deepLinkAction.action === DeepLinkAction.InAppBrowser) { - yield* call(handleInAppBrowser, deepLinkAction.data.targetUrl, deepLinkAction.data.openInApp) } // If there is no active account, we don't want to handle other deep links } else { @@ -81,6 +77,10 @@ export function* handleDeepLink(action: ReturnType) { }) break } + case DeepLinkAction.UniswapExternalBrowserLink: { + yield* call(openUri, { uri: deepLinkAction.data.url.toString(), openExternalBrowser: true }) + break + } case DeepLinkAction.WalletConnectAsParam: case DeepLinkAction.UniswapWalletConnect: { yield* call(handleWalletConnectDeepLink, deepLinkAction.data.wcUri) @@ -102,10 +102,6 @@ export function* handleDeepLink(action: ReturnType) { yield* call(handleUwuLinkDeepLink, deepLinkAction.data.url.toString()) break } - case DeepLinkAction.InAppBrowser: { - yield* call(handleInAppBrowser, deepLinkAction.data.targetUrl, deepLinkAction.data.openInApp) - break - } case DeepLinkAction.TransactionScreen: case DeepLinkAction.ShowTransactionAfterFiatOnRamp: case DeepLinkAction.ShowTransactionAfterFiatOffRampScreen: diff --git a/apps/mobile/src/features/deepLinking/handleInAppBrowserSaga.ts b/apps/mobile/src/features/deepLinking/handleInAppBrowserSaga.ts deleted file mode 100644 index 3503cfc20f4..00000000000 --- a/apps/mobile/src/features/deepLinking/handleInAppBrowserSaga.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { call } from 'typed-redux-saga' -import { openUri } from 'uniswap/src/utils/linking' -import { logger } from 'utilities/src/logger/logger' - -/** - * Opens a URL in a browser window (in-app or external). - * - * @param url - The URL to open - * @param openInApp - If true, opens in in-app browser; if false, opens in external browser - */ -export function* handleInAppBrowser(url: string, openInApp: boolean = true) { - try { - const browserType = openInApp ? 'in-app browser' : 'external browser' - yield* call(logger.info, 'handleInAppBrowserSaga', 'handleInAppBrowser', `Opening URL in ${browserType}: ${url}`) - - // Open the URL using openUri with the specified browser preference - yield* call(openUri, { - uri: url, - openExternalBrowser: !openInApp, // Use external browser if openInApp is false - isSafeUri: true, // URL has been allowlisted so it's safe - }) - - yield* call( - logger.info, - 'handleInAppBrowserSaga', - 'handleInAppBrowser', - `Successfully opened URL in ${browserType}: ${url}`, - ) - } catch (error) { - yield* call(logger.error, error, { - tags: { file: 'handleInAppBrowserSaga', function: 'handleInAppBrowser' }, - extra: { url }, - }) - } -} diff --git a/apps/mobile/src/features/deepLinking/handleOnRampReturnLinkSaga.ts b/apps/mobile/src/features/deepLinking/handleOnRampReturnLinkSaga.ts index da904ede2ea..8edff2d8379 100644 --- a/apps/mobile/src/features/deepLinking/handleOnRampReturnLinkSaga.ts +++ b/apps/mobile/src/features/deepLinking/handleOnRampReturnLinkSaga.ts @@ -1,9 +1,8 @@ +import { FeatureFlags, getFeatureFlag } from '@universe/gating' import { navigate } from 'src/app/navigation/rootNavigation' import { HomeScreenTabIndex } from 'src/screens/HomeScreen/HomeScreenTabIndex' import { dismissInAppBrowser } from 'src/utils/linking' import { call, put } from 'typed-redux-saga' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { getFeatureFlag } from 'uniswap/src/features/gating/hooks' import { forceFetchFiatOnRampTransactions } from 'uniswap/src/features/transactions/slice' import { MobileScreens } from 'uniswap/src/types/screens/mobile' diff --git a/apps/mobile/src/features/deepLinking/handleTransactionLinkSaga.ts b/apps/mobile/src/features/deepLinking/handleTransactionLinkSaga.ts index fefafa0b2dd..c6e5d16b175 100644 --- a/apps/mobile/src/features/deepLinking/handleTransactionLinkSaga.ts +++ b/apps/mobile/src/features/deepLinking/handleTransactionLinkSaga.ts @@ -1,9 +1,8 @@ +import { FeatureFlags, getFeatureFlag } from '@universe/gating' import { navigate } from 'src/app/navigation/rootNavigation' import { closeAllModals } from 'src/features/modals/modalSlice' import { HomeScreenTabIndex } from 'src/screens/HomeScreen/HomeScreenTabIndex' import { call, put } from 'typed-redux-saga' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { getFeatureFlag } from 'uniswap/src/features/gating/hooks' import { MobileScreens } from 'uniswap/src/types/screens/mobile' export function* handleTransactionLink() { diff --git a/apps/mobile/src/features/deepLinking/parseSwapLink.test.ts b/apps/mobile/src/features/deepLinking/parseSwapLink.test.ts index c6a97acd5ba..921fe494337 100644 --- a/apps/mobile/src/features/deepLinking/parseSwapLink.test.ts +++ b/apps/mobile/src/features/deepLinking/parseSwapLink.test.ts @@ -186,19 +186,6 @@ describe('parseSwapLink', () => { expect(result.exactAmountToken).toBe('1.5') }) - it('should parse valid MonadTestnet link', () => { - const url = new URL( - `https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.MonadTestnet}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.MonadTestnet}-0x1234567890123456789012345678901234567890¤cyField=output&amount=100`, - ) - - const result = parseSwapLinkMobileFormatOrThrow(url) - - expect(result.inputAsset?.chainId).toBe(UniverseChainId.MonadTestnet) - expect(result.outputAsset?.chainId).toBe(UniverseChainId.MonadTestnet) - expect(result.exactCurrencyField).toBe(CurrencyField.OUTPUT) - expect(result.exactAmountToken).toBe('100') - }) - 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`, @@ -325,14 +312,14 @@ describe('parseSwapLink', () => { expect(result.outputAsset?.chainId).toBe(UniverseChainId.UnichainSepolia) }) - it('should allow swaps between MonadTestnet and Sepolia', () => { + it('should allow swaps between UnichainSepolia and Sepolia', () => { const url = new URL( - `https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.MonadTestnet}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Sepolia}-0x1c7d4b196cb0c7b01d743fbc6116a902379c7238¤cyField=output&amount=50`, + `https://uniswap.org/mobile-redirect?screen=swap&inputCurrencyId=${UniverseChainId.UnichainSepolia}-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee&outputCurrencyId=${UniverseChainId.Sepolia}-0x1c7d4b196cb0c7b01d743fbc6116a902379c7238¤cyField=output&amount=50`, ) const result = parseSwapLinkMobileFormatOrThrow(url) - expect(result.inputAsset?.chainId).toBe(UniverseChainId.MonadTestnet) + expect(result.inputAsset?.chainId).toBe(UniverseChainId.UnichainSepolia) expect(result.outputAsset?.chainId).toBe(UniverseChainId.Sepolia) expect(result.exactCurrencyField).toBe(CurrencyField.OUTPUT) }) diff --git a/apps/mobile/src/features/externalProfile/ProfileContextMenu.tsx b/apps/mobile/src/features/externalProfile/ProfileContextMenu.tsx index e81e599aec4..05edb8d131f 100644 --- a/apps/mobile/src/features/externalProfile/ProfileContextMenu.tsx +++ b/apps/mobile/src/features/externalProfile/ProfileContextMenu.tsx @@ -4,9 +4,8 @@ import { useTranslation } from 'react-i18next' import { NativeSyntheticEvent, Share } from 'react-native' import ContextMenu, { ContextMenuOnPressNativeEvent } from 'react-native-context-menu-view' import { useDispatch } from 'react-redux' -import { TripleDot } from 'src/components/icons/TripleDot' -import { Flex, TouchableArea } from 'ui/src' -import { iconSizes } from 'ui/src/theme' +import { TouchableArea } from 'ui/src' +import { Ellipsis } from 'ui/src/components/icons' import { uniswapUrls } from 'uniswap/src/constants/urls' import { useUnitagsAddressQuery } from 'uniswap/src/data/apiClients/unitagsApi/useUnitagsAddressQuery' import { getChainInfo } from 'uniswap/src/features/chains/chainInfo' @@ -121,10 +120,8 @@ export function ProfileContextMenu({ address }: { address: Address }): JSX.Eleme await menuActions[e.nativeEvent.index]?.action() }} > - - - - + + ) diff --git a/apps/mobile/src/features/externalProfile/ProfileHeader.tsx b/apps/mobile/src/features/externalProfile/ProfileHeader.tsx index 3977d01811a..b20e8a63c14 100644 --- a/apps/mobile/src/features/externalProfile/ProfileHeader.tsx +++ b/apps/mobile/src/features/externalProfile/ProfileHeader.tsx @@ -156,7 +156,7 @@ export const ProfileHeader = memo(function ProfileHeader({ address }: ProfileHea {/* header row */} - + diff --git a/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx b/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx index d2915126388..8934eb2d257 100644 --- a/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx +++ b/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx @@ -75,7 +75,7 @@ interface FiatOnRampAmountSectionProps { } export type FiatOnRampAmountSectionRef = { - textInputRef: RefObject + textInputRef: RefObject triggerShakeAnimation: () => void } diff --git a/apps/mobile/src/features/import/InputWIthSuffixProps.ts b/apps/mobile/src/features/import/InputWIthSuffixProps.ts index b78f87f62f9..bf24dd3d029 100644 --- a/apps/mobile/src/features/import/InputWIthSuffixProps.ts +++ b/apps/mobile/src/features/import/InputWIthSuffixProps.ts @@ -14,7 +14,7 @@ export interface InputWithSuffixProps { multiline?: boolean textAlign?: 'left' | 'right' | 'center' lineHeight?: number - textInputRef: React.RefObject + textInputRef: React.RefObject onBlur?: () => void onFocus?: () => void onChangeText?: (text: string) => void diff --git a/apps/mobile/src/features/import/__snapshots__/GenericImportForm.test.tsx.snap b/apps/mobile/src/features/import/__snapshots__/GenericImportForm.test.tsx.snap index 8e1ea06dfc3..285b81bdcad 100644 --- a/apps/mobile/src/features/import/__snapshots__/GenericImportForm.test.tsx.snap +++ b/apps/mobile/src/features/import/__snapshots__/GenericImportForm.test.tsx.snap @@ -14,42 +14,17 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = ` - - + + diff --git a/apps/mobile/src/features/nfts/item/__snapshots__/CollectionPreviewCard.test.tsx.snap b/apps/mobile/src/features/nfts/item/__snapshots__/CollectionPreviewCard.test.tsx.snap index 5bbef99eb00..ab767ca37f6 100644 --- a/apps/mobile/src/features/nfts/item/__snapshots__/CollectionPreviewCard.test.tsx.snap +++ b/apps/mobile/src/features/nfts/item/__snapshots__/CollectionPreviewCard.test.tsx.snap @@ -5,11 +5,34 @@ exports[`renders collection preview card 1`] = ` collapsable={false} focusVisibleStyle={{}} forwardedRef={[Function]} + jestAnimatedProps={ + { + "value": {}, + } + } jestAnimatedStyle={ { "value": {}, } } + jestInlineStyle={ + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + ] + } onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -22,25 +45,26 @@ exports[`renders collection preview card 1`] = ` onStartShouldSetResponder={[Function]} role="button" style={ - { - "backgroundColor": "transparent", - "borderBottomLeftRadius": 12, - "borderBottomRightRadius": 12, - "borderTopLeftRadius": 12, - "borderTopRightRadius": 12, - "flexDirection": "column", - "opacity": 1, - "transform": [ - { - "scale": 1, - }, - ], - } + [ + { + "backgroundColor": "transparent", + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, + "flexDirection": "column", + "opacity": 1, + "transform": [ + { + "scale": 1, + }, + ], + }, + {}, + ] } > { OneSignal.initialize(config.onesignalAppId) + startSilentPushListener() + OneSignal.Notifications.addEventListener('foregroundWillDisplay', (event) => { const notification = event.getNotification() const additionalData = notification.additionalData as { notification_type?: string } | undefined @@ -72,6 +75,12 @@ export const initOneSignal = (): void => { export const promptPushPermission = async (): Promise => { const response = await OneSignal.Notifications.requestPermission(true) logger.debug('Onesignal', 'promptForPushNotificationsWithUserResponse', `Prompt response: ${response}`) + + // Explicitly opt in to push notifications if permission was granted + if (response) { + OneSignal.User.pushSubscription.optIn() + } + return response } diff --git a/apps/mobile/src/features/notifications/SilentPushListener.ts b/apps/mobile/src/features/notifications/SilentPushListener.ts new file mode 100644 index 00000000000..395825bd05e --- /dev/null +++ b/apps/mobile/src/features/notifications/SilentPushListener.ts @@ -0,0 +1,70 @@ +import { NativeEventEmitter, NativeModules, Platform } from 'react-native' +import { WalletEventName } from 'uniswap/src/features/telemetry/constants/wallet' +import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' +import { logger } from 'utilities/src/logger/logger' +import { isMobileApp } from 'utilities/src/platform' + +const EVENT_NAME = 'SilentPushReceived' + +interface SilentPushEventEmitterInterface { + addListener: (eventName: string) => void + removeListeners: (count: number) => void +} + +declare module 'react-native' { + interface NativeModulesStatic { + SilentPushEventEmitter: SilentPushEventEmitterInterface + } +} + +const { SilentPushEventEmitter } = NativeModules + +const eventEmitter = isMobileApp ? new NativeEventEmitter(SilentPushEventEmitter) : undefined + +let subscription: { remove: () => void } | undefined + +const handleSilentPush = (payload: Record): void => { + logger.debug('SilentPush', 'handleSilentPush', 'Silent push received', payload) + + // Onesignal Silent Push payload stores the template id in the 'custom' object with key 'i' + if ('custom' in payload && payload.custom) { + try { + const customPayload = typeof payload.custom === 'string' ? JSON.parse(payload.custom) : payload.custom + + if (!('i' in customPayload) || typeof customPayload.i !== 'string') { + return + } + + sendAnalyticsEvent(WalletEventName.SilentPushReceived, { + template_id: customPayload.i, + }) + logger.debug('SilentPush', 'handleSilentPush', 'Silent push event sent', { + template_id: customPayload.i, + }) + } catch (error) { + logger.error(error, { + tags: { + file: 'SilentPushListener.ts', + function: 'handleSilentPush', + }, + }) + } + } +} + +export const startSilentPushListener = (): void => { + if (subscription) { + return + } + + if (!eventEmitter) { + logger.warn('SilentPush', 'startSilentPushListener', 'Native event emitter unavailable', { + platform: Platform.OS, + moduleLoaded: Boolean(SilentPushEventEmitter), + }) + return + } + + subscription = eventEmitter.addListener(EVENT_NAME, handleSilentPush) + logger.debug('SilentPush', 'startSilentPushListener', 'Listener registered') +} diff --git a/apps/mobile/src/features/notifications/constants.ts b/apps/mobile/src/features/notifications/constants.ts index ca53da9644f..00726dd0e13 100644 --- a/apps/mobile/src/features/notifications/constants.ts +++ b/apps/mobile/src/features/notifications/constants.ts @@ -11,6 +11,7 @@ export enum OneSignalUserTagField { SwapLastCompletedAt = 'swap_last_completed_at', AccountIsUnfunded = 'account_is_unfunded', GatingUnfundedWalletsEnabled = 'gating_unfunded_wallets_enabled', + ActiveWalletAddress = 'active_wallet_address', } export enum NotificationType { diff --git a/apps/mobile/src/features/notifications/saga.ts b/apps/mobile/src/features/notifications/saga.ts index 2f25dd8ed5e..edef583fd6c 100644 --- a/apps/mobile/src/features/notifications/saga.ts +++ b/apps/mobile/src/features/notifications/saga.ts @@ -6,14 +6,18 @@ import { call, select, takeEvery } from 'typed-redux-saga' import { finalizeTransaction } from 'uniswap/src/features/transactions/slice' import { TransactionStatus, TransactionType } from 'uniswap/src/features/transactions/types/transactionDetails' import { ONE_SECOND_MS } from 'utilities/src/time/time' -import { selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors' +import { selectActiveAccountAddress, selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors' +import { removeAccounts, setAccountAsActive } from 'wallet/src/features/wallet/slice' export function* pushNotificationsWatcherSaga() { yield* call(syncWithOneSignal) + yield* call(syncActiveWalletAddressTag) yield* takeEvery(initNotifsForNewUser.type, initNewUser) yield* takeEvery(updateNotifSettings.type, syncWithOneSignal) yield* takeEvery(finalizeTransaction.type, processFinalizedTx) + yield* takeEvery(setAccountAsActive.type, syncActiveWalletAddressTag) + yield* takeEvery(removeAccounts.type, syncActiveWalletAddressTag) } /** @@ -51,3 +55,12 @@ function* processFinalizedTx(action: ReturnType) { ) } } + +function* syncActiveWalletAddressTag() { + const activeAddress = yield* select(selectActiveAccountAddress) + if (activeAddress) { + yield* call(OneSignal.User.addTag, OneSignalUserTagField.ActiveWalletAddress, activeAddress) + } else { + yield* call(OneSignal.User.removeTag, OneSignalUserTagField.ActiveWalletAddress) + } +} diff --git a/apps/mobile/src/features/send/SendFlow.tsx b/apps/mobile/src/features/send/SendFlow.tsx index f8e1d17ce62..dabbd43c62f 100644 --- a/apps/mobile/src/features/send/SendFlow.tsx +++ b/apps/mobile/src/features/send/SendFlow.tsx @@ -12,11 +12,13 @@ import { SendReviewScreen } from 'src/features/send/SendReviewScreen' import { useWalletRestore } from 'src/features/wallet/useWalletRestore' import { ModalName, SectionName } from 'uniswap/src/features/telemetry/constants' import Trace from 'uniswap/src/features/telemetry/Trace' +import { TransactionSettingsStoreContextProvider } from 'uniswap/src/features/transactions/components/settings/stores/transactionSettingsStore/TransactionSettingsStoreContextProvider' import { TransactionModal } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModal' import { TransactionScreen, useTransactionModalContext, } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModalContext' +import { SwapFormStoreContextProvider } from 'uniswap/src/features/transactions/swap/stores/swapFormStore/SwapFormStoreContextProvider' import { SendContextProvider, useSendContext } from 'wallet/src/features/transactions/contexts/SendContext' export function SendFlow(): JSX.Element { @@ -45,9 +47,13 @@ export function SendFlow(): JSX.Element { walletNeedsRestore={walletNeedsRestore} onClose={onClose} > - - - + + + + + + + ) } diff --git a/apps/mobile/src/features/send/SendFormButton.tsx b/apps/mobile/src/features/send/SendFormButton.tsx index 2b7019008c5..740f40d2e3d 100644 --- a/apps/mobile/src/features/send/SendFormButton.tsx +++ b/apps/mobile/src/features/send/SendFormButton.tsx @@ -2,14 +2,13 @@ import React, { Dispatch, SetStateAction, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { Button, Flex } from 'ui/src' -import { checkIsBridgedAsset } from 'uniswap/src/components/BridgedAsset/utils' import { WarningLabel } from 'uniswap/src/components/modals/WarningModal/types' import { nativeOnChain } from 'uniswap/src/constants/tokens' import { AccountType } from 'uniswap/src/features/accounts/types' import { selectHasDismissedLowNetworkTokenWarning } from 'uniswap/src/features/behaviorHistory/selectors' import { UniswapEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import { useDismissedCompatibleAddressWarnings } from 'uniswap/src/features/tokens/slice/hooks' +import { useDismissedCompatibleAddressWarnings } from 'uniswap/src/features/tokens/warnings/slice/hooks' import { useTransactionModalContext } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModalContext' import { useIsBlocked } from 'uniswap/src/features/trm/hooks' import { TestID } from 'uniswap/src/test/fixtures/testIDs' @@ -60,7 +59,7 @@ export function SendFormButton({ const { tokenWarningDismissed: isCompatibleAddressDismissed } = useDismissedCompatibleAddressWarnings( currencyInInfo?.currency, ) - const isUnichainBridgedAsset = checkIsBridgedAsset(currencyInInfo ?? undefined) && !isCompatibleAddressDismissed + const isUnichainBridgedAsset = Boolean(currencyInInfo?.isBridged) && !isCompatibleAddressDismissed const insufficientGasFunds = warnings.warnings.some((warning) => warning.type === WarningLabel.InsufficientGasFunds) diff --git a/apps/mobile/src/features/send/SendTokenForm.tsx b/apps/mobile/src/features/send/SendTokenForm.tsx index bc64573a93b..1fd1ae1690e 100644 --- a/apps/mobile/src/features/send/SendTokenForm.tsx +++ b/apps/mobile/src/features/send/SendTokenForm.tsx @@ -125,7 +125,7 @@ export function SendTokenForm(): JSX.Element { // Decimal pad logic const decimalPadRef = useRef(null) const maxDecimals = isFiatInput ? MAX_FIAT_INPUT_DECIMALS : (currencyIn?.decimals ?? 0) - const selectionRef = useRef() + const selectionRef = useRef(undefined) const onInputSelectionChange = useCallback( (start: number, end: number) => { @@ -295,7 +295,7 @@ export function SendTokenForm(): JSX.Element { style={StyleSheet.absoluteFill} > - + @@ -364,7 +364,11 @@ export function SendTokenForm(): JSX.Element { {!nftIn && ( <> - + { proposalNamespaces: mockNamespaces, chains: [UniverseChainId.Mainnet], dappRequestInfo, - verifyStatus: 'VERIFIED' as WalletConnectVerifyStatus, + verifyStatus: DappVerificationStatus.Verified, }, } @@ -140,25 +141,26 @@ describe('WalletConnect Saga', () => { .run() }) - it('falls back to dapp.url when verifyContext.verified.origin is not available', () => { - // Create a proposal without verified origin (null origin falls back to dapp.url) + it('dispatches addPendingSession with correct parameters for valid proposal', () => { + // Create a mock verification context for the verified status const mockVerifyContext: Verify.Context = { verified: { verifyUrl: 'https://verify.walletconnect.com', - validation: 'INVALID', - origin: null as unknown as string, // null origin should fallback to dapp.url + validation: 'VALID', + origin: 'https://valid-dapp.com', }, } + // Create a valid proposal with eip155 namespace const mockProposal = { - id: 890, + id: 456, proposer: { publicKey: 'test-public-key', metadata: { - name: 'Fallback Dapp', - description: 'Fallback Dapp Description', - url: 'https://fallback-dapp.com', // This should be used when verified origin is empty - icons: ['https://fallback-dapp.com/icon.png'], + name: 'Valid Dapp', + description: 'Valid Dapp Description', + url: 'https://valid-dapp.com', + icons: ['https://valid-dapp.com/icon.png'], }, }, relays: [], @@ -169,14 +171,14 @@ describe('WalletConnect Saga', () => { events: [], }, }, - pairingTopic: 'fallback-pairing-topic', + pairingTopic: 'valid-pairing-topic', expiryTimestamp: Date.now() + 1000 * 60 * 5, verifyContext: mockVerifyContext, } as unknown as ProposalTypes.Struct & { verifyContext?: Verify.Context } const activeAccountAddress = '0x1234567890abcdef' - // Mock namespaces + // Mock namespaces that would be returned by buildApprovedNamespaces const mockNamespaces = { eip155: { accounts: [`eip155:1:${activeAccountAddress}`], @@ -186,27 +188,29 @@ describe('WalletConnect Saga', () => { }, } + // Mock the buildApprovedNamespaces function to return our mock namespaces const buildApprovedNamespacesMock = buildApprovedNamespaces as jest.Mock buildApprovedNamespacesMock.mockReturnValue(mockNamespaces) - // Mock parseVerifyStatus to return INVALID for this test + // Mock parseVerifyStatus to return VERIFIED for this test const parseVerifyStatusMock = parseVerifyStatus as jest.Mock - parseVerifyStatusMock.mockReturnValue('INVALID') + parseVerifyStatusMock.mockReturnValue('VERIFIED') + // Create properly typed dappRequestInfo const dappRequestInfo: DappRequestInfo = { - name: 'Fallback Dapp', - url: 'https://fallback-dapp.com', // Should use dapp.url when verified origin is null - icon: 'https://fallback-dapp.com/icon.png', + name: 'Valid Dapp', + url: 'https://valid-dapp.com', + icon: 'https://valid-dapp.com/icon.png', requestType: DappRequestType.WalletConnectSessionRequest, } const expectedPendingSession = { wcSession: { - id: '890', + id: '456', proposalNamespaces: mockNamespaces, chains: [UniverseChainId.Mainnet], dappRequestInfo, - verifyStatus: 'INVALID' as WalletConnectVerifyStatus, + verifyStatus: DappVerificationStatus.Verified, }, } @@ -216,6 +220,7 @@ describe('WalletConnect Saga', () => { if (selector === selectActiveAccountAddress) { return activeAccountAddress } + // For any other selectors that might access wallet state return next() }, }) @@ -231,26 +236,17 @@ describe('WalletConnect Saga', () => { .run() }) - it('dispatches addPendingSession with correct parameters for valid proposal', () => { - // Create a mock verification context for the verified status - const mockVerifyContext: Verify.Context = { - verified: { - verifyUrl: 'https://verify.walletconnect.com', - validation: 'VALID', - origin: 'https://valid-dapp.com', - }, - } - - // Create a valid proposal with eip155 namespace + it('falls back to dapp.url when verifyContext.verified.origin is not available', () => { + // Create a proposal WITHOUT verifyContext.verified.origin const mockProposal = { - id: 456, + id: 999, proposer: { publicKey: 'test-public-key', metadata: { - name: 'Valid Dapp', - description: 'Valid Dapp Description', - url: 'https://valid-dapp.com', - icons: ['https://valid-dapp.com/icon.png'], + name: 'Fallback Dapp', + description: 'Fallback Dapp Description', + url: 'https://fallback-dapp.com', + icons: ['https://fallback-dapp.com/icon.png'], }, }, relays: [], @@ -261,9 +257,9 @@ describe('WalletConnect Saga', () => { events: [], }, }, - pairingTopic: 'valid-pairing-topic', + pairingTopic: 'fallback-pairing-topic', expiryTimestamp: Date.now() + 1000 * 60 * 5, - verifyContext: mockVerifyContext, + // No verifyContext provided } as unknown as ProposalTypes.Struct & { verifyContext?: Verify.Context } const activeAccountAddress = '0x1234567890abcdef' @@ -282,25 +278,25 @@ describe('WalletConnect Saga', () => { const buildApprovedNamespacesMock = buildApprovedNamespaces as jest.Mock buildApprovedNamespacesMock.mockReturnValue(mockNamespaces) - // Mock parseVerifyStatus to return VERIFIED for this test + // Mock parseVerifyStatus to return UNVERIFIED when no verifyContext const parseVerifyStatusMock = parseVerifyStatus as jest.Mock - parseVerifyStatusMock.mockReturnValue('VERIFIED') + parseVerifyStatusMock.mockReturnValue('UNVERIFIED') - // Create properly typed dappRequestInfo + // Create properly typed dappRequestInfo - should use dapp.url as fallback const dappRequestInfo: DappRequestInfo = { - name: 'Valid Dapp', - url: 'https://valid-dapp.com', - icon: 'https://valid-dapp.com/icon.png', + name: 'Fallback Dapp', + url: 'https://fallback-dapp.com', // Should fallback to dapp.url + icon: 'https://fallback-dapp.com/icon.png', requestType: DappRequestType.WalletConnectSessionRequest, } const expectedPendingSession = { wcSession: { - id: '456', + id: '999', proposalNamespaces: mockNamespaces, chains: [UniverseChainId.Mainnet], dappRequestInfo, - verifyStatus: 'VERIFIED' as WalletConnectVerifyStatus, + verifyStatus: DappVerificationStatus.Unverified, }, } @@ -310,7 +306,6 @@ describe('WalletConnect Saga', () => { if (selector === selectActiveAccountAddress) { return activeAccountAddress } - // For any other selectors that might access wallet state return next() }, }) diff --git a/apps/mobile/src/features/walletConnect/saga.ts b/apps/mobile/src/features/walletConnect/saga.ts index ed6d1af97bb..b52b1b8d603 100644 --- a/apps/mobile/src/features/walletConnect/saga.ts +++ b/apps/mobile/src/features/walletConnect/saga.ts @@ -1,6 +1,7 @@ /* eslint-disable max-lines */ import { AnyAction } from '@reduxjs/toolkit' import { WalletKitTypes } from '@reown/walletkit' +import { FeatureFlags, getFeatureFlag } from '@universe/gating' import { PendingRequestTypes, ProposalTypes, SessionTypes, Verify } from '@walletconnect/types' import { buildApprovedNamespaces, getSdkError, populateAuthPayload } from '@walletconnect/utils' import { Alert } from 'react-native' @@ -39,8 +40,6 @@ import { UniverseChainId } from 'uniswap/src/features/chains/types' import { getChainLabel } from 'uniswap/src/features/chains/utils' import { EthMethod } from 'uniswap/src/features/dappRequests/types' import { isSelfCallWithData } from 'uniswap/src/features/dappRequests/utils' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { getFeatureFlag } from 'uniswap/src/features/gating/hooks' import { pushNotification } from 'uniswap/src/features/notifications/slice/slice' import { AppNotificationType } from 'uniswap/src/features/notifications/slice/types' import { Platform } from 'uniswap/src/features/platforms/types/Platform' diff --git a/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts b/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts index f52cf35c930..1efd04ca0f0 100644 --- a/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts +++ b/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts @@ -137,7 +137,15 @@ function* signWcRequest(params: SignMessageParams | SignTransactionParams) { } const { transactionHash } = yield* call(executeTransaction, txParams) - result = { id: params.request.id, capabilities: {} } + result = { + id: params.request.id, + capabilities: { + caip345: { + caip2: `eip155:${params.request.chainId}`, + transactionHashes: [transactionHash], + }, + }, + } // Store the batch transaction in Redux yield* put( diff --git a/apps/mobile/src/features/walletConnect/utils.test.ts b/apps/mobile/src/features/walletConnect/utils.test.ts index 275dba4fe5e..7969c56e888 100644 --- a/apps/mobile/src/features/walletConnect/utils.test.ts +++ b/apps/mobile/src/features/walletConnect/utils.test.ts @@ -1,5 +1,6 @@ import { utils } from 'ethers' import { + convertCapabilitiesToScopedProperties, decodeMessage, getAccountAddressFromEIP155String, getChainIdFromEIP155String, @@ -499,3 +500,87 @@ describe(parseGetCallsStatusRequest, () => { }) }) }) + +describe(convertCapabilitiesToScopedProperties, () => { + it('converts single chain capability to CAIP-2 format', () => { + const capabilities = { + '0xa': { + atomic: { status: 'supported' }, + }, + } + + const result = convertCapabilitiesToScopedProperties(capabilities) + + expect(result).toEqual({ + 'eip155:10': { + atomic: { status: 'supported' }, + }, + }) + }) + + it('converts multiple chain capabilities to CAIP-2 format', () => { + const capabilities = { + '0x1': { + atomic: { status: 'supported' }, + }, + '0x89': { + atomic: { status: 'unsupported' }, + }, + '0xa': { + atomic: { status: 'supported' }, + paymasterService: { supported: true }, + }, + } + + const result = convertCapabilitiesToScopedProperties(capabilities) + + expect(result).toEqual({ + 'eip155:1': { + atomic: { status: 'supported' }, + }, + 'eip155:137': { + atomic: { status: 'unsupported' }, + }, + 'eip155:10': { + atomic: { status: 'supported' }, + paymasterService: { supported: true }, + }, + }) + }) + + it('returns empty object when given empty capabilities', () => { + const capabilities = {} + + const result = convertCapabilitiesToScopedProperties(capabilities) + + expect(result).toEqual({}) + }) + + it('handles invalid hex chain IDs that cause errors', () => { + const capabilities = { + '0x1': { + atomic: { status: 'supported' }, + }, + 'invalid-hex': { + atomic: { status: 'unsupported' }, + }, + '0x89': { + atomic: { status: 'supported' }, + }, + } + + const result = convertCapabilitiesToScopedProperties(capabilities) + + // hexToNumber returns NaN for invalid hex, which should be excluded + // The function continues execution despite the invalid chain ID + expect(result).toHaveProperty('eip155:1') + expect(result).toHaveProperty('eip155:137') + expect(result).not.toHaveProperty('eip155:NaN') + expect(result['eip155:1']).toEqual({ + atomic: { status: 'supported' }, + }) + expect(result['eip155:137']).toEqual({ + atomic: { status: 'supported' }, + }) + }) +}) diff --git a/apps/mobile/src/features/walletConnect/utils.ts b/apps/mobile/src/features/walletConnect/utils.ts index 5a5cab9f75e..685871502dc 100644 --- a/apps/mobile/src/features/walletConnect/utils.ts +++ b/apps/mobile/src/features/walletConnect/utils.ts @@ -5,7 +5,6 @@ import { wcWeb3Wallet } from 'src/features/walletConnect/walletConnectClient' import { SignRequest, TransactionRequest, - WalletConnectVerifyStatus, WalletGetCallsStatusRequest, WalletGetCapabilitiesRequest, WalletSendCallsRequest, @@ -15,8 +14,15 @@ import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { EthMethod, EthSignMethod, WalletConnectEthMethod } from 'uniswap/src/features/dappRequests/types' import { DappRequestInfo, DappRequestType } from 'uniswap/src/types/walletConnect' import { hexToNumber } from 'utilities/src/addresses/hex' +import { logger } from 'utilities/src/logger/logger' import { generateBatchId } from 'wallet/src/features/batchedTransactions/utils' -import { GetCallsStatusParams, SendCallsParams } from 'wallet/src/features/dappRequests/types' +import { + Capability, + DappVerificationStatus, + GetCallsStatusParams, + SendCallsParams, +} from 'wallet/src/features/dappRequests/types' + /** * Construct WalletConnect 2.0 session namespaces to complete a new pairing. Used when approving a new pairing request. * Assumes each namespace has been validated and is supported by the app with `validateProposalNamespaces()`. @@ -352,23 +358,65 @@ export async function pairWithWalletConnectURI(uri: string): Promise, +): Record> { + const scopedProperties: Record> = {} + + for (const [hexChainId, capability] of Object.entries(capabilities)) { + try { + const chainId = hexToNumber(hexChainId) + if (isNaN(chainId)) { + continue + } + const caip2Key = `eip155:${chainId}` + scopedProperties[caip2Key] = capability + } catch (error) { + logger.error(error, { + tags: { function: 'convertCapabilitiesToScopedProperties', file: 'walletConnect/utils.ts' }, + }) + continue + } + } + + return scopedProperties } diff --git a/apps/mobile/src/features/walletConnect/walletConnectSlice.ts b/apps/mobile/src/features/walletConnect/walletConnectSlice.ts index 7184969625c..6acc756db31 100644 --- a/apps/mobile/src/features/walletConnect/walletConnectSlice.ts +++ b/apps/mobile/src/features/walletConnect/walletConnectSlice.ts @@ -4,20 +4,14 @@ import { UniverseChainId } from 'uniswap/src/features/chains/types' import { EthMethod, EthSignMethod } from 'uniswap/src/features/dappRequests/types' import { DappRequestInfo, EthTransaction, UwULinkMethod } from 'uniswap/src/types/walletConnect' import { logger } from 'utilities/src/logger/logger' -import { Call, Capability } from 'wallet/src/features/dappRequests/types' - -export enum WalletConnectVerifyStatus { - Verified = 'VERIFIED', - Unverified = 'UNVERIFIED', - Threat = 'THREAT', -} +import { Call, Capability, DappVerificationStatus } from 'wallet/src/features/dappRequests/types' export type WalletConnectPendingSession = { id: string chains: UniverseChainId[] dappRequestInfo: DappRequestInfo proposalNamespaces: ProposalTypes.OptionalNamespaces - verifyStatus: WalletConnectVerifyStatus + verifyStatus: DappVerificationStatus } export type WalletConnectSession = { @@ -31,6 +25,13 @@ export type WalletConnectSession = { * is tracking as the active account based on session events (approve session, change account, etc). */ activeAccount: string + + /** + * EIP-5792 capabilities for this session, stored in hex chainId format. + * Contains atomic batch support status per chain. + * Only populated if EIP-5792 feature flag was enabled during session approval. + */ + capabilities?: Record } interface BaseRequest { @@ -99,9 +100,16 @@ export type WalletConnectSigningRequest = | UwuLinkErc20Request | WalletSendCallsEncodedRequest +type PersonalSignRequest = SignRequest & { + type: EthMethod.PersonalSign | EthMethod.EthSign +} + export const isTransactionRequest = (request: WalletConnectSigningRequest): request is TransactionRequest => request.type === EthMethod.EthSendTransaction || request.type === UwULinkMethod.Erc20Send +export const isPersonalSignRequest = (request: WalletConnectSigningRequest): request is PersonalSignRequest => + request.type === EthMethod.PersonalSign || request.type === EthMethod.EthSign + export const isBatchedTransactionRequest = ( request: WalletConnectSigningRequest, ): request is WalletSendCallsEncodedRequest => request.type === EthMethod.WalletSendCalls diff --git a/apps/mobile/src/screens/ActivityScreen.tsx b/apps/mobile/src/screens/ActivityScreen.tsx index d3005fa1c06..68e8a23175a 100644 --- a/apps/mobile/src/screens/ActivityScreen.tsx +++ b/apps/mobile/src/screens/ActivityScreen.tsx @@ -1,8 +1,6 @@ -import { useApolloClient } from '@apollo/client' import { useScrollToTop } from '@react-navigation/native' -import { useQuery } from '@tanstack/react-query' -import { GQLQueries } from '@universe/api' -import { useCallback, useEffect, useMemo, useRef } from 'react' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' +import { useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch } from 'react-redux' import { ESTIMATED_BOTTOM_TABS_HEIGHT } from 'src/app/navigation/tabs/CustomTabBar/constants' @@ -10,35 +8,12 @@ import { ActivityContent } from 'src/components/activity/ActivityContent' import { Screen } from 'src/components/layout/Screen' import { Text } from 'ui/src' import { spacing } from 'ui/src/theme' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' +import { AccountType } from 'uniswap/src/features/accounts/types' import { useSelectAddressHasNotifications } from 'uniswap/src/features/notifications/slice/hooks' import { setNotificationStatus } from 'uniswap/src/features/notifications/slice/slice' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' -import { ReactQueryCacheKey } from 'utilities/src/reactQuery/cache' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' -const useRefreshActivityData = (owner: Address): { refreshing: boolean; onRefreshActivityData: () => void } => { - const apolloClient = useApolloClient() - - const refreshFn = useCallback( - () => - apolloClient.refetchQueries({ - include: [GQLQueries.TransactionList], - }), - [apolloClient], - ) - - const { refetch, isRefetching } = useQuery({ - queryKey: [ReactQueryCacheKey.ActivityScreenRefresh, owner], - enabled: false, - retry: 0, - queryFn: refreshFn, - }) - - return { refreshing: isRefetching, onRefreshActivityData: refetch } -} - export function ActivityScreen(): JSX.Element { const { t } = useTranslation() const activeAccount = useActiveAccountWithThrow() @@ -47,8 +22,6 @@ export function ActivityScreen(): JSX.Element { useScrollToTop(scrollRef) - const { refreshing, onRefreshActivityData } = useRefreshActivityData(activeAccount.address) - const insets = useAppInsets() const isBottomTabsEnabled = useFeatureFlag(FeatureFlags.BottomTabs) @@ -77,10 +50,9 @@ export function ActivityScreen(): JSX.Element { ) diff --git a/apps/mobile/src/screens/AppLoadingScreen.tsx b/apps/mobile/src/screens/AppLoadingScreen.tsx index 27192763892..10f97edf4aa 100644 --- a/apps/mobile/src/screens/AppLoadingScreen.tsx +++ b/apps/mobile/src/screens/AppLoadingScreen.tsx @@ -1,4 +1,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' +import { DynamicConfigs, OnDeviceRecoveryConfigKey, useDynamicConfigValue } from '@universe/gating' import dayjs from 'dayjs' import { isEnrolledAsync } from 'expo-local-authentication' import { useCallback, useEffect, useState } from 'react' @@ -15,8 +16,6 @@ import { import { useHideSplashScreen } from 'src/features/splashScreen/useHideSplashScreen' import { RecoveryWalletInfo, useOnDeviceRecoveryData } from 'src/screens/Import/useOnDeviceRecoveryData' import { AccountType } from 'uniswap/src/features/accounts/types' -import { DynamicConfigs, OnDeviceRecoveryConfigKey } from 'uniswap/src/features/gating/configs' -import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import Trace from 'uniswap/src/features/telemetry/Trace' diff --git a/apps/mobile/src/screens/DevScreen.tsx b/apps/mobile/src/screens/DevScreen.tsx index 78f6bd38e9f..020b1a6d33c 100644 --- a/apps/mobile/src/screens/DevScreen.tsx +++ b/apps/mobile/src/screens/DevScreen.tsx @@ -14,7 +14,7 @@ import { resetDismissedBridgedAssetWarnings, resetDismissedCompatibleAddressWarnings, resetDismissedWarnings, -} from 'uniswap/src/features/tokens/slice/slice' +} from 'uniswap/src/features/tokens/warnings/slice/slice' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { setClipboard } from 'uniswap/src/utils/clipboard' diff --git a/apps/mobile/src/screens/ExploreScreen.tsx b/apps/mobile/src/screens/ExploreScreen.tsx index ff0cbf76986..f3f16e5283f 100644 --- a/apps/mobile/src/screens/ExploreScreen.tsx +++ b/apps/mobile/src/screens/ExploreScreen.tsx @@ -1,5 +1,7 @@ -import { useIsFocused, useNavigation, useScrollToTop } from '@react-navigation/native' +import type { RouteProp } from '@react-navigation/native' +import { useIsFocused, useNavigation, useRoute, useScrollToTop } from '@react-navigation/native' import { SharedEventName } from '@uniswap/analytics-events' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { type TextInput } from 'react-native' @@ -7,6 +9,7 @@ import type { FlatList } from 'react-native-gesture-handler' import { useAnimatedRef } from 'react-native-reanimated' import type { Edge } from 'react-native-safe-area-context' import { useDispatch } from 'react-redux' +import type { ExploreStackParamList } from 'src/app/navigation/types' import { ExploreSections } from 'src/components/explore/ExploreSections/ExploreSections' import { ExploreScreenSearchResultsList } from 'src/components/explore/search/ExploreScreenSearchResultsList' import { Screen } from 'src/components/layout/Screen' @@ -16,8 +19,6 @@ import { HandleBar } from 'uniswap/src/components/modals/HandleBar' import { NetworkFilter, type NetworkFilterProps } from 'uniswap/src/components/network/NetworkFilter' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' import type { UniverseChainId } from 'uniswap/src/features/chains/types' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { useFilterCallbacks } from 'uniswap/src/features/search/SearchModal/hooks/useFilterCallbacks' import { CancelBehaviorType, SearchTextInput } from 'uniswap/src/features/search/SearchTextInput' import { MobileEventName, ModalName, SectionName } from 'uniswap/src/features/telemetry/constants' @@ -39,6 +40,9 @@ export function ExploreScreen(): JSX.Element { const { chains } = useEnabledChains() const isBottomTabsEnabled = useFeatureFlag(FeatureFlags.BottomTabs) const navigation = useNavigation() + const route = useRoute>() + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- route.params can be null + const { chainId, orderByMetric, showFavorites } = route.params ?? {} const { isSheetReady } = useBottomSheetContext({ forceSafeReturn: isBottomTabsEnabled }) @@ -54,6 +58,8 @@ export function ExploreScreen(): JSX.Element { // Use refs to avoid stale closures in the event listener const isAtTopRef = useRef(isAtTop) const isFocusedRef = useRef(isFocused) + // Track the previous route name to detect true double-tap behavior + const prevRouteNameRef = useRef(null) isAtTopRef.current = isAtTop isFocusedRef.current = isFocused @@ -77,18 +83,25 @@ export function ExploreScreen(): JSX.Element { const unsubscribe = navigation.addListener('state', (e) => { const currentRouteName = e.data.state.routeNames[e.data.state.index] as unknown as string | undefined - - // Check if we're navigating to the Explore screen const isOnExploreScreen = currentRouteName === MobileScreens.Explore - // Only handle this if: - // 1. We were already focused before the state change (i.e., tab was pressed while already on this screen) - // 2. The current route is the Explore screen - // 3. The screen is currently focused - if (!isOnExploreScreen || !isFocusedRef.current) { + // Double-tap detection: Only trigger focus when user taps Explore tab while already on Explore + // This distinguishes between: + // - Initial navigation to Explore (prevRoute !== Explore) → No auto-focus + // - Tab double-tap (prevRoute === Explore && currentRoute === Explore) → Focus search + const isDoubleTap = prevRouteNameRef.current === MobileScreens.Explore && isOnExploreScreen + + // Update the previous route for next navigation event + prevRouteNameRef.current = currentRouteName ?? null + + // Only handle double-tap behavior when: + // 1. This is a true double-tap (was on Explore, tapped Explore again) + // 2. The screen is currently focused + if (!isDoubleTap || !isFocusedRef.current) { return } + // Double-tap behavior: Focus search if at top, scroll to top otherwise if (isAtTopRef.current) { textInputRef.current?.focus() } else { @@ -98,13 +111,13 @@ export function ExploreScreen(): JSX.Element { }) return unsubscribe - }, [isBottomTabsEnabled, navigation, listRef]) + }, [isBottomTabsEnabled, navigation]) // TODO(WALL-5482): investigate list rendering performance/scrolling issue const canRenderList = useRenderNextFrame(isSheetReady && !isSearchMode) const { onChangeChainFilter, onChangeText, searchFilter, chainFilter, parsedChainFilter, parsedSearchFilter } = - useFilterCallbacks(null, ModalName.Search) + useFilterCallbacks(chainId ?? null, ModalName.Search) const onSearchChangeText = useEvent((newSearchFilter: string): void => { onChangeText(newSearchFilter) @@ -145,6 +158,7 @@ export function ExploreScreen(): JSX.Element { ) : isSheetReady && canRenderList ? ( - + ) : null} ) @@ -188,7 +208,7 @@ export function ExploreScreen(): JSX.Element { */ const useRenderNextFrame = (condition: boolean): boolean => { const [canRender, setCanRender] = useState(false) - const rafRef = useRef() + const rafRef = useRef(undefined) const mountedRef = useRef(true) const conditionRef = useRef(condition) diff --git a/apps/mobile/src/screens/FiatOnRampScreen.tsx b/apps/mobile/src/screens/FiatOnRampScreen.tsx index 7a69327b7f9..3efcf35a2d1 100644 --- a/apps/mobile/src/screens/FiatOnRampScreen.tsx +++ b/apps/mobile/src/screens/FiatOnRampScreen.tsx @@ -121,7 +121,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { const [selectingCountry, setSelectingCountry] = useState(false) const [decimalPadReady, setDecimalPadReady] = useState(false) const decimalPadRef = useRef(null) - const selectionRef = useRef() + const selectionRef = useRef(undefined) const amountUpdatedTimeRef = useRef(0) const [value, setValue] = useState('') const valueRef = useRef('') @@ -566,6 +566,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { id={DecimalPadCalculatedSpaceId.FiatOnRamp} decimalPadRef={decimalPadRef} additionalElementsHeight={DECIMAL_PAD_EXTRA_ELEMENTS_HEIGHT} + isDecimalPadReady={decimalPadReady} /> ): JSX.Element const activeAccount = useActiveAccountWithThrow() const { t } = useTranslation() const colors = useSporeColors() + const darkColors = useSporeColors('dark') const media = useMedia() const insets = useAppInsets() const dimensions = useDeviceDimensions() @@ -112,6 +118,10 @@ function HomeScreen(props?: AppStackScreenProp): JSX.Element const { requiredForTransactions: requiresBiometrics } = useBiometricAppSettings() const isBottomTabsEnabled = useFeatureFlag(FeatureFlags.BottomTabs) + const isWrappedBannerEnabled = useFeatureFlag(FeatureFlags.UniswapWrapped2025) + + const hasDismissedWrappedBanner = useSelector(selectHasDismissedUniswapWrapped2025Banner) + const shouldShowWrappedBanner = isWrappedBannerEnabled && !hasDismissedWrappedBanner const { showEmptyWalletState, isTabsDataLoaded } = useHomeScreenState() @@ -145,7 +155,9 @@ function HomeScreen(props?: AppStackScreenProp): JSX.Element const tabs: Array = [ { key: SectionName.HomeTokensTab, title: tokensTitle }, { key: SectionName.HomeNFTsTab, title: nftsTitle }, - ...(!isBottomTabsEnabled ? [{ key: SectionName.HomeActivityTab, title: activityTitle }] : []), + ...(!isBottomTabsEnabled + ? [{ key: SectionName.HomeActivityTab, title: activityTitle, enableNotificationBadge: true }] + : []), ] return tabs @@ -285,6 +297,20 @@ function HomeScreen(props?: AppStackScreenProp): JSX.Element const viewOnlyLabel = t('home.warning.viewOnly') + const handleDismissWrappedBanner = useCallback(() => { + dispatch(setHasDismissedUniswapWrapped2025Banner(true)) + }, [dispatch]) + + const handlePressWrappedBanner = useCallback(async () => { + try { + const url = buildWrappedUrl(UNISWAP_WEB_URL, activeAccount.address) + await openUri({ uri: url, openExternalBrowser: true }) + dispatch(setHasDismissedUniswapWrapped2025Banner(true)) + } catch (error) { + logger.error(error, { tags: { file: 'HomeScreen', function: 'handlePressWrappedBanner' } }) + } + }, [activeAccount.address, dispatch]) + const promoBanner = useMemo( () => , [showEmptyWalletState, isTabsDataLoaded], @@ -297,9 +323,25 @@ function HomeScreen(props?: AppStackScreenProp): JSX.Element pb={showEmptyWalletState ? '$spacing8' : '$spacing16'} px={isBottomTabsEnabled ? '$none' : '$spacing12'} > + {shouldShowWrappedBanner && ( + + + + + )} - + {isSignerAccount ? ( @@ -318,6 +360,9 @@ function HomeScreen(props?: AppStackScreenProp): JSX.Element }, [ showEmptyWalletState, isBottomTabsEnabled, + shouldShowWrappedBanner, + handleDismissWrappedBanner, + handlePressWrappedBanner, activeAccount.address, isSignerAccount, onPressViewOnlyLabel, @@ -394,11 +439,9 @@ function HomeScreen(props?: AppStackScreenProp): JSX.Element ) const statusBarStyle = useAnimatedStyle(() => ({ - backgroundColor: interpolateColor( - currentScrollValue.value, - [0, headerHeightDiff], - [colors.surface1.val, colors.surface1.val], - ), + backgroundColor: shouldShowWrappedBanner + ? darkColors.surface1.val + : interpolateColor(currentScrollValue.value, [0, headerHeightDiff], [colors.surface1.val, colors.surface1.val]), })) const apolloClient = useApolloClient() diff --git a/apps/mobile/src/screens/HomeScreen/HomeScreenQuickActions.tsx b/apps/mobile/src/screens/HomeScreen/HomeScreenQuickActions.tsx index 30cf4f84917..44b5ac900e0 100644 --- a/apps/mobile/src/screens/HomeScreen/HomeScreenQuickActions.tsx +++ b/apps/mobile/src/screens/HomeScreen/HomeScreenQuickActions.tsx @@ -1,3 +1,4 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FlatList, ListRenderItemInfo } from 'react-native' @@ -10,8 +11,6 @@ import { ArrowDownCircle, Bank, SendAction, SwapDotted } from 'ui/src/components import { iconSizes, spacing } from 'ui/src/theme' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' import { useHighestBalanceNativeCurrencyId } from 'uniswap/src/features/dataApi/balances/balances' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { useHapticFeedback } from 'uniswap/src/features/settings/useHapticFeedback/useHapticFeedback' import { ElementName, MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { Trace } from 'uniswap/src/features/telemetry/Trace' diff --git a/apps/mobile/src/screens/Import/ImportMethodScreen.tsx b/apps/mobile/src/screens/Import/ImportMethodScreen.tsx index 572ca36bbcc..fe00d212b5d 100644 --- a/apps/mobile/src/screens/Import/ImportMethodScreen.tsx +++ b/apps/mobile/src/screens/Import/ImportMethodScreen.tsx @@ -1,4 +1,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' import { navigate } from 'src/app/navigation/rootNavigation' @@ -17,8 +18,6 @@ import { Flex, SpinningLoader, Text, TouchableArea } from 'ui/src' import { Eye, WalletFilled } from 'ui/src/components/icons' import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode' import { iconSizes } from 'ui/src/theme' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { authenticateWithPasskeyForSeedPhraseExport } from 'uniswap/src/features/passkey/embeddedWallet' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import Trace from 'uniswap/src/features/telemetry/Trace' diff --git a/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx b/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx index 95c14285751..0172fcfffc9 100644 --- a/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx +++ b/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx @@ -1,6 +1,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' import { ReactNavigationPerformanceView } from '@shopify/react-native-performance-navigation' import { SharedEventName } from '@uniswap/analytics-events' +import { DynamicConfigs, OnDeviceRecoveryConfigKey, useDynamicConfigValue } from '@universe/gating' import dayjs from 'dayjs' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -20,8 +21,6 @@ import { iconSizes } from 'ui/src/theme' import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types' import { WarningModal } from 'uniswap/src/components/modals/WarningModal/WarningModal' import { AccountType } from 'uniswap/src/features/accounts/types' -import { DynamicConfigs, OnDeviceRecoveryConfigKey } from 'uniswap/src/features/gating/configs' -import { useDynamicConfigValue } from 'uniswap/src/features/gating/hooks' import { Platform } from 'uniswap/src/features/platforms/types/Platform' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' diff --git a/apps/mobile/src/screens/Import/RestoreMethodScreen.tsx b/apps/mobile/src/screens/Import/RestoreMethodScreen.tsx index 994f0ce214b..d4b12c53b99 100644 --- a/apps/mobile/src/screens/Import/RestoreMethodScreen.tsx +++ b/apps/mobile/src/screens/Import/RestoreMethodScreen.tsx @@ -1,4 +1,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React from 'react' import { useTranslation } from 'react-i18next' import { OnboardingStackParamList } from 'src/app/navigation/types' @@ -13,8 +14,6 @@ import { useNavigationHeader } from 'src/utils/useNavigationHeader' import { Flex, Text, TouchableArea } from 'ui/src' import { WalletFilled } from 'ui/src/components/icons' import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import Trace from 'uniswap/src/features/telemetry/Trace' import { TestID } from 'uniswap/src/test/fixtures/testIDs' diff --git a/apps/mobile/src/screens/Import/SelectWalletScreen.tsx b/apps/mobile/src/screens/Import/SelectWalletScreen.tsx index 8e0f065ad69..666a9ede9f1 100644 --- a/apps/mobile/src/screens/Import/SelectWalletScreen.tsx +++ b/apps/mobile/src/screens/Import/SelectWalletScreen.tsx @@ -1,4 +1,5 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { ComponentProps, useCallback } from 'react' import { Trans, useTranslation } from 'react-i18next' import { ScrollView } from 'react-native' @@ -10,8 +11,6 @@ import { Button, Flex, Loader, Text, TouchableArea, useLayoutAnimationOnChange } import { WalletFilled } from 'ui/src/components/icons' import { spacing } from 'ui/src/theme' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import Trace from 'uniswap/src/features/telemetry/Trace' import { TestID } from 'uniswap/src/test/fixtures/testIDs' @@ -109,9 +108,8 @@ export function SelectWalletScreen({ navigation, route: { params } }: Props): JS return null } return ( - + diff --git a/apps/mobile/src/screens/Import/__snapshots__/RestoreCloudBackupPasswordScreen.test.tsx.snap b/apps/mobile/src/screens/Import/__snapshots__/RestoreCloudBackupPasswordScreen.test.tsx.snap index 2ba8b7f5615..e5a5af4bcc8 100644 --- a/apps/mobile/src/screens/Import/__snapshots__/RestoreCloudBackupPasswordScreen.test.tsx.snap +++ b/apps/mobile/src/screens/Import/__snapshots__/RestoreCloudBackupPasswordScreen.test.tsx.snap @@ -4,12 +4,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` { // disables looping animation during e2e tests which was preventing js thread from idle actionButtonsOpacity.value = withDelay(LANDING_ANIMATION_DURATION, withTiming(1, { duration: ONE_SECOND_MS })) - }, [actionButtonsOpacity]) + }, []) // Disables testnet mode on mount if enabled (eg upon removing a wallet) useEffect(() => { diff --git a/apps/mobile/src/screens/Onboarding/ManualBackupScreen.tsx b/apps/mobile/src/screens/Onboarding/ManualBackupScreen.tsx index 8e6235aca99..88227707d53 100644 --- a/apps/mobile/src/screens/Onboarding/ManualBackupScreen.tsx +++ b/apps/mobile/src/screens/Onboarding/ManualBackupScreen.tsx @@ -1,7 +1,8 @@ +import { useFocusEffect } from '@react-navigation/core' import { NativeStackScreenProps } from '@react-navigation/native-stack' import { SharedEventName } from '@uniswap/analytics-events' import { addScreenshotListener } from 'expo-screen-capture' -import React, { useEffect, useReducer, useState } from 'react' +import React, { useCallback, useEffect, useReducer, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch } from 'react-redux' import { navigate } from 'src/app/navigation/rootNavigation' @@ -86,16 +87,18 @@ export function ManualBackupScreen({ navigation, route: { params } }: Props): JS navigate(MobileScreens.Home) } - useEffect(() => { - if (view !== View.SeedPhrase) { - return undefined - } + useFocusEffect( + useCallback(() => { + if (view !== View.SeedPhrase) { + return undefined + } - const listener = addScreenshotListener(() => - navigate(ModalName.ScreenshotWarning, { acknowledgeText: t('common.button.ok') }), - ) - return () => listener.remove() - }, [view, t]) + const listener = addScreenshotListener(() => { + navigate(ModalName.ScreenshotWarning, { acknowledgeText: t('common.button.ok') }) + }) + return () => listener.remove() + }, [view, t]), + ) useEffect(() => { if (confirmContinueButtonPressed && hasBackup(BackupType.Manual, account)) { diff --git a/apps/mobile/src/screens/Onboarding/TermsOfService.tsx b/apps/mobile/src/screens/Onboarding/TermsOfService.tsx index cf8623168c3..8913c9cb725 100644 --- a/apps/mobile/src/screens/Onboarding/TermsOfService.tsx +++ b/apps/mobile/src/screens/Onboarding/TermsOfService.tsx @@ -10,6 +10,7 @@ export function TermsOfService(): JSX.Element { components={{ highlightTerms: ( => openUri({ uri: uniswapUrls.termsOfServiceUrl })} @@ -17,6 +18,7 @@ export function TermsOfService(): JSX.Element { ), highlightPrivacy: ( => openUri({ uri: uniswapUrls.privacyPolicyUrl })} diff --git a/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap b/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap index 167f5fdd4ad..126056f6548 100644 --- a/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap +++ b/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap @@ -4,12 +4,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = ` diff --git a/apps/mobile/src/screens/SettingsScreen.tsx b/apps/mobile/src/screens/SettingsScreen.tsx index 87db6bed817..6fa0cf28f23 100644 --- a/apps/mobile/src/screens/SettingsScreen.tsx +++ b/apps/mobile/src/screens/SettingsScreen.tsx @@ -1,4 +1,5 @@ import { useNavigation } from '@react-navigation/core' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { default as React, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { ListRenderItemInfo } from 'react-native' @@ -56,8 +57,6 @@ import { iconSizes } from 'ui/src/theme' import { uniswapUrls } from 'uniswap/src/constants/urls' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' import { useAppFiatCurrencyInfo } from 'uniswap/src/features/fiatCurrency/hooks' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { useCurrentLanguageInfo } from 'uniswap/src/features/language/hooks' import { setIsTestnetModeEnabled } from 'uniswap/src/features/settings/slice' import { useHapticFeedback } from 'uniswap/src/features/settings/useHapticFeedback/useHapticFeedback' @@ -227,7 +226,7 @@ export function SettingsScreen(): JSX.Element { }, { navigationModal: ModalName.PortfolioBalanceModal, - text: t('settings.setting.smallBalances.title'), + text: t('settings.setting.balancesActivity.title'), icon: , }, { diff --git a/apps/mobile/src/screens/TokenDetailsHeaders.tsx b/apps/mobile/src/screens/TokenDetailsHeaders.tsx index ba0b0744664..66cadffd71e 100644 --- a/apps/mobile/src/screens/TokenDetailsHeaders.tsx +++ b/apps/mobile/src/screens/TokenDetailsHeaders.tsx @@ -1,6 +1,8 @@ import React, { memo } from 'react' import { useTranslation } from 'react-i18next' import { FadeIn } from 'react-native-reanimated' +import { MODAL_OPEN_WAIT_TIME } from 'src/app/navigation/constants' +import { navigate } from 'src/app/navigation/rootNavigation' import { useTokenDetailsContext } from 'src/components/TokenDetails/TokenDetailsContext' import { TokenDetailsFavoriteButton } from 'src/components/TokenDetails/TokenDetailsFavoriteButton' import { useTokenDetailsCurrentChainBalance } from 'src/components/TokenDetails/useTokenDetailsCurrentChainBalance' @@ -21,7 +23,9 @@ import { TokenMenuActionType, useTokenContextMenuOptions, } from 'uniswap/src/features/portfolio/balances/hooks/useTokenContextMenuOptions' +import { ModalName } from 'uniswap/src/features/telemetry/constants' import { TestID } from 'uniswap/src/test/fixtures/testIDs' +import { useEvent } from 'utilities/src/react/hooks' import { useBooleanState } from 'utilities/src/react/useBooleanState' export const HeaderTitleElement = memo(function HeaderTitleElement(): JSX.Element { @@ -62,6 +66,22 @@ export const HeaderRightElement = memo(function HeaderRightElement(): JSX.Elemen useTokenDetailsContext() const currentChainBalance = useTokenDetailsCurrentChainBalance() + const openReportTokenModal = useEvent(() => { + setTimeout(() => { + navigate(ModalName.ReportTokenIssue, { + source: 'token-details', + currency: currencyInfo?.currency, + isMarkedSpam: currencyInfo?.isSpam, + }) + }, MODAL_OPEN_WAIT_TIME) + }) + + const openReportDataIssueModal = useEvent(() => { + setTimeout(() => { + navigate(ModalName.ReportTokenData, { currency: currencyInfo?.currency, isMarkedSpam: currencyInfo?.isSpam }) + }, MODAL_OPEN_WAIT_TIME) + }) + const { value: isOpen, setTrue: openMenu, setFalse: closeMenu } = useBooleanState(false) const menuActions = useTokenContextMenuOptions({ excludedActions: EXCLUDED_ACTIONS, @@ -70,6 +90,8 @@ export const HeaderRightElement = memo(function HeaderRightElement(): JSX.Elemen tokenSymbolForNotification: currencyInfo?.currency.symbol, portfolioBalance: currentChainBalance, openContractAddressExplainerModal, + openReportDataIssueModal, + openReportTokenModal, copyAddressToClipboard, closeMenu: () => {}, }) diff --git a/apps/mobile/src/screens/TokenDetailsScreen.tsx b/apps/mobile/src/screens/TokenDetailsScreen.tsx index 37413162e96..63e3176d019 100644 --- a/apps/mobile/src/screens/TokenDetailsScreen.tsx +++ b/apps/mobile/src/screens/TokenDetailsScreen.tsx @@ -1,9 +1,12 @@ import { useApolloClient } from '@apollo/client' import { ReactNavigationPerformanceView } from '@shopify/react-native-performance-navigation' import { GQLQueries, GraphQLApi } from '@universe/api' -import React, { memo, useCallback, useEffect, useMemo } from 'react' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' +import React, { memo, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FadeInDown, FadeOutDown } from 'react-native-reanimated' +import { useDispatch } from 'react-redux' +import { MODAL_OPEN_WAIT_TIME } from 'src/app/navigation/constants' import { navigate } from 'src/app/navigation/rootNavigation' import type { AppStackScreenProp } from 'src/app/navigation/types' import { HeaderScrollScreen } from 'src/components/layout/screens/HeaderScrollScreen' @@ -21,12 +24,14 @@ import { useTokenDetailsCTAVariant } from 'src/components/TokenDetails/useTokenD import { useTokenDetailsCurrentChainBalance } from 'src/components/TokenDetails/useTokenDetailsCurrentChainBalance' import { HeaderRightElement, HeaderTitleElement } from 'src/screens/TokenDetailsHeaders' import { useIsScreenNavigationReady } from 'src/utils/useIsScreenNavigationReady' -import { Flex, Separator } from 'ui/src' +import { Flex, Separator, Text } from 'ui/src' import { ArrowDownCircle, ArrowUpCircle, Bank, SendRoundedAirplane } from 'ui/src/components/icons' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' -import { getBridgedAsset } from 'uniswap/src/components/BridgedAsset/utils' import type { MenuOptionItem } from 'uniswap/src/components/menus/ContextMenuV2' +import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types' +import { WarningModal } from 'uniswap/src/components/modals/WarningModal/WarningModal' +import { LearnMoreLink } from 'uniswap/src/components/text/LearnMoreLink' import { PollingInterval } from 'uniswap/src/constants/misc' import { useCrossChainBalances } from 'uniswap/src/data/balances/hooks/useCrossChainBalances' import { @@ -39,12 +44,16 @@ import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledCh import { TokenList } from 'uniswap/src/features/dataApi/types' import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils/currencyIdToContractInput' import { useIsSupportedFiatOnRampCurrency } from 'uniswap/src/features/fiatOnRamp/hooks' +import { pushNotification } from 'uniswap/src/features/notifications/slice/slice' +import { AppNotificationType } from 'uniswap/src/features/notifications/slice/types' import { useOnChainNativeCurrencyBalance } from 'uniswap/src/features/portfolio/api' import { ModalName } from 'uniswap/src/features/telemetry/constants' import Trace from 'uniswap/src/features/telemetry/Trace' -import { TokenWarningCard } from 'uniswap/src/features/tokens/TokenWarningCard' -import TokenWarningModal from 'uniswap/src/features/tokens/TokenWarningModal' +import { TokenWarningCard } from 'uniswap/src/features/tokens/warnings/TokenWarningCard' +import TokenWarningModal from 'uniswap/src/features/tokens/warnings/TokenWarningModal' +import { AZTEC_URL } from 'uniswap/src/features/transactions/swap/hooks/useSwapWarnings/getAztecUnavailableWarning' import { useAppInsets } from 'uniswap/src/hooks/useAppInsets' +import { useShouldShowAztecWarning } from 'uniswap/src/hooks/useShouldShowAztecWarning' import type { CurrencyField } from 'uniswap/src/types/currency' import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { AddressStringFormat, normalizeAddress } from 'uniswap/src/utils/addresses' @@ -152,12 +161,12 @@ const TokenDetailsErrorCard = memo(function _TokenDetailsErrorCard(): JSX.Elemen const apolloClient = useApolloClient() const { error, setError } = useTokenDetailsContext() - const onRetry = useCallback(() => { + const onRetry = useEvent(() => { setError(undefined) apolloClient .refetchQueries({ include: [GQLQueries.TokenDetailsScreen, GQLQueries.TokenPriceHistory] }) .catch((e) => setError(e)) - }, [apolloClient, setError]) + }) return error ? ( @@ -167,7 +176,10 @@ const TokenDetailsErrorCard = memo(function _TokenDetailsErrorCard(): JSX.Elemen }) const TokenDetailsModals = memo(function _TokenDetailsModals(): JSX.Element { + const { t } = useTranslation() + const dispatch = useDispatch() const { navigateToSwapFlow } = useWalletNavigation() + const isAztecDisabled = useFeatureFlag(FeatureFlags.DisableAztecToken) const { chainId, @@ -176,15 +188,13 @@ const TokenDetailsModals = memo(function _TokenDetailsModals(): JSX.Element { currencyInfo, isTokenWarningModalOpen, isContractAddressExplainerModalOpen, + isAztecWarningModalOpen, closeTokenWarningModal, closeContractAddressExplainerModal, + closeAztecWarningModal, copyAddressToClipboard, } = useTokenDetailsContext() - const onCloseTokenWarning = useEvent(() => { - closeTokenWarningModal() - }) - const onAcknowledgeTokenWarning = useEvent(() => { closeTokenWarningModal() if (activeTransactionType !== undefined) { @@ -199,6 +209,15 @@ const TokenDetailsModals = memo(function _TokenDetailsModals(): JSX.Element { } }) + const onTokenWarningReportSuccess = useEvent(() => { + dispatch( + pushNotification({ + type: AppNotificationType.Success, + title: t('common.reported'), + }), + ) + }) + return ( <> {isTokenWarningModalOpen && currencyInfo && ( @@ -206,7 +225,8 @@ const TokenDetailsModals = memo(function _TokenDetailsModals(): JSX.Element { isInfoOnlyWarning currencyInfo0={currencyInfo} isVisible={isTokenWarningModalOpen} - closeModalOnly={onCloseTokenWarning} + closeModalOnly={closeTokenWarningModal} + onReportSuccess={onTokenWarningReportSuccess} onAcknowledge={onAcknowledgeTokenWarning} /> )} @@ -214,6 +234,26 @@ const TokenDetailsModals = memo(function _TokenDetailsModals(): JSX.Element { {isContractAddressExplainerModalOpen && ( )} + + {isAztecWarningModalOpen && isAztecDisabled && ( + + + {t('swap.warning.aztecUnavailable.message')} + + + + } + acknowledgeText={t('common.button.close')} + onClose={closeAztecWarningModal} + onAcknowledge={closeAztecWarningModal} + /> + )} ) }) @@ -224,8 +264,19 @@ const TokenDetailsActionButtonsWrapper = memo(function _TokenDetailsActionButton const activeAddress = useActiveAccountAddressWithThrow() const { isTestnetModeEnabled } = useEnabledChains() - const { currencyId, chainId, address, currencyInfo, openTokenWarningModal, tokenColorLoading, navigation } = - useTokenDetailsContext() + const { + currencyId, + chainId, + address, + currencyInfo, + openTokenWarningModal, + openAztecWarningModal, + tokenColorLoading, + navigation, + } = useTokenDetailsContext() + const showAztecWarning = useShouldShowAztecWarning( + currencyInfo?.currency.isToken ? currencyInfo.currency.address : '', + ) const { navigateToFiatOnRamp, navigateToSwapFlow, navigateToSend, navigateToReceive } = useWalletNavigation() @@ -259,44 +310,52 @@ const TokenDetailsActionButtonsWrapper = memo(function _TokenDetailsActionButton currencyChainId: chainId, }) - const onPressSwap = useCallback( - (currencyField: CurrencyField) => { - if (isBlocked) { - openTokenWarningModal() - } else { - navigateToSwapFlow({ currencyField, currencyAddress: address, currencyChainId: chainId }) - } - }, - [isBlocked, openTokenWarningModal, navigateToSwapFlow, address, chainId], - ) + const onPressSwap = useEvent((currencyField: CurrencyField) => { + if (showAztecWarning) { + openAztecWarningModal() + } else if (isBlocked) { + openTokenWarningModal() + } else { + navigateToSwapFlow({ currencyField, currencyAddress: address, currencyChainId: chainId }) + } + }) - const onPressBuyFiatOnRamp = useCallback( - (isOfframp = false): void => { + const onPressBuyFiatOnRamp = useEvent((isOfframp: boolean = false): void => { + if (showAztecWarning) { + openAztecWarningModal() + } else { navigateToFiatOnRamp({ prefilledCurrency: fiatOnRampCurrency, isOfframp }) - }, - [navigateToFiatOnRamp, fiatOnRampCurrency], - ) + } + }) - const onPressGet = useCallback(() => { - navigate(ModalName.BuyNativeToken, { - chainId, - currencyId, - }) - }, [chainId, currencyId]) + const onPressGet = useEvent(() => { + if (showAztecWarning) { + openAztecWarningModal() + } else { + navigate(ModalName.BuyNativeToken, { + chainId, + currencyId, + }) + } + }) - const onPressSend = useCallback(() => { - navigateToSend({ currencyAddress: address, chainId }) - }, [address, chainId, navigateToSend]) + const onPressSend = useEvent(() => { + if (showAztecWarning) { + openAztecWarningModal() + } else { + navigateToSend({ currencyAddress: address, chainId }) + } + }) - const onPressWithdraw = useCallback(() => { + const onPressWithdraw = useEvent(() => { setTimeout(() => { navigate(ModalName.Wormhole, { currencyInfo, }) - }, 300) // delay is needed to prevent menu from not closing properly - }, [currencyInfo]) + }, MODAL_OPEN_WAIT_TIME) + }) - const bridgedAsset = getBridgedAsset(currencyInfo) + const bridgedWithdrawalInfo = currencyInfo?.bridgedWithdrawalInfo const isScreenNavigationReady = useIsScreenNavigationReady({ navigation }) @@ -317,22 +376,32 @@ const TokenDetailsActionButtonsWrapper = memo(function _TokenDetailsActionButton const actions: MenuOptionItem[] = [] if (fiatOnRampCurrency) { - actions.push({ label: t('common.button.buy'), Icon: Bank, onPress: () => onPressBuyFiatOnRamp() }) + actions.push({ + label: t('common.button.buy'), + Icon: Bank, + onPress: () => onPressBuyFiatOnRamp(), + disabled: showAztecWarning, + }) } - if (!!bridgedAsset && hasTokenBalance) { + if (bridgedWithdrawalInfo && hasTokenBalance) { actions.push({ label: t('common.withdraw'), Icon: ArrowUpCircle, onPress: () => onPressWithdraw(), - subheader: t('bridgedAsset.wormhole.toNativeChain', { nativeChainName: bridgedAsset.nativeChain }), + subheader: t('bridgedAsset.wormhole.toNativeChain', { nativeChainName: bridgedWithdrawalInfo.chain }), actionType: 'external-link', height: 56, }) } if (hasTokenBalance && fiatOnRampCurrency) { - actions.push({ label: t('common.button.sell'), Icon: ArrowUpCircle, onPress: () => onPressBuyFiatOnRamp(true) }) + actions.push({ + label: t('common.button.sell'), + Icon: ArrowUpCircle, + onPress: () => onPressBuyFiatOnRamp(true), + disabled: showAztecWarning, + }) } if (hasTokenBalance) { @@ -346,8 +415,9 @@ const TokenDetailsActionButtonsWrapper = memo(function _TokenDetailsActionButton }, [ fiatOnRampCurrency, t, - bridgedAsset, + bridgedWithdrawalInfo, hasTokenBalance, + showAztecWarning, onPressWithdraw, onPressSend, navigateToReceive, @@ -369,13 +439,15 @@ const TokenDetailsActionButtonsWrapper = memo(function _TokenDetailsActionButton actionMenuOptions={actionMenuOptions} userHasBalance={hasTokenBalance} onPressDisabled={ - isTestnetModeEnabled - ? (): void => - navigate(ModalName.TestnetMode, { - unsupported: true, - descriptionCopy: t('tdp.noTestnetSupportDescription'), - }) - : openTokenWarningModal + showAztecWarning + ? openAztecWarningModal + : isTestnetModeEnabled + ? (): void => + navigate(ModalName.TestnetMode, { + unsupported: true, + descriptionCopy: t('tdp.noTestnetSupportDescription'), + }) + : openTokenWarningModal } /> diff --git a/apps/mobile/src/screens/ViewPrivateKeys/ViewPrivateKeysScreen.tsx b/apps/mobile/src/screens/ViewPrivateKeys/ViewPrivateKeysScreen.tsx index 618d1ae0c4d..5d5bf6a3765 100644 --- a/apps/mobile/src/screens/ViewPrivateKeys/ViewPrivateKeysScreen.tsx +++ b/apps/mobile/src/screens/ViewPrivateKeys/ViewPrivateKeysScreen.tsx @@ -1,5 +1,6 @@ import { CommonActions } from '@react-navigation/core' import { NativeStackScreenProps } from '@react-navigation/native-stack' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { ScrollView } from 'react-native-gesture-handler' @@ -17,8 +18,6 @@ import { AlertTriangleFilled } from 'ui/src/components/icons/AlertTriangleFilled import { HiddenWordView } from 'ui/src/components/placeholders/HiddenWordView' import { AddressDisplay } from 'uniswap/src/components/accounts/AddressDisplay' import { Modal } from 'uniswap/src/components/modals/Modal' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import { Trace } from 'uniswap/src/features/telemetry/Trace' import { TestID } from 'uniswap/src/test/fixtures/testIDs' diff --git a/apps/mobile/src/test/fixtures/redux.ts b/apps/mobile/src/test/fixtures/redux.ts index 1b1d8b45b36..ef3153c669c 100644 --- a/apps/mobile/src/test/fixtures/redux.ts +++ b/apps/mobile/src/test/fixtures/redux.ts @@ -14,7 +14,14 @@ type PreloadedMobileStateOptions = { account: Account | undefined } -export const preloadedMobileState = createFixture, PreloadedMobileStateOptions>({ +type PreloadedMobileStateFactory = ( + overrides?: Partial & PreloadedMobileStateOptions>, +) => PreloadedState + +export const preloadedMobileState: PreloadedMobileStateFactory = createFixture< + PreloadedState, + PreloadedMobileStateOptions +>({ account: undefined, })(({ account }) => ({ ...preloadedWalletPackageState({ account }), diff --git a/apps/mobile/src/utils/hooks.ts b/apps/mobile/src/utils/hooks.ts index 157e3e630de..1bb4493e8a0 100644 --- a/apps/mobile/src/utils/hooks.ts +++ b/apps/mobile/src/utils/hooks.ts @@ -37,7 +37,7 @@ export function useFunctionAfterNavigationTransitionEndWithDelay(fn: () => void, const navigation = useAppStackNavigation() useEffect(() => { - let timeout: NodeJS.Timeout | null = null + let timeout: NodeJS.Timeout | number | null = null const unsubscribe = navigation.addListener('transitionEnd', () => { timeout = setTimeout(fn, timeoutMs) diff --git a/apps/mobile/tsconfig.json b/apps/mobile/tsconfig.json index 0f4eef55ad2..14599dc3084 100644 --- a/apps/mobile/tsconfig.json +++ b/apps/mobile/tsconfig.json @@ -13,6 +13,12 @@ { "path": "../../packages/uniswap" }, + { + "path": "../../packages/sessions" + }, + { + "path": "../../packages/gating" + }, { "path": "../../packages/api" } diff --git a/apps/web/.depcheckrc b/apps/web/.depcheckrc index 0469d010fbb..6d78dbec8d8 100644 --- a/apps/web/.depcheckrc +++ b/apps/web/.depcheckrc @@ -83,6 +83,7 @@ ignores: [ 'lib', 'locales', 'nft', + 'notification-service', 'pages', 'polyfills', 'rpc', diff --git a/apps/web/.env.staging b/apps/web/.env.staging index d22934ffff9..0a1782bda6d 100644 --- a/apps/web/.env.staging +++ b/apps/web/.env.staging @@ -1,3 +1,4 @@ # These API keys are intentionally public. Please do not report them - thank you for your concern. REACT_APP_JUPITER_PROXY_URL="https://entry-gateway.backend-prod.api.uniswap.org/jupiter" +ENTRY_GATEWAY_API_URL_OVERRIDE="https://entry-gateway.api.corn-staging.com" diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js index 331eff80908..3f9abcfcb2b 100644 --- a/apps/web/.eslintrc.js +++ b/apps/web/.eslintrc.js @@ -21,6 +21,32 @@ module.exports = { }, overrides: [ + { + // Portfolio pages must not use useAccount directly. Use usePortfolioAddress (or a domain-specific hook) instead. + files: ['src/pages/Portfolio/*.{ts,tsx}', 'src/pages/Portfolio/**/*.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: 'hooks/useAccount', + message: + "Do not import 'useAccount' in portfolio pages. Use 'pages/Portfolio/hooks/usePortfolioAddress' (or a domain-specific hook) instead.", + }, + ], + }, + ], + 'no-restricted-syntax': [ + 'error', + { + selector: 'CallExpression[callee.name="useAccount"]', + message: + "Do not call 'useAccount' in portfolio pages. Use 'pages/Portfolio/hooks/usePortfolioAddress' (or a domain-specific hook) instead.", + }, + ], + }, + }, { files: [ 'src/index.tsx', diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 9f93a7a3c96..abc356fe394 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -62,3 +62,5 @@ package-lock.json # Storybook *storybook.log storybook-static/ + +coverage/ diff --git a/apps/web/.storybook/__mocks__/tty.js b/apps/web/.storybook/__mocks__/tty.js new file mode 100644 index 00000000000..60d53bca0ed --- /dev/null +++ b/apps/web/.storybook/__mocks__/tty.js @@ -0,0 +1,10 @@ +// Mock for Node.js tty module to work in browser environment +// Used by @storybook/instrumenter + +module.exports = { + isatty: function () { + return false + }, + ReadStream: function () {}, + WriteStream: function () {}, +} diff --git a/apps/web/.storybook/main.ts b/apps/web/.storybook/main.ts index d5325632a17..a9341f02f1f 100644 --- a/apps/web/.storybook/main.ts +++ b/apps/web/.storybook/main.ts @@ -1,9 +1,8 @@ import type { StorybookConfig } from '@storybook/react-webpack5' import { dirname, join, resolve } from 'path' +import TerserPlugin from 'terser-webpack-plugin' import { DefinePlugin } from 'webpack' -const isDev = process.env.NODE_ENV === 'development' - /** * This function is used to resolve the absolute path of a package. * It is needed in projects that use Yarn PnP or are set up within a monorepo. @@ -40,7 +39,8 @@ const config: StorybookConfig = { config.plugins.push( new DefinePlugin({ - __DEV__: isDev, + __DEV__: process.env.NODE_ENV === 'development', + 'process.env.IS_UNISWAP_EXTENSION': JSON.stringify(process.env.STORYBOOK_EXTENSION || 'false'), }), ) @@ -66,21 +66,81 @@ const config: StorybookConfig = { use: ['@svgr/webpack'], }) + // Add babel-loader for TypeScript/JavaScript transpilation + // @storybook/preset-create-react-app removes TypeScript rules + config?.module?.rules && + config.module.rules.push({ + test: /\.(tsx?|jsx?)$/, + // Exclude node_modules except for expo packages and related modules + exclude: /node_modules\/(?!(expo-.*|@expo|@react-native|@uniswap\/.*)\/).*/, + use: { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + ['@babel/preset-react', { runtime: 'automatic' }], + '@babel/preset-typescript', + ], + cacheDirectory: true, + }, + }, + }) + + // Add babel-loader for React Native packages in node_modules config?.module?.rules && config.module.rules.push({ - test: /\.tsx?$/, - use: 'ts-loader', - exclude: /node_modules/, + test: /\.(tsx?|jsx?)$/, + // Exclude node_modules except for expo packages and related modules + exclude: /node_modules\/(?!(expo-.*|@expo|@react-native|@uniswap\/.*)\/).*/, + use: { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + ['@babel/preset-react', { runtime: 'automatic' }], + '@babel/preset-typescript', + ], + cacheDirectory: true, + }, + }, + }) + + // Add babel-loader for React Native packages in node_modules + config?.module?.rules && + config.module.rules.push({ + test: /\.(tsx?|jsx?)$/, + include: [ + /node_modules\/react-native-reanimated/, + /node_modules\/react-native-gesture-handler/, + /node_modules\/@react-native/, + /node_modules\/react-native\//, + ], + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-react', '@babel/preset-typescript'], + plugins: [], + cacheDirectory: true, + }, + }, }) config.resolve ??= {} - // Add fallback for Node.js 'os' module + // Configure resolve extensions to prefer .web files + config.resolve.extensions = ['.web.tsx', '.web.ts', '.web.jsx', '.web.js', '.tsx', '.ts', '.jsx', '.js'] + + // Add fallback for Node.js modules not available in browser config.resolve.fallback = { ...(config.resolve.fallback || {}), os: false, + tty: require.resolve('./__mocks__/tty.js'), + fs: false, + path: false, + util: false, } + // Configure webpack aliases for React Native and compatibility config.resolve = { ...config.resolve, alias: { @@ -92,6 +152,109 @@ const config: StorybookConfig = { config.resolve.modules = [resolve(__dirname, '../src'), 'node_modules'] + // Configure optimization - disable minimization in dev to prevent Storybook errors + if (process.env.NODE_ENV === 'production') { + config.optimization = { + ...config.optimization, + minimize: true, + minimizer: [ + new TerserPlugin({ + parallel: 2, // Reduce from default (~8 CPU cores) to prevent memory exhaustion + terserOptions: { + compress: { + drop_console: true, + drop_debugger: true, + pure_funcs: ['console.log', 'console.info'], + }, + mangle: true, + output: { + comments: false, + }, + }, + extractComments: false, + }), + ], + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + priority: 10, + }, + tamagui: { + test: /[\\/]node_modules[\\/]tamagui[\\/]/, + name: 'tamagui', + priority: 20, + }, + reactNative: { + test: /[\\/]node_modules[\\/]react-native/, + name: 'react-native', + priority: 20, + }, + }, + maxSize: 500000, // 500KB chunks to reduce memory footprint + }, + } + } else { + // In development, explicitly disable minimization + config.optimization = { + ...config.optimization, + minimize: false, + minimizer: [], + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + priority: 10, + }, + tamagui: { + test: /[\\/]node_modules[\\/]tamagui[\\/]/, + name: 'tamagui', + priority: 20, + }, + reactNative: { + test: /[\\/]node_modules[\\/]react-native/, + name: 'react-native', + priority: 20, + }, + }, + maxSize: 500000, + }, + } + } + + // Disable source maps for production Storybook builds to save memory + if (process.env.NODE_ENV === 'production') { + config.devtool = false + } + + // Remove ForkTsCheckerWebpackPlugin - it checks entire app, not just stories + const tsCheckerPlugin = + config.plugins && config.plugins.find((plugin) => plugin?.constructor.name === 'ForkTsCheckerWebpackPlugin') + + if (tsCheckerPlugin) { + config.plugins = config.plugins?.filter((p) => p !== tsCheckerPlugin) + } + + // Enable webpack persistent caching for faster rebuilds + config.cache = { + type: 'filesystem', + cacheDirectory: resolve(__dirname, '../node_modules/.cache/storybook'), + buildDependencies: { + config: [__filename], + }, + } + + // Configure performance hints + config.performance = { + maxAssetSize: 512000 * 2, + maxEntrypointSize: 512000 * 2, + hints: 'warning', + } + return config }, } diff --git a/apps/web/CLAUDE.md b/apps/web/CLAUDE.md index a5256033071..0098f3b1840 100644 --- a/apps/web/CLAUDE.md +++ b/apps/web/CLAUDE.md @@ -22,18 +22,24 @@ bun lint bun lint:fix # Run tests -bun run test # Run all tests +bun run test # Run all tests bun run test:watch # Watch mode bun run test:set1 # Components only bun run test:set2 # Pages and state bun run test:set3 # Hooks, NFT, utils bun run test:set4 # Remaining tests -# Run E2E tests -bun playwright:test - # Build for production bun build:production + +# Run production preview web server +bun preview + +# Run E2E Playwright tests +bun e2e # Run all e2e tests +bun e2e:no-anvil # Run non-anvil e2e tests +bun e2e:anvil # Run anvil e2e tests +bun e2e ExampleTest.e2e.test # Run a specific test file ``` ### Monorepo Commands (from root) diff --git a/apps/web/functions/api/image/pools.tsx b/apps/web/functions/api/image/pools.tsx index 79b1f698a04..c62d4c9a7ff 100644 --- a/apps/web/functions/api/image/pools.tsx +++ b/apps/web/functions/api/image/pools.tsx @@ -1,13 +1,13 @@ // biome-ignore-all lint/correctness/noRestrictedElements: ignoring for the whole file -import { GraphQLApi } from '@universe/api' +import { ProtocolVersion } from '@universe/api/src/clients/graphql/__generated__/schema-types' import { ImageResponse } from '@vercel/og' import { WATERMARK_URL } from 'functions/constants' import getFont from 'functions/utils/getFont' import getNetworkLogoUrl from 'functions/utils/getNetworkLogoURL' import getPool from 'functions/utils/getPool' import { getRequest } from 'functions/utils/getRequest' -import { Context } from 'hono' +import { type Context } from 'hono' function UnknownTokenImage({ symbol }: { symbol?: string }) { const ticker = symbol?.slice(0, 3) @@ -174,7 +174,7 @@ export async function poolImageHandler(c: Context) { > {data.name}
- {data.poolData?.protocolVersion === GraphQLApi.ProtocolVersion.V2 && ( + {data.poolData?.protocolVersion === ProtocolVersion.V2 && (
(type) => type; } } - + - + - + - + diff --git a/apps/web/functions/explore/pools/pool.test.ts b/apps/web/functions/explore/pools/pool.test.ts index 803b6369f2f..7511682a029 100644 --- a/apps/web/functions/explore/pools/pool.test.ts +++ b/apps/web/functions/explore/pools/pool.test.ts @@ -14,7 +14,7 @@ const pools = [ { address: '0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3', network: 'optimism', - name: 'USDC/WLD', + name: 'USDC.e/WLD', image: 'http://localhost:3000/api/image/pools/optimism/0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3', }, ] diff --git a/apps/web/package.json b/apps/web/package.json index aa3129581df..247b5e9febb 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,9 +17,13 @@ "build:production": "nx build:production web", "build:staging": "nx build:staging web", "build:staging:preview": "nx build:staging:preview web", + "build:e2e": "nx build:e2e web", "preview": "nx preview web", "preview:staging": "nx preview:staging web", "preview:e2e": "nx preview:e2e web", + "e2e": "nx e2e web --outputStyle tui", + "e2e:anvil": "nx e2e:anvil web --outputStyle tui", + "e2e:no-anvil": "nx e2e:no-anvil web --outputStyle tui", "analyze": "nx analyze web", "lint:biome": "nx lint:biome web", "lint:biome:fix": "nx lint:biome:fix web", @@ -75,8 +79,8 @@ "@storybook/test-runner": "0.21.0", "@swc/core": "1.5.7", "@swc/plugin-styled-components": "1.5.97", - "@testing-library/jest-dom": "6.6.3", - "@testing-library/react": "16.1.0", + "@testing-library/jest-dom": "6.8.0", + "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.5.1", "@typechain/ethers-v5": "7.2.0", "@types/array.prototype.flat": "1.2.5", @@ -87,17 +91,17 @@ "@types/multicodec": "1.0.0", "@types/node": "22.13.1", "@types/qs": "6.9.2", - "@types/react": "18.3.18", - "@types/react-dom": "18.3.1", + "@types/react": "19.0.10", + "@types/react-dom": "19.0.6", "@types/react-redux": "7.1.30", "@types/react-virtualized-auto-sizer": "1.0.0", "@types/react-window": "1.8.2", "@types/rebass": "4.0.7", - "@types/styled-components": "5.1.25", + "@types/styled-components": "5.1.34", "@types/uuid": "9.0.1", "@types/wcag-contrast": "3.0.0", "@types/xml2js": "0.4.14", - "@uniswap/client-trading": "0.1.0", + "@uniswap/client-trading": "0.1.2", "@uniswap/default-token-list": "11.19.0", "@uniswap/eslint-config": "workspace:^", "@vercel/og": "0.5.8", @@ -114,7 +118,7 @@ "dotenv": "16.0.3", "dotenv-cli": "7.1.0", "esbuild-register": "3.6.0", - "eslint": "8.44.0", + "eslint": "8.57.1", "eslint-plugin-import": "2.27.5", "eslint-plugin-storybook": "0.8.0", "http-server": "14.1.1", @@ -138,7 +142,7 @@ "terser": "5.24.0", "terser-webpack-plugin": "5.3.9", "tsafe": "1.6.4", - "typescript": "5.3.3", + "typescript": "5.8.3", "vite": "npm:rolldown-vite@7.0.10", "vite-plugin-bundlesize": "0.2.0", "vite-plugin-commonjs": "0.10.4", @@ -153,7 +157,7 @@ }, "dependencies": { "@amplitude/analytics-browser": "1.12.1", - "@apollo/client": "3.10.4", + "@apollo/client": "3.11.10", "@binance/w3w-wagmi-connector-v2": "1.2.5", "@cloudflare/vite-plugin": "1.11.1", "@datadog/browser-logs": "5.20.0", @@ -177,9 +181,9 @@ "@solana/wallet-adapter-react": "0.15.39", "@solana/web3.js": "1.92.0", "@svgr/webpack": "8.0.1", - "@tamagui/core": "1.125.17", - "@tamagui/react-native-svg": "1.125.17", - "@tamagui/vite-plugin": "1.125.17", + "@tamagui/core": "1.136.1", + "@tamagui/react-native-svg": "1.136.1", + "@tamagui/vite-plugin": "1.136.1", "@tanstack/query-sync-storage-persister": "5.75.0", "@tanstack/react-query": "5.77.2", "@tanstack/react-query-persist-client": "5.77.2", @@ -187,12 +191,14 @@ "@types/react-scroll-sync": "0.9.0", "@uniswap/analytics": "1.7.2", "@uniswap/analytics-events": "2.43.0", + "@uniswap/client-data-api": "0.0.24", "@uniswap/client-explore": "0.0.17", - "@uniswap/client-pools": "0.0.17", + "@uniswap/client-liquidity": "0.0.6", + "@uniswap/client-notification-service": "0.0.3", "@uniswap/merkle-distributor": "1.0.1", "@uniswap/permit2-sdk": "1.3.0", "@uniswap/router-sdk": "2.0.2", - "@uniswap/sdk-core": "7.7.2", + "@uniswap/sdk-core": "7.9.0", "@uniswap/token-lists": "1.0.0-beta.33", "@uniswap/uniswapx-sdk": "3.0.0-beta.7", "@uniswap/universal-router-sdk": "4.19.5", @@ -203,6 +209,9 @@ "@uniswap/v3-sdk": "3.25.2", "@uniswap/v4-sdk": "1.21.2", "@universe/api": "workspace:^", + "@universe/gating": "workspace:^", + "@universe/notifications": "workspace:^", + "@universe/sessions": "workspace:^", "@visx/group": "2.17.0", "@visx/responsive": "3.12.0", "@visx/shape": "2.18.0", @@ -239,14 +248,13 @@ "polyfill-object.fromentries": "1.0.1", "porto": "0.0.80", "qs": "6.11.0", - "react": "18.3.1", - "react-dom": "18.3.1", + "react": "19.0.0", + "react-dom": "19.0.0", "react-feather": "2.0.10", "react-helmet-async": "2.0.4", "react-i18next": "14.1.0", - "react-is": "18.3.1", - "react-native-gesture-handler": "2.22.1", - "react-native-reanimated": "3.16.7", + "react-native-gesture-handler": "2.24.0", + "react-native-reanimated": "3.19.3", "react-popper": "2.3.0", "react-redux": "8.0.5", "react-router": "7.6.3", @@ -258,7 +266,7 @@ "redux-persist": "6.0.0", "redux-saga": "1.2.2", "sonner": "1.7.2", - "styled-components": "5.3.11", + "styled-components": "6.1.19", "tiny-invariant": "1.3.1", "ts-loader": "9.5.1", "typed-redux-saga": "1.5.0", @@ -278,7 +286,7 @@ "engines": { "npm": "please-use-bun", "node": "=22.13.1", - "bun": ">=1.2.0" + "bun": ">=1.3.1" }, "sideEffects": ["*.css", "src/sideEffects.ts", "src/tracing/index.ts"] } diff --git a/apps/web/playwright.config.ts b/apps/web/playwright.config.ts index 26e615d9c3f..ece5a5baaa5 100644 --- a/apps/web/playwright.config.ts +++ b/apps/web/playwright.config.ts @@ -1,13 +1,13 @@ import { defineConfig, devices } from '@playwright/test' -import dotenv from 'dotenv' +import { config } from 'dotenv' import ms from 'ms' import path from 'path' -const IS_CI = process.env.CI === 'true' +// Load environment variables from .env file +// This ensures the VSCode Playwright extension has access to env vars +config({ path: path.resolve(__dirname, '.env') }) -if (!IS_CI) { - dotenv.config({ path: path.resolve(__dirname, '.env.local') }) -} +const IS_CI = process.env.CI === 'true' // Handle asset files and platform-specific imports for Node.js // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -51,15 +51,16 @@ export default defineConfig({ testMatch: '**/*.e2e.test.ts', globalTeardown: './src/playwright/anvil/global-teardown.ts', workers: 1, // this is manually configured in the github action depending on type of tests - fullyParallel: true, + fullyParallel: false, maxFailures: IS_CI ? 10 : undefined, retries: IS_CI ? 3 : 0, reporter: IS_CI && process.env.REPORT_TO_SLACK ? [['blob'], ['list']] : 'list', - timeout: ms('60s'), + timeout: ms('120s'), expect: { - timeout: ms('10s'), + timeout: ms('15s'), }, use: { + actionTimeout: ms('30s'), screenshot: 'off', video: 'retain-on-failure', trace: 'retain-on-failure', diff --git a/apps/web/project.json b/apps/web/project.json index 9d2524a729b..589e9cd881d 100644 --- a/apps/web/project.json +++ b/apps/web/project.json @@ -10,7 +10,7 @@ "wait-for-webserver": { "executor": "nx:run-commands", "options": { - "command": "wait-on http://localhost:3000 --timeout 60000" + "command": "wait-on http://localhost:3000 --timeout 60000 --interval 1000 --delay 5000" } }, "e2e": { @@ -19,7 +19,7 @@ "command": "nx playwright:test web", "cwd": "{projectRoot}" }, - "dependsOn": ["anvil:base", "preview", "wait-for-webserver"] + "dependsOn": ["preview:e2e", "wait-for-webserver"] }, "e2e:anvil": { "executor": "nx:run-commands", @@ -27,7 +27,7 @@ "command": "nx playwright:test:anvil web", "cwd": "{projectRoot}" }, - "dependsOn": ["anvil:base", "preview", "wait-for-webserver"] + "dependsOn": ["preview:e2e", "wait-for-webserver"] }, "e2e:no-anvil": { "executor": "nx:run-commands", @@ -35,7 +35,7 @@ "command": "nx playwright:test:no-anvil web", "cwd": "{projectRoot}" }, - "dependsOn": ["preview", "wait-for-webserver"] + "dependsOn": ["preview:e2e", "wait-for-webserver"] }, "prepare": { "command": "bun ajv", @@ -56,14 +56,14 @@ } }, "anvil:mainnet": { - "command": "dotenv -- bash -c 'RUST_LOG=debug anvil --print-traces --fork-url https://${REACT_APP_QUICKNODE_ENDPOINT_NAME}.quiknode.pro/${REACT_APP_QUICKNODE_ENDPOINT_TOKEN} --hardfork prague --no-rate-limit'", + "command": "dotenv -- bash -c './scripts/start-anvil.sh \"https://${REACT_APP_QUICKNODE_ENDPOINT_NAME}.quiknode.pro/${REACT_APP_QUICKNODE_ENDPOINT_TOKEN}\" 8545'", "options": { "cwd": "{projectRoot}" }, "continuous": true }, "anvil:base": { - "command": "dotenv -- bash -c 'RUST_LOG=debug anvil --print-traces --fork-url https://${REACT_APP_QUICKNODE_ENDPOINT_NAME}.base-mainnet.quiknode.pro/${REACT_APP_QUICKNODE_ENDPOINT_TOKEN} --hardfork prague --port 8546'", + "command": "dotenv -- bash -c './scripts/start-anvil.sh \"https://${REACT_APP_QUICKNODE_ENDPOINT_NAME}.base-mainnet.quiknode.pro/${REACT_APP_QUICKNODE_ENDPOINT_TOKEN}\" 8546'", "options": { "cwd": "{projectRoot}" }, @@ -131,12 +131,13 @@ }, "defaultConfiguration": "staging" }, - "preview:e2e": { - "command": "ROLLDOWN_OPTIONS_VALIDATION=loose vite preview --port 3000", + "build:e2e": { + "command": "REACT_APP_SKIP_CSP=1 VITE_DISABLE_SOURCEMAP=true nx run web:build:production", "options": { "cwd": "{projectRoot}" }, - "continuous": true + "defaultConfiguration": "production", + "inputs": ["default", "^production", "!{projectRoot}/build"] }, "preview": { "command": "ROLLDOWN_OPTIONS_VALIDATION=loose vite preview --port 3000", @@ -154,6 +155,14 @@ "continuous": true, "dependsOn": ["build:staging:preview"] }, + "preview:e2e": { + "command": "REACT_APP_SKIP_CSP=1 VITE_DISABLE_SOURCEMAP=true ROLLDOWN_OPTIONS_VALIDATION=loose vite preview --port 3000", + "options": { + "cwd": "{projectRoot}" + }, + "continuous": true, + "dependsOn": ["build:e2e"] + }, "analyze": { "command": "source-map-explorer 'build/assets/*.js' --no-border-checks --gzip", "options": { diff --git a/apps/web/public/app-sitemap.xml b/apps/web/public/app-sitemap.xml index bc83f2fc08c..06bb6000f83 100644 --- a/apps/web/public/app-sitemap.xml +++ b/apps/web/public/app-sitemap.xml @@ -126,4 +126,16 @@ weekly 0.5 + + https://app.uniswap.org/positions/create + 2024-09-17T19:57:27.976Z + weekly + 0.7 + + + https://app.uniswap.org/positions + 2024-09-17T19:57:27.976Z + weekly + 0.7 + diff --git a/apps/web/public/csp.json b/apps/web/public/csp.json index bd7590b734f..53f051b7aac 100644 --- a/apps/web/public/csp.json +++ b/apps/web/public/csp.json @@ -1,6 +1,6 @@ { "defaultSrc": ["'self'"], - "scriptSrc": ["'self'", "'wasm-unsafe-eval'"], + "scriptSrc": ["'self'", "'wasm-unsafe-eval'", "https://challenges.cloudflare.com"], "styleSrc": [ "'self'", "'unsafe-inline'", @@ -20,7 +20,8 @@ "https://buy.moonpay.com/", "https://verify.walletconnect.com/", "https://verify.walletconnect.org/", - "https://id.porto.sh" + "https://id.porto.sh", + "https://wrapped.uniswap.org" ], "mediaSrc": ["self", "*"], "formAction": ["'none'"], diff --git a/apps/web/public/images/notifications/monad_banner_light.png b/apps/web/public/images/notifications/monad_banner_light.png new file mode 100644 index 00000000000..7d6af153b91 Binary files /dev/null and b/apps/web/public/images/notifications/monad_banner_light.png differ diff --git a/apps/web/public/images/notifications/monad_logo_filled.png b/apps/web/public/images/notifications/monad_logo_filled.png new file mode 100644 index 00000000000..9e839715080 Binary files /dev/null and b/apps/web/public/images/notifications/monad_logo_filled.png differ diff --git a/apps/web/public/images/portfolio_page_promo/dark.png b/apps/web/public/images/portfolio_page_promo/dark.png new file mode 100644 index 00000000000..910c183e24d Binary files /dev/null and b/apps/web/public/images/portfolio_page_promo/dark.png differ diff --git a/apps/web/public/images/portfolio_page_promo/light.png b/apps/web/public/images/portfolio_page_promo/light.png new file mode 100644 index 00000000000..0387c71c436 Binary files /dev/null and b/apps/web/public/images/portfolio_page_promo/light.png differ diff --git a/apps/web/public/nfts-sitemap.xml b/apps/web/public/nfts-sitemap.xml index 384bc3a2dfd..ad5990a816c 100644 --- a/apps/web/public/nfts-sitemap.xml +++ b/apps/web/public/nfts-sitemap.xml @@ -2,707 +2,667 @@ https://app.uniswap.org/nfts/collection/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x60e4d786628fea6478f785a6d7e704777c86a7c6 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c544 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x34d85c9cdeb23fa97cb08333b511ac86e1c4e258 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x99a9b7c1116f9ceeb1652de04d5969cce509b069 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb7f7f6c52f2e2fdb1963eab30438024864c313f6 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x23581767a106ae21c074b2276d25e5c3e136a68b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x8a90cab2b38dba80c64b7734e58ee1db38b8992e - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xba30e5f9bb24caa003e9f2f0497ad287fdf95623 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xbd3531da5cf5857e7cfaa92426877b022e612cf8 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x7bd29408f11d2bfc23c34f18275bbf23bb716bc7 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x306b1ea3ecdf94ab739f1910bbda052ed4a9f949 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x1a92f7381b9f03921564a437210bb9396471050c - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x5cc5b05a8a13e3fbdb0bb9fccd98d38e50f90c38 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x5af0d9827e0c53e4799bb226655a1de152a425a5 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x3bf2922f4520a8ba0c2efc3d2a1539678dad5e9d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xe785e82358879f061bc3dcac6f0444462d4b5330 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x76be3b62873462d2142405439777e971754e8e77 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xfd43af6d3fe1b916c026f6ac35b3ede068d1ca01 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x1cb1a5e65610aeff2551a50f76a87a7d3fb649c6 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x6339e5e072086621540d0362c4e3cea0d643e114 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb932a70a57673d89f4acffbe830e8ed7f75fb9e0 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x79fcdef22feed20eddacbb2587640e45491b757f - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xa3aee8bce55beea1951ef834b99f3ac60d1abeeb - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x769272677fab02575e84945f03eca517acc544cc - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x4db1f25d3d98600140dfc18deb7515be5bd293af - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x34eebee6942d8def3c125458d1a86e0a897fd6f9 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x59468516a8259058bad1ca5f8f4bff190d30e066 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x394e3d3044fc89fcdd966d3cb35ac0b32b0cda91 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x60bb1e2aa1c9acafb4d34f71585d7e959f387769 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x28472a58a490c5e09a238847f66a68a47cc76f0f - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x341a1c534248966c4b6afad165b98daed4b964ef - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x82c7a8f707110f5fbb16184a5933e9f78a34c6ab - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xccc441ac31f02cd96c153db6fd5fe0a2f4e6a68d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x764aeebcf425d56800ef2c84f2578689415a2daa - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x160c404b2b49cbc3240055ceaee026df1e8497a0 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xd2f668a8461d6761115daf8aeb3cdf5f40c532c6 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x39ee2c7b3cb80254225884ca001f57118c8f21b6 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xd774557b647330c91bf44cfeab205095f7e6c367 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x1792a96e5668ad7c167ab804a100ce42395ce54d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x04afa589e2b933f9463c5639f412b183ec062505 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xe75512aa3bec8f00434bbd6ad8b0a3fbff100ad6 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x348fc118bcc65a92dc033a951af153d14d945312 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x892848074ddea461a15f337250da3ce55580ca85 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x5946aeaab44e65eb370ffaa6a7ef2218cff9b47d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x4b15a9c28034dc83db40cd810001427d3bd7163d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x7ea3cca10668b8346aec0bf1844a49e995527c8b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb852c6b5892256c264cc2c888ea462189154d8d7 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x9378368ba6b85c1fba5b131b530f5f5bedf21a18 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x2acab3dea77832c09420663b0e1cb386031ba17b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x0c2e57efddba8c768147d1fdf9176a0a6ebd5d83 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x08d7c0242953446436f34b4c78fe9da38c73668d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x8943c7bac1914c9a7aba750bf2b6b09fd21037e0 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x364c828ee171616a39897688a831c2499ad972ec - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x7f36182dee28c45de6072a34d29855bae76dbe2f - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xf61f24c2d93bf2de187546b14425bf631f28d6dc - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x797a48c46be32aafcedcfd3d8992493d8a1f256b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x123b30e25973fecd8354dd5f41cc45a3065ef88c - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x6632a9d63e142f17a668064d41a21193b49b41a0 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xf4ee95274741437636e748ddac70818b4ed7d043 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x57a204aa1042f6e66dd7730813f4024114d74f37 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xd1258db6ac08eb0e625b75b371c023da478e94a9 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x75e95ba5997eb235f40ecf8347cdb11f18ff640b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xd532b88607b1877fe20c181cba2550e3bbd6b31c - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xa1d4657e0e6507d5a94d06da93e94dc7c8c44b51 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xedb61f74b0d09b2558f1eeb79b247c1f363ae452 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x7d8820fa92eb1584636f4f5b8515b5476b75171a - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x231d3559aa848bf10366fb9868590f01d34bf240 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xad9fd7cb4fc7a0fbce08d64068f60cbde22ed34c - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x0e9d6552b85be180d941f1ca73ae3e318d2d4f1f - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb716600ed99b4710152582a124c697a7fe78adbf - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xaadc2d4261199ce24a4b0a57370c4fcf43bb60aa - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x4e1f41613c9084fdb9e34e11fae9412427480e56 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x79986af15539de2db9a5086382daeda917a9cf0c - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xc99c679c50033bbc5321eb88752e89a93e9e83c5 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x3110ef5f612208724ca51f5761a69081809f03b7 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x036721e5a769cc48b3189efbb9cce4471e8a48b1 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x524cab2ec69124574082676e6f654a18df49a048 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x7ab2352b1d2e185560494d5e577f9d3c238b78c5 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x32973908faee0bf825a343000fe412ebe56f802a - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x7daec605e9e2a1717326eedfd660601e2753a057 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xc1caf0c19a8ac28c41fe59ba6c754e4b9bd54de9 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x33fd426905f149f8376e227d0c9d3340aad17af1 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x466cfcd0525189b573e794f554b8a751279213ac - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x6be69b2a9b153737887cfcdca7781ed1511c7e36 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x80336ad7a747236ef41f47ed2c7641828a480baa - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x9401518f4ebba857baa879d9f76e1cc8b31ed197 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x4b61413d4392c806e6d0ff5ee91e6073c21d6430 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xc3f733ca98e0dad0386979eb96fb1722a1a05e69 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x09233d553058c2f42ba751c87816a8e9fae7ef10 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x960b7a6bcd451c9968473f7bbfd9be826efd549a - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x36d30b3b85255473d27dd0f7fd8f35e36a9d6f06 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x698fbaaca64944376e2cdc4cad86eaa91362cf54 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x497a9a79e82e6fc0ff10a16f6f75e6fcd5ae65a8 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x41a322b28d0ff354040e2cbc676f0320d8c8850d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xa9c0a07a7cb84ad1f2ffab06de3e55aab7d523e8 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x942bc2d3e7a589fe5bd4a5c6ef9727dfd82f5c8a - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x8821bee2ba0df28761afff119d66390d594cd280 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x8c6def540b83471664edc6d5cf75883986932674 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x8d9710f0e193d3f95c0723eaaf1a81030dc9116d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x86825dfca7a6224cfbd2da48e85df2fc3aa7c4b1 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x629a673a8242c2ac4b7b8c5d8735fbeac21a6205 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x9a534628b4062e123ce7ee2222ec20b86e16ca8f - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xc2c747e0f7004f9e8817db2ca4997657a7746928 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x73da73ef3a6982109c4d5bdb0db9dd3e3783f313 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xc92ceddfb8dd984a89fb494c376f9a48b999aafc - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x3248e8ba90facc4fdd3814518c14f8cc4d980e4b - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x67d9417c9c3c250f61a83c7e8658dac487b56b09 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb6a37b5d14d502c3ab0ae6f3a0e058bc9517786e - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x86c10d10eca1fca9daf87a279abccabe0063f247 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x4b3406a41399c7fd2ba65cbc93697ad9e7ea61e5 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xb0640e8b5f24bedc63c33d371923d68fde020303 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xd3d9ddd0cf0a5f0bfb8f7fceae075df687eaebab - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xa5c0bd78d1667c13bfb403e2a3336871396713c5 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x4d7d2e237d64d1484660b55c0a4cc092fa5e6716 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xfcb1315c4273954f74cb16d5b663dbf479eec62e - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x66d1db16101502ed0ca428842c619ca7b62c8fef - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x128675d4fddbc4a0d3f8aa777d8ee0fb8b427c2f - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x19b86299c21505cdf59ce63740b240a9c822b5e4 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xacf63e56fd08970b43401492a02f6f38b6635c91 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x0bebad1ff25c623dff9605dad4a8f782d5da37df - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0xdceaf1652a131f32a821468dc03a92df0edd86ea - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x273f7f8e6489682df756151f5525576e322d51a3 - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x77372a4cc66063575b05b44481f059be356964a4 - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0xf5b0a3efb8e8e4c201e2a935f110eaaf3ffecb8d - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x22c36bfdcef207f9c0cc941936eff94d4246d14a - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x59325733eb952a92e069c87f0a6168b29e80627f - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x0e3a2a1f2146d86a604adc220b4967a898d7fe07 - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x3af2a97414d1101e2107a70e7f33955da1346305 - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x5ab21ec0bfa0b29545230395e3adaca7d552c948 - 2025-04-11T20:35:02.652Z - 0.7 - - - https://app.uniswap.org/nfts/collection/0x617913dd43dbdf4236b85ec7bdf9adfd7e35b340 - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 - https://app.uniswap.org/nfts/collection/0x3fe1a4c1481c8351e91b64d5c398b159de07cbc5 - 2025-04-11T20:35:02.652Z + https://app.uniswap.org/nfts/collection/0xd4e4078ca3495de5b1d4db434bebc5a986197782 + 2025-03-20T21:18:53.078Z 0.7 - https://app.uniswap.org/nfts/collection/0xd4e4078ca3495de5b1d4db434bebc5a986197782 - 2025-04-11T20:35:02.652Z + https://app.uniswap.org/nfts/collection/0x77372a4cc66063575b05b44481f059be356964a4 + 2025-03-20T21:18:53.078Z 0.7 https://app.uniswap.org/nfts/collection/0x062e691c2054de82f28008a8ccc6d7a1c8ce060d - 2025-04-11T20:35:02.652Z + 2025-03-20T21:18:53.078Z 0.7 \ No newline at end of file diff --git a/apps/web/public/pools-sitemap.xml b/apps/web/public/pools-sitemap.xml index 08339144e0f..ae63050287b 100644 --- a/apps/web/public/pools-sitemap.xml +++ b/apps/web/public/pools-sitemap.xml @@ -2,13132 +2,8147 @@ https://app.uniswap.org/explore/pools/ethereum/0xcbcdf9626bc03e24f779434178a73a0b4bad62ed - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4e68ccd3e89f51c3074ca5072bbac773960dfa36 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4585fe77225b41b697c938b018e2ac67ac5a20c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc63b0708e2f7e69cb8a1df0e1389a98c35a76d52 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x99ac8ca7087fa4a2a1fb6357269965a2014abc35 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x11b815efb8f581194ae79006d24e0d814b7697f6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa6cc3c2531fdaa6ae1a3ca84c2855806728693e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5777d92f208679db4b9778590fa3cab3ac9e2168 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1d42064fc4beb5f8aaf85f4617ae8b3b5b8bd801 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc2e9f25be6257c210d7adf0d4cd6e3e881ba25f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x11950d141ecb863f01007add7d1a342041227b58 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc5c134a1f112efa96003f8559dba6fac0ba77692 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1df4c6e36d61416813b42fe32724ef11e363eddc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x12d6867fa648d269835cf69b49f125147754b54d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3416cf6c708da44db2624d63ea0aaef7113527c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe8c6c9227491c0a8156a0106a0204d881bb7e531 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x04708077eca6bb527a5bbbd6358ffb043a9c1c14 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9db9e0e53058c89e5b94e29621a205198648425b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf239009a101b6b930a527deaab6961b6e7dec8a6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xfe0df74636bc25c7f2400f22fe7dae32d39443d2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf4c5e0f4590b6679b3030d29a84857f226087fef - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5764a6f2212d502bc5970f9f129ffcd61e5d7563 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa3f558aebaecaf0e11ca4b2199cc5ed341edfd74 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x99132b53ab44694eeb372e87bced3929e4ab8456 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x6c6bc977e13df9b0de53b251522280bb72383700 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9d96880952b4c80a55099b9c258250f2cc5813ec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3afdc5e6dfc0b0a507a8e023c9dce2cafc310316 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x290a6a7460b308ee3f19023d2d00de604bcf5b42 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xac4b3dacb91461209ae9d41ec517c2b9cb1b7daf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x60594a405d53811d3bc4766596efd80fd545a270 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x331399c614ca67dee86733e5a2fba40dbb16827c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4b5ab61593a2401b1075b90c04cbcdd3f87ce011 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x844eb5c280f38c7462316aad3f338ef9bda62668 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe936f0073549ad8b1fa53583600d629ba9375161 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2f62f2b4c5fcd7570a709dec05d68ea19c82a9ec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x381fe4eb128db1621647ca00965da3f9e09f4fac - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x97e7d56a0408570ba1a7852de36350f7713906ec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xcd423f3ab39a11ff1d9208b7d37df56e902c932b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe15e6583425700993bd08f51bf6e7b73cd5da91b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x69d91b94f0aaf8e8a2586909fa77a5c2c89818d5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe42318ea3b998e8355a3da364eb9d48ec725eb45 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xad9ef19e289dcbc9ab27b83d2df53cdeff60f02d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3b685307c8611afb2a9e83ebc8743dc20480716e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7bea39867e4169dbe237d55c8242a8f2fcdcc387 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7b1e5d984a43ee732de195628d20d05cfabc3cc7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7858e59e0c01ea06df3af3d20ac7b0003275d4bf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xae2a25cbdb19d0dc0dddd1d2f6b08a6e48c4a9a9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x14af1804dbbf7d621ecc2901eef292a24a0260ea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x80a9ae39310abf666a87c743d6ebbd0e8c42158e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc31e54c7a869b9fcbecc14363cf510d1c41fa443 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2f5e87c9312fa29aed5c179e456625d79015299c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc6962004f452be9203591991d15f6b388e09e8d0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc6f780497a95e246eb9449f5e4770916dcd6396a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x641c00a822e8b671738d32a431a4fb6074e5c79d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x92c63d0e701caae670c9415d91c474f686298f00 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1aeedd3727a6431b8f070c0afaa81cc74f273882 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xcda53b1f66614552f834ceef361a8d12a0b8dad8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x35218a1cbac5bbc3e57fd9bd38219d37571b3537 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x17c14d2c404d167802b16c450d3c99f88f2c4f4d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x468b88941e7cc0b88c1869d68ab6b570bcef62ff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xdbaeb7f0dfe3a0aafd798ccecb5b22e708f7852c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x149e36e72726e0bcea5c59d40df2c43f60f5a22d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xbaaf1fc002e31cb12b99e4119e5e350911ec575b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa67f72f21bd9f91db2da2d260590da5e6c437009 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x92fd143a8fa0c84e016c2765648b9733b0aa519e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7cf803e8d82a50504180f417b8bc7a493c0a0503 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x81c48d31365e6b526f6bbadc5c9aafd822134863 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x446bf9748b4ea044dd759d9b9311c70491df8f29 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc82819f72a9e77e2c0c3a69b3196478f44303cf4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x50c7390dfdd3756139e6efb5a461c2eb7331ceb4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1dfc1054e0e2a10e33c9ca21aad5aa8a1cce91e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc91b7b39bbb2c733f0e7459348fd0c80259c8471 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x59d72ddb29da32847a4665d08ffc8464a7185fae - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x09ba302a3f5ad2bf8853266e271b005a5b3716fe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa77d77c9773c35e910acc2e30cefe52b54a58414 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8da66e470403b3d3eee66c67e2c61fda6e248ad1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2f020e708811c054f146eebcc4d5a215fd4eec26 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7e7fb3cceca5f2ac952edf221fd2a9f62e411980 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x68c685fd52a56f04665b491d491355a624540e85 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa8328bf492ba1b77ad6381b3f7567d942b000baf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc0cf0f380ddb44dbcaf19a86d094c8bba3efa04a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa169d1ab5c948555954d38700a6cdaa7a4e0c3a0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1862200e8e7ce1c0827b792d0f9546156f44f892 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x05bbaaa020ff6bea107a9a1e06d2feb7bfd79ed2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xd02a4969dc12bb889754361f8bcf3385ac1b2077 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc24f7d8e51a64dc1238880bd00bb961d54cbeb29 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7c06736e41236fecd681dd3353aa77ecd19ea565 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc473e2aee3441bf9240be85eb122abb059a3b57c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x14353445c8329df76e6f15e9ead18fa2d45a8bb6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2039f8c9cd32ba9cd2ea7e575d5b1abea93f7527 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xd3e11119d2680c963f1cdcffece0c4ade823fb58 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8e295789c9465487074a65b1ae9ce0351172393f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x97bca422ec0ee4851f2110ea743c1cd0a14835a1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xbe3ad6a5669dc0b8b12febc03608860c31e2eef6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x56ebd63a756b94d3de9cea194896b4920b64fb01 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe2ddd33585b441b9245085588169f35108f85a6e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x84436a2af97f37018db116ae8e1b691666db3d00 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x68f5c0a2de713a54991e01858fd27a3832401849 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4533bad2dc588f0fadf8d2e72386d4cd6a19b519 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x85149247691df622eaf1a8bd0cafd40bc45154a9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0392b358ce4547601befa962680bede836606ae2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1c3140ab59d6caf9fa7459c6f83d4b52ba881d36 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd1f1bad4c9e6c44dec1e9bf3b94902205c5cd6c3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x03af20bdaaffb4cc0a521796a223f7d85e2aac31 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x73b14a78a0d396c521f954532d43fd5ffe385216 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xac85eaf55e9c60ed40a683de7e549d23fdfbeb33 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x04f6c85a1b00f6d9b75f91fd23835974cc07e65c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x730691cdac3cbd4d41fc5eb9d8abbb0cea795b94 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x535541f1aa08416e69dc4d610131099fa2ae7222 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xfc1f3296458f9b2a27a0b91dd7681c4020e09d05 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x85c31ffa3706d1cce9d525a00f1c7d4a2911754c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd52533a3309b393afebe3176620e8ccfb6159f8a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xff7fbdf7832ae524deda39ca402e03d92adff7a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb589969d38ce76d3d7aa319de7133bc9755fd840 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf334f6104a179207ddacfb41fa3567feea8595c2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1fb3cf6e48f1e7b10213e7b6d87d4c073c7fdb7b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd4344ea0c5ade7e22b9b275f0bde7a145dec5a23 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x5b42a63d6741416ce9a7b9f4f16d8c9231ccddd4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x252cbdff917169775be2b552ec9f6781af95e7f6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2ab22ac86b25bd448a4d9dc041bd2384655299c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc858a329bf053be78d6239c4a4343b8fbd21472b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa73c628eaf6e283e26a7b1f8001cf186aa4c0e8e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb533c12fb4e7b53b5524eab9b47d93ff6c7a456f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2ae3d6096d8215ac2acddf30c60caa984ea5debe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x19ea026886cbb7a900ecb2458636d72b5cae223b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6f32061f59a21086c334d0d45f804089ce374aaf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xfaf037caafa9620bfaebc04c298bf4a104963613 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xadb35413ec50e0afe41039eac8b930d313e94fa4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe9e3893921de87b1194a8108f9d70c24bde71c27 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf1f199342687a7d78bcc16fce79fa2665ef870e1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf44acaa38be5e965c5ddf374e7a2ba270e580684 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x36e42931a765022790b797963e42c5522d6b585a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x5adba6c5589c50791dd65131df29677595c7efa7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x3249e3e3e4133ee18e65347daf586610cc265f54 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xca1b837c87c6563910c2befa48834fa2a8c3d72d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6ef7b14bcd8d989cef8f8ec8ba4bf371b2ac95fd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x37ffd11972128fd624337ebceb167c8c0a5115ff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe62bd99a9501ca33d98913105fc2bec5bae6e5dd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb2ac2e5a3684411254d58b1c5a542212b782114d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb0efaf46a1de55c54f333f93b1f0641e73bc16d0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd0fa3b5264ccde31e8b094b86bca4a1e97d3c603 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xad4c666fc170b468b19988959eb931a3676f0e9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x790fde1fd6d2568050061a88c375d5c2e06b140b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xaefc1edaede6adadcdf3bb344577d45a80b19582 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa8a5356ee5d02fe33d72355e4f698782f8f199e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x55bc964fe3b0c8cc2d4c63d65f1be7aef9bb1a3c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x95d9d28606ee55de7667f0f176ebfc3215cfd9c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x45dda9cb7c25131df268515131f647d726f50608 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x50eaedb835021e4a108b7290636d62e9765cc6d7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x167384319b41f7094e62f7506409eb38079abff8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa374094527e1673a86de625aa59517c5de346d32 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x86f1d8390222a3691c28938ec7404a1661e618e0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xeda1094f59a4781456734e5d258b95e6be20b983 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x847b64f9d3a95e977d157866447a5c0a5dfa0ee5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x94ab9e4553ffb839431e37cc79ba8905f45bfbea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0e44ceb592acfc5d3f09d996302eb4c499ff8c10 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x1e5bd2ab4c308396c06c182e1b7e7ba8b2935b83 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9b08288c3be4f62bbf8d1c20ac9c5e6f9467d8b7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb6e57ed85c4c9dbfef2a68711e9d6f36c56e0fcb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3e31ab7f37c048fc6574189135d108df80f0ea26 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd36ec33c8bed5a9f7b6630855f1533455b98a418 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xdac8a8e6dbf8c690ec6815e0ff03491b2770255d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xfe343675878100b344802a6763fd373fdeed07a4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0a28c2f5e0e8463e047c203f00f649812ae67e4f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x88f3c15523544835ff6c738ddb30995339ad57d6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x98b9162161164de1ed182a0dfa08f5fbf0f733ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xeef1a9507b3d505f0062f2be9453981255b503c8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc4c06c9a239f94fc0a1d3e04d23c159ebe8316f1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x849ec65748107aedc518dbc42961f358ea1361a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2db87c4831b2fec2e35591221455834193b50d1b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa4d8c89f0c20efbe54cba9e7e7a7e509056228d9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x642f28a89fa9d0fa30e664f71804bfdd7341d21f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2aceda63b5e958c45bd27d916ba701bc1dc08f7a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x781067ef296e5c4a4203f81c593274824b7c185d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4ccd010148379ea531d6c587cfdd60180196f9b1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd866fac7db79994d08c0ca2221fee08935595b4b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x941061770214613ba0ca3db9a700c39587bb89b6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa9077cdb3d13f45b8b9d87c43e11bce0e73d8631 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa01f64fa1b923dd9c5c7618b39a6ba8098a88863 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa830ff28bb7a46570a7e43dc24a35a663b9cfc2e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8837a61644d523cbe5216dde226f8f85e3aa9be3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xca5d44977d6de1846530eb434167b208752fba7d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4d05f2a005e6f36633778416764e82d1d12e7fbb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x41e64a5bc929fa8e6a9c8d7e3b81a13b21ff3045 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3ea34cfc9322273311f7843826a2581c4a00fd39 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x785061ed819414dc4269d2a5d5974069c0daea96 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3f5228d0e7d75467366be7de2c31d0d098ba2c23 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2e3f22e9a1c2470b2e293351f48c99e1fd788f32 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2a08c38c7e1fa969325e2b64047abb085dec3756 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe6c36eed27c2e8ecb9a233bf12da06c9730b5955 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xefa98fdf168f372e5e9e9b910fcdfd65856f3986 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x76fa081e510f43ac8335efdb4db88c9ff1894413 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc6832ef0af793336aa44a936e54b992bff47e7cd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x865f456479a21e2b3d866561d7171a3d0a7b112d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xbd934a7778771a7e2d9bf80596002a214d8c9304 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9ab9f658104467604b5afa9a3e1df62f35f7b208 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x6e430d59ba145c59b73a6db674fe3d53c1f31cae - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x9e37cb775a047ae99fc5a24dded834127c4180cd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x48413707b70355597404018e7c603b261fcadf3f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xade9bcd4b968ee26bed102dd43a55f6a8c2416df - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xda679706ff21114ac9fac5198bff24543f357a16 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xba3f945812a83471d709bce9c3ca699a19fb46f7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc9034c3e7f58003e6ae0c8438e7c8f4598d5acaa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x4c36388be6f416a29c8d8eee81c771ce6be14b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa1b2457c0b627f97f6cc892946a382451e979014 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x4b0aaf3ebb163dd45f663b38b6d93f6093ebc2d3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xae2ce200bdb67c472030b31f602f0756c9aeb61c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x3bc5180d5439b500f381f9a46f15dd6608101671 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x5122e02898ece3bc62df8c1efdb29a9e914244d3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x24e1cbd6fed006ceed9af0dce688acc7951d57a9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2556230ac694093d4d3b7b965a2f2d77d4c403a4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xdaca082c2c7d052a96fa83ea9d3a7b6839e39586 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa555149210075702a734968f338d5e1cbd509354 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x10648ba41b8565907cfa1496765fa4d95390aa0d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x00bcec1526dae1e170a53017b8775a93b7810d7c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x20e068d76f9e90b90604500b84c7e19dcb923e7e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x6b93950a9b589bc32b82a5df4e5148f98a7fae27 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd9caa6dbe6791fcb7fc9fb59d1a6b3dd8c1c2339 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x62e81e93136ac42a1ada48d4098f5f9e703e7455 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x84206d33845c9d811438b6fe4e7a0c634748dc50 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd0b53d9277642d899df5c87a3966a349a798f224 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xcfa7c4bb565915f1c4f9475e2a0536d31efad776 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa7de21f28ca460b45373b217cd4eb111c3faeff8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xb64dff20dd5c47e6dbb56ead80d23568006dec1e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xad4e969f4193878e5cc89cefb57faf6c7c0048da - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xdf5eb97e3e23ca7f5a5fd2264680377c211310ba - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xf16baaae8eb7b37f4280e72924479f69e7a61f32 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xe745a591970e0fa981204cf525e170a2b9e4fb93 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x64b74c66b9ba60ca668b781289767ae7298f37ae - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x17e1ebd791e7253a5e606fd94c5b66c14d873136 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x46715bd57b9ec01deadb35fe096fb44acda79414 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x3447accd4b8e735329d1065244aad2ed630f0122 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2feb7f3ffc243f7de94d5ea5975533d301584e07 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0d5959a52e7004b601f0be70618d01ac3cdce976 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2170ca774e48a3f51559917ada6f9d7ae8f7bfea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x62a76dfa8951aefcff787e790782db3633ebf422 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x8073679e0b3b2d1d665777cf1b2b5b1c2d3d2d0c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x143f1a6f3fb32e6ab3f22d3cc6b417b5c2197599 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x82ad659c2f152aad59bb37cbc5e7663a2de0c607 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa4efe9e8e2a2d5a2ac46805f233b8e49d0e11955 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xfcc89a1f250d76de198767d33e1ca9138a7fb54b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2faa2b42b782d578a160f61bb7cd763a17476730 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xdd44c0e83c2570062d1e6fdd440b4724862e8f31 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xe3930a14641786e123e7bbe842d701fa1cbfe2df - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x6d03360ce4764e862ed81660c1f76cc2711b14b6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc055f66f228105072315247785c00299d0ce27e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xcae1d141ab11cef0a415cf0440025e1e5e962e06 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0f338ec12d3f7c3d77a4b9fcc1f95f3fb6ad0ea6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4eaa90264d6a3567228dcb5cfc242200da586437 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x6fe9e9de56356f7edbfcbb29fab7cd69471a4869 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf420603317a0996a3fce1b1a80993eaef6f7ae1a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x47a90a2d92a8367a91efa1906bfc8c1e05bf10c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x41bf5eeae051fbd2e97b76b5f8f0fdcc1a1e526b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x28df0835942396b7a1b7ae1cd068728e6ddbbafd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa3f3664a52f01b42557524bd14556e379daf5669 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x1fd22fa7274bafebdfb1881321709f1219744829 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe39cfc1a2e51a09ecbd060a24ee4eef5a97697bb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x06396509195eb9e07c38a016694dc9ff535b128a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5a1c486edefda2f09d3b349fadc38524f1743826 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5bf1cf153c102a79d9e18b7fb7c79ba57fa70d0c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2c3c320d49019d4f9a92352e947c7e5acfe47d68 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4141325bac36affe9db165e854982230a14e6d48 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x17507bef4c3abc1bc715be723ee1baf571256e05 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8149b92ea743cc382aada523b68b8834733b9015 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc98f01bf2141e1140ef8f8cad99d4b021d10718f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7f9d307973cdabe42769d9712df8ee1cc1a28d10 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5c87da28a45e5089b762dcbbd86f743d14c54317 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2cd97604ef77bbcb1fa0cff47545dff8ec7def08 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7862d9b4be2156b15d54f41ee4ede2d5b0b455e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x554548b404213c7efcdbab933f52edfe3c581834 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x63008c5ea4e47f5421e0e1428b1c5043a507d0d0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0350ca994791c4b07a5b02b08aaf9d6fc8ab510e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x32776ed4d96ed069a2d812773f0ad8ad9ef83cf8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x84f3ca9b7a1579ff74059bd0e8929424d3fa330e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5289a8dbf7029ee0b0498a84777ed3941d9acfec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb2bc284ab4c953b7f7a06d59c0ceb2de26405f22 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x508acf810857fefa86281499068ad5d19ebce325 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xccdfcd1aac447d5b29980f64b831c532a6a33726 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4fb87838a29b37598099ef5aa6b3fbeeef987c50 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x515e94dc736b9d8b7d28ecf1cece0aba3d75da97 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xfd6e5b7c30538dff2752058e425ad01a56b831cc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xcb99fe720124129520f7a09ca3cbef78d58ed934 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xd2f21358c1549be193537b2a4c5dc7f0228ae011 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x93094ed1c907e4bca7eb041cb659da94f7e1b58e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xd37e6ecb991d1a0e7610c89666817665713362a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x73234630bd159384c8d43f145407312d64614f43 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xad1ddf00c4ae50573e4dc98e6c5ee93baa04a0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa765593c821f7df9ad81119509a37961e7ffa6c5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9b501a7ad3087d603ceb34424b7b2a6c348ad0b7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xafebb7cfa1a15fcac4121b609b456cbce3137c20 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0adaf134ae0c4583b3a38fc3168a83e33162651e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf9878a5dd55edc120fde01893ea713a4f032229c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x84e47c7f2fe86f6b5efbe14fee46b8bb871b2e05 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf3e5bec78654049990965f666b0612e116b94fb2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x33e59edd3214e97cb68450c6d3d6c167de072aba - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2ca76c7e466e560e0cb11a91269bb953e41254bc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xbb124e35ab9e85f8d59ba83500e559dc052b9368 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd88d5f9e6c10e6febc9296a454f6c2589b1e8fae - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb90fe7da36ac89448e6dfd7f2bb1e90a66659977 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xbd6313d0796984c578cae6bc5b5e23b27c5540c5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1f18cd7d1c7ba0dbe3d9abe0d3ec84ce1ad10066 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7da99753ff017f1b7afb2c8c0542718dc9f15f21 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x079e7a44f42e9cd2442c3b9536244be634e8f888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1c8dafd358d308b880f71edb5170b010b106ca60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xbd0f6f34baa3c1329448a69bab90111a20756f01 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x3420720e561f3082f1e514a4545f0f2e0c955a5d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xea3fb6e3313a2a90757e4ca3d6749efd0107b0b6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf130f72f8190f662522774c3367e6e8814f5e219 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4a46c053bd5c10a959aea258228217b9d3405f3d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb83258bf5940c98abf54f26c5a02710bd6b83b2c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6a209c5329f0a225fa1890d4177823c096016f34 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xdb24905b1b080f65dedb0ad978aad5c76363d3c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xddff2cdad11898b901a661e32e9fa010780263a0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x72dd8fe09b5b493012e5816068dfc6fb26a2a9e6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x54fc722a66abfb6500a36d8b7b2646129d0e836a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x53b612b32233c80ec439a64325a29766ce95be7f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe5edcbe72d1bc223097a1bed1fe6c0e404b4290c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb928c37b8bd9754d321dc3d3c6ef374d332fe761 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2d70cbabf4d8e61d5317b62cbe912935fd94e0fe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x953e2937f0515c43ca7995e80c84aedcbbb9385e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x84394d80830ae963b599ded7d9149b90059f182f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa1777e082fa1746eb78dd9c1fbb515419cf6e538 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x112466c8b6e5abe42c78c47eb1b9d40baa3f943c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9491d57c5687ab75726423b55ac2d87d1cda2c3f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x978799f1845c00c9a4d9fd2629b9ce18df66e488 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xdc55d1fd1c04e005051a40bd59c5f95623257bc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x34757893070b0fc5de37aaf2844255ff90f7f1e0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7faf167615419228f3f7d71d52d840dab154913c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa4d7b6a50dd4c55334ca6f175dbc6561f269d264 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0ed413cefde954d8e5c54d981d7d182b587e98e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x524375d0c6a04439128428f400b00eae81a2e9e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4b7a4530d56ff55a4dce089d917ede812e543307 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x84bb5b9bf1b6782c87cfa3e396f2f571c8e49646 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x723292eea7e1576ae482a5c317934054c0199e24 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9b42940e8184d866aac6595a91f8d8952a59d3b9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x37622453c614f625d288151101ffe48fd222ced1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4a94130b9e8eb0a0959c2c0f1ee9583213773fd9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x51514b3dc24afc1db95586242b99f0063bea17c5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc130254e9196d48bbd9f91240390a6e8203132e9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x60ac25da2ada3be14a2a8c04e45b072bed965966 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4e392a3883a84225260ff857318517eb50e5d128 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xca0aa06385a42242fe9523cd7015f6d01cd8f6b2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x3e448c17043ce1481bbe53c0fd19481bad8b98a6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x81060e6bf2a683f208b8799a33c7c09830cabed1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x463fe9f646b61ccfb43a022bf947075411cd71c7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x197d7010147df7b99e9025c724f13723b29313f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8dbee21e8586ee356130074aaa789c33159921ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x570febdf89c07f256c75686caca215289bb11cfc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe3d3551bb608e7665472180a20280630d9e938aa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4e34da137f0b317c633838458e0c923a5e088752 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xbf16ef186e715668aa29cef57e2fd7f9d48adfe6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x5645dcb64c059aa11212707fbf4e7f984440a8cf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x3ad4913fa896391c9822a81d8d869cc0d783bdd7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7a415b19932c0105c82fdb6b720bb01b0cc2cae3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9b3423373e6e786c9ac367120533abe4ee398373 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4a25dbdf9629b1782c3e2c7de3bdce41f1c7f801 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xbe80225f09645f172b079394312220637c440a63 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x059615ebf32c946aaab3d44491f78e4f8e97e1d3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x435664008f38b0650fbc1c9fc971d0a3bc2f1e47 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4b62fa30fea125e43780dc425c2be5acb4ba743b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc3db44adc1fcdfd5671f555236eae49f4a8eea18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe5cf22ee4988d54141b77050967e1052bd9c7f7a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7f580f8a02b759c350e6b8340e7c2d4b8162b6a9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x48b0ab72c2591849e678e7d6f272b75ef9b863f7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x74d0ae8b8e1fca6039707564704a25ad2ee036b0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x5969efdde3cf5c0d9a88ae51e47d721096a97203 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe32efff8f8b5fdc53803405aa3f623f03f8a8767 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe8629b6a488f366d27dad801d1b5b445199e2ada - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x066b28f0c160935cf285f75ed600967bf8417035 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x146b020399769339509c98b7b353d19130c150ec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd28f71e383e93c570d3edfe82ebbceb35ec6c412 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xadab76dd2dca7ae080a796f0ce86170e482afb4a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0fb07e6d6e1f52c839608e1436d2ea810cf07257 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x95d2483d2a0fff034004f91c53d649623d993896 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x19c5505638383337d2972ce68b493ad78e315147 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc143161ed3ed8049bb63d8da42907c08a10e2269 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc3286373599dd5af2a17a572ebb7561f05f88bec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xbb98b3d2b18aef63a3178023a920971cf5f29be4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x647fb01a63de9a551b39c7915693b25e6bcec502 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa90c1c009dc8292bd04ced30f9b53a5ff7a806a0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xfb765ff72a14735550f1d798a5efd1311f2ddee7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x3537f2a5f99f08f59eb1417073db1fadbebf0c74 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xde8ed0277ee0e84c25756a73ffa7374e4aeadf46 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd8f3a72d2b2220a5067abe8c38aea57dc2d69a5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x7ec18abf80e865c6799069df91073335935c4185 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x14b1911dd6b451c2771661ae8cd70637d726c356 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x9ae8084c21752971d867597c07f2673765d949a1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xcfaf75a3d292c3535ea3acdb16ed2ee58c2bb091 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x8055e6de251e414e8393b20adab096afb3cf8399 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xffec10fe1355c2d8df4f62affcdeffdb04f06569 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc16454420f100b2e771d8bc4c5b6200068129a34 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x046f405e4ae1d0e786eda4959adadbd417d13ad8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xeccb34691c06c1c9c31ceb2228b22cbd242b5879 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xe22a2dfaaaaec8a7b2b7acb4909eaaa5c5bd6e64 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xe2dda0911e227e73d9fd94745b851c8bc6504610 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0f082a7870908f8cebbb2cd27a42a9225c19f898 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x69d667281778db0c3bc8177efea3a91ee95c3068 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x30d61bb28a6789f9f49d8c7fb198d63b6aba4b61 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x090f3fd9110621df127c3f9be5c6f58c02f2d5eb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd56f086e7b796b313d49f2bc926fac4bdd2a2b0b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x7eb847a214192aab8fa1b503f4d4c9ddd2a08db6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x81b3bc0ef974c16d71b8614adb8c22ccc045da01 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xc9b44ca4159dbaf5722a3dc8618e9d4b5f39d5b2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xbeef35a63fc62a3334630d9d3b4db27093d95317 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x3d5d143381916280ff91407febeb52f2b60f33cf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x68c9325cc268df8b9ed4a06429587f28471b5f84 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa00cc1fb7ac185222294777c6b23a13c013f07ce - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x77021e63bcbd3c5296b0cdd8a3c3770fb0ea8fa2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xcc28456d4ff980cee3457ca809a257e52cd9cdb0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xec0b7e8e44c9d60efd67a89dba1d4a6e02a7a4a0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0c8fed5dd65542ca5f0add1acab14c2e470c9110 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd56da2b74ba826f19015e6b7dd9dae1903e85da1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x5482c2b11951bbb92b87858242e17abde802b398 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xd95bae63641d822dc591bd4aca7a64e53eac76f9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x06959273e9a65433de71f5a452d529544e07ddd0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x24bf2ee2e09477082d1ddf2f0603baa460b3f5f3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x56d8f846415e08c5e663d89505e79f522d33f947 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x548e923281f372d28a40287d3a2d30dce482fc66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x9d744d3d905897608d24c1b8c1c7db0d30c36cd4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/base/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xab46d39cb398fb3649ecba781180016fef75f50b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x25048028ad87484b7fce99bc4e22dcb6c3307470 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xdb2177fee5b0ebdc7b8038cb70f3964bb6d14143 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x42d749f736051d8933b118324cded52d1f92bec1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb1a1b707b143b911c36e1a0f4f901c5017791aca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x3319a81a316abd4c086f7048904e31ff86648b38 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4a978a2d4fb7393063babfb0cee741b8bcd4dd4b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xea403e36fb592fdfdc342c38e94284ddbb0d2105 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe3fb01794d6912f0773171e32e723471ee8df061 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x916d7f23ccbb1d10118dcfc6ad5a10b6446ff73e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6cde5f5a192fbf3fd84df983aa6dc30dbd9f8fac - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd80d28850bebe6208433c298334392bc940b4fc7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7f7c4335ccac291ddedcef4429a626c442b627ed - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x628cb3a5a206956423d158009612813b64b19dab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x116361f4f45e310347b43cd098fdfa459760ea7f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x5dc631ad6c26bea1a59fbf2c2680cf3df43d249f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1a810e0b6c2dd5629afa2f0c898b9512c6f78846 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xac1cb6d3d419da9ead0b53e62d6fb4bb53473523 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0115d04a88990889471a88e85817aac9e961c07b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd3409b7f3f54bb097433d0f4cd31c48ac33e569b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x493bfc1adb2e60805693197f23132350ffd2a04e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcf4f103759770c21f945413781ca787620316988 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb135ebde27d366b0d62e579bae4118cb991b820e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xecbc2f008c20729b9239317408367377c5473812 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x96e0c440d3377c2dfe4f2a82add0b045e46cbe64 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6f5304c22ac77e228e8af4732ac6677c46e09030 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcb037f27eb3952222810966e28e0ceb650c65cd9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x7baece5d47f1bc5e1953fbe0e9931d54dab6d810 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x6dcba3657ee750a51a13a235b4ed081317da3066 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x83abecf7204d5afc1bea5df734f085f2535a9976 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x56534741cd8b152df6d48adf7ac51f75169a83b2 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xe8f7c89c5efa061e340f2d2f206ec78fd8f7e124 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x87428a53e14d24ab19c6ca4939b4df93b8996ca9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x99950bae3d0b79b8bee86a8a208ae1b087b9dcb0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xb2eb5849e2606f99fc492e9add0103c667f806d3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xcbfb0745b8489973bf7b334d54fdbd573df7ef3c + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x53c6ca2597711ca7a73b6921faf4031eedf71339 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x5ab53ee1d50eef2c1dd3d5402789cd27bb52c1bb + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xf8e349d1d827a6edf17ee673664cfad4ca78c533 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x202a6012894ae5c288ea824cbc8a9bfb26a49b93 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xc1cd3d0913f4633b43fcddbcd7342bc9b71c676f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xd35937ecd47b04a1474f8569f457fc5ac395921a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x5d27fdd96c8e4028edbabf3d667be24769425199 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xfb82dd4d657033133eea6e5b7015042984c5825f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xe333e366503f620e0242796431dc74fffd258e66 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xb2c86ff752f18499b70e8f642b3421405d50d6e9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x6b75f2189f0e11c52e814e09e280eb1a9a8a094a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x9dbe5dffaeb4ac2e0ac14f8b4e08b3bc55de5232 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xb372b5abdb7c2ab8ad9e614be9835a42d0009153 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xa7b3bcc6c88da2856867d29f11c67c3a85634882 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xf369277650ad6654f25412ea8bfbd5942733babc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x543842cbfef3b3f5614b2153c28936967218a0e6 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x127452f3f9cdc0389b0bf59ce6131aa3bd763598 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xdd2e0d86a45e4ef9bd490c2809e6405720cc357c + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x9a772018fbd77fcd2d25657e5c547baff3fd7d16 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x4898cf312fbff8814cab80a8d7f6ee5ad0dc73fb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x73a38006d23517a1d383c88929b2014f8835b38b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x5e78afc6c804d4382bede3a0712d210e657e9b4f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xc4ce8e63921b8b6cbdb8fcb6bd64cc701fb926f2 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x86b211ca7915a0c8d4659dd98242d9e801d88ab4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xd0a4c8a1a14530c7c9efdad0ba37e8cf4204d230 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xb637f7c82fd774c280e23cebc725e7cd807c66d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xc39e83fe4e412a885c0577c08eb53bdb6548004a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xd249c43faabc58d6dd4b0a4de598b5a956c5d8d7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xb26a868ffa4cbba926970d7ae9c6a36d088ee38c + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x1fbae785ce68b79f7ed4f7b27c3af3ef0e0bc3d4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x0c30062368eefb96bf3ade1218e685306b8e89fa + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3c1376fb8487da57d4ffb263d9d01b578c7b586b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xab22d1d671bb5cee8735c5ba29ea651ccda48a8e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x7b24bed19856f4bb1d4c0421cfb328026cd936bd - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x7cf887a863d81e6a483ee947dee05cb51914923c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x588c8cf031809486f015908864ee8699b44017e4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3987d38a4ff8520a8ef6bcc6f98d6da8bcd69b89 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xde67d05242b18af00b28678db34feec883cc9cd6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x4a5a8b0108f446df7c1c8a459fcfb54e844b7343 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xf6ba006abf768ab2d1b5bba2d22d9f13eb1269d4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/ethereum/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x27807dd7adf218e1f4d885d54ed51c70efb9de50 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xdd672b3b768a16b9bcb4ee1060d3e8221435beaa + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x6f38e884725a116c9c7fbf208e79fe8828a2595f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xa95b0f5a65a769d82ab4f3e82842e45b8bbaf101 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xc1738d90e2e26c35784a0d3e3d8a9f795074bca4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x4cef551255ec96d89fec975446301b5c4e164c59 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x421803da50d3932caa36bd1731d36a0e2af93542 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xbbf3209130df7d19356d72eb8a193e2d9ec5c234 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xda908c0bf14ad0b61ea5ebe671ac59b2ce091cbf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x44af8d03393e498eec5fcfc7936ebc381f02974d + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x254aa3a898071d6a2da0db11da73b02b4646078f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x32a5746ba6826828716cc1a394bc33301ebc7656 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xcb198a55e2a88841e855be4eacaad99422416b33 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x50e7b9293aef80c304234e86c84a01be8401c530 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x41824081f2e7beb83048bf52465ddd7c8e471da2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xb08a8794a5d3ccca3725d92964696858d3201909 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xa0c2ce1723b3939f47ad01a293292f2f75dc629d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x8c9d230d45d6cfee39a6680fb7cb7e8de7ea8e71 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xc42442f6402b68626e791a447d87b35cb1c6236e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x53c6ca2597711ca7a73b6921faf4031eedf71339 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x84537db6f6aaa2afdb71f325d14b9f5f7825bef1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x4f122edcd91af8cda38c3a87158afa8687bab57c + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x13933689ed2c6c66e83aed64336df14896efb7e2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x4fd47e5102dfbf95541f64ed6fe13d4ed26d2546 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xa961f0473da4864c5ed28e00fcc53a3aab056c1b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x663b1d43c27e41e5e512bf59010133997d1cd304 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x039df62583ddc1c5fda75db152b87113d863b6d6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xc0f05732d1cda6f59487ceeef4390abcad86ea3e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x52f9d14bed8ce6536da063aaf274ae2747ef4853 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc39e83fe4e412a885c0577c08eb53bdb6548004a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xdbac78be00503d10ae0074e5e5873a61fc56647c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc1cd3d0913f4633b43fcddbcd7342bc9b71c676f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x6c4c7f46d9d4ef6bc5c9e155f011ad19fc4ef321 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xb2c86ff752f18499b70e8f642b3421405d50d6e9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x16588709ca8f7b84829b43cc1c5cb7e84a321b16 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xd0a4c8a1a14530c7c9efdad0ba37e8cf4204d230 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xf92f2e3fca01491baba0975264362cc38b1cab7b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x3e6e23198679419cd73bb6376518dcc5168c8260 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x531b6a4b3f962208ea8ed5268c642c84bb29be0b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x553e9c493678d8606d6a5ba284643db2110df823 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xe3170d65018882a336743a9c396c52ea4b9c5563 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x1385fc1fe0418ea0b4fcf7adc61fc7535ab7f80d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/arbitrum/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x5cd0ad98ba6288ed7819246a1ebc0386c32c314b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xe612cb2b5644aef0ad3e922bae70a8374c63515f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xa39fe8f7a00ce28b572617d3a0bc1c2b44110e79 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xc4329493d7566525a4d51698a33f43ad240e9290 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xd4cb5566b5c16ef2f4a08b1438052013171212a2 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x9c92ed19a86986124447a73b27625230dd52f805 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x0ad1e922e764df5ab6d636f5d21ecc2e41e827f0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x5e2cd0da3411449152010d8b7f2b624eb29cca59 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xc1738d90e2e26c35784a0d3e3d8a9f795074bca4 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x6b3a3d6ed64faf933a7a4b1bd44b2efba47614ac + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x85e8d0fddf559a57aac6404e7695142cd53eb808 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x87dddd2e152bf1955e7e03d9f23a9dcc163eebf6 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x6b3a3d6ed64faf933a7a4b1bd44b2efba47614ac - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x679420c54cc4806d0f480925772965746d9f9779 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x4ce4a1a593ea9f2e6b2c05016a00a2d300c9ffd8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x740601243a6aa25ce4ee2d196eef83ac3bec6c65 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x0843e0f56b9e7fdc4fb95fabba22a01ef4088f41 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x8323d063b1d12acce4742f1e3ed9bc46d71f4222 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xe30e4dfdbb10949c27501922f845e20cfa579f09 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x7e02ae3f794ebade542c92973eb1c46d7e2e935d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xfa22d298e3b0bc1752e5ef2849cec1149d596674 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x8066ee17156e4184d69277e26fa8cbca3a845edf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x418de8e0ab58abfe916a47821a055c59b9502deb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xfb9caae5a5c0ab91f68542124c05d1efbb97d151 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xb68606a75b117906e06caa0755896ad2b3dd0272 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x6e33c0f5e16b45114679eac217e0c0138cefcd2e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xd64fb39a5681908ad488b487d65f5d8479cb235c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/optimism/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xe30e4dfdbb10949c27501922f845e20cfa579f09 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xda67d7c01c4c8f757c105c0890d94ac489952cd5 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xda908c0bf14ad0b61ea5ebe671ac59b2ce091cbf + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x0217fc17c642d29b890bcf888e21be2378493e01 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x254aa3a898071d6a2da0db11da73b02b4646078f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x099d23a43da5a8a9282266dbefeaaef958150300 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xd9abecb39a5885d1e531ed3599adfed620e2fc8a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xd92e0767473d1e3ff11ac036f2b1db90ad0ae55f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x1f6082db7c8f4b199e17090cd5c8831a1dad1997 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x25fb97799f80433e422f47e75173314e54dae174 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xf369277650ad6654f25412ea8bfbd5942733babc + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x22d1c3c541dd649ea4a8709fc787f348dc069e95 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x357faf5843c7fd7fb4e34fbeabdac16eabe8a5bc + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x40c547e7fd88f60d94788953b83d9342d8d133c6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x019c29d5c97f8cbaa67013e2cf4b6506a5cf183a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x397433498c7befde4b4049b98a7ff081a2c17387 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x8066ee17156e4184d69277e26fa8cbca3a845edf + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xf9be03505869d719ba194757943575ed2af001f2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x88aaeed1fcfca2eda30749afa9ad45a75c80e292 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x18c40bb9281a07627ff25cea45b7511f68fd0076 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x0a63d3910ffc1529190e80e10855c4216407cc45 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x270d89e983d9821a418bf193684736414fab78c5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x50defb73a76efe5d5d35cf267ffb02dfd6cd96bc + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xb125aa15ad943d96e813e4a06d0c34716f897e26 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x14653ce9f406ba7f35a7ffa43c81fa7ecd99c788 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x813c0decbb1097fff46d0ed6a39fb5f6a83043f4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x258a4b7373f6863db5a17de191e0cebb1e0bbc8a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x9a7ac628ba9f330341486380af729c8975388959 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x96239bd7ae3d9bc253b1cc7cf7a84f3a67ca5369 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xf2c9339945bff71dd0bffd3c142164112cd05dc6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x56fcb902bee19a645f9607cd1e1c0737b6358feb + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x12a4619c0bd9710732fbc458e9baa73df6c3d35f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x96530dac7817f186390b64ba63d13becd079b28d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x18fc1e95adb68b556212ebbad777f3fbb644db98 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xabbeb324b090550ca6d15ec71019915813f54f90 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x86d708404d0db1d97843e66d4ed6b86d11be705b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xbfbba3de6a260c8374f8299c38898312c2d6e9a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xd31d41dffa3589bb0c0183e46a1eed983a5e5978 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x391e8501b626c623d39474afca6f9e46c2686649 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/polygon/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xd0fc8ba7e267f2bc56044a7715a489d851dc6d78 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xd364eb55e17700b54bd75feb3f14582ed7a29444 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x4fd47e5102dfbf95541f64ed6fe13d4ed26d2546 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xfbb6eed8e7aa03b138556eedaf5d271a5e1e43ef + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xe9033c0011f35547fa90d3f8a6ad4b666a590759 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x6c561b446416e1a00e8e93e221854d6ea4171372 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x0c3561d3b72e17378d99684414aa8669daeb8bd0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x8c7080564b5a792a33ef2fd473fba6364d5495e5 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x14653ce9f406ba7f35a7ffa43c81fa7ecd99c788 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xd2fdfd5059a83e15bf362f094a2ae63f03b554ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3204e9734a56a4d7c6f4f5822e14182d9d1a43c4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x68b27e9066d3aadc6078e17c8611b37868f96a1d + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x43faefd4c0c25e969ac211cd97a4a51e52c729b7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x037818b04ac34ea8b54b6683b79ef24d23c0e7cb + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xa652ab3be697c7a01fbdce4d73f8e8acd990251c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xbf6ef625de5df898cc1d0f91868aae03976a2e2d + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x29962083891241aad61ad97bae46d032c9c0c55c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x183ea22691c54806fe96555436dd312b6befac2f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x26bf3601b77be9c31b13b22ebca02914db9c7468 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x12146c8e7469be19ec6c7f58b80246548144f8b8 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x0d2edd335982f56662d772b93d86901eb9bd2ff9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x7aea2e8a3843516afa07293a10ac8e49906dabd1 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xbaed273edd493930711fe88690ebd1f30f7f55ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xc1a6fbedae68e1472dbb91fe29b51f7a0bd44f97 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x16033643947bf4d8a1ae37b055edf57cb183106a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xaec085e5a5ce8d96a7bdd3eb3a62445d4f6ce703 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xf59abf32c1e8c5d2c6e3faa2131533bbcd466194 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xa23fab21d0653c231166b31cb6274ff45eba2ee5 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x0312187403bf72b8d2d80729894d6ac3300bd63f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x4fe87203b27a105a772f195d3f30dea714d1ecf0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x416fdbc4fb8d4d1f48d0d3778c59dfa5352e9b15 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x61928bf5f2895b682ecc9b13957aa5a5fe040cc0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xb3fb7ccf7b681e9562c6da467db4859a8ef0b8de + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x5918aca9ae924e6eaaa3d293bb92bdec9ab79338 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xfdbaf04326acc24e3d1788333826b71e3291863a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x8270e64d22cf13e92c641c4006408c7d7e3ff341 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x0fb597d6cfe5be0d5258a7f017599c2a4ece34c7 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x16503510c58da73486950b72a12ead3d1d8355dd - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x7b9fda92bfa6fdadfdc4f6c72c0cc8336e7d7497 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xf1fdc83c3a336bdbdc9fb06e318b08eaddc82ff4 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x7505159f644ddc5eae21c119e328d0d5bee574b0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x1d69099803b4580efb8df0c7ef083e550a1c42c1 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xe870bfe4aacb6e234b645e535d26c53790d50e78 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xa213c82265cd3d94f972f735a4f5130e34df81bc + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x2e2d190ad4e0d7be9569baebd4d33298379b0502 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x8c7adf7bcfdfca0a27f3d7ad49698b9e11c1f20b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x5116773e18a9c7bb03ebb961b38678e45e238923 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xb834093d7e46f7644be45e77281394d31003e866 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xb3adde966b8a1a6f22a04914ee9fe0798e71fc5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xb5a1fd804342cfb679bd8ada75718bc3ec43097e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x9399da51c1a85e64cce4b30b554875d2b89b2445 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x43f34a518e20b9454c94bf4026ec9024ed84a062 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x9e71e2b14d7e6d30811628ab0965f28e4e2edbce - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x9c087eb773291e50cf6c6a90ef0f4500e349b903 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xa011da4a0c9261ecf4694bf73a74d113aa261133 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x670e77c361375be9013869ccc516027ccc90383f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x7ab922c1bfdf7df977c7531c5782074d866f3adc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x0eb7fbe43045426938ddadc11dc41338e0907659 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xe2d2050430e341a8f3988e2726e44d9370f8cd3a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x76bf0abd20f1e0155ce40a62615a90a709a6c3d8 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xed66ba3ea44425805a085b1ca80d00467b055b38 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x40dade19adc198125ec237a2c48b3408568b2f81 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x166bc40da621d3cb978e24334f844b84ddef25f8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x76bf0abd20f1e0155ce40a62615a90a709a6c3d8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x6948d6c8532c6b0006cb67c6fb9c399792c8ac91 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x4e40cf4a7d8724e5adc2b791bbf9451d1e260b93 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x90908e414d3525e33733d320798b5681508255ea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xd6b4cce96ddf8aab2e5750983af9a901f17fbc36 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x4cef551255ec96d89fec975446301b5c4e164c59 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xdd0c6bae8ad5998c358b823df15a2a4181da1b80 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/base/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xfe4fe5b4575c036ac6d5cccfe13660020270e27a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x813c0decbb1097fff46d0ed6a39fb5f6a83043f4 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xa3adcaaf941a01eec47655c550dd5504637d029a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xb0bb2c1d32c7b27f21eec4402c6d1c38795c090a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x5e6ff2fa4ca244b6b33c7286d368120822eacc11 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xfa7d79f971a70771e5e92bd80ab955edc8602f4d + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x98efd62b4bfbde6393b18b063c506ce5a77f4810 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x26b4103c8da21725909955fe85f7f6249d05dd9e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3c5096df639262db0a6cd0172f08709d4161094b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xafa5421fe7997c16e11458659f5a87d67f1e8651 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xae31f0e673fc5f33cfc0e9abb426d8051404a7c5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x86d708404d0db1d97843e66d4ed6b86d11be705b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x4d170f8714367c44787ae98259ce8adb72240067 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xd10456ce05b9af05c8eede0f93ea8aa80a0daa2f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x065c22a16f6531706681fabbc8df135fe6eb1c2e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x7e4fb9e08cf122feb925117bace017ea234944d3 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x8ab8d851c6b31d8a4d42fd7d3e47b20861b025f2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x4094915f7849b26e8d43dee1f7e3b7b477a0b5bb + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x039df62583ddc1c5fda75db152b87113d863b6d6 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xb125aa15ad943d96e813e4a06d0c34716f897e26 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x2982d3295a0e1a99e6e88ece0e93ffdfc5c761ae - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xb007dda6ca7a57785ce04981c30a1934995a197a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc593fe9193b745447e86b45ea0bf62565ee030cc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x16033643947bf4d8a1ae37b055edf57cb183106a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x88051b0eea095007d3bef21ab287be961f3d8598 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x4cd04970dcad72d09c3af2e09f15bbcd2eb1d5d4 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xf54eba95d7f8dbe4bfeb0b6e038b3c2bedd3e40a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x9a7ac628ba9f330341486380af729c8975388959 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xb31273fd2dfc05e6fd91a3b8a2a681aeb0fbcf48 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x7ef0a523c49b1dd07e3593198c5260a95ad7859a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xaf7b48ae2f4773fd44f9208cca3db5ae7bfa7e37 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x70c132a2ddeccf0d76cc9b64a749ffe375a79a21 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xc2125a452115ff5a300cc2a6ffae99637f6e329d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x026428e531f30b2714ceff3781d7cdf5d278e96a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xb08a8794a5d3ccca3725d92964696858d3201909 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x96d5d78b179169ee0a0a0104dc514988f2a797fe + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x3373a22cb07cb49651b82cf6f174ef434e4dbaa8 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xae99efe6b04bbe5b8b4ad567946fb84b35681abb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x6696710b8e3dc0d844c8b9244767962a4a61ad97 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xcde77ef185a8f886d03b109573cc1dcdcf3cf1f8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x35f5387decce5a234da1a32ca3c9e338a48bcf37 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4178dd7eb2eb983ba7f7e41648cf91db6be20190 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xb6c8f9490314394cfc6edacb8717bfdc1eb8dab5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x1625fe58cdb3726e5841fb2bb367dde9aaa009b3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xb1ed164c736909ba7ddbc1feb7ced4eaad854a87 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x95faa9a91cd6c1c018e4b1a6fc4c89d4f1695e5d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xa143ccf73c25eec6f38bd1b741043ebea228b8e9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x2e067e0eab7fd31c01473c0f56f3295afb82e461 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xbc83c60e853398d263c1d88899cf5a8b408f9654 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x202a6012894ae5c288ea824cbc8a9bfb26a49b93 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x744159757cac173a7a3ecf5e97adb10d1a725377 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x127452f3f9cdc0389b0bf59ce6131aa3bd763598 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x2264ba9dc0b257c69eeae7782e8ff608cc65d6a7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x00a59c2d0f0f4837028d47a391decbffc1e10608 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xad6e8f6a34087bddfb03815e2c10e4f7bfd4395b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xd5bb156cb73bfca62f68dc3dff7e5ec4e305b861 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xc0d8f259578c985947a050802fb4857261af0bf3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/bnb/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x7b9a5bc920610f54881f2f6359007957de504862 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x74f7a360eb36a46b675ea932ea07094a3ace441f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xb466d5429d6ad9999bf112c225d9d7b15e96c658 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x626761cc5b9fafe4696bf8def4aa015576bb4bef - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xf27d0dac09460b236d4d9e0da316fe9c3a99b4a2 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x95faa9a91cd6c1c018e4b1a6fc4c89d4f1695e5d + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xc767c0b2e2e56c455fd29f9ee9b6e6f035c71ed4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xb6c8f9490314394cfc6edacb8717bfdc1eb8dab5 + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0x625cb959213d18a9853973c2220df7287f1e5b7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x7138eae57e8a214f7297e5e67bb6e183df3572d5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc7bbec68d12a0d1830360f8ec58fa599ba1b0e9b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x840deeef2f115cf50da625f7368c24af6fe74410 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xda71299ff6bdac31bdcafde52a41d460f17e3ad9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xabebc245a9a47166ecd10933d43817c8ef6fb825 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xb007dda6ca7a57785ce04981c30a1934995a197a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x0de383928e4fcf0f90ad2d6a5ee18eb3b9d16a55 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x1625fe58cdb3726e5841fb2bb367dde9aaa009b3 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x0a36df020fe3f132e6557899f272bf3d4591620e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xa143ccf73c25eec6f38bd1b741043ebea228b8e9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xc767c0b2e2e56c455fd29f9ee9b6e6f035c71ed4 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xaa97f0689660ea15b7d6f84f2e5250b63f2b381a + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x8c9d230d45d6cfee39a6680fb7cb7e8de7ea8e71 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xb1ed164c736909ba7ddbc1feb7ced4eaad854a87 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xcb198a55e2a88841e855be4eacaad99422416b33 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x05efb437e4e97efea6450321eca8d7585a731369 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x9b371948735f612be19195f5f6e5ebc03839cdaf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x065c22a16f6531706681fabbc8df135fe6eb1c2e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xb3709d0e16b618b15ee4bcf82d19b9e7d4100914 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xbc83c60e853398d263c1d88899cf5a8b408f9654 + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe426e1305f5e6093864762bf9d2d8b44bc211c59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x7b9a5bc920610f54881f2f6359007957de504862 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xfb82dd4d657033133eea6e5b7015042984c5825f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x92560c178ce069cc014138ed3c2f5221ba71f58a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xac70bd92f89e6739b3a08db9b6081a923912f73d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x1ebcf8831b93450ea81b0619c5e05b98751c8322 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x470d0d72c975a7f328bd63808bfffd28194b3eb6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xa961f0473da4864c5ed28e00fcc53a3aab056c1b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xa5b6d588ceb3aa1bf543d095038479188f884690 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xb1419a7f9e8c6e434b1d05377e0dbc4154e3de78 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x70c132a2ddeccf0d76cc9b64a749ffe375a79a21 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x6ef7d514d75b5a5a3c500dba1b161a81e842e7a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x1b942ce8bf08290f740b9e825c91e07fcd0bfe75 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x8ab8d851c6b31d8a4d42fd7d3e47b20861b025f2 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x9263bb7e2d3570593d80d087ea2cfc72882cfb2c + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x4f122edcd91af8cda38c3a87158afa8687bab57c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x357596dd7a0ef5cb703c5aae4da01edff176ae95 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xed3fe08bd12f24dad0f1a1e58610644debe374fb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x7766bdc5ff15d3aceb4d37914963aebaccf3de15 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xc973c86afc23ed731ce1a14d7179003a1601205f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x7bc815ca2c2115f896bb14b31b8196388c05e99b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xd29c2df656b2e4ae6b6817ccc2ebe932fc6a950b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x1f6082db7c8f4b199e17090cd5c8831a1dad1997 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc64f886397988ff16d72123dbe3d46e5bf33ffac - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0d2c430c6f7ef48ed34bf4aad0ec377e03cc53cf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x2b11a34f52e354ef197f0a2397008699b875ae7e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xde27bdec962a74a72fa1c5ef50bff6f3da083e05 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x7766bdc5ff15d3aceb4d37914963aebaccf3de15 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x5016cd7b785a773f7f3a3ff4035a1e7a76543946 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:18:53.078Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x67ab7dc903a10838a0de8861dfdff3287cf98e5c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x88aaeed1fcfca2eda30749afa9ad45a75c80e292 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2c8e9a1586ed822f79c0a241e1a4d48e839b3182 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x847165954680b989902e354f34d08b09afab3cd9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x590269935821d760c54b32d31db66ba47d4e53b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x03d70bf9e6afbf8cac09ef0c45f9a00a841c2bed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x8b238f615c1f312d22a65762bcf601a37f1eeec7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x5280d5e63b416277d0f81fae54bb1e0444cabdaa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xf4e43a4a17d2820c7cf724e46844943931a47894 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x5ab53ee1d50eef2c1dd3d5402789cd27bb52c1bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xe6ff8b9a37b0fab776134636d9981aa778c4e718 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x44af8d03393e498eec5fcfc7936ebc381f02974d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x4094915f7849b26e8d43dee1f7e3b7b477a0b5bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xc3f5e0d4cdff86e85486cf6bd20cc0884df5f98e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x87428a53e14d24ab19c6ca4939b4df93b8996ca9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x9dbe5dffaeb4ac2e0ac14f8b4e08b3bc55de5232 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xa7bb0d95c6ba0ed0aca70c503b34bc7108589a47 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xbcfac19a0036ada56496316ee5cf388c2af2bf58 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x296b88b607ea3a03c821ca4dc34dd9e7e4efa041 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x019c29d5c97f8cbaa67013e2cf4b6506a5cf183a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x30442fcebbd75a5bb58377c0174d5ce637e297d7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x6c561b446416e1a00e8e93e221854d6ea4171372 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0fb597d6cfe5be0d5258a7f017599c2a4ece34c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xe9b7057f9b81a0120c09306d35f22859473f18cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8deb37b048f4b3c7bd61eca7dfccbef7cba726de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x455fd3ae52a8ab80f319a1bf912457aa8296695a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xe11d03bef391ee0a4b670176e23eb44aad490f12 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xe7f850731fed6af4c36cce93eccfbcda0634a030 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xadad4ce0c68f50a19cf5063e0b91d701daab1df1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x5e9bb3d7682a9537db831060176c4247ab80d1ec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xe9ed60539a8ea7a4da04ebfa524e631b1fd48525 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0511791eb6fb175a1aaa645114f0f5c8689ec163 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xf3c7b93db3f28580b0fd10365e619eedceb40e76 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x58ecf9cec06bc58fde9280d348f79ed8f3d3046e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xedc7f0dfd9751ef95bb8786a3b130f490743bb0e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x6bcb0ba386e9de0c29006e46b2f01f047ca1806e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xc3576f38c32e95e36bbd8d91e6cbe646a3723110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x8d58e202016122aae65be55694dbce1b810b4072 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x34a43471377dcce420ce8e3ffd9360b2e08fa7b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x766854992bd5363ebeeff0113f5a5795796befab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x9438a9d1bdeece02ed4431ac59613a128201e0b9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x0a63d3910ffc1529190e80e10855c4216407cc45 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x89084692453ab2305f5f8ac7d70d5efd37a86b8f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xb34a5657988da5b9888952c439756594613507aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x05efb437e4e97efea6450321eca8d7585a731369 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xc973c86afc23ed731ce1a14d7179003a1601205f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x0f44a1c2b66418f784607d2067fe695703809bff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0da6253560822973185297d5f32ff8fa38243afe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xa95b0f5a65a769d82ab4f3e82842e45b8bbaf101 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x886b4f0cb357e0d6ec07b7a3985f346cc17ece7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x50defb73a76efe5d5d35cf267ffb02dfd6cd96bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x7aea2e8a3843516afa07293a10ac8e49906dabd1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8c7080564b5a792a33ef2fd473fba6364d5495e5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8f81b80d950e5996346530b76aba2962da5c9edb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x7bc0f74d8d94e8e9fdaa40bbc04cc44fb8e0f081 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x09c149c856e6fb6e40aa39209142411b554b1a41 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x7ef0a523c49b1dd07e3593198c5260a95ad7859a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x622270721fb38fde831ab23a8e177665557f6fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x15aa01580ae866f9ff4dbe45e06e307941d90c7b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x4548280ac92507c9092a511c7396cbea78fa9e49 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xe0554a476a092703abdb3ef35c80e0d76d32939f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x421803da50d3932caa36bd1731d36a0e2af93542 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x258a4b7373f6863db5a17de191e0cebb1e0bbc8a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x45126b956401daaec92afba2a9953e14b16fb83f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xa3eaa52b505cf61aadcfe21424d43a6847dd6331 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x722bcf6c16dadcc29914e4e64290c46aa1406de8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x1e1367dcebe168554e82552e0e659a4116926d10 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x4d170f8714367c44787ae98259ce8adb72240067 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xaa97f0689660ea15b7d6f84f2e5250b63f2b381a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xb736330326cf379ecd918dba10614bd63c2713da - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xe3d4faff3179f0a664a3a84c3e1da3b90e27f186 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x50e7b9293aef80c304234e86c84a01be8401c530 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x87dddd2e152bf1955e7e03d9f23a9dcc163eebf6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xd9dd34576c7034beb0b11a99afffc49e91011235 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x394a9fcbab8599437d9ec4e5a4a0eb7cb1fd2f69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xb3adde966b8a1a6f22a04914ee9fe0798e71fc5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xa2d4a8e00daad32acace1a0dd0905f6aaf57e84e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x2392ae4ba6daf181ce7343d237b695cdf525e233 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc2c390c6cd3c4e6c2b70727d35a45e8a072f18ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x3dd2fdba71282083d440687cce9e4231aaac534e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xe4d9faddd9bca5d8393bee915dc56e916ab94d27 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x9c92ed19a86986124447a73b27625230dd52f805 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x740601243a6aa25ce4ee2d196eef83ac3bec6c65 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xafbb6fcc92ddb091dbc13e9073c3360c7d9600cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xf54eba95d7f8dbe4bfeb0b6e038b3c2bedd3e40a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x05c0a0b84b6b67499c33e6403686f45cab063810 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x9169bf3657353e4b2b81c75e235f22bc299a7780 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x95f4408736988549212db071b1c8d20f7c4e6304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xb0bb2c1d32c7b27f21eec4402c6d1c38795c090a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x534d3930edba2c0b90a7973549a0287141c987ef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xf27d0dac09460b236d4d9e0da316fe9c3a99b4a2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xdd672b3b768a16b9bcb4ee1060d3e8221435beaa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xa39fe8f7a00ce28b572617d3a0bc1c2b44110e79 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x96d5d78b179169ee0a0a0104dc514988f2a797fe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xb466d5429d6ad9999bf112c225d9d7b15e96c658 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x0c3fdf9c70835f9be9db9585ecb6a1ee3f20a6c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8a35d2635aeca1aaf667d77ed9ff3b21e48ede24 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xe566e99d65b17974fd9db02e25e24ea8020f7a0e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x5c3edc45ae71a353c669cfa71e6488951dce4618 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xa7b3bcc6c88da2856867d29f11c67c3a85634882 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x543842cbfef3b3f5614b2153c28936967218a0e6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc45a81bc23a64ea556ab4cdf08a86b61cdceea8b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xe333e366503f620e0242796431dc74fffd258e66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x42161084d0672e1d3f26a9b53e653be2084ff19c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xe24f62341d84d11078188d83ca3be118193d6389 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x782dcc2cd3a65405baeb794269703e9c29a175cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xae8d5b91fca627410a3bef77f55fcfe208409a40 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xa42eb1c1a212da9e24058c6afc0ea906fecb8351 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0e3529cf622dc1141a31cfc0fc85f679f558c92b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x6f5ec7c65c2744a963064f6d49df0f4eea7d7d90 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x9a772018fbd77fcd2d25657e5c547baff3fd7d16 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xfc1505b3d4cd16bb2336394ad11071638710950f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8e0a7d4018fb2674346d5742055174f899fe1826 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xf8aa1db87d84118b0b461e2135190ac27fc1859d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xe8f7c89c5efa061e340f2d2f206ec78fd8f7e124 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x56534741cd8b152df6d48adf7ac51f75169a83b2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x73a38006d23517a1d383c88929b2014f8835b38b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xf5d63f66a36be31a106631f276794223b8ce5280 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xcf0bb95967cd006f5eaa1463c9d710d1e1550a96 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xdc9bf303e72a5780c45d53fc12799164e5ba8271 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x1d4dab3f27c7f656b6323c1d6ef713b48a8f72f1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xafd8f9b89e2af8246523573a369010daf9489b12 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xbd045175d2a1451a015079f5f3f59ca5c05524ea - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x859ec3d336bb5508f6d87fea2d49c9294adae311 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x8544383f6f2eb43711fba8d918b30658856b9806 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x5166c1bd4603cf67dbb9a98940e38d2bd0a7f294 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xf215cedbae999571e4ba5d80c10b6e835f88d5ec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xd4cb5566b5c16ef2f4a08b1438052013171212a2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x357faf5843c7fd7fb4e34fbeabdac16eabe8a5bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2bbfb5a2496f405d4094d4b854daeb9ce70d0029 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x3373a22cb07cb49651b82cf6f174ef434e4dbaa8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xc8d19b4ea42939a4b14260f0c8b4a0d6f70c8496 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xb2290db2f409201c33c507d266becabf19228dd1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0c30062368eefb96bf3ade1218e685306b8e89fa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x6f38e884725a116c9c7fbf208e79fe8828a2595f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xe612cb2b5644aef0ad3e922bae70a8374c63515f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xdef705a1864bcba65e4e275bffd58de21b5d44a0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x717358a47ac99f3cd233e723be331756b3951164 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x9166a0139cab9661e08779cd01b1358aaea7b95f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc1a6fbedae68e1472dbb91fe29b51f7a0bd44f97 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x3a3dc4a26d1aceae12fd1026a5856f12d20658ea - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xa2375dad211fe6e538d29c98ec526246e38be4ec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x4e4a4c4c46d3488ff35ff05a0233785a30f03ec4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x5d27fdd96c8e4028edbabf3d667be24769425199 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x1944ac04bd9fed9a2bcdb38b70c35949c864ec35 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x5e2cd0da3411449152010d8b7f2b624eb29cca59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x85e8d0fddf559a57aac6404e7695142cd53eb808 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xead1cd21ddf8793debc9484a0b8d286230c9b5a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc4ecaf115cbce3985748c58dccfc4722fef8247c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2f42df4af5312b492e9d7f7b2110d9c7bf2d9e4f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x021235b92a4f52c789f43a1b01453c237c265861 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc6e291f54532f12391ab59d7af75453db2dd784a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xfe4fe5b4575c036ac6d5cccfe13660020270e27a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xfa7d79f971a70771e5e92bd80ab955edc8602f4d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x023b6298e2f9ae728b324757599f2a36e002a55a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x53d3e59faac08184720bcb2816f4cf5b36d6767d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x7e9cb8ad4a7683070e233f3eb1d07d87272b9b26 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xa213c82265cd3d94f972f735a4f5130e34df81bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xf1fdc83c3a336bdbdc9fb06e318b08eaddc82ff4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x3d9228f1847b07e6b2c8eaaf393d5a4db2dbedc2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x9263bb7e2d3570593d80d087ea2cfc72882cfb2c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x360b9726186c0f62cc719450685ce70280774dc8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x663b1d43c27e41e5e512bf59010133997d1cd304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xfea834a5c47b923add607cc5b96288d18ffb9c3f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x273d580e9ceadca5b2a8ceb5ebb38a70511377cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x3b241fb91c65f42432ebdbca029e0b511c8a1707 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xe9a65059e895dd5d49806f6a71b63fed0ffffd4b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x01a1f5758c3a53057b6c819ec7331e39c167794a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x40fc7cda03139ebf7a0d3fc01f12b9d9a878cc92 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x11e26bbd1a5547895a50fc39a2d4c0025dec0bda - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x21cbb0e695b0ac79be756a87da690fd80bef4bff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x68b27e9066d3aadc6078e17c8611b37868f96a1d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x2af64d33a47e7a98eafc20ce9f6af59927d10260 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x9ec9620e1fda9c1e57c46782bc3232903cacb59b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x1112956589a2bea1b038732db4ea6b0c416ef130 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc4ce8e63921b8b6cbdb8fcb6bd64cc701fb926f2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x9febc984504356225405e26833608b17719c82ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x59c38b6775ded821f010dbd30ecabdcf84e04756 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xcec31e540163ddf45a394e00b11ae442ddc0d704 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x7f9121b4f4e040fd066e9dc5c250cf9b4338d5bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x316f12517630903035a0e0b4d6e617593ee432ba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x61928bf5f2895b682ecc9b13957aa5a5fe040cc0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x9399da51c1a85e64cce4b30b554875d2b89b2445 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0962a51e121aa8371cd4bb0458b7e5a08c1cbd29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xfbb6eed8e7aa03b138556eedaf5d271a5e1e43ef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x913a4ed1636c474e6451b5e9249d94046a24bb33 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x8e3ecc0b261f1a4db62321090575eb299844f077 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x99950bae3d0b79b8bee86a8a208ae1b087b9dcb0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x948b54a93f5ad1df6b8bff6dc249d99ca2eca052 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x5738df8073ad05d0c0fcf60e358033268ebf16cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x52f9d14bed8ce6536da063aaf274ae2747ef4853 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xc0f05732d1cda6f59487ceeef4390abcad86ea3e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x28117b7b8dba890041d7ebe646082af043533da2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xf3ca4ade682c5b99507db9a72549318b8708f137 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x9c087eb773291e50cf6c6a90ef0f4500e349b903 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x037818b04ac34ea8b54b6683b79ef24d23c0e7cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xbf6ef625de5df898cc1d0f91868aae03976a2e2d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xfdbaf04326acc24e3d1788333826b71e3291863a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xc68b994e2147b8bcf18f82c201ac3ee1e97be33d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x47808ddbc91646b21b307fefbaf7ee200b004ccc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xafa5421fe7997c16e11458659f5a87d67f1e8651 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x26b4103c8da21725909955fe85f7f6249d05dd9e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x79dbd26d3c1e44171205f258aadfae84933b69b8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0f9e3c4b905e6292b33f5ef96af18054ded12ac8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xb0e1a214130245d289ae425db7826576694a5b5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x32a5746ba6826828716cc1a394bc33301ebc7656 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x0f725b113979e025c69da0ffce3fbf5b6063cc5c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x056c5ff8380625cc94efe865a4c178a33ed546f8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xda67d7c01c4c8f757c105c0890d94ac489952cd5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x4fe87203b27a105a772f195d3f30dea714d1ecf0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x670e77c361375be9013869ccc516027ccc90383f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xaec085e5a5ce8d96a7bdd3eb3a62445d4f6ce703 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x92d90f7f8413749bd4bea26dde4e29efc9e9a0b6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2c114787d52e9f080464dbed8e285e07ec4e120f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x303b00d7a2ad12a480db7c04de5835ec9ccc37b0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x6f9d09253f99d2b6843b5ec62c23496c37327216 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x4cd15f2bc9533bf6fac4ae33c649f138cb601935 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x63d134fa19fbb35ad689dbb6b659879de1e7fb29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x5cbddc44f31067df328aa7a8da03aca6f2edd2ad - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x72da5b3c28b8cee48158f469e0e9215607fe06d7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xa7cca1c4a7d4b70f687380e0454e5ae418db53b1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xd364eb55e17700b54bd75feb3f14582ed7a29444 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xb3fb7ccf7b681e9562c6da467db4859a8ef0b8de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0eb7fbe43045426938ddadc11dc41338e0907659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xa23fab21d0653c231166b31cb6274ff45eba2ee5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xf68001b66cb98345c05b2e3efdee1db8fc01a76c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xa3adcaaf941a01eec47655c550dd5504637d029a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x6dcba3657ee750a51a13a235b4ed081317da3066 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xbbf3209130df7d19356d72eb8a193e2d9ec5c234 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x6de5072c06cbf37da96ccc0fc85c85ca82fe9d13 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x183ea22691c54806fe96555436dd312b6befac2f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x6f169193b181d9492f9bde038109cce6dfe19321 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xe9d448cc1ed83891ab5b381face53f0cadb3a8e5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xcbfb0745b8489973bf7b334d54fdbd573df7ef3c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xa04d13f092f68f603a193832222898b0d9f52c71 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0df407bc6abe9af2093dcb4c974e18d40a6a381a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x56fcb902bee19a645f9607cd1e1c0737b6358feb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x7b9fda92bfa6fdadfdc4f6c72c0cc8336e7d7497 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x1d2bdb7117a5a7d7fe4c1d95681a92e4df13bb69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x7e4fb9e08cf122feb925117bace017ea234944d3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0xb2c5d104f481d0beb056842bd5312be6fd831429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x867b321132b18b5bf3775c0d9040d1872979422e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xb26a868ffa4cbba926970d7ae9c6a36d088ee38c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xc4329493d7566525a4d51698a33f43ad240e9290 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x96239bd7ae3d9bc253b1cc7cf7a84f3a67ca5369 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2ff525c71cb7b29fcde4bea8c8f601b1dd22480a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x43f34a518e20b9454c94bf4026ec9024ed84a062 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x12146c8e7469be19ec6c7f58b80246548144f8b8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x026428e531f30b2714ceff3781d7cdf5d278e96a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x22d1c3c541dd649ea4a8709fc787f348dc069e95 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xd9abecb39a5885d1e531ed3599adfed620e2fc8a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x25fb97799f80433e422f47e75173314e54dae174 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x3e2e284d55926f5f6e86987da6be216aef292e76 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x2049df3435bdbb36d22f98fcd2e5027049a1f3ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xc8219b876753a85025156b22176c2edea17aac53 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x27807dd7adf218e1f4d885d54ed51c70efb9de50 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x1d69099803b4580efb8df0c7ef083e550a1c42c1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xec558e484cc9f2210714e345298fdc53b253c27d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x37bb450b17721c6720040a150029e504766e9777 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x4cd04970dcad72d09c3af2e09f15bbcd2eb1d5d4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xc9d0cae8343a2231b1647ab00e639eabdc766147 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x8c7adf7bcfdfca0a27f3d7ad49698b9e11c1f20b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x98e4799d58982ed714159c30a34b4bdb20ba20b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x8ff82952cee74c095c1734ee144143a755d3c600 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xf7513c120b92fa4dd5cbfa78dffefcb4ced5743f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xdd2e0d86a45e4ef9bd490c2809e6405720cc357c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0xd2fdfd5059a83e15bf362f094a2ae63f03b554ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x5116773e18a9c7bb03ebb961b38678e45e238923 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0xbac01354f2109eb3aebcb46b3ee43813dbae1a7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x357596dd7a0ef5cb703c5aae4da01edff176ae95 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/celo/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xf8e349d1d827a6edf17ee673664cfad4ca78c533 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x48da0965ab2d2cbf1c17c09cfb5cbe67ad5b1406 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x49d27c3e1cac5bcf7615f2f8e1c1af6ab9db8225 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x679420c54cc4806d0f480925772965746d9f9779 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x20b795a5bb2cca7598b67739dba8666ab3c506f8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x55703b183e4676d3e72289995c2e14fa0cc29c1b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/bnb/0x3e7995110680e6a55e6e430a1c511e921f896316 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xd19a1c8278d0420bfb2c825d99dc31ff86224607 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x9c42751954513c0461481a9600c9d11a059ddd12 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/base/0x7d68d2a3b22824a7895ad475f4fc3ca9ac8a240a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x65081cb48d74a32e9ccfed75164b8c09972dbcf1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x765c3773f641ad8073795765c5c41c075f91b140 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x18b268965e4e702bdf13469205937894b8ab0ee8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x6b918c9f87b46a758c2b51bce427c8028dacb720 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xbda709a0665b340898856b8b29ff87079bb130d3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xd49174dba635489c67fa628864c2d0d04824ebd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x9e2ef5522b2f9eac00912f25082f6e652123b54d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xc673d5164103357a7537c36438a6326776a14bbd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xe97a0889d2b0660fddc144a8893b3ac9236756a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x12095933f1eeb066176dd2e41e5a2f8be6974616 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x8eb4b07affbd1083f42032eed35cd32e382ee8b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xfbc45ab96d02e150b2ddeb7dd4eacd3d8c674f4a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x9d7151413833dcb13ae284d6a7fccd93989c47a1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x18ce92e7a37d994657f97c3defaf880a805f08d5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xa9c6669de2c04c2adb22ac7a65d75b47fee30e35 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x6e4e17973cb963c9931617d8d0e35813cb5eb886 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x8927058918e3cff6f55efe45a58db1be1f069e49 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/unichain/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xfae3f424a0a47706811521e3ee268f00cfb5c45e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x7b602f98d71715916e7c963f51bfebc754ade2d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xb978a8c502ce97b04043036a91548b846067f9ea - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x804226ca4edb38e7ef56d16d16e92dc3223347a0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x9ba9c677d19347abfba1d6b6d6ceb61942071561 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x5e8754aaa7c2da42c218f40435cbdedbae88bfc5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xeb7e0191f4054868d97f33ca7a4176b226ccbd2f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xd1356d360f37932059e5b89b7992692aa234eda6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x27b571f3e7f7827b13d927d2d59244e3e58a7d1a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xb2dc1235bf4b36628a8665aeb668bf202759528a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x2e587b9e7aa638d7eb7db5fe7447513bc4d0d28b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x724f6a02ed2eb82d8d45034b280903cf663731ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xe54dadf5b4f8779256e1bbb94eca00b124311208 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xa7141c79d3d4a9ad67ba95d2b97fe7eed9fb92b3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x28f577ed1cb3b0add148d745d9c0a2c0c40cc48d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x12089f58e0e7eadfc7d60cf1b1ed6839a811672a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x3859de09906bed098879cbef34d80817357244c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x01ed0722f62a5dd3212b04aed2a2065623a705bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x178b99eaa2f6bc97d16f84edbce2e23ae6b8140b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0e663593657b064e1bae76d28625df5d0ebd4421 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xd18384f4398bf5415902ad5d87eaa96549fd4f1e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x97bb1788dcd64e17166931b87789cae97c154009 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x83b9a48793f4e5be12cfe1de3b1d55a83c4f1a9a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x6d61ae1b0d94941dc702581daaaafc7665d1c6d7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x9a68f0bbced6d6f1f63f0a61215742377ac9d325 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x01c7c6066ec10b1cd4821e13b9fb063680ffa083 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xcea3c61705b68fefd8da1048ca69db1344180fcb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xf39f633a18042105454ff64c6abd07beb8cbcead - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x7fd0055020a6d9c43c31c8ea755c5038ebd39722 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xe1e870fdff3ad67f2879542d841d8ab3e1406f4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc0a57a72cb7da55da2e50f664b1641570941618f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xfd584fcc3a74429d85c9a2294eaf0f566ddfd593 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xfc1e7bc60adfc151565c033521ee6ccbde027f54 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x78b4c0301b7e6e3d31f259b3f112ced639f030a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x94a0d838379e9703b69a777191d8a8951ada2e8e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xd399718256c5206d9586e866169dbdf131193e02 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x43fb9c3fd6715e872272b0caab968a97692726eb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x9d1bcb75a7bc5defe7daed505c462572b5e022f9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xd312a35320a5936409edfd5e2eb0d17bcf99910b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x118bc22a76a71ff3187577422af2d57210277bab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x90fbfd28f16fa163b914be6b9b3eeeb1c3e02fe5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x44f904512639f2a4530dc3b0cce8927b09226a43 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xaf4ceda77ec614099ffe67b7f4fe4427d52e75cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xd494b33229ee2c857a43b94ee7117be05e5fd88c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0e7a5a8177d5711dc90ba9a6ded590060f52dd29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xcec63fa2b71744e1cdd48f71e34acedd46b496aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0fbb556c73226b12cd7cfb2bed56e77fc177f0ec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x15309a0e8c954c46601d8028153125c3b92884b6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xdbd8a9c9d060973557f3ac7c7f642a9523529d68 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc58a6a266ede99e0b8069f3f45a3028c2aaf4c0c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/avalanche/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x85287626e78602d0da569332e419154b0bdea035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xf52b4b69123cbcf07798ae8265642793b2e8990c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x1cf4cfc7a984a474ab03f444ccedb30c3ae6f56c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xf5a23bdd36a56ede75d503f6f643d5eaf25b1a8f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x57529f9e2a23cc53fc387b162d2ab0f1df3ed701 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xf4b646bc85458cc74497c773e2bc8b9ec1351e97 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xf08ff66b8ec0db053711f4989c40b084564f7de3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x0000fb2a9a0f3f35d72d7eedb8689c6db1d30225 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xbb60bb410182d8e96c41dfc92e017dd79f5100bf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x0f6c5fdcb927b99b3040c609bef07bdfc59a6173 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xaeba85e3328d6b77b58130f43815ac9c59603d38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xbf796b95d09729815806dd50de07c1111aa3926f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x63f59866a7e9a8628e7f1577ba55da134d64d8c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x78337095c61035056a85b7d430d6e9875f177ae2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x258ba3b253e5cc3bab01c28d2f527aacd6d96793 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x7b8086f5442f130a6be2d62dff02319018344feb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x9dfb11cf311a8fa1296f6958057f8bb02be51a4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xf95141f5552e592dc58c41e54d65d0f645ab7d7e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/blast/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/soneium/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x610e319b3a3ab56a0ed5562927d37c233774ba39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xdce053d9ba0fa2c5f772416b64f191158cbcc32e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x5f835420502a7702de50cd0e78d8aa3608b2137e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x3672722f9c4413c63d8e275bea51ee526449a92d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x0d8322f0422664ccf845fccedc10a82b292f9d3a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x494d68e3cab640fa50f4c1b3e2499698d1a173a0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x02371da6173cf95623da4189e68912233cc7107c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x7c3c2a92598d6d77e57fd55c50e99af4b291f595 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x6787de741bb42ca7ff7dd1b9aad6098c850cdc6a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x56505f73cf8f5ef637bb37d9e635c3f520c9b0e5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xad710fbc1161b26ea427c158f49a93f6d9d871b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/worldchain/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x3d7264539e6e3f596bb485e3091f3ae02ad01ef8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xeecb86c38c4667b46487255f41c6904df3d76f8f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xa0769a3c6af68812bb3a5cbd511f7879033440eb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x23c77a553aac0ad009441c856c05d117c1131e3d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xedb4833e1cae54b3b7637f71edacf9abfcfbd1bd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xf8c42655373a280e8800beee44fcc12ffc99e797 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x4b2dfca17caadc23c9d28eb77ca27b52731e3aba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x105a6f7c2f23270db6eed8d9ee8474323091d30c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x4152fdbf1ce1957b6fafd55737f96a26b787ee9a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x3f618967492945c02d5222d333e903345fde741a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xff577f0e828a878743ecc5e2632cbf65cecf17cf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x3e3dd517fec2e70eddba2a626422a4ba286e8c38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x433587150898e706b21d68b48833cdf274987743 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc99bcff6564bafc70ba1b53c53a03541f780a546 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xdc4359ca3a73c05b83759d3fe6618b499ff5f656 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xa07028b453a1f6ac277e93f3a0ea73b4be5c7d63 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc1fcd2a14df1a10f91cdd0d9b6191ca264356eec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x3f777c16829c7d1885a7a46912560f1ba764218d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xcbd38eba8170f475063bcc2c56cb213f8db1f9e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xbeea3b382696669e0e67c08ea9f4aae8d528af0f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xd864e059aae2d400ef3ab5b4d38b4370d63f1277 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x9e83bb5cf96fd382affb9f9f4d1bbdb49e7a5e7b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc4ea014402cddb4c6d2c4cb6d1c696eded93630a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xd8ba14df11d963bd3c00ada3569a361d1810d4b1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x663a655203ec965a3f642772ef49ba1e99c1520d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x1017e7b5efbb2d230979cf166078c1a96cdeeaef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xbffd6eebdd42038067b10e04d3682e6373278ffe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x7657d138111306459def4ba2285730f35ed6066f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x2dee3855d991a07ad3ed1fe3b26343320a122963 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xe5bacf3e9b092c71de7cfe28124beb4c9d85783c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x80643ea8601be7f65362d4c2dc17b435dfa22762 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xfc93ec2d9d0d390209365013aaa6358db9f77936 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x4d8295d7a043007760e1b2ea3ec07c93a906874d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x341584a7aa4cbfe1381009762aabdd0659542348 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xed1def6744ba38d53ef80b59ac010b6d9392bcae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xffbd38130ff590d0c4d82e3851f6f4fdf9a3d3ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x1fa900dbb20ed45d18883849c00632bca16f6610 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x81111cd2ab53ccb3d060c0fd7b303654151c3b9d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x6dbdeea3d4127913420eedd6ff25c7c6765e104a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x42e2209e6d9b4ed0cce3d06a5a5d7b65d577cf5c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zksync/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x86c2fd1c99d8b7ff541767a4748b2eb38fd43da8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xbc59f8f3b275aa56a90d13bae7cce5e6e11a3b17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x1ed9b524d6f395ecc61aa24537f87a0482933069 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xc3aee7c0e80f65ff13655955fa51d971e5d8d535 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xaea25cc307dc4bac390816f3d85edcbc805c589d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x33604ba99eb25c593cabd53c096c131a72a74752 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x3c127629c99355f3671f128ebef2f49b4d17e4e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x4684c6198243fcd8bcfa706d8e29b6b0531c6172 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xb14db2b0cdb55dcf97f7388a2f70b7ad28c80885 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x180efc1349a69390ade25667487a826164c9c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xc555d55279023e732ccd32d812114caf5838fd46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x69c7bd26512f52bf6f76fab834140d13dda673ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x0b07188b12e3bba6a680e553e23c4079e98a034b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xb771f724c504b329623b0ce9199907137670600e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x6ec94f50cadcc79984463688de42a0ca696ec2db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x0da7096f14303eddd634c0241963c064e0244984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/zora/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x970a7749ecaa4394c8b2bf5f2471f41fd6b79288 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xf7fd0860922bd3352e2dbaf725a182b74bf7a2e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xab22d1d671bb5cee8735c5ba29ea651ccda48a8e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xb003df4b243f938132e8cadbeb237abc5a889fb4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/ethereum/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x00c8e9500f32237beecfc8179ae064606d457577 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x5151c83ad4e1c8c4ea0d1eaf91c246a8c6dab2a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xad397a0472503b066ab4b311d66fa1f659f4cb61 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/arbitrum/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/optimism/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0xaf948ca24669f5ca9f5b6f90d9f2cef12f4a0d20 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x7d4324293304797cb662c6ea1b904b6af2b485f5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/pools/polygon/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x80e4f4f8bb89dab206c6bb4bddbf2ad72500394d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xdfcfdf5dd0569d591e0bce28b5da3b13de09e3cb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x73ccdeaa8957422c0a64e83f50c8814c7b33fe99 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/base/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe3f5da07bcbfeb310ca65a6f98656dd41c3d3b4a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x329eeb1a58bdb3b804d1f94623c7a29a67a9b6b7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/celo/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:18:53.078Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x65081cb48d74a32e9ccfed75164b8c09972dbcf1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x5b16de420b1d093b962c0bc03dd91b6d423f8c4a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x18b268965e4e702bdf13469205937894b8ab0ee8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x0d76866f78fcbca2730e60c5997d59d6ba585613 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x6b918c9f87b46a758c2b51bce427c8028dacb720 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xbda709a0665b340898856b8b29ff87079bb130d3 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xd49174dba635489c67fa628864c2d0d04824ebd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x9e2ef5522b2f9eac00912f25082f6e652123b54d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xd279f8bcc5f037f08cad776a0186acde0417c339 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xc673d5164103357a7537c36438a6326776a14bbd + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1dfab2147401be78ea05f05a0379f86ec87a81cc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x8eb4b07affbd1083f42032eed35cd32e382ee8b7 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x12095933f1eeb066176dd2e41e5a2f8be6974616 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xfbc45ab96d02e150b2ddeb7dd4eacd3d8c674f4a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xa9c6669de2c04c2adb22ac7a65d75b47fee30e35 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x18ce92e7a37d994657f97c3defaf880a805f08d5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xf2b8c40f98dae03f261d41f312bd204f68430acc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x9d7151413833dcb13ae284d6a7fccd93989c47a1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xa8c90356d7c7dc508eef63670927bb15a0dc0298 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xe97a0889d2b0660fddc144a8893b3ac9236756a6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x75951bafe00c9ec72df8597444f4e8fa156110a7 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x8927058918e3cff6f55efe45a58db1be1f069e49 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x48466012f56a07f89656dab41a996986602fd1aa - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x073b315457c5f4f5e658f6c06998a60abb5a7b90 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xc19bc89ac024426f5a23c5bb8bc91d8017c90684 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x859f7092f56c43bb48bb46de7119d9c799716cdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xe532b04d2f2e921dfec69e132e9214d2f82df304 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x2a6c361b43a2edcae08e2bd5448e90e9369cced9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x9c84f58bb51fabd18698efe95f5bab4f33e96e8f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x4e6c10e2b505a1e8324aa64d3a92de764cf86783 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x953764548d1ca834e2b73fcd0d26a495336c99c8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4361aaffc616809a8536ea3d5afff3d1b87921a4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc275a7390966e4bcbf331b837cd7316c4a3efa83 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x1e1dfff79d95725aaafd6b47af4fbc28d859ce28 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x7de4c593fe83417ca6ef98d7cf59c99d304f41c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xd38d1ab8a150e6ee0ae70c86a8e9fb0c83255b76 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x06d7874037e622d6ef42294cf32eb259806cb1c6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x37fd96e1d24f69f20172ab97040f806284a31ae5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xa4bc5aa6229e6f2baa4b8851b19342a1d1217c08 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1d6ae37db0e36305019fb3d4bad2750b8784adf9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x5c90b076af8cb73ef064efc09eae7936132bebcf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x9a601b332698e64aa90fd468ce858d504e43e7df - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xb431c70f800100d87554ac1142c4a94c5fe4c0c4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x1bc877326b683d8aeaca8dbbc604b649e5ad78e6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xedc625b74537ee3a10874f53d170e9c17a906b9c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1b01fba73ff847e3d96162a8bcd5426f6cde56a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x5c75bfb6194d7d763d33ea292cbc50cda806451b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/unichain/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xfae3f424a0a47706811521e3ee268f00cfb5c45e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x7b602f98d71715916e7c963f51bfebc754ade2d0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xb978a8c502ce97b04043036a91548b846067f9ea + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xd3249cfbb4c00dd81f377ef5113848c5cc848780 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x804226ca4edb38e7ef56d16d16e92dc3223347a0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x83c6bdb0355c6b69277a388416e4dca992a81d6f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9ba9c677d19347abfba1d6b6d6ceb61942071561 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xc566b786309bf2fe34dd48cea1267b13cead02bb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x5e8754aaa7c2da42c218f40435cbdedbae88bfc5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xeb7e0191f4054868d97f33ca7a4176b226ccbd2f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xd1356d360f37932059e5b89b7992692aa234eda6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x27b571f3e7f7827b13d927d2d59244e3e58a7d1a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xe54dadf5b4f8779256e1bbb94eca00b124311208 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xb2dc1235bf4b36628a8665aeb668bf202759528a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x724f6a02ed2eb82d8d45034b280903cf663731ab + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x2e587b9e7aa638d7eb7db5fe7447513bc4d0d28b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xa7141c79d3d4a9ad67ba95d2b97fe7eed9fb92b3 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x01c7c6066ec10b1cd4821e13b9fb063680ffa083 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x01ed0722f62a5dd3212b04aed2a2065623a705bb + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x28f577ed1cb3b0add148d745d9c0a2c0c40cc48d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x12089f58e0e7eadfc7d60cf1b1ed6839a811672a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x3859de09906bed098879cbef34d80817357244c7 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x178b99eaa2f6bc97d16f84edbce2e23ae6b8140b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0e663593657b064e1bae76d28625df5d0ebd4421 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x67324985b5014b36b960273353deb3d96f2f18c2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xd18384f4398bf5415902ad5d87eaa96549fd4f1e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x3885fbe4cd8aed7b7e9625923927fa1ce30662a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x97bb1788dcd64e17166931b87789cae97c154009 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x5281e311734869c64ca60ef047fd87759397efe6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x83b9a48793f4e5be12cfe1de3b1d55a83c4f1a9a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x4905220ca9d1001daa7be72e877243f4996002e0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x6d61ae1b0d94941dc702581daaaafc7665d1c6d7 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x2fa9d6085c91151200e61a3e627d35001772c0d1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9a68f0bbced6d6f1f63f0a61215742377ac9d325 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xa1d79325ec44d5d5a69119010823da0ec746e615 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xcea3c61705b68fefd8da1048ca69db1344180fcb + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xf39f633a18042105454ff64c6abd07beb8cbcead + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x7fd0055020a6d9c43c31c8ea755c5038ebd39722 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xe1e870fdff3ad67f2879542d841d8ab3e1406f4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc0a57a72cb7da55da2e50f664b1641570941618f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x43fb9c3fd6715e872272b0caab968a97692726eb + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xfc1e7bc60adfc151565c033521ee6ccbde027f54 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xd399718256c5206d9586e866169dbdf131193e02 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x57a63adebf02680c996a89413c324901dc0df801 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9d1bcb75a7bc5defe7daed505c462572b5e022f9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xe5028e8e8fb3488c2003c09fffc00876bc974b1a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xfd584fcc3a74429d85c9a2294eaf0f566ddfd593 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x90fbfd28f16fa163b914be6b9b3eeeb1c3e02fe5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xdfea50f83fd27967741f2220110449d8663a1b4f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x118bc22a76a71ff3187577422af2d57210277bab + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x94a0d838379e9703b69a777191d8a8951ada2e8e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xd312a35320a5936409edfd5e2eb0d17bcf99910b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x78b4c0301b7e6e3d31f259b3f112ced639f030a3 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xaf4ceda77ec614099ffe67b7f4fe4427d52e75cc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0e7a5a8177d5711dc90ba9a6ded590060f52dd29 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x704ad8d95c12d7fea531738faa94402725acb035 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9a601b332698e64aa90fd468ce858d504e43e7df + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x841820459769cd629b10a36fd12e603938cc2679 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x15309a0e8c954c46601d8028153125c3b92884b6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xd494b33229ee2c857a43b94ee7117be05e5fd88c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x38fd16cebb0ec8bbc3041a9c40b4394d2743e77a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xdbd8a9c9d060973557f3ac7c7f642a9523529d68 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x6200b24e69eb9a12c06d09f21b0335a9b0eeb227 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xcec63fa2b71744e1cdd48f71e34acedd46b496aa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0fbb556c73226b12cd7cfb2bed56e77fc177f0ec + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc566b786309bf2fe34dd48cea1267b13cead02bb + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xbc9df7f489b3d5d38da7c5a6f7d751bdaa88f254 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xd19c0dbbc5ba2ec4faa0e3fff892f0e95f23d9e0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x76a67a7fb64253fb4b0d80e1adbd71bd5865d68a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xff3d4af6aed14f62c202d55871f75c37951409c3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xc799b2440fd3b8618b87dfbf6b0dabf218e92274 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1ae3579d37ede91662003a9e9eed3997f3339eff - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x59ca2b1d97c98d585402531a7056321a9935e052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x8d58a3348f302f2daad2bab66a83849d900effd9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x5b16de420b1d093b962c0bc03dd91b6d423f8c4a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xbd50f13489f8901e692862f440a5062d5f1aa062 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1db0bca1d1a09e5a86213d349fa4fe33f8fe7fe2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x20a613705ed7ee62821ae0879bfc47a2450eb0dd - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x52393e963583683a75cff607792410bd48aa02d0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xfa20fa20b90c4c2411532c565cad7c14f9bc4a56 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x4d661231d973ff9c7c1b0e34a28b94bb70cb3894 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1c276bd1a3bc05b034d50a7369b0bd5b39223a65 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x338e7862fcae6c3dec1c49606c0fe297422fce39 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xed79818e8168083883d5279c181eb76054d16c87 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xff878f095ea20f29d3b0967052c96526fc6ee14f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xcd4255ceae51803a9333aa1a559991e17b024efc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xd68e61880190ebf2bda21a66d3064625866ddabd - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xecbe401654382b37ba3b5fea8950874b47e83a56 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x1a4abfe77b9aec6259757805bd0d3c46fca6d494 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/avalanche/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xf52b4b69123cbcf07798ae8265642793b2e8990c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x308c6fbd6a14881af333649f17f2fde9cd75e2a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x85287626e78602d0da569332e419154b0bdea035 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x1cf4cfc7a984a474ab03f444ccedb30c3ae6f56c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xfff8d5fff6ee3226fa2f5d7d5d8c3ff785be9c74 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xf5a23bdd36a56ede75d503f6f643d5eaf25b1a8f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x57529f9e2a23cc53fc387b162d2ab0f1df3ed701 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x7a11472726c3cf31b9a4c6900118aacd2a3e584a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xf4b646bc85458cc74497c773e2bc8b9ec1351e97 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xfdfc89d953e044f84faa2ed4953190a066328ee0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xf08ff66b8ec0db053711f4989c40b084564f7de3 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xff9722cb0712261a7f02a451dd178de10234ad0c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x0000fb2a9a0f3f35d72d7eedb8689c6db1d30225 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xbb60bb410182d8e96c41dfc92e017dd79f5100bf + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x45e844b4d3b81aba43697ac73c55204bd7b727e7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x0f6c5fdcb927b99b3040c609bef07bdfc59a6173 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xc0808a4d70b0c1d6811c3526ca358570516214fd - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xaeba85e3328d6b77b58130f43815ac9c59603d38 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x075b6f68b05fe21205f8827323a6f9b747b09e93 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xbf796b95d09729815806dd50de07c1111aa3926f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x8ce4c6020fccf7428d0b6ec2d4410c0442626630 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x63f59866a7e9a8628e7f1577ba55da134d64d8c2 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x78337095c61035056a85b7d430d6e9875f177ae2 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x258ba3b253e5cc3bab01c28d2f527aacd6d96793 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xa2e7d6051bed36e1552ba982dab7e6743785598e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x7b8086f5442f130a6be2d62dff02319018344feb + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x9dfb11cf311a8fa1296f6958057f8bb02be51a4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xf95141f5552e592dc58c41e54d65d0f645ab7d7e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x2aeee741fa1e21120a21e57db9ee545428e683c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xe6d7ebb9f1a9519dc06d557e03c522d53520e76a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x529d2863a1521d0b57db028168fde2e97120017c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x8d421b0d641193d67dd1aa024dab17fcde0bfc89 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x81c7294b66955824bc04acb642ae8dc58e6ce507 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xafecdd2fc04f0939d7b6835529677608470c063d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x1219b06380157f0ea0468f4f714d66e7f89d6956 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x8a82f2333101195b926e2c1dd56a116bb57d41c9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x40857abd8b284205a81fe6e0dfcebe261f47d854 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xe32915f74d76b6fe4945cb4c4c77474e2fdb6d63 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xe8a1f39c16eea2844e98f951d711bb4bb31557ad - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xfe530931da161232ec76a7c3bea7d36cf3811a0d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xc8da14d7467814a91384796db3ca2c273b30b361 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x0b1c2dcbbfa744ebd3fc17ff1a96a1e1eb4b2d69 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xf150d29d92e7460a1531cbc9d1abeab33d6998e4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x7b39b6693f4ca739b01ca1c42a343c8159d96e23 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4c1d39e6b736a6a99105ee2a9e7c44bfc56af860 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4f60db16e8235eae877305c1e9ef2f648be2971c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x15689276dc6e7d2702f7e1900c42f885284ffc46 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xcd695596b54099613b84862bc60907651c5917be - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xcbfbec4704a6e34a9c562796e0606750bcd73675 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x8bdacdf255a57b66d0adeb271f4f76403ff4dffa - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xb1baaf9b23dafb25775111a689c23f4016fd0a73 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x042c2083fb30d1324e102fffe527d1dc689d3c60 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x1688d62c82abebf4d33ecea96d983fc1627966f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x0b599ebf4e05af48b56d38e2dde520570c366460 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xe51a3d36ecad2d2fbeafb6295fe0beb4f8f8f30d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x23d17764f41aea93fdbb5beffa83571f0bf3f8b2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xa236278bec0e0677a48527340cfb567b4e6e9adc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/blast/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x387a86d863420ffa2ef88b2524e54513a0ded845 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4b54900d3801b7a27657a0e63ce7a819365e0940 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xc2b9d0b3db9afe1567d4952ec3aede504b7357cf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x7cbee2f89305beb746e2b0807365119a8ff2513b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x399f600c9667ee748d31821b2df0c004b3432dc9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x68fd2f81ac30ef3186dd618947f8be49edb27886 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xf9f588394ec5c3b05511368ce016de5fd3812446 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x827f0a2a4376bc26729f398b865f424dc8456841 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xd8cc6bfdee087148c220e9141a075d18418abbac - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x944b4364cd6faddfe8f83865aabdbf57d0d9358c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xdb18729070d3abdc72f9cd57d3b949540cc4486a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xf55791afbb35ad42984f18d6fe3e1ff73d81900c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x61ef8708fc240dc7f9f2c0d81c3124df2fd8829f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x87dec9a2589d9e6511df84c193561b3a16cf6238 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x2ac5baa668a8a58fd0e302b9896717484fd217b0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x6bab3afa6d0c42d539bcbc33ffb68c0406913413 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x14e577e42d45fd2200a9b0e31d87fe826467111a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x4495f525c4ecacf9713a51ec3e8d1e81d7dff870 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xc5a5caebf3bf6220a3efa222710ab488943a73f8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x40b3737b8984d14a2e8f96d8c680b2d475719fdf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xe92c7cc875245a86faf088febe4614e1e318bef5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xdb6e3b03a3255e5ddfc99b98f18e6e8557a2ed96 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x9331f571f79d1e186a095c93756b5680a43932ac - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe1acb466421ed24dd8bd381d1205bad0ad43ca9c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x2876ce407e520ba49177d83ff4e2c7338b6eeae4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xf12533a96712133d9bb97c24de5bcf52f48851bd - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x28dfa7adabff80fdf4ffd7db0c0796fb86d26700 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x1b4a3f6ad0b7254e373f47a3e442eea60af693fc - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x929fcf81102c5577243ee614c2c455acd6681f1a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/soneium/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x3202c46666e774b44ba463eafaa6da9a968a058f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x610e319b3a3ab56a0ed5562927d37c233774ba39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xdce053d9ba0fa2c5f772416b64f191158cbcc32e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xa6a2d598365cd758ba0a72cd95b7f8805248a5d5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x5f835420502a7702de50cd0e78d8aa3608b2137e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x494d68e3cab640fa50f4c1b3e2499698d1a173a0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x052e77018bfb98dc6373c37d757bb1d3fad09ec5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x02371da6173cf95623da4189e68912233cc7107c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xeff7f8fe083d7a446717b992bf84391253e54789 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x6787de741bb42ca7ff7dd1b9aad6098c850cdc6a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xf9bb9137256193af73d2e8b3e377727756fd98be - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x7c3c2a92598d6d77e57fd55c50e99af4b291f595 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x56505f73cf8f5ef637bb37d9e635c3f520c9b0e5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x7840fafdea1292068c1167f5da035a54bd31fae5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xad710fbc1161b26ea427c158f49a93f6d9d871b4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xed85676d391be656e6efa22ea4f3c663ffdd1683 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe6b4903642b4a1637d7b411d009af5c91617860c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x6a61d4c9c359d2ce0203378983beb4f69f85106c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x97e4442bca2c069f9060f3b8eef52eb25c98c245 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xd40be16e9c2bddd7f36dd31c1ae39ca6cb2b20ce - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x40f4928332584198d5c68f3f39631245dda5c200 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xeb2a20f397c37f9f36799dab5dbe762da58c5194 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x873056a02255872514f05249d93228d788fe4fb4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x53ead11073fc0651dce70572666f0ed0752abfea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xa0ca5bebc42cdbf3623b1c09206ae4e3975b0fc7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x18aa3faeedb077aa604d748587093adcc9c5172b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xb655dc66ecead581d1f1a5759c2c37c2dbef2275 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x2e8b5cf35680f8b7df0957d05c6a0a4ae1d00cde - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x87c0676255be413399c9a205d9cbeb2a04814cca - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xc09b0b0bde3b1f97ed3d3b5f5b0a74a74ee99768 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x9950c1f3754fb8a3ebbaf24b8573cafc7474c00f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x9fca4c864a30c03c21cc8743ee0c73312deda0ec - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x9b03dd0d04c2953a6a74471c53f24f15f592df1e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x260e34c78b27ece3fc6de8cd170e3ffb49fac35f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x3dc10d7bfb94eeb009203e84a653e5764f71771d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x0e5828c7eb7c7c9066201718f7ff044cf9fa10d9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xb0828467656ef9a9804e0d57da3fbd79374877f4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x075138693e0d65fa038753e3f3f97aad0989dd43 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/worldchain/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xcd83055557536eff25fd0eafbc56e74a1b4260b3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x3d7264539e6e3f596bb485e3091f3ae02ad01ef8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xeecb86c38c4667b46487255f41c6904df3d76f8f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x23c77a553aac0ad009441c856c05d117c1131e3d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x91308bc9ce8ca2db82aa30c65619856cc939d907 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xa0769a3c6af68812bb3a5cbd511f7879033440eb + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x1442097733acf0a2b5c4ab422f1c0186e95d52ba - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xf8c42655373a280e8800beee44fcc12ffc99e797 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x4b2dfca17caadc23c9d28eb77ca27b52731e3aba + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x29f07e631a2f990e1f6117c6285a44e746b1f090 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x105a6f7c2f23270db6eed8d9ee8474323091d30c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x4152fdbf1ce1957b6fafd55737f96a26b787ee9a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x3f618967492945c02d5222d333e903345fde741a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x393e58375ca7bcaa89ed90e661ee7cc46466eccf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xff577f0e828a878743ecc5e2632cbf65cecf17cf + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x3e3dd517fec2e70eddba2a626422a4ba286e8c38 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x433587150898e706b21d68b48833cdf274987743 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc99bcff6564bafc70ba1b53c53a03541f780a546 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x91020dd18c800eb5fee593ad58902b0ebdcfbfd2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xdc4359ca3a73c05b83759d3fe6618b499ff5f656 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xa07028b453a1f6ac277e93f3a0ea73b4be5c7d63 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc1fcd2a14df1a10f91cdd0d9b6191ca264356eec + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xa4f1768e3e1cd62c6faf4deab1ccef804a50c34e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x3f777c16829c7d1885a7a46912560f1ba764218d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xcbd38eba8170f475063bcc2c56cb213f8db1f9e1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xbeea3b382696669e0e67c08ea9f4aae8d528af0f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xd864e059aae2d400ef3ab5b4d38b4370d63f1277 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x9e83bb5cf96fd382affb9f9f4d1bbdb49e7a5e7b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x3016a43b482d0480460f6625115bd372fe90c6bf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc4ea014402cddb4c6d2c4cb6d1c696eded93630a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x4efc6d91b5170b670a1bd5cfb8d4ae50283400eb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xd8ba14df11d963bd3c00ada3569a361d1810d4b1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x993defde3e6ef50610eb6d994823dc82565ad3ba - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x663a655203ec965a3f642772ef49ba1e99c1520d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xab04a352d4ec8d0f1812a4e5ebe7807fa67acd48 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x1017e7b5efbb2d230979cf166078c1a96cdeeaef + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x49768b215014fac2c66680b03045fe32936b21e6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xedb4833e1cae54b3b7637f71edacf9abfcfbd1bd + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x5ae13baaef0620fdae1d355495dc51a17adb4082 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xbffd6eebdd42038067b10e04d3682e6373278ffe + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xdea629c5587037d0925ff85f1961d95db62bedd6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x7657d138111306459def4ba2285730f35ed6066f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xba51d338a319f5e1b5da46065946576149874acf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x2dee3855d991a07ad3ed1fe3b26343320a122963 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe47727f8c9256ab31dd97d431dfe9a5d9c098238 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xe5bacf3e9b092c71de7cfe28124beb4c9d85783c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xafe235b62d5d7fe6335428d52df1a6204de002b8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x80643ea8601be7f65362d4c2dc17b435dfa22762 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x76c54e356ccf357882e5632127983d6c975d7573 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xfc93ec2d9d0d390209365013aaa6358db9f77936 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xd491076c7316bc28fd4d35e3da9ab5286d079250 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x4d8295d7a043007760e1b2ea3ec07c93a906874d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x7d98804119551f915a75ce278526ac7ee5077087 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xed1def6744ba38d53ef80b59ac010b6d9392bcae + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xcc630802c847766bf386fb7f4403ee5990f13e0a - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x341584a7aa4cbfe1381009762aabdd0659542348 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x9d66f536b5d0d4a6086ffbef06a12c5caa9a1460 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xffbd38130ff590d0c4d82e3851f6f4fdf9a3d3ab + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xf5ffdca301d3b2a0f4b3de313e4a176515d9bdbf - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x1fa900dbb20ed45d18883849c00632bca16f6610 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x6b86794e11cfb1d454d9b38ac5ab5ee2d1f87434 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x81111cd2ab53ccb3d060c0fd7b303654151c3b9d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x89692e5e664c923b1dbbba13c57e07a0bacc5207 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x6dbdeea3d4127913420eedd6ff25c7c6765e104a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x0ab8855c73d567f0ec633cddec43580c5f7e2555 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x42e2209e6d9b4ed0cce3d06a5a5d7b65d577cf5c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xe04b2344211942751334e848f717a27a10eb8c45 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x75a0eae18bac14a3e9bebb4e2e5a12a926f2d230 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x64850db70da55c031c9b6b8518502988b5734633 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xaacb6baf9cf61be287b8744a0e0c1027709dc26f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xd828b499b3ed234af94c4d5521c1a82a787da86e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x1846464de29a2e0d7cfbd3f350d7cb4675236d48 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x919b20ac45304aeb09c9df5c604b3cd9d99a51ca - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x9144a6e8ad6f56db96dac444eb173831660ba3e5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xfb559d225343a61884d46eee91c1a805759f758b - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x30db6dfdb8817765797bd62316e41f5f4e431e93 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x7deef378b6befa291e2e255294e532b2c1bca419 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x8bfb0fb037b30562fdb7be3f71440575664ab74e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x9338f9d099be4011991f8ca784d67acdf901f376 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x2e066d83ad074b1a43cd14af5e08f2cf8a275021 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xef947aa8af8160cf78455292eaf3deb6b39e4bef - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x5b2c9ef1bf180a844389c0fd42b15c8281e69052 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xe101e63ee82f4c8acc5e8f0e03da8b444be71c68 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc74f05c1e7b86fa42ab07ab4a1361286b9a90087 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x31fa55e03bad93c7f8affdd2ec616ebfde246001 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x263f7b865de80355f91c00dfb975a821effbea24 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xbc5592c48bf9d4354de4953f57de4f6295dd51b0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x0893e340ee2b0263ddad2f3b8bd23dba11859aea - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zksync/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x86c2fd1c99d8b7ff541767a4748b2eb38fd43da8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xbc59f8f3b275aa56a90d13bae7cce5e6e11a3b17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x1ed9b524d6f395ecc61aa24537f87a0482933069 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xff8dd24ffd38eca6bbe58c7aca49abc2d9abb574 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xc3aee7c0e80f65ff13655955fa51d971e5d8d535 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xeab00687c6558cd648ec288f58de4b0a6de026ba - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xaea25cc307dc4bac390816f3d85edcbc805c589d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4ef938b633d704f29e593a8b51148d43429d0bc4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x33604ba99eb25c593cabd53c096c131a72a74752 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x85144212383a34479f3b0abe6f6f9ee69240fb55 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x3c127629c99355f3671f128ebef2f49b4d17e4e1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x4684c6198243fcd8bcfa706d8e29b6b0531c6172 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xb14db2b0cdb55dcf97f7388a2f70b7ad28c80885 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0x5bcebcee72f13004f1d00d7da7bf22b082f93f70 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x7b73644935b8e68019ac6356c40661e1bc315860 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0x2837809fd68e4a4104af76bbec5b622b6146b2cb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/ethereum/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x52c77b0cb827afbad022e6d6caf2c44452edbc39 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xb4e27c8e10856daa165a852f44462d1ca945e25c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0x68ebadf62ee5acce5f8d64211d24b4710eeb2029 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x180efc1349a69390ade25667487a826164c9c6e4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/arbitrum/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0x1b19825a9e32b1039080acb1e1f9271314938b96 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xc555d55279023e732ccd32d812114caf5838fd46 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/optimism/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x718a038c465efd88962fafe1008d233bf52bef44 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x25647e01bd0967c1b9599fa3521939871d1d0888 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0x9913a93c082fdc69f9e7d146b0e4ce9070d5a104 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x09d1d767edf8fa23a64c51fa559e0688e526812f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x48d20b3e529fb3dd7d91293f80638df582ab2daa + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/polygon/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xb4cb800910b228ed3d0834cf79d697127bbb00e5 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x1ffec7119e315b15852557f654ae0052f76e6ae1 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xd3d2e2692501a5c9ca623199d38826e513033a17 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/base/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x4610464356ba6bba15eec558619d84b72fea260f - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x65d5b99ba46b29005856ddb7d5c6675aab77b204 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0x5066103530d8de7e582ff2f623c59f4b2eca8bf6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xf7932a224761fb8df2db546dd0e587e60439b4b4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x8c1c499b1796d7f3c2521ac37186b52de024e58c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xb1c5374164f17fc74f7401fb4ea88c2604ba75c4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x7924a818013f39cf800f5589ff1f1f0def54f31f + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/bnb/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x470e8de2ebaef52014a47cb5e6af86884947f08c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x57332c214e647063bb4c5a73e5a8b7bba79be1e4 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xf6c4e4f339912541d3f8ed99dba64a1372af5e5b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0x8917ba2a352c6d3e1acd5ea0a0bf7f203046149e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xb771f724c504b329623b0ce9199907137670600e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x0f23d49bc92ec52ff591d091b3e16c937034496e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/celo/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/unichain/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0x7145084e49429057f28d4c5c955cf277a027ae93 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x69c7bd26512f52bf6f76fab834140d13dda673ca + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x0b07188b12e3bba6a680e553e23c4079e98a034b + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/avalanche/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xc5be99a02c6857f9eac67bbce58df5572498f40c + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x5ced44f03ff443bbe14d8ea23bc24425fb89e3ed + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/blast/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/soneium/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xaf21b0ec0197e63a5c6cc30c8e947eb8165c6212 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xc91ef786fbf6d62858262c82c63de45085dea659 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/worldchain/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x0da7096f14303eddd634c0241963c064e0244984 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zksync/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x2aeee741fa1e21120a21e57db9ee545428e683c9 + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xa2107fa5b38d9bbd2c461d6edf11b11a50f6b974 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e + 2025-03-20T21:32:51.328Z 0.8 - https://app.uniswap.org/explore/pools/zora/0xc2b7888a8d7b62e2a518bbc79fbbd6b75da524b6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/pools/zora/0x96aa22baedc5a605357e0b9ae20ab6b10a472e03 + 2025-03-20T21:32:51.328Z 0.8 \ No newline at end of file diff --git a/apps/web/public/sitemap.xml b/apps/web/public/sitemap.xml index f344621757a..b035ec1e5db 100644 --- a/apps/web/public/sitemap.xml +++ b/apps/web/public/sitemap.xml @@ -9,7 +9,4 @@ https://app.uniswap.org/pools-sitemap.xml - - https://app.uniswap.org/nfts-sitemap.xml - diff --git a/apps/web/public/vercel-csp.json b/apps/web/public/staging-csp.json similarity index 69% rename from apps/web/public/vercel-csp.json rename to apps/web/public/staging-csp.json index bdf0d87f9a3..691682ae648 100644 --- a/apps/web/public/vercel-csp.json +++ b/apps/web/public/staging-csp.json @@ -1,4 +1,5 @@ { "defaultSrc": ["https://vercel.live/", "https://vercel.com"], - "scriptSrc": ["'sha256-jhb+qiJiVBxruYChJWIrsskRE2fAoV/F/B10cAoiSB0='"] + "scriptSrc": ["'sha256-jhb+qiJiVBxruYChJWIrsskRE2fAoV/F/B10cAoiSB0='"], + "connectSrc": ["https://*.corn-staging.com"] } diff --git a/apps/web/public/tokens-sitemap.xml b/apps/web/public/tokens-sitemap.xml index a865ac527e2..72c1eed8bad 100644 --- a/apps/web/public/tokens-sitemap.xml +++ b/apps/web/public/tokens-sitemap.xml @@ -2,11117 +2,3382 @@ https://app.uniswap.org/explore/tokens/ethereum/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdac17f958d2ee523a2206206994597c13d831ec7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6982508145454ce325ddbe47a25d4ec3d2311933 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6b175474e89094c44da98b954eedeac495271d0f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6123b0049f904d730db3c36a31167d9d4121fa6b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcf0c122c6b73ff809c693db761e7baebe62b6a2e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xfaba6f8e4a5e8ab82f62fe7c39859fa577269be3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x58cb30368ceb2d194740b144eab4c2da8a917dcb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4c9edd5852cd905f086c759e8383e09bff1e68b3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaaee1a9723aadb7afa2810263653a34ba2c21c7a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x514910771af9ca656af840dff83e8264ecf986ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5b7533812759b45c2b44c19e320ba2cd2681b542 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xae78736cd615f374d3085123a210448e74fc6393 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb9f599ce614feb2e1bbe58f180f370d05b39344e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd5f7838f5c461feff7fe49ea5ebaf7728bb0adfa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd31a59c85ae9d8edefec411d448f90841571b89c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6a7eff1e2c355ad6eb91bebb5ded49257f3fed98 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x576e2bed8f7b46d34016198911cdf9886f78bea7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1258d60b224c0c5cd888d37bbf31aa5fcfb7e870 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x62d0a8458ed7719fdaf978fe5929c6d342b0bfce - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x77e06c9eccf2e797fd462a92b6d7642ef85b0a44 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x24fcfc492c1393274b6bcd568ac9e225bec93584 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x27702a26126e0b3702af63ee09ac4d1a084ef628 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd46ba6d942050d489dbd938a2c909a5d5039a161 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbe9895146f7af43049ca1c1ae358b0541ea49704 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x72f713d11480dcf08b37e1898670e736688d218d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0001a500a6b18995b03f44bb040a5ffc28e45cb0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9e9fbde7c7a83c43913bddc8779158f1368f0413 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5f98805a4e8be255a32880fdec7f6728c6568ba0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2b591e99afe9f32eaa6214f7b7629768c40eeb39 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1ae7e1d0ce06364ced9ad58225a1705b3e5db92b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x046eee2cc3188071c02bfc1745a6b17c656e3f3d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x84018071282d4b2996272659d9c01cb08dd7327f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x12970e6868f88f6557b76120662c1b3e50a646bf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaea46a60368a7bd060eec7df8cba43b7ef41ad85 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6de037ef9ad2725eb40118bb1702ebb27e4aeb24 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc01154b4ccb518232d6bbfc9b9e6c5068b766f82 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5a98fcbea516cf06857215779fd812ca3bef1b32 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x102c776ddb30c754ded4fdcc77a19230a60d4e4f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x72e4f9f808c49a2a61de9c5896298920dc4eeea9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x467719ad09025fcc6cf6f8311755809d45a5e5f3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf19308f923582a6f7c465e5ce7a9dc1bec6665b1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x710287d1d39dcf62094a83ebb3e736e79400068a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf951e335afb289353dc249e82926178eac7ded78 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf017d3690346eb8234b85f74cee5e15821fee1f4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8c282c35b5e1088bb208991c151182a782637699 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xeaa63125dd63f10874f99cdbbb18410e7fc79dd3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xde342a3e269056fc3305f9e315f4c40d917ba521 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2dff88a56767223a5529ea5960da7a3f5f766406 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x626e8036deb333b408be468f951bdb42433cbf18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdd66781d0e9a08d4fbb5ec7bac80b691be27f21d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb23d80f5fefcddaa212212f028021b41ded428cf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbaac2b4491727d78d2b78815144570b9f2fe8899 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf8ebf4849f1fa4faf0dff2106a173d3a6cb2eb3a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb90b2a35c65dbc466b04240097ca756ad2005295 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1614f18fc94f47967a3fbe5ffcd46d4e7da3d787 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf1df7305e4bab3885cab5b1e4dfc338452a67891 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x91fbb2503ac69702061f1ac6885759fc853e6eae - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa9e8acf069c58aec8825542845fd754e41a9489a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2c95d751da37a5c1d9c5a7fd465c1d50f3d96160 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe453c3409f8ad2b1fe1ed08e189634d359705a5b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x89d584a1edb3a70b3b07963f9a3ea5399e38b136 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4507cef57c46789ef8d1a19ea45f4216bae2b528 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd1d2eb1b1e90b638588728b4130137d262c87cae - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe92344b4edf545f3209094b192e46600a19e7c2d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8a0a9b663693a22235b896f70a229c4a22597623 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1bbe973bef3a977fc51cbed703e8ffdefe001fed - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa41d2f8ee4f47d3b860a149765a7df8c3287b7f0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x761d38e5ddf6ccf6cf7c55759d5210750b5d60f3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc18360217d8f7ab5e7c516566761ea12ce7f9d72 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe28b3b32b6c345a34ff64674606124dd5aceca30 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x168e209d7b2f58f1f24b8ae7b7d35e662bbf11cc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb131f4a55907b10d1f0a50d8ab8fa09ec342cd74 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3472a5a71965499acd81997a54bba8d852c6e53d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7dd9c5cba05e151c895fde1cf355c9a1d5da6429 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x19efa7d0fc88ffe461d1091f8cbe56dc2708a84f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x14fee680690900ba0cccfc76ad70fd1b95d10e16 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3c3a81e81dc49a522a592e7622a7e711c06bf354 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa1290d69c65a6fe4df752f95823fae25cb99e5a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x92f419fb7a750aed295b0ddf536276bf5a40124f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2c06ba9e7f0daccbc1f6a33ea67e85bb68fbee3a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3d658390460295fb963f54dc0899cfb1c30776df - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8e870d67f660d95d5be530380d0ec0bd388289e1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x853d955acef822db058eb8505911ed77f175b99e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1294f4183763743c7c9519bec51773fb3acd78fd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4e15361fd6b4bb609fa63c81a2be19d873717870 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x695d38eb4e57e0f137e36df7c1f0f2635981246b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x40a7df3df8b56147b781353d379cb960120211d7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaaef88cea01475125522e117bfe45cf32044e238 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x163f8c2467924be0ae7b5347228cabf260318753 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x30672ae2680c319ec1028b69670a4a786baa0f35 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc944e90c64b2c07662a292be6244bdf05cda44a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x15e6e0d4ebeac120f9a97e71faa6a0235b85ed12 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7d225c4cc612e61d26523b099b0718d03152edef - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x82af49447d8a07e3bd95bd0d56f35241523fbab1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xaf88d065e77c8cc2239327c5edb3a432268e5831 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xff970a61a04b1ca14834a43f5de4533ebddb5cc8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x912ce59144191c1204e64559fe8253a0e49e6548 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x5979d7b546e38e414f7e9822514be443a4800529 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x35751007a407ca6feffe80b3cb397736d2cf4dbe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xda10009cbd5d07dd0cecc66161fc93d7c9000da1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xeb466342c4d449bc9f53a865d5cb90586f405215 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x0c880f6761f1af8d9aa9c466984b80dab9a8c9e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf97f4df75117a78c1a5a0dbb814af92458539fb4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9623063377ad1b27544c965ccd7342f7ea7e88c7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x539bde0d7dbd336b79148aa742883198bbf60342 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3082cc23568ea640225c2467653db90e9250aaa0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x18c11fd286c5ec11c3b683caa813b77f5163a122 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x289ba1701c2f088cf0faf8b3705246331cb8a839 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4cb9a7ae498cedcbb5eae9f25736ae7d428c9d66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x00cbcf7b3d37844e44b888bc747bdd75fcf4e555 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd79bb960dc8a206806c3a428b31bca49934d18d7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3096e7bfd0878cc65be71f8899bc4cfb57187ba3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x13ad51ed4f1b7e9dc168d8a00cb3f4ddd85efa60 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4e352cf164e64adcbad318c3a1e222e9eba4ce42 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x11cdb42b0eb46d95f990bedd4695a6e3fa034978 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xba5ddd1f9d7f570dc94a51479a000e3bce967196 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc8ccbd97b96834b976c995a67bf46e5754e2c48e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd07d35368e04a839dee335e213302b21ef14bb4a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x323665443cef804a3b5206103304bd4872ea4253 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x83d6c8c06ac276465e4c92e7ac8c23740f435140 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x87aaffdf26c6885f6010219208d5b161ec7609c0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1b8d516e2146d7a32aca0fcbf9482db85fd42c3a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xafccb724e3aec1657fc9514e3e53a0e71e80622d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4425742f1ec8d98779690b5a3a6276db85ddc01a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xec70dcb4a1efa46b8f2d97c310c9c4790ba5ffa8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3419875b4d3bca7f3fdda2db7a476a79fd31b4fe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3b60ff35d3f7f62d636b067dd0dc0dfdad670e4e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x58b9cb810a68a7f3e1e4f8cb45d1b9b3c79705e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfa5ed56a203466cbbc2430a43c66b9d8723528e7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x95146881b86b3ee99e63705ec87afe29fcc044d9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x088cd8f5ef3652623c22d48b1605dcfe860cd704 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xbfd5206962267c7b4b4a8b3d76ac2e1b2a5c4d5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6daf586b7370b14163171544fca24abcc0862ac5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9d2f299715d94d8a7e6f5eaa8e654e8c74a988a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x580e933d90091b9ce380740e3a4a39c67eb85b4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x655a6beebf2361a19549a99486ff65f709bd2646 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9e64d3b9e8ec387a9a58ced80b71ed815f8d82b5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2297aebd383787a160dd0d9f71508148769342e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6694340fc020c5e6b96567843da2df01b2ce1eb6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x772598e9e62155d7fdfe65fdf01eb5a53a8465be - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x431402e8b9de9aa016c743880e04e517074d8cec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd74f5255d557944cf7dd0e45ff521520002d5748 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6fd58f5a2f3468e35feb098b5f59f04157002407 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x561877b6b3dd7651313794e5f2894b2f18be0766 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf9ca0ec182a94f6231df9b14bd147ef7fb9fa17c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd77b108d4f6cefaa0cae9506a934e825becca46e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd56734d7f9979dd94fae3d67c7e928234e71cd4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf1264873436a0771e440e2b28072fafcc5eebd01 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x5575552988a3a80504bbaeb1311674fcfd40ad4b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x0341c0c0ec423328621788d4854119b97f44e391 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x764bfc309090e7f93edce53e5befa374cdcb7b8e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xaaa6c1e32c55a7bfa8066a6fae9b42650f262418 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9e20461bc2c4c980f62f1b279d71734207a6a356 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x7fb7ede54259cb3d4e1eaf230c7e2b1ffc951e9a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3a18dcc9745edcd1ef33ecb93b0b6eba5671e7ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x000000000026839b3f4181f2cf69336af6153b99 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x8b0e6f19ee57089f7649a455d89d7bc6314d04e8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x31c91d8fb96bff40955dd2dbc909b36e8b104dde - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x25d887ce7a35172c62febfd67a1856f20faebb00 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd4d42f0b6def4ce0383636770ef773390d85c61a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf8388c2b6edf00e2e27eef5200b1befb24ce141d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x619c82392cb6e41778b7d088860fea8447941f4c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x94025780a1ab58868d9b2dbbb775f44b32e8e6e5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xad4b9c1fbf4923061814dd9d5732eb703faa53d4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd7a892f28dedc74e6b7b33f93be08abfc394a360 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3269a3c00ab86c753856fd135d97b87facb0d848 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4568ca00299819998501914690d6010ae48a59ba - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x21e60ee73f17ac0a411ae5d690f908c3ed66fe12 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd3188e0df68559c0b63361f6160c57ad88b239d8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2b41806cbf1ffb3d9e31a9ece6b738bf9d6f645f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf19547f9ed24aa66b03c3a552d181ae334fbb8db - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x35e6a59f786d9266c7961ea28c7b768b33959cbb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x59a729658e9245b0cf1f8cb9fb37945d2b06ea27 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb56c29413af8778977093b9b4947efeea7136c36 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x43ab8f7d2a8dd4102ccea6b438f6d747b1b9f034 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1d987200df3b744cfa9c14f713f5334cb4bc4d5d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3404149e9ee6f17fb41db1ce593ee48fbdcd9506 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x080f6aed32fc474dd5717105dba5ea57268f46eb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb5a628803ee72d82098d4bcaf29a42e63531b441 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1622bf67e6e5747b81866fe0b85178a93c7f86e3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x7dd747d63b094971e6638313a6a2685e80c7fb2e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xa2f9ecf83a48b86265ff5fd36cdbaaa1f349916c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x17a8541b82bf67e10b0874284b4ae66858cb1fd5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xbcd4d5ac29e06e4973a1ddcd782cd035d04bc0b7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x42069d11a2cc72388a2e06210921e839cfbd3280 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xbbea044f9e7c0520195e49ad1e561572e7e1b948 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe85b662fe97e8562f4099d8a1d5a92d4b453bf30 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3d9907f9a368ad0a51be60f7da3b97cf940982d8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4e51ac49bc5e2d87e0ef713e9e5ab2d71ef4f336 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4200000000000000000000000000000000000006 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x7f5c764cbc14f9669b88837ca1490cca17c31607 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4200000000000000000000000000000000000042 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x0b2c639c533813f4aa9d7837caf62653d097ff85 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x1f32b1c2345538c0c6f582fcb022739c4a194ebb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x68f180fcce6836688e9084f035309e29bf0a2095 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x94b008aa00579c1307b0ef2c499ad98a8ce58e58 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xda10009cbd5d07dd0cecc66161fc93d7c9000da1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xdc6ff44d5d932cbd77b52e5612ba0529dc6226f1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x8700daec35af8ff88c16bdf0418774cb3d7599b4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x920cf626a271321c151d027030d5d08af699456b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9e1028f5f1d5ede59748ffcee5532509976840e0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xeb466342c4d449bc9f53a865d5cb90586f405215 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x350a791bfc2c21f9ed5d10980dad2e2638ffa7f6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x17aabf6838a6303fc6e9c5a227dc1eb6d95c829a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xf467c7d5a4a9c4687ffc7986ac6ad5a4c81e1404 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x76fb31fb4af56892a25e32cfc43de717950c9278 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xc5b001dc33727f8f26880b184090d3e252470d45 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9560e827af36c94d2ac33a39bce1fe78631088db - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9bcef72be871e61ed4fbbc7630889bee758eb81d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x50c5725949a6f0c72e6c4a641f24049a917db0cb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xf98dcd95217e15e05d8638da4c91125e59590b07 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4b03afc91295ed778320c2824bad5eb5a1d852dd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xc40f949f8a4e094d1b49a23ea9241d289b7b2819 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x323665443cef804a3b5206103304bd4872ea4253 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x50bce64397c75488465253c0a034b8097fea6578 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x296f55f8fb28e498b858d0bcda06d955b2cb3f97 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x2598c30330d5771ae9f983979209486ae26de875 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x0994206dfe8de6ec6920ff4d779b0d950605fb53 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xc3248a1bd9d72fa3da6e6ba701e58cbf818354eb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x6fd9d7ad17242c41f7131d257212c54a0e816691 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x14778860e937f509e651192a90589de711fb88a9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xdfa46478f9e5ea86d57387849598dbfb2e964b02 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9b88d293b7a791e40d36a39765ffd5a1b9b5c349 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x3eb398fec5f7327c6b15099a9681d9568ded2e82 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x217d47011b23bb961eb6d93ca9945b7501a5bb11 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xbfd5206962267c7b4b4a8b3d76ac2e1b2a5c4d5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x1cef2d62af4cd26673c7416957cc4ec619a696a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9fd22a17b4a96da3f83797d122172c450381fb88 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xaddb6a0412de1ba0f936dcaeb8aaa24578dcf3b2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2791bca1f2de4661ed88a30c99a7a9449aa84174 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3c499c542cef5e3811e1192ce70d8cc03d5c3359 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc2132d05d31c914a87c6611c10748aeb04b58e8f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x53e0bca35ec356bd5dddfebbd1fc0fd03fabad39 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x61299774020da444af134c82fa83e3810b309991 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd6df932a45c0f255f85145f286ea0b292b21c90b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2ad2934d5bfb7912304754479dd1f096d5c807da - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc3c7d422809852031b44ab29eec9f1eff2a58756 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8f3cf7ad23cd3cadbd9735aff958023239c6a063 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x750e4c4984a9e0f12978ea6742bc1c5d248f40ed - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x111111517e4929d3dcbdfa7cce55d30d4b6bc4d6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd0258a3fd00f38aa8090dfee343f10a9d4d30d3f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x430ef9263e76dae63c84292c3409d61c598e9682 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb33eaad8d922b1083446dc23f610c2567fb5180f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xdc3326e71d45186f113a2f448984ca0e8d201995 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x311434160d7537be358930def317afb606c0d737 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe3f2b1b2229c0333ad17d03f179b87500e7c5e01 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xac0f66379a6d7801d7726d5a943356a172549adb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf88332547c680f755481bf489d890426248bb275 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe5417af564e4bfda1c483642db72007871397896 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe261d618a959afffd53168cd07d12e37b26761db - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe0b52e49357fd4daf2c15e02058dce6bc0057db4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xbbba073c31bf03b8acf7c28ef0738decf3695683 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe238ecb42c424e877652ad82d8a939183a04c35f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3b56a704c01d650147ade2b8cee594066b3f9421 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x5fe2b58c013d7601147dcdd68c143a77499f5531 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x172370d5cd63279efa6d502dab29171933a610af - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x53df32548214f51821cf1fe4368109ac5ddea1ff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xff76c0b48363a7c7307868a81548d340049b0023 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x6f8a06447ff6fcf75d803135a7de15ce88c1d4ec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x50b728d8d964fd00c2d0aad81718b71311fef68a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3a58a54c066fdc0f2d55fc9c89f0415c92ebf3c4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x03b54a6e9a984069379fae1a4fc4dbae93b3bccd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd93f7e271cb87c23aaa73edc008a79646d1f9912 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x200c234721b5e549c3693ccc93cf191f90dc2af9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x11cd37bb86f65419713f30673a480ea33c826872 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8a16d4bf8a0a716017e8d2262c4ac32927797a2f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa1c57f48f0deb89f569dfbe6e2b7f46d33606fd4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x190eb8a183d22a4bdf278c6791b152228857c033 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2f6f07cdcf3588944bf4c42ac74ff24bf56e7590 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x235737dbb56e8517391473f7c964db31fa6ef280 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0b220b82f3ea3b7f6d9a1d8ab58930c064a2b5bf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8bff1bd27e2789fe390acabc379c380a83b68e84 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb58458c52b6511dc723d7d6f3be8c36d7383b4a8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x323665443cef804a3b5206103304bd4872ea4253 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2760e46d9bb43dafcbecaad1f64b93207f9f0ed7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x18ec0a6e18e5bc3784fdd3a3634b31245ab704f6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x431d5dff03120afa4bdf332c61a6e1766ef37bdb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x6f7c932e7684666c9fd1d44527765433e01ff61d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xeee3371b89fc43ea970e908536fcddd975135d8a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe5b49820e5a1063f6f4ddf851327b5e8b2301048 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xaa3717090cddc9b227e49d0d84a28ac0a996e6ff - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x62a872d9977db171d9e213a5dc2b782e72ca0033 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x381caf412b45dac0f62fbeec89de306d3eabe384 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe0bceef36f3a6efdd5eebfacd591423f8549b9d5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x23d29d30e35c5e8d321e1dc9a8a61bfd846d4c5c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x282d8efce846a88b159800bd4130ad77443fa1a1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x74dd45dd579cad749f9381d6227e7e02277c944b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x714db550b574b3e927af3d93e26127d15721d4c2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xfa68fb4628dff1028cfec22b4162fccd0d45efb6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe631dabef60c37a37d70d3b4f812871df663226f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xdb725f82818de83e99f1dac22a9b5b51d3d04dd4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3c59798620e5fec0ae6df1a19c6454094572ab92 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0d0b8488222f7f83b23e365320a4021b12ead608 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa380c0b01ad15c8cf6b46890bddab5f0868e87f3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8a953cfe442c5e8855cc6c61b1293fa648bae472 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x45c32fa6df82ead1e2ef74d17b76547eddfaff89 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x11cd72f7a4b699c67f225ca8abb20bc9f8db90c7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0c9c7712c83b3c70e7c5e11100d33d9401bdf9dd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x77a6f2e9a9e44fd5d5c3f9be9e52831fc1c3c0a0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xbfc70507384047aa74c29cdc8c5cb88d0f7213ac - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xfcb54da3f4193435184f3f647467e12b50754575 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9a6a40cdf21a0af417f1b815223fd92c85636c58 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe111178a87a3bff0c8d18decba5798827539ae99 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x82617aa52dddf5ed9bb7b370ed777b3182a30fd1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2ab0e9e4ee70fff1fb9d67031e44f6410170d00e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa486c6bc102f409180ccb8a94ba045d39f8fc7cb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc4a206a306f0db88f98a3591419bc14832536862 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf0059cc2b3e980065a906940fbce5f9db7ae40a7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x16eccfdbb4ee1a85a33f3a9b21175cd7ae753db4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x553d3d295e0f695b9228246232edf400ed3560b5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x14af1f2f02dccb1e43402339099a05a5e363b83c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7bdf330f423ea880ff95fc41a280fd5ecfd3d09f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8505b9d2254a7ae468c0e9dd10ccea3a837aef5c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe2aa7db6da1dae97c5f5c6914d285fbfcc32a128 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb7b31a6bc18e48888545ce79e83e06003be70930 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1631244689ec1fecbdd22fb5916e920dfc9b8d30 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf6372cdb9c1d3674e83842e3800f2a62ac9f3c66 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x692ac1e363ae34b6b489148152b12e2785a3d8d6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0266f4f08d82372cf0fcbccc0ff74309089c74d1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7fbc10850cae055b27039af31bd258430e714c62 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa3fa99a148fa48d14ed51d610c367c61876997f1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9dbfc1cbf7a1e711503a29b4b5f9130ebeccac96 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x236aa50979d5f3de3bd1eeb40e81137f22ab794b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf86df9b91f002cfeb2aed0e6d05c4c4eaef7cf02 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4200000000000000000000000000000000000006 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6921b130d297cc43754afba22e5eac0fbf8db75b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5babfc2f240bc5de90eb7e19d789412db1dec402 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x532f27101965dd16442e59d40670faf5ebb142e4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4ed4e862860bed51a9570b96d89af5e1b0efefed - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xac1bd2486aaf3b5c0fc3fd868558b082a531b2b4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0d97f261b1e88845184f678e2d1e7a98d9fd38de - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8129b94753f22ec4e62e2c4d099ffe6773969ebc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3f14920c99beb920afa163031c4e47a3e03b3e4a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x940181a94a35a4569e4529a3cdfb74e38fd98631 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3419875b4d3bca7f3fdda2db7a476a79fd31b4fe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xa067436db77ab18b1a315095e4b816791609897c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xafb89a09d82fbde58f18ac6437b3fc81724e4df6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x489fe42c267fe0366b16b0c39e7aeef977e841ef - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xdc46c1e93b71ff9209a0f8076a9951569dc35855 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x91f45aa2bde7393e0af1cc674ffe75d746b93567 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x236aa50979d5f3de3bd1eeb40e81137f22ab794b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xf6e932ca12afa26665dc4dde7e27be02a7c02e50 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x524d524b4c9366be706d3a90dcf70076ca037ae3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5b5dee44552546ecea05edea01dcd7be7aa6144a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2598c30330d5771ae9f983979209486ae26de875 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfa980ced6895ac314e7de34ef1bfae90a5add21b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x469fda1fb46fcb4befc0d8b994b516bd28c87003 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4e496c0256fb9d4cc7ba2fdf931bc9cbb7731660 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x27d2decb4bfc9c76f0309b8e88dec3a601fe25a8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbfd5206962267c7b4b4a8b3d76ac2e1b2a5c4d5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9e1028f5f1d5ede59748ffcee5532509976840e0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3c3aa127e6ee3d2f2e432d0184dd36f2d2076b52 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xba5e6fa2f33f3955f0cef50c63dcc84861eab663 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x97c806e7665d3afd84a8fe1837921403d59f3dcc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8ee73c484a26e0a5df2ee2a4960b789967dd0415 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x00e57ec29ef2ba7df07ad10573011647b2366f6d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8f019931375454fe4ee353427eb94e2e0c9e0a8c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x93e6407554b2f02640ab806cd57bd83e848ec65d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x55d398326f99059ff775485246999027b3197955 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2170ed0880ac9a755fd29b2688956bd959f933f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xfdc66a08b0d0dc44c17bbd471b88f49f50cdd20f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xe9e7cea3dedca5984780bafc599bd69add087d56 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xfa54ff1a158b5189ebba6ae130ced6bbd3aea76e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x570a5d26f7765ecb712c0924e4de545b89fd43df - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x47c454ca6be2f6def6f32b638c80f91c9c3c5949 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xad86d0e9764ba90ddd68747d64bffbd79879a238 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xd691d9a68c887bdf34da8c36f63487333acfd103 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1294f4183763743c7c9519bec51773fb3acd78fd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xb04906e95ab5d797ada81508115611fee694c2b3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x111111111117dc0aa78b770fa6a738034120c302 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xcc42724c6683b7e57334c4e856f4c9965ed682bd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x90c97f71e18723b0cf0dfa30ee176ab653e89f40 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2b72867c32cf673f7b02d208b26889fed353b1f8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x031b41e504677879370e9dbcf937283a8691fa7f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1ce0c2827e2ef14d5c4f29a091d735a204794041 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xcf3bb6ac0f6d987a5727e2d15e39c2d6061d5bec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8ff795a6f4d97e7887c79bea79aba5cc76444adf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2dff88a56767223a5529ea5960da7a3f5f766406 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x003d87d02a2a01e9e8a20f507c83e15dd83a33d1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x4b0f1812e5df2a09796481ff14017e6005508003 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbf5140a22578168fd562dccf235e5d43a02ce9b1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xca1c644704febf4ab81f85daca488d1623c28e63 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x51e72dd1f2628295cc2ef931cb64fdbdc3a0c599 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbbca42c60b5290f2c48871a596492f93ff0ddc82 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x555296de6a86e72752e5c5dc091fe49713aa145c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0808bf94d57c905f1236212654268ef82e1e594e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8457ca5040ad67fdebbcc8edce889a335bc0fbfb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xcebef3df1f3c5bfd90fde603e71f31a53b11944d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x90ed8f1dc86388f14b64ba8fb4bbd23099f18240 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x9840652dc04fb9db2c43853633f0f62be6f00f98 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xba2ae424d960c26247dd6c32edc70b295c744c43 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0782b6d8c4551b9760e74c0545a9bcd90bdc41e5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbe2b6c5e31f292009f495ddbda88e28391c9815e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8f0528ce5ef7b51152a59745befdd91d97091d2f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xffeecbf8d7267757c2dc3d13d730e97e15bfdf7f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0eb3a705fc54725037cc9e008bdede697f62f335 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf21768ccbc73ea5b6fd3c687208a7c2def2d966e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0000028a2eb8346cd5c0267856ab7594b7a55308 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x76a797a59ba2c17726896976b7b3747bfd1d220f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xc79d1fd14f514cd713b5ca43d288a782ae53eab2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xad29abb318791d579433d831ed122afeaf29dcfe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x3203c9e46ca618c8c1ce5dc67e7e9d75f5da2377 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xdb021b1b247fe2f1fa57e0a87c748cc1e321f07f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x7083609fce4d1d8dc0c979aab8c869ea2c873402 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xc5f0f7b66764f6ec8c8dff7ba683102295e16409 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xe29142e14e52bdfbb8108076f66f49661f10ec10 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xb0d502e938ed5f4df2e681fe6e419ff29631d62b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x6730f7a6bbb7b9c8e60843948f7feb4b6a17b7f7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1613957159e9b0ac6c80e824f7eea748a32a0ae2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x471ece3750da237f93b8e339c536989b8978a438 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x765de816845861e75a25fca122bb6898b8b1282a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x66803fb87abd4aac3cbb3fad7c3aa01f6f3fb207 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xd8763cba276a3738e6de85b4b3bf5fded6d6ca73 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x37f750b7cc259a2f741af45294f6a16572cf5cad - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xd71ffd0940c920786ec4dbb5a12306669b5b81ef - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xe8537a3d056da446677b9e9d6c5db704eaab4787 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x4f604735c1cf31399c6e711d5962b2b3e0225ad3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x02de4766c272abc10bc88c220d214a26960a7e92 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xceba9300f2b948710d2653dd7b07f33a8b32118c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xc16b81af351ba9e64c1a069e3ab18c244a1e3049 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x728f30fa2f100742c7949d1961804fa8e0b1387d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x41ea5d41eeacc2d5c4072260945118a13bb7ebce - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf21661d0d1d76d3ecb8e1b9f1c923dbfffae4097 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb0ecc6ac0073c063dcfc026ccdc9039cae2998e1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x00f932f0fe257456b32deda4758922e56a4f4b42 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xa4af354d466e8a68090dd9eb2cb7caf162f4c8c2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xba50933c268f567bdc86e1ac131be072c6b0b71a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd29da236dd4aac627346e1bba06a619e8c22d7c5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1bfce574deff725a3f483c334b790e25c8fa9779 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9e18d5bab2fa94a6a95f509ecb38f8f68322abd3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbf5495efe5db9ce00f80364c8b423567e58d2110 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x065b4e5dfd50ac12a81722fd0a0de81d78ddf7fb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x57e114b691db790c35207b2e685d4a43181e6061 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0b7f0e51cd1739d6c96982d55ad8fa634dd43a9c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc56c7a0eaa804f854b536a5f3d5f49d2ec4b12b8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x594daad7d77592a2b97b725a7ad59d7e188b5bfa - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8355dbe8b0e275abad27eb843f3eaf3fc855e525 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2a961d752eaa791cbff05991e4613290aec0d9ac - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x38e68a37e401f7271568cecaac63c6b1e19130b4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1131d427ecd794714ed00733ac0f851e904c8398 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1495bc9e44af1f8bcb62278d2bec4540cf0c05ea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x808507121b80c02388fad14726482e061b8da827 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x44971abf0251958492fee97da3e5c5ada88b9185 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x320623b8e4ff03373931769a31fc52a4e78b5d70 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6e5970dbd6fc7eb1f29c6d2edf2bc4c36124c0c1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd40c688da9df74e03566eaf0a7c754ed98fbb8cc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8afe4055ebc86bd2afb3940c0095c9aca511d852 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9ce84f6a69986a83d92c324df10bc8e64771030f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbe4d9c8c638b5f0864017d7f6a04b66c42953847 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x68bbed6a47194eff1cf514b50ea91895597fc91e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x69420e3a3aa9e17dea102bb3a9b3b73dcddb9528 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7420b4b9a0110cdc71fb720908340c03f9bc03ec - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x03aa6298f1370642642415edc0db8b957783e8d6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd533a949740bb3306d119cc777fa900ba034cd52 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf14dd7b286ce197019cba54b189d2b883e70f761 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa35923162c49cf95e6bf26623385eb431ad920d3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8cefbeb2172a9382753de431a493e21ba9694004 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x120a3879da835a5af037bb2d1456bebd6b54d4ba - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x69457a1c9ec492419344da01daf0df0e0369d5d0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf6ce4be313ead51511215f1874c898239a331e37 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x73d7c860998ca3c01ce8c808f5577d94d545d1b4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xeff49b0f56a97c7fd3b51f0ecd2ce999a7861420 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x236501327e701692a281934230af0b6be8df3353 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5026f006b85729a8b14553fae6af249ad16c9aab - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x66761fa41377003622aee3c7675fc7b5c1c2fac5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9f9c8ec3534c3ce16f928381372bfbfbfb9f4d24 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd8c978de79e12728e38aa952a6cb4166f891790f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7122985656e38bdc0302db86685bb972b145bd3c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x582d872a1b094fc48f5de31d3b73f2d9be47def1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x504624040e0642921c2c266a9ac37cafbd8cda4e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc548e90589b166e1364de744e6d35d8748996fe8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4c11249814f11b9346808179cf06e71ac328c1b5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x423f4e6138e475d85cf7ea071ac92097ed631eea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8390a1da07e376ef7add4be859ba74fb83aa02d5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf94e7d0710709388bce3161c32b4eea56d3f91cc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaa95f26e30001251fb905d264aa7b00ee9df6c18 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2416092f143378750bb29b79ed961ab195cceea5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x71eeba415a523f5c952cc2f06361d5443545ad28 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x88a269df8fe7f53e590c561954c52fccc8ec0cfb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x429fed88f10285e61b12bdf00848315fbdfcc341 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb299751b088336e165da313c33e3195b8c6663a6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf0a479c9c3378638ec603b8b6b0d75903902550b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb59c8912c83157a955f9d715e556257f432c35d7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xba0dda8762c24da9487f5fa026a9b64b695a07ea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc24a365a870821eb83fd216c9596edd89479d8d7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xa586b3b80d7e3e8d439e25fbc16bc5bcee3e2c85 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xef04804e1e474d3f9b73184d7ef5d786f3fce930 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2e9a6df78e42a30712c10a9dc4b1c8656f8f2879 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x13a7dedb7169a17be92b0e3c7c2315b46f4772b3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1dd6b5f9281c6b4f043c02a83a46c2772024636c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf525e73bdeb4ac1b0e741af3ed8a8cbb43ab0756 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe4177c1400a8eee1799835dcde2489c6f0d5d616 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xed5740209fcf6974d6f3a5f11e295b5e468ac27c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe10d4a4255d2d35c9e23e2c4790e073046fbaf5c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x10398abc267496e49106b07dd6be13364d10dc71 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x2218a117083f5b482b0bb821d27056ba9c04b1d3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x395ae52bb17aef68c2888d941736a71dc6d4e125 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9a601c5bb360811d96a23689066af316a30c3027 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xbac3368b5110f3a3dda8b5a0f7b66edb37c47afe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1d3c629ca5c1d0ab3bdf74600e81b4145615df8e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe9c21de62c5c5d0ceacce2762bf655afdceb7ab3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x658cda444ac43b0a7da13d638700931319b64014 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3d2bd0e15829aa5c362a4144fdf4a1112fa29b5c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3fb83a9a2c4408909c058b0bfe5b4823f54fafe2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x00e5646f60ac6fb446f621d146b6e1886f002905 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x12a4cebf81f8671faf1ab0acea4e3429e42869e7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9ff62d1fc52a907b6dcba8077c2ddca6e6a9d3e1 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc61f39418cd27820b5d4e9ba4a7197eefaeb8b05 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x15b7c0c907e4c6b9adaaaabc300c08991d6cea05 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7f67639ffc8c93dd558d452b8920b28815638c44 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x276c9cbaa4bdf57d7109a41e67bd09699536fa3d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x041fdf3f472d2c8a7ecc458fc3b7f543e6c57ef7 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3c281a39944a2319aa653d81cfd93ca10983d234 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x96419929d7949d6a801a6909c145c8eef6a40431 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfea9dcdc9e23a9068bf557ad5b186675c61d33ea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xdb6e0e5094a25a052ab6845a9f1e486b9a9b3dde - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xcde172dc5ffc46d228838446c57c1227e0b82049 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xff0c532fdb8cd566ae169c1cb157ff2bdc83e105 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9a26f5433671751c3276a065f57e5a02d2817973 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3636a7734b669ce352e97780df361ce1f809c58c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x50c5725949a6f0c72e6c4a641f24049a917db0cb - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xe3086852a4b125803c815a158249ae468a3254ca - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbeb0fd48c2ba0f1aacad2814605f09e08a96b94e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbc45647ea894030a4e9801ec03479739fa2485f0 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x768be13e1680b5ebe0024c42c896e3db59ec0149 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x928a6a9fc62b2c94baf2992a6fba4715f5bb0066 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbf4db8b7a679f89ef38125d5f84dd1446af2ea3b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xed899bfdb28c8ad65307fa40f4acab113ae2e14c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1b6a569dd61edce3c383f6d565e2f79ec3a12980 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x76734b57dfe834f102fb61e1ebf844adf8dd931e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4621b7a9c75199271f773ebd9a499dbd165c3191 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xaf07d812d1dcec20bf741075bc18660738d226dd - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7f12d13b34f5f4f0a9449c16bcd42f0da47af200 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x55a6f6cb50db03259f6ab17979a4891313be2f45 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x968d6a288d7b024d5012c0b25d67a889e4e3ec19 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7a8a5012022bccbf3ea4b03cd2bb5583d915fb1a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xcde90558fc317c69580deeaf3efc509428df9080 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0028e1e60167b48a938b785aa5292917e7eaca8b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x76e7447bafa3f0acafc9692629b1d1bc937ca15d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x15ac90165f8b45a80534228bdcb124a011f62fee - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4045b33f339a3027af80013fb5451fdbb01a4492 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xddf98aad8180c3e368467782cd07ae2e3e8d36a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x698dc45e4f10966f6d1d98e3bfd7071d8144c233 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3c8665472ec5af30981b06b4e0143663ebedcc1e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x18a8bd1fe17a1bb9ffb39ecd83e9489cfd17a022 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xba0dda8762c24da9487f5fa026a9b64b695a07ea - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x13741c5df9ab03e7aa9fb3bf1f714551dd5a5f8a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xebff2db643cf955247339c8c6bcd8406308ca437 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfadb26be94c1f959f900bf88cd396b3e803481d6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x52c2b317eb0bb61e650683d2f287f56c413e4cf6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x38d513ec43dda20f323f26c7bef74c5cf80b6477 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x33ad778e6c76237d843c52d7cafc972bb7cf8729 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x290814ad0fbd2b935f34d7b40306102313d4c63e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5e432eecd01c12ee7071ee9219c2477a347da192 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbdf5bafee1291eec45ae3aadac89be8152d4e673 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xff62ddfa80e513114c3a0bf4d6ffff1c1d17aadf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8c81b4c816d66d36c4bf348bdec01dbcbc70e987 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6b82297c6f1f9c3b1f501450d2ee7c37667ab70d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x42069babe14fb1802c5cb0f50bb9d2ad6fef55e2 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x72499bddb67f4ca150e1f522ca82c87bc9fb18c8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0578d8a44db98b23bf096a382e016e29a5ce0ffe - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8fe815417913a93ea99049fc0718ee1647a2a07c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7d12aeb5d96d221071d176980d23c213d88d9998 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb166e8b140d35d9d8226e40c09f757bac5a4d87d - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8853f0c059c27527d33d02378e5e4f6d5afb574a - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xf3c052f2baab885c610a748eb01dfbb643ba835b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xcd1cffa8ebc66f1a2cf7675b48ba955ffcb82d8e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xde7a416ac821c77478340eebaa21b68297025ef3 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2da56acb9ea78330f947bd57c54119debda7af71 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8972ab69d499b5537a31576725f0af8f67203d38 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x88faea256f789f8dd50de54f9c807eef24f71b16 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x42069de48741db40aef864f8764432bbccbd0b69 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9a27c6759a6de0f26ac41264f0856617dec6bc3f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfaa4f3bcfc87d791e9305951275e0f62a98bcb10 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfd9fa4f785331ce88b5af8994a047ba087c705d8 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x21eceaf3bf88ef0797e3927d855ca5bb569a47fc - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7d9ce55d54ff3feddb611fc63ff63ec01f26d15f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4229c271c19ca5f319fb67b4bc8a40761a6d6299 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x80f45eacf6537498ecc660e4e4a2d2f99e195cf4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1a475d06d967aeb686c98de80d079d72097aeacf - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4fb9b20dafe45d91ae287f2e07b2e79709308178 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xd3741ac9b3f280b0819191e4b30be4ecd990771e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x09579452bc3872727a5d105f342645792bb8a82b - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8a24d7260cd02d3dfd8eefb66bc17ad4b17d494c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xd88611a629265c9af294ffdd2e7fa4546612273e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9a86980d3625b4a6e69d8a4606d51cbc019e2002 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1c7a460413dd4e964f96d8dfc56e7223ce88cd85 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x776aaef8d8760129a0398cf8674ee28cefc0eab9 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x28e29ec91db66733a94ee8e3b86a6199117baf99 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb9898511bd2bad8bfc23eba641ef97a08f27e730 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x76baa16ff15d61d32e6b3576c3a8c83a25c2f180 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2816a491dd0b7a88d84cbded842a618e59016888 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0xa7ea9d5d4d4c7cf7dbde5871e6d108603c6942a5 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/base/0x586e10db93630a4d2da6c6a34ba715305b556f04 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf486ad071f3bee968384d2e39e2d8af0fcf6fd46 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x76d36d44dc4595e8d2eb3ad745f175eda134284f - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1fa4a73a3f0133f0025378af00236f3abdee5d63 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xb3ed0a426155b79b898849803e3b36552f7ed507 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0ef4a107b48163ab4b57fca36e1352151a587be4 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x62694d43ccb9b64e76e38385d15e325c7712a735 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xa2b726b1145a4773f68593cf171187d8ebe4d495 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf275e1ac303a4c9d987a2c48b8e555a77fec3f1c - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x11a31b833d43853f8869c9eec17f60e3b4d2a753 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e - 2025-10-17T22:04:33.647Z + 2025-03-20T21:19:45.690Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xbadff0ef41d2a68f22de21eabca8a59aaf495cf0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/NATIVE + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x1fdd61ef9a5c31b9a2abc7d39c139c779e8412af - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/NATIVE + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x4ade2b180f65ed752b6f1296d0418ad21eb578c0 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/bnb/NATIVE + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x0c5cb676e38d6973837b9496f6524835208145a2 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xb69753c06bb5c366be51e73bfc0cc2e3dc07e371 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x8143182a775c54578c8b7b3ef77982498866945d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x76e222b07c53d28b89b0bac18602810fc22b49a8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x73a15fed60bf67631dc6cd7bc5b6e8da8190acf5 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x18aaa7115705e8be94bffebde57af9bfc265b998 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x18084fba666a33d37592fa2633fd49a74dd93a88 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x7d8146cf21e8d7cbe46054e01588207b51198729 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x8236a87084f8b84306f72007f36f2618a5634494 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xfe0c30065b384f05761f15d0cc899d4f9f9cc0eb - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/optimism/NATIVE + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x1ce270557c1f68cfb577b856766310bf8b47fd9c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x26e550ac11b26f78a04489d5f20f24e3559f7dd9 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x793a5d8b30aab326f83d20a9370c827fea8fdc51 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/polygon/0x0000000000000000000000000000000000001010 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xff836a5821e69066c87e268bc51b849fab94240c - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x9d39a5de30e57443bff2a8307a4256c8797a3497 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xf4d2888d29d722226fafa5d9b24f9164c092421e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x4c1746a800d224393fe2470c70a35717ed4ea5f1 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x8ed97a637a790be1feff5e888d43629dc05408f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x31c8eacbffdd875c74b94b077895bd78cf1e64a3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/arbitrum/NATIVE + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xc55126051b22ebb829d00368f4b12bde432de5da - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x3593d125a4f7849a1b059e64f4517a86dd60c95d + 2025-03-20T21:28:30.528Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe0f63a424a4439cbe457d80e4f4b51ad25b2c56c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8881562783028f5c1bcb985d2283d5e170d88888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x67466be17df832165f8c80a5a120ccc652bd7e69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd939212f16560447ed82ce46ca40a63db62419b5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x88417754ff7062c10f4e3a4ab7e9f9d9cbda6023 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5afe3855358e112b5647b952709e6165e1c1eeee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x02e7f808990638e9e67e1f00313037ede2362361 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd2bdaaf2b9cc6981fd273dcb7c04023bfbe0a7fe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x112b08621e27e10773ec95d250604a041f36c582 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x32b053f2cba79f80ada5078cb6b305da92bde6e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5ac34c53a04b9aaa0bf047e7291fb4e8a48f2a18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x26ebb8213fb8d66156f1af8908d43f7e3e367c1d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe3b9cfb8ea8a4f1279fbc28d3e15b4d2d86f18a0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8207c1ffc5b6804f6024322ccf34f29c3541ae26 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x255f1b39172f65dc6406b8bee8b08155c45fe1b6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x092baadb7def4c3981454dd9c0a0d7ff07bcfc86 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x53bcf6698c911b2a7409a740eacddb901fc2a2c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x2ac2b254bc18cd4999f64773a966e4f4869c34ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x17fc002b466eec40dae837fc4be5c67993ddbd6f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xc8a4eea31e9b6b61c406df013dd4fec76f21e279 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xe4dddfe67e7164b0fe14e218d80dc4c08edc01cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x7c8a1a80fdd00c9cccd6ebd573e9ecb49bfa2a59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1debd73e752beaf79865fd6446b0c970eae7732f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xaf5db6e1cc585ca312e8c8f7c499033590cf5c98 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x65559aa14915a70190438ef90104769e5e890a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x7fb688ccf682d58f86d7e38e03f9d22e7705448b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x73cb180bf0521828d8849bc8cf2b920918e23032 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x2e3d870790dc77a83dd1d18184acc7439a53f475 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xa00e3a3511aac35ca78530c85007afcd31753819 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x528cdc92eab044e1e39fe43b9514bfdab4412b98 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x4f604735c1cf31399c6e711d5962b2b3e0225ad3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x1c954e8fe737f99f68fa1ccda3e51ebdb291948c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xf50d05a1402d0adafa880d36050736f9f6ee7dee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xab0b2ddb9c7e440fac8e140a89c0dbcbf2d7bbff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x8bc3ec2e7973e64be582a90b08cadd13457160fe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x64060ab139feaae7f06ca4e63189d86adeb51691 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x5ec03c1f7fa7ff05ec476d19e34a22eddb48acdc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x9627a3d6872be48410fcece9b1ddd344bf08c53e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x1ed02954d60ba14e26c230eec40cbac55fa3aeea - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8d3419b9a18651f3926a205ee0b1acea1e7192de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb56d0839998fd79efcd15c27cf966250aa58d6d3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x81f91fe59ee415735d59bd5be5cca91a0ea4fa69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x87c211144b1d9bdaa5a791b8099ea4123dc31d21 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf4210f93bc68d63df3286c73eba08c6414f40c0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xece7b98bd817ee5b1f2f536daf34d0b6af8bb542 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4c96a67b0577358894407af7bc3158fc1dffbeb5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x70737489dfdf1a29b7584d40500d3561bd4fe196 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x39353a32eceafe4979a8606512c046c3b6398cc4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x92fb1b7d9730b2f1bd4e2e91368c1eb6fdd2a009 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x174e33ef2effa0a4893d97dda5db4044cc7993a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfdc944fb59201fb163596ee5e209ebc8fa4dcdc5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x388e543a5a491e7b42e3fbcd127dd6812ea02d0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x56a38e7216304108e841579041249feb236c887b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1804e3db872eed4141e482ff74c56862f2791103 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9de16c805a3227b9b92e39a446f9d56cf59fe640 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb8d98a102b0079b69ffbc760c8d857a31653e56e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5d6812722c3693078e4a0dbe3e9affc27a0b2768 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x255f1b39172f65dc6406b8bee8b08155c45fe1b6 - 2025-10-17T22:04:33.647Z + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/base/0xc2fe011c3885277c7f0e7ffd45ff90cadc8ecd12 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc1ffaef4e7d553bbaf13926e258a1a555a363a07 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4e73420dcc85702ea134d91a262c8ffc0a72aa70 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xecaf81eb42cd30014eb44130b89bcd6d4ad98b92 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x4eae52907dba9c370e9ee99f0ce810602a4f2c63 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x25d887ce7a35172c62febfd67a1856f20faebb00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x382ea807a61a418479318efd96f1efbc5c1f2c21 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6468e79a80c0eab0f9a2b574c8d5bc374af59414 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3106a0a076bedae847652f42ef07fd58589e001f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd015422879a1308ba557510345e944b912b9ab73 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5de8ab7e27f6e7a1fff3e5b337584aa43961beef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcf078da6e85389de507ceede0e3d217e457b9d49 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1bbf25e71ec48b84d773809b4ba55b6f4be946fb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7039cd6d7966672f194e8139074c3d5c4e6dcf65 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x943af17c37207c9d7a27d12cb5055542a0b7afa8 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0xbdf43ecadc5cef51b7d1772f722e40596bc1788b + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x6d68015171eaa7af9a5a0a103664cf1e506ff699 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/unichain/NATIVE + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x6942806d1b2d5886d95ce2f04314ece8eb825833 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/arbitrum/0x6985884c4392d348587b19cb9eaaf157f13271cd + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x949d48eca67b17269629c7194f4b727d4ef9e5d6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0x9e6a46f294bb67c20f1d1e7afb0bbef614403b55 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x9361adf2b72f413d96f81ff40d794b47ce13b331 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x6c3ea9036406852006290770bedfcaba0e23a0e8 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x3bb1be077f3f96722ae92ec985ab37fd0a0c4c51 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x467bccd9d29f223bce8043b84e8c8b282827790f + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xdbb7a34bf10169d6d2d0d02a6cbb436cf4381bfa - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0x3ec2156d4c0a9cbdab4a016633b7bcf6a8d68ea2 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x66bff695f3b16a824869a8018a3a6e3685241269 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0xc0634090f2fe6c6d75e61be2b949464abb498973 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x85d19fb57ca7da715695fcf347ca2169144523a7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/arbitrum/0xe80772eaf6e2e18b651f160bc9158b2a5cafca65 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x069d89974f4edabde69450f9cf5cf7d8cbd2568d - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x35d8949372d46b7a3d5a56006ae77b215fc69bc0 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x0fe13ffe64b28a172c58505e24c0c111d149bd47 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x1abaea1f7c830bd89acc67ec4af516284b1bc33c + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x111111111117dc0aa78b770fa6a738034120c302 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0xfde4c96c8593536e31f229ea8f37b2ada2699bb2 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xdc7ac5d5d4a9c3b5d8f3183058a92776dc12f4f3 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x58d97b57bb95320f9a05dc918aef65434969c2b2 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x482702745260ffd69fc19943f70cffe2cacd70e9 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xc555d625828c4527d477e595ff1dd5801b4a600e - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0xbad6c59d72d44512616f25b3d160c79db5a69ddf + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x9eec1a4814323a7396c938bc86aec46b97f1bd82 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0x1bc0c42215582d5a085795f4badbac3ff36d1bcb + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x87d73e916d7057945c9bcd8cdd94e42a6f47f776 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x31c8eacbffdd875c74b94b077895bd78cf1e64a3 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0x067def80d66fb69c276e53b641f37ff7525162f6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x667102bd3413bfeaa3dffb48fa8288819e480a88 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/ethereum/0xdd157bd06c1840fa886da18a138c983a7d74c1d7 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/polygon/0xdf7837de1f2fa4631d716cf2502f8b230f1dcc32 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/arbitrum/0xe80772eaf6e2e18b651f160bc9158b2a5cafca65 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0x45804880de22913dafe09f4980848ece6ecbaf78 + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/arbitrum/0xb6093b61544572ab42a0e43af08abafd41bf25a6 - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/ethereum/0xd9fcd98c322942075a5c3860693e9f4f03aae07b + 2025-03-20T21:28:30.528Z 0.8 - https://app.uniswap.org/explore/tokens/arbitrum/0x35ca1e5a9b1c09fa542fa18d1ba4d61c8edff852 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x83e60b9f7f4db5cdb0877659b1740e73c662c55b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4d01397994aa636bdcc65c9e8024bc497498c3bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xc3abc47863524ced8daf3ef98d74dd881e131c38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4d15a3a2286d883af0aa1b3f21367843fac63e07 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xfb7f8a2c0526d01bfb00192781b7a7761841b16c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x3809dcdd5dde24b37abe64a5a339784c3323c44f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x85955046df4668e1dd369d2de9f3aeb98dd2a369 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x554cd6bdd03214b10aafa3e0d4d42de0c5d2937b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x4318cb63a2b8edf2de971e2f17f77097e499459d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xab9cb20a28f97e189ca0b666b8087803ad636b3c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x6a8ec2d9bfbdd20a7f5a4e89d640f7e7ceba4499 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x385eeac5cb85a38a9a07a70c73e0a3271cfb54a7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x0169ec1f8f639b32eec6d923e24c2a2ff45b9dd6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe161be4a74ab8fa8706a2d03e67c02318d0a0ad6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4d58608eff50b691a3b76189af2a7a123df1e9ba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x420b0fa3de2efcf2b2fd04152eb1df36a09717cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1cd38856ee0fdfd65c757e530e3b1de3061008d3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfad8cb754230dbfd249db0e8eccb5142dd675a0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xda761a290e01c69325d12d82ac402e5a73d62e81 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xafb5d4d474693e68df500c9c682e6a2841f9661a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfc5462143a3178cf044e97c491f6bcb5e38f173e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xed1978d01d4a8a9d6a43ac79403d5b8dfbed739b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xba71cb8ef2d59de7399745793657838829e0b147 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x10c1b6f768e13c624a4a23337f1a5ba5c9be0e4b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1b1514c76c54ce8807d7fdedf85c664eee734ece - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x58cd93c4a91c3940109fa27d700f5013b18b5dc2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xea6f7e7e0f46a9e0f4e2048eb129d879f609d632 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x30d19fb77c3ee5cfa97f73d72c6a1e509fa06aef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xe2dca969624795985f2f083bcd0b674337ba130a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xbb7d61d2511fd2e63f02178ca9b663458af9fc63 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x59f4f336bf3d0c49dbfba4a74ebd2a6ace40539a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x62d0a8458ed7719fdaf978fe5929c6d342b0bfce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb8fda5aee55120247f16225feff266dfdb381d4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xca530408c3e552b020a2300debc7bd18820fb42f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3ffeea07a27fab7ad1df5297fa75e77a43cb5790 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcfeb09c3c5f0f78ad72166d55f9e6e9a60e96eec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x467bccd9d29f223bce8043b84e8c8b282827790f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2077d81d0c5258230d5a195233941547cb5f0989 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa0bbbe391b0d0957f1d013381b643041d2ca4022 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd1b89856d82f978d049116eba8b7f9df2f342ff3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x62f03b52c377fea3eb71d451a95ad86c818755d1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3927fb89f34bbee63351a6340558eebf51a19fb8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xacd2c239012d17beb128b0944d49015104113650 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x86b69f38bea3e02f68ff88534bc61ec60e772b19 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6873c95307e13beb58fb8fcddf9a99667655c9e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x18084fba666a33d37592fa2633fd49a74dd93a88 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6e79b51959cf968d87826592f46f819f92466615 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x80ee5c641a8ffc607545219a3856562f56427fe9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0414d8c87b271266a5864329fb4932bbe19c0c49 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf57e7e7c23978c3caec3c3548e3d615c346e79ff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb0ffa8000886e57f86dd5264b9582b2ad87b2b91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1c986661170c1834db49c3830130d4038eeeb866 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x9ed7e4b1bff939ad473da5e7a218c771d1569456 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x7f9a7db853ca816b9a138aee3380ef34c437dee0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x371c7ec6d8039ff7933a2aa28eb827ffe1f52f07 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb1bc21f748ae2be95674876710bc6d78235480e0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xadf5dd3e51bf28ab4f07e684ecf5d00691818790 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x1eba7a6a72c894026cd654ac5cdcf83a46445b08 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x38022a157b95c52d43abcac9bd09f028a1079105 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd2507e7b5794179380673870d88b22f94da6abe0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xc708d6f2153933daa50b2d0758955be0a93a8fec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x0052074d3eb1429f39e5ea529b54a650c21f5aa4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x4e78011ce80ee02d2c3e649fb657e45898257815 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x7583feddbcefa813dc18259940f76a02710a8905 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xe78aee6ccb05471a69677fb74da80f5d251c042b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x04f177fcacf6fb4d2f95d41d7d3fee8e565ca1d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xa6da8c8999c094432c77e7d318951d34019af24b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6d3b8c76c5396642960243febf736c6be8b60562 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7cf7132ede0ca592a236b6198a681bb7b42dd5ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3afeae00a594fbf2e4049f924e3c6ac93296b6e8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0a93a7be7e7e426fc046e204c44d6b03a302b631 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc9b6ef062fab19d3f1eabc36b1f2e852af1acd18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1754e5aadce9567a95f545b146a616ce34eead53 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xdb173587d459ddb1b9b0f2d6d88febef039304a2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x10a7a84c91988138f8dbbc82a23b02c8639e2552 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x92af6f53febd6b4c6f5293840b6076a1b82c4bc2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xeb9e49fb4c33d9f6aefb1b03f9133435e24c0ec6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1b2c141479757b8643a519be4692904088d860b2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4d25e94291fe8dcfbfa572cbb2aaa7b755087c91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8e0e798966382e53bfb145d474254cbe065c17dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4b6f82a4ed0b9e3767f53309b87819a78d041a7f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x004aa1586011f3454f487eac8d0d5c647d646c69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x741777f6b6d8145041f73a0bddd35ae81f55a40f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xc6c58f600917de512cd02d2b6ed595ab54b4c30f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x03aa6298f1370642642415edc0db8b957783e8d6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x3ee2200efb3400fabb9aacf31297cbdd1d435d47 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x0d8ce2a99bb6e3b7db580ed848240e4a0f9ae153 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa697e272a73744b343528c3bc4702f2565b2f422 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x301af3eff0c904dc5ddd06faa808f653474f7fcc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x776f9987d9deed90eed791cbd824d971fd5ccf09 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf7de7e8a6bd59ed41a4b5fe50278b3b7f31384df - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x19e6bfc1a6e4b042fb20531244d47e252445df01 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x4338665cbb7b2485a8855a139b75d5e34ab0db94 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x2940566eb50f15129238f4dc599adc4f742d7d8e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xbb73bb2505ac4643d5c0a99c2a1f34b3dfd09d11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x4ea98c1999575aaadfb38237dd015c5e773f75a2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x1d18d0386f51ab03e7e84e71bda1681eba865f1f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x57b96d4af698605563a4653d882635da59bf11af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd33526068d116ce69f19a9ee46f0bd304f21a51f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2a5fa016ffb20c70e2ef36058c08547f344677aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbe0ed4138121ecfc5c0e56b40517da27e6c5226b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9fd9278f04f01c6a39a9d1c1cd79f7782c6ade08 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x054c9d4c6f4ea4e14391addd1812106c97d05690 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7613c48e0cd50e42dd9bf0f6c235063145f6f8dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x614da3b37b6f66f7ce69b4bbbcf9a55ce6168707 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x069e4aa272d17d9625aa3b6f863c7ef6cfb96713 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x24da31e7bb182cb2cabfef1d88db19c2ae1f5572 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7d4a23832fad83258b32ce4fd3109ceef4332af4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb58e61c3098d85632df34eecfb899a1ed80921cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x67c4d14861f9c975d004cfb3ac305bee673e996e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x69babe9811cc86dcfc3b8f9a14de6470dd18eda4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x32f0d04b48427a14fb3cbc73db869e691a9fec6f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4cff49d0a19ed6ff845a9122fa912abcfb1f68a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x51cb253744189f11241becb29bedd3f1b5384fdb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcf4c91ecafc43c9f382db723ba20b82efa852821 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6968676661ac9851c38907bdfcc22d5dd77b564d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0d438f3b5175bebc262bf23753c1e53d03432bde - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb98d4c97425d9908e66e53a6fdf673acca0be986 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x68a47fe1cf42eba4a030a10cd4d6a1031ca3ca0a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8a370c951f34e295b2655b47bb0985dd08d8f718 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x525574c899a7c877a11865339e57376092168258 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd9a442856c234a39a81a089c06451ebaa4306a72 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1c43d05be7e5b54d506e3ddb6f0305e8a66cd04e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb766039cc6db368759c1e56b79affe831d0cc507 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x18c14c2d707b2212e17d1579789fc06010cfca23 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xe0ee18eacafddaeb38f8907c74347c44385578ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x56659245931cb6920e39c189d2a0e7dd0da2d57b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xb6a5ae40e79891e4deadad06c8a7ca47396df21c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x04565fe9aa3ae571ada8e1bebf8282c4e5247b2a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf8a99f2bf2ce5bb6ce4aafcf070d8723bc904aa2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3b9728bd65ca2c11a817ce39a6e91808cceef6fd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6797b6244fa75f2e78cdffc3a4eb169332b730cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe2c86869216ac578bd62a4b8313770d9ee359a05 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x47b464edb8dc9bc67b5cd4c9310bb87b773845bd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x28a730de97dc62a8c88363e0b1049056f1274a70 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xba5ede8d98ab88cea9f0d69918dde28dc23c2553 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8319767a7b602f88e376368dca1b92d38869b9b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x461ee40928677644b8195662ab91bcdaae6ef105 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x24569d33653c404f90af10a2b98d6e0030d3d267 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x22222bd682745cf032006394750739684e45a5f8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9124577428c5bd73ad7636cbc5014081384f29d6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xaa6cccdce193698d33deb9ffd4be74eaa74c4898 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe095780ba2a64a4efa7a74830f0b71656f0b0ad4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb59c8912c83157a955f9d715e556257f432c35d7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7771450ece9c61430953d2646f995e33a06c91f5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc48823ec67720a04a9dfd8c7d109b2c3d6622094 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x9ec02756a559700d8d9e79ece56809f7bcc5dc27 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3593d125a4f7849a1b059e64f4517a86dd60c95d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb0ffa8000886e57f86dd5264b9582b2ad87b2b91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa045fe936e26e1e1e1fb27c1f2ae3643acde0171 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbeef698bd78139829e540622d5863e723e8715f1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x426a688ee72811773eb64f5717a32981b56f10c1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x873259322be8e50d80a4b868d186cc5ab148543a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x661c70333aa1850ccdbae82776bb436a0fcfeefb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0a2c375553e6965b42c135bb8b15a8914b08de0c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6fba952443be1de22232c824eb8d976b426b3c38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1abaea1f7c830bd89acc67ec4af516284b1bc33c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb60fdf036f2ad584f79525b5da76c5c531283a1b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5a3e6a77ba2f983ec0d371ea3b475f8bc0811ad5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x55296f69f40ea6d20e478533c15a6b08b654e758 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1a7e4e63778b4f12a199c062f3efdd288afcbce8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x45804880de22913dafe09f4980848ece6ecbaf78 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe5018913f2fdf33971864804ddb5fca25c539032 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x2c650dab03a59332e2e0c0c4a7f726913e5028c1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x9aee3c99934c88832399d6c6e08ad802112ebeab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x439c0cf1038f8002a4cad489b427e217ba4b42ad - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb79dd08ea68a908a97220c76d19a6aa9cbde4376 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4b61e2f1bbdee6d746209a693156952936f1702c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7480527815ccae421400da01e052b120cc4255e9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7466de7bb8b5e41ee572f4167de6be782a7fa75d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x298d411511a05dc1b559ed8f79c56bee06687b14 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8e16d46cb2da01cdd49601ec73d7b0344969ae33 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x18dd5b087bca9920562aff7a0199b96b9230438b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x37f0c2915cecc7e977183b8543fc0864d03e064c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x37f24b26bcefbfac7f261b97f8036da98f81a299 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xacb5b33ce55ba7729e38b2b59677e71c0112f0d9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xc71b5f631354be6853efe9c3ab6b9590f8302e81 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7e744bbb1a49a44dfcc795014a4ba618e418fbbe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0c04ff41b11065eed8c9eda4d461ba6611591395 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x636bd98fc13908e475f56d8a38a6e03616ec5563 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x590246bfbf89b113d8ac36faeea12b7589f7fe5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x80034f803afb1c6864e3ca481ef1362c54d094b9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x73fbd93bfda83b111ddc092aa3a4ca77fd30d380 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xff33a6b3dc0127862eedd3978609404b22298a54 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc770eefad204b5180df6a14ee197d99d808ee52d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa0385e7283c83e2871e9af49eec0966088421ddd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb2617246d0c6c0087f18703d576831899ca94f01 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xba386a4ca26b85fd057ab1ef86e3dc7bdeb5ce70 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9ebb0895bd9c7c9dfab0d8d877c66ba613ac98ea - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd12a99dbc40036cec6f1b776dccd2d36f5953b94 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8ab2ff0116a279a99950c66a12298962d152b83c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x420698cfdeddea6bc78d59bc17798113ad278f9d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa8c8cfb141a3bb59fea1e2ea6b79b5ecbcd7b6ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd8e8438cf7beed13cfabc82f300fb6573962c9e3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb1c9d42fa4ba691efe21656a7e6953d999b990c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xdadeca1167fe47499e53eb50f261103630974905 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xa05245ade25cc1063ee50cf7c083b4524c1c4302 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x4fafad147c8cd0e52f83830484d164e960bdc6c3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4dd9077269dd08899f2a9e73507125962b5bc87f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8931ee05ec111325c1700b68e5ef7b887e00661d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x26f1bb40ea88b46ceb21557dc0ffac7b7c0ad40f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x642e993fa91ffe9fb24d39a8eb0e0663145f8e92 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0c41f1fc9022feb69af6dc666abfe73c9ffda7ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf7ccb8a6e3400eb8eb0c47619134f7516e025215 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2416092f143378750bb29b79ed961ab195cceea5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf0268c5f9aa95baf5c25d646aabb900ac12f0800 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0c067fc190cde145b0c537765a78d4e19873a5cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbe5614875952b1683cb0a2c20e6509be46d353a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x87a0233a8cb4392ec3eb8fa467817fc0b6a326dd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xdfbea88c4842d30c26669602888d746d30f9d60d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb6fe221fe9eef5aba221c348ba20a1bf5e73624c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x80b3455e1db60b4cba46aba12e8b1e256dd64979 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x747747e47a48c669be384e0dfb248eee6ba04039 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x50e85c754929840b58614f48e29c64bc78c58345 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x02f92800f57bcd74066f5709f1daa1a4302df875 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x967da4048cd07ab37855c090aaf366e4ce1b9f48 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x729031b3995538ddf6b6bce6e68d5d6fdeb3ccb5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6dea81c8171d0ba574754ef6f8b412f2ed88c54d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x97a9a15168c22b3c137e6381037e1499c8ad0978 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5faa989af96af85384b8a938c2ede4a7378d9875 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4691937a7508860f876c9c0a2a617e7d9e945d4b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x037a54aab062628c9bbae1fdb1583c195585fe41 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xcb8b5cd20bdcaea9a010ac1f8d835824f5c87a04 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xdfb8be6f8c87f74295a87de951974362cedcfa30 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x354a6da3fcde098f8389cad84b0182725c6c91de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x3f56e0c36d275367b8c502090edf38289b3dea0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x6f9590958ce2beaf9c92a3a8fca6d1ddf310e052 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x3e5d9d8a63cc8a88748f229999cf59487e90721e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xecc68d0451e20292406967fe7c04280e5238ac7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xf1c1a3c2481a3a8a3f173a9ab5ade275292a6fa3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xb5e0cfe1b4db501ac003b740665bf43192cc7853 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xffa188493c15dfaf2c206c97d8633377847b6a52 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xb5c064f955d8e7f38fe0460c556a72987494ee17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x4f604735c1cf31399c6e711d5962b2b3e0225ad3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xf0949dd87d2531d665010d6274f06a357669457a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x14e5386f47466a463f85d151653e1736c0c50fc3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xadac33f543267c4d59a8c299cf804c303bc3e4ac - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xcfa3ef56d303ae4faaba0592388f19d7c3399fb4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x67ce18961c3269ca03c2e5632f1938cc53e614a1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x48164ea5df090e80a0eaee1147e466ea28669221 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3054e8f8fba3055a42e5f5228a2a4e2ab1326933 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x42069d11a2cc72388a2e06210921e839cfbd3280 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x74ff3cbf86f95fea386f79633d7bc4460d415f34 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2d6a3893966dda77749cc7e4003ab15f5cfa3cc1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x51b75da3da2e413ea1b8ed3eb078dc712304761c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8ad5b9007556749de59e088c88801a3aaa87134b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbd97693278f1948c59f65f130fd87e7ff7c61d11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3992b27da26848c2b19cea6fd25ad5568b68ab98 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x34980c35353a8d7b1a1ba02e02e387a8383e004a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xdebd6e2da378784a69dc6ec99fe254223b312287 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x456a3d042c0dbd3db53d5489e98dfb038553b0d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x9995cc8f20db5896943afc8ee0ba463259c931ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x30d20208d987713f46dfd34ef128bb16c404d10f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x19848077f45356b21164c412eff3d3e4ff6ebc31 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x53206bf5b6b8872c1bb0b3c533e06fde2f7e22e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x07ddacf367f0d40bd68b4b80b4709a37bdc9f847 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbdbe9f26918918bd3f43a0219d54e5fda9ce1bb3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb9d09bc374577dac1ab853de412a903408204ea8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe72b141df173b999ae7c1adcbf60cc9833ce56a8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x214549b0317564de15770561221433fb3e8c995c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc82e3db60a52cf7529253b4ec688f631aad9e7c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf3dcbc6d72a4e1892f7917b7c43b74131df8480e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x62e3b3c557c792c4a70765b3cdb5b56b1879f82d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2598c30330d5771ae9f983979209486ae26de875 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd4f4d0a10bcae123bb6655e8fe93a30d01eebd04 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xa0995d43901551601060447f9abf93ebc277cec2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x40379a439d4f6795b6fc9aa5687db461677a2dba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x433cde5a82b5e0658da3543b47a375dffd126eb6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x619c4bbbd65f836b78b36cbe781513861d57f39d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1e0bb24ed6c806c01ef2f880a4b91adb90099ea7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0dd7913197bfb6d2b1f03f9772ced06298f1a644 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfbb75a59193a3525a8825bebe7d4b56899e2f7e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc3de830ea07524a0761646a6a4e4be0e114a3c83 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3792dbdd07e87413247df995e692806aa13d3299 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x527856315a4bcd2f428ea7fa05ea251f7e96a50a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x292fcdd1b104de5a00250febba9bc6a5092a0076 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd749b369d361396286f8cc28a99dd3425ac05619 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfe3e6a25e6b192a42a44ecddcd13796471735acf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa1faa113cbe53436df28ff0aee54275c13b40975 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8802269d1283cdb2a5a329649e5cb4cdcee91ab6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0000bdaa645097ef80f9d475f341d0d107a45b3a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x683a4ac99e65200921f556a19dadf4b0214b5938 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x36c7188d64c44301272db3293899507eabb8ed43 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8a2279d4a90b6fe1c4b30fa660cc9f926797baa2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf418588522d5dd018b425e472991e52ebbeeeeee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6135177a17e02658df99a07a2841464deb5b8589 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcf91b70017eabde82c9671e30e5502d312ea6eb2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x45080a6531d671ddff20db42f93792a489685e32 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x790814cd782983fab4d7b92cf155187a865d9f18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9e6be44cc1236eef7e1f197418592d363bedcd5a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xaa7a9ca87d3694b5755f213b5d04094b8d0f0a6f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x69ee720c120ec7c9c52a625c04414459b3185f23 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x408e41876cccdc0f92210600ef50372656052a38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5cf04716ba20127f1e2297addcf4b5035000c9eb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8290333cef9e6d528dd5618fb97a76f268f3edd4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1929761e87667283f087ea9ab8370c174681b4e9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x888888848b652b3e3a0f34c96e00eec0f3a23f72 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf944e35f95e819e752f3ccb5faf40957d311e8c5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1f70300bce8c2302780bd0a153ebb75b8ca7efcb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x3de81ce90f5a27c5e6a5adb04b54aba488a6d14e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xc87b37a581ec3257b734886d9d3a581f5a9d056c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1a6b3a62391eccaaa992ade44cd4afe6bec8cff1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x65c936f008bc34fe819bce9fa5afd9dc2d49977f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x07d65c18cecba423298c0aeb5d2beded4dfd5736 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x51fc0f6660482ea73330e414efd7808811a57fa2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xcbe94d75ec713b7ead84f55620dc3174beeb1cfe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd3144ff5f388d36c0a445686c08540296d8b209b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x433e39ce74aef8f409182541269e417ad9b56011 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb1a03eda10342529bbf8eb700a06c60441fef25d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6b9bb36519538e0c073894e964e90172e1c0b41f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x689644b86075ed61c647596862c7403e1c474dbf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9a6d24c02ec35ad970287ee8296d4d6552a31dbe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x506beb7965fc7053059006c7ab4c62c02c2d989f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x31b28012f61fc3600e1c076bafc9fd997fb2da90 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd7d919ea0c33a97ad6e7bd4f510498e2ec98cb78 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xef553b6914dbd17567393f7e55fbd773fff7d0cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe642657e4f43e6dcf0bd73ef24008394574dee28 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf8b1b47aa748f5c7b5d0e80c726a843913eb573a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd064c53f043d5aee2ac9503b13ee012bf2def1d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfc60aa1ffca50ce08b3cdec9626c0bb9e9b09bec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x82c8f48ac694841360de84d649a0d48d239b61f8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7d89e05c0b93b24b5cb23a073e60d008fed1acf9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7546e0d4d947a15f914e33de6616ffed826f45ef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x9a5350edf28c1f93bb36d6e94b5c425fde8e222d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xaa076b62efc6f357882e07665157a271ab46a063 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6a6aa13393b7d1100c00a57c76c39e8b6c835041 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x07040971246a73ebda9cf29ea1306bb47c7c4e76 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6df0e641fc9847c0c6fde39be6253045440c14d3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2b640a99991dea2916205ecdc9f9c58f80017ed8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x38e4adb44ef08f22f5b5b76a8f0c2d0dcbe7dca1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x42069cc15f5befb510430d22ff1c9a1b3ae22cfe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x89fd2d8fd8d937f55c89b7da3ceed44fa27e4a81 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x76bc677d444f1e9d57daf5187ee2b7dc852745ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa0084063ea01d5f09e56ef3ff6232a9e18b0bacd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4abd5745f326932b1b673bfa592a20d7bb6bc455 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe53ec727dbdeb9e2d5456c3be40cff031ab40a55 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf43f21384d03b5cbbddd58d2de64071e4ce76ab0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x33349b282065b0284d756f0577fb39c158f935e6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x33c88d4cac6ac34f77020915a2a88cd0417dc069 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xdce765f021410b3266aa0053c93cb4535f1e12e0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb50a8e92cb9782c9b8f3c88e4ee8a1d0aa2221d7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x0a84edf70f30325151631ce7a61307d1f4d619a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xc11158c5da9db1d553ed28f0c2ba1cbedd42cfcb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xb0b195aefa3650a6908f15cdac7d92f8a5791b0b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xdc4f4ed9872571d5ec8986a502a0d88f3a175f1e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9beec80e62aa257ced8b0edd8692f79ee8783777 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf95e1c0a67492720ca22842122fe7fa63d5519e5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xca8e8d244f0d219a6fc9e4793c635cea98d0399c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6a4f69da1e2fb2a9b11d1aad60d03163fe567732 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0718f45bbf4781ce891e4e18182f025725f0fc95 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x132bbda4a40d4d6288be49b637ec2c113b5d7600 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9aaae745cf2830fb8ddc6248b17436dc3a5e701c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x24fcfc492c1393274b6bcd568ac9e225bec93584 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x21fd16cd0ef24a49d28429921e335bb0c1bfadb3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa469b7ee9ee773642b3e93e842e5d9b5baa10067 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8c19f7854b27758ddffdcdc8908f22bf55e00736 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xf2ae0038696774d65e67892c9d301c5f2cbbda58 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6bc40d4099f9057b23af309c08d935b890d7adc0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xee2a03aa6dacf51c18679c516ad5283d8e7c2637 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7f911119435d8ded9f018194b4b6661331379a3d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x777be1c6075c20184c4fd76344b7b0b7c858fe6b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x812ba41e071c7b7fa4ebcfb62df5f45f6fa853ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x881d4c8618d68872fa404518b2460ea839a02a6a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xba2ae4e0a9c6ecaf172015aa2cdd70a21f5a290b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1caf237d7a2d103e3e9b1855988c01ac10344600 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7d4a7be025652995364e0e232063abd9e8d65e6e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x620aa20875ec1144126ea47fb27ecfe6e10d0c56 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfae103dc9cf190ed75350761e95403b7b8afa6c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xae7ab96520de3a18e5e111b5eaab095312d7fe84 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x04c154b66cb340f3ae24111cc767e0184ed00cc6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x70e8de73ce538da2beed35d14187f6959a8eca96 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfb7b4564402e5500db5bb6d63ae671302777c75a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6810e776880c02933d47db1b9fc05908e5386b96 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x11e969e9b3f89cb16d686a03cd8508c9fc0361af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x8b5d1d8b3466ec21f8ee33ce63f319642c026142 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x3ed03e95dd894235090b3d4a49e0c3239edce59e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb3f13b0c61d65d67d7d6215d70c89533ee567a91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xfea31d704deb0975da8e77bf13e04239e70d7c28 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x66e535e8d2ebf13f49f3d49e5c50395a97c137b1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x9a06db14d639796b25a6cec6a1bf614fd98815ec - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7fdd7419428955dbf36d4176af5a8f09ad29d1f3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8c9037d1ef5c6d1f6816278c7aaf5491d24cd527 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa9f5031b54c44c3603b4300fde9b8f5cd18ad06f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x57f5fbd3de65dfc0bd3630f732969e5fb97e6d37 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9ef1139e6b420cc929dd912a5a7adeced6f12e91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x120edc8e391ba4c94cb98bb65d8856ae6ec1525f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd7ea82d19f1f59ff1ae95f1945ee6e6d86a25b96 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2c9ab600d71967ff259c491ad51f517886740cbc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf4c8e32eadec4bfe97e0f595add0f4450a863a11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x8c49a510756224e887b3d99d00d959f2d86dda1c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7777cec341e7434126864195adef9b05dcc3489c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x19af07b52e5faa0c2b1e11721c52aa23172fe2f5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb7109df1a93f8fe2b8162c6207c9b846c1c68090 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbbc2ae13b23d715c30720f079fcd9b4a74093505 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x595832f8fc6bf59c85c527fec3740a1b7a361269 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7316d973b0269863bbfed87302e11334e25ea565 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2be8e422cb4a5a7f217a8f1b0658952a79132f28 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x83e6f1e41cdd28eaceb20cb649155049fac3d5aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbabe3ce7835665464228df00b03246115c30730a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2e6a60492fb5b58f5b5d08c7cafc75e740e6dc8e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc08e7e23c235073c6807c2efe7021304cb7c2815 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x955d5c14c8d4944da1ea7836bd44d54a8ec35ba1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3540abe4f288b280a0740ad5121aec337c404d15 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfe8526a77a2c3590e5973ba81308b90bea21fbff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd807f7e2818db8eda0d28b5be74866338eaedb86 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4186bfc76e2e237523cbc30fd220fe055156b41f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xd5d3aa404d7562d09a848f96a8a8d5d65977bf90 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xa3f751662e282e83ec3cbc387d225ca56dd63d3a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd24157aa1097486dc9d7cf094a7e15026e566b5d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xbed0b9240bdbcc8e33f66d2ca650a5ef60a5bab0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5d559ea7bb2dae4b694a079cb8328a2145fd32f6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x97b959385dfdcaf252223838746beb232ac601aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x18e692c03de43972fe81058f322fa542ae1a5e2c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x38029c62dfa30d9fd3cadf4c64e9b2ab21dbda17 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x4507cef57c46789ef8d1a19ea45f4216bae2b528 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x73f93dcc49cb8a239e2032663e9475dd5ef29a08 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x9e523234d36973f9e38642886197d023c88e307e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5de758bba013e58dae2693aea3f0b12b31a3023d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1001271083c249bd771e1bb76c22d935809a61ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9d39a5de30e57443bff2a8307a4256c8797a3497 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf3768d6e78e65fc64b8f12ffc824452130bd5394 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf2ec4a773ef90c58d98ea734c0ebdb538519b988 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0f2d719407fdbeff09d87557abb7232601fd9f29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x180000dda70eb7fb7f3e10e52e88ce88f46e3b3a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xed89fc0f41d8be2c98b13b7e3cd3e876d73f1d30 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x17c50d62e6e8d20d2dc18e9ad79c43263d0720d9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3b50805453023a91a8bf641e279401a0b23fa6f9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfd03723a9a3abe0562451496a9a394d2c4bad4ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfe67a4450907459c3e1fff623aa927dd4e28c67a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc5fb36dd2fb59d3b98deff88425a3f425ee469ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x6b021b3f68491974be6d4009fee61a4e3c708fd6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x7ae9ab13fc8945323b778b3f8678145e80ec2efb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xbc4c97fb9befaa8b41448e1dfcc5236da543217f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x93919784c523f39cacaa98ee0a9d96c3f32b593e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd55fce7cdab84d84f2ef3f99816d765a2a94a509 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x32e0f9d26d1e33625742a52620cc76c1130efde6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9b700b043e9587dde9a0c29a9483e2f8fa450d54 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0b1594b0e896bf165d925956e0df733b8443af6a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x891502ba08132653151f822a3a430198f1844115 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc702b80a1bebac118cab22ce6f2978ef59563b3f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1287a235474e0331c0975e373bdd066444d1bd35 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xab36452dbac151be02b16ca17d8919826072f64a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xcc7ff230365bd730ee4b352cc2492cedac49383e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa9b038285f43cd6fe9e16b4c80b4b9bccd3c161b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x77be1ba1cd2d7a63bffc772d361168cc327dd8bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x00000000efe302beaa2b3e6e1b18d08d69a9012a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd101dcc414f310268c37eeb4cd376ccfa507f571 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd09eb9099fac55edcbf4965e0a866779ca365a0c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7b0df1cd724ec34ec9bc4bd19749b01afb490761 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x71297312753ea7a2570a5a3278ed70d9a75f4f44 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9e32b13ce7f2e80a01932b42553652e053d6ed8e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6942040b6d25d6207e98f8e26c6101755d67ac89 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3301ee63fb29f863f2333bd4466acb46cd8323e6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfefe157c9d0ae025213092ff9a5cb56ab492bab8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x44108f0223a3c3028f5fe7aec7f9bb2e66bef82f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1121acc14c63f3c872bfca497d10926a6098aac5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf1376bcef0f78459c0ed0ba5ddce976f1ddf51f4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xce722f60f35c37ab295adc4e6ba45bcc7ca89dd6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x614577036f0a024dbc1c88ba616b394dd65d105a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x93fa0b88c0c78e45980fa74cdd87469311b7b3e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xe22c452bd2ade15dfc8ad98286bc6bdf0c9219b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x00000000000451f49c692bfc24971cacea2db678 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x00000000702749f73e5210b08b0a3d440078f888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x86f65121804d2cdbef79f9f072d4e0c2eebabc08 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x127e47aba094a9a87d084a3a93732909ff031419 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x52b492a33e447cdb854c7fc19f1e57e8bfa1777d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x55027a5b06f4340cc4c82dcc74c90ca93dcb173e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x32b133add6d99d085ff23f522662b546b70d54a1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2ad3d80c917ddbf08acc04277f379e00e4d75395 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc73dc7ae7a4fa40517aafa941ae1ee436b91a12c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9f235d23354857efe6c541db92a9ef1877689bcb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0c90c756350fb803a7d5d9f9ee5ac29e77369973 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xac12f930318be4f9d37f602cbf89cd33e99aa9d4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x1c45366641014069114c78962bdc371f534bc81c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc328a59e7321747aebbc49fd28d1b32c1af8d3b2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x90edf25b14393350f0c1b5b12b6cb3cd3781fb4a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x590f820444fa3638e022776752c5eef34e2f89a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1fdb29ad49330b07ae5a87483f598aa6b292039e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4a220e6096b25eadb88358cb44068a3248254675 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xabd4c63d2616a5201454168269031355f4764337 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4c1b1302220d7de5c22b495e78b72f2dd2457d45 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x050c24dbf1eec17babe5fc585f06116a259cc77a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x57211299bc356319ba5ca36873eb06896173f8bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfde4c96c8593536e31f229ea8f37b2ada2699bb2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf9b738c2e7adc4f299c57afd0890b925a5efea6f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x04c0599ae5a44757c0af6f9ec3b93da8976c150a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x99b2b1a2adb02b38222adcd057783d7e5d1fcc7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf9569cfb8fd265e91aa478d86ae8c78b8af55df4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa3d1a8deb97b111454b294e2324efad13a9d8396 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd85eff20288ca72ea9eecffb428f89ee5066ca5c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x13f4196cc779275888440b3000ae533bbbbc3166 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x160452f95612699d1a561a70eeeeede67c6812af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x5ce12f6d9f2fcaf0b11494a1c39e09eeb16ca7e8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x6894cde390a3f51155ea41ed24a33a4827d3063d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6db6fdb5182053eecec778afec95e0814172a474 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc0cfbe1602dd586349f60e4681bf4badca584ec9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x289ff00235d2b98b0145ff5d4435d3e92f9540a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcb76314c2540199f4b844d4ebbc7998c604880ca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd7cfdb3cdc33dbeb9e9a4c95b61953cf12a008b3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xce176825afc335d9759cb4e323ee8b31891de747 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8f2bf2f59cdf7be4aee71500b9419623202b8636 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x744d70fdbe2ba4cf95131626614a1763df805b9e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x52e6654aee5d59e13ae30b48f8f5dbeb97f708cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x38f9bf9dce51833ec7f03c9dc218197999999999 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x7189fb5b6504bbff6a852b13b7b82a3c118fdc27 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x38f9bf9dce51833ec7f03c9dc218197999999999 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x8349314651ede274f8c5fef01aa65ff8da75e57c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x38f9bf9dce51833ec7f03c9dc218197999999999 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x1adcef5c780d8895ac77e6ee9239b4b3ecb76da2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x38f9bf9dce51833ec7f03c9dc218197999999999 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x917f39bb33b2483dd19546b1e8d2f09ce481ee44 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8b67f2e56139ca052a7ec49cbcd1aa9c83f2752a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x029c58a909fbe3d4be85a24f414dda923a3fde0f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x655a51e6803faf50d4ace80fa501af2f29c856cf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x9ca5dfa3b0b187d7f53f4ef83ca435a2ec2e4070 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xb68a20b9e9b06fde873897e12ab3372ce48f1a8a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x0203d275d2a65030889af45ed91d472be3948b92 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa00453052a36d43a99ac1ca145dfe4a952ca33b8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8236a87084f8b84306f72007f36f2618a5634494 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbc5ca3c518c8a2930947661237b1b562e34f22b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfd0205066521550d7d7ab19da8f72bb004b4c341 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x880226cbcce551eeafd18c9a9e883c85811b82fc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfc21540d6b89667d167d42086e1feb04da3e9b21 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x41d06390b935356b46ad6750bda30148ad2044a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8149745670881d99700078ede5903a1a7bebe262 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcf01a5c02c9b9dd5bf73a5a56bcdbc9dca483d43 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xae0fe8474cf5b1b412b3e4327a1c535ea12b77b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc98d64da73a6616c42117b582e832812e7b8d57f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x70c0b83501a3989d4f8a8693581bb7010194abb5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x80122c6a83c8202ea365233363d3f4837d13e888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x455e53cbb86018ac2b8092fdcd39d8444affc3f6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x58aea10748a00d1781d6651f9d78a414ea32ca46 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x406d59819bc2aef682f4ff2769085c98a264f97b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xc4ce1d6f5d98d65ee25cf85e9f2e9dcfee6cb5d6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x94025780a1ab58868d9b2dbbb775f44b32e8e6e5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xf33687811f3ad0cd6b48dd4b39f9f977bd7165a2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa88594d404727625a9437c3f886c7643872296ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7e72d6410803c40e73806f2a72e3eade5d075cc0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x31ea904a7eca45122890deb8da3473a2081bc9d1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x48c6740bcf807d6c47c864faeea15ed4da3910ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc5fecc3a29fb57b5024eec8a2239d4621e111cbe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x184cff0e719826b966025f93e05d8c8b0a79b3f9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0c2e08e459fc43ddd1e2718c122f566473f59665 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1a3a8cf347b2bf5890d3d6a1b981c4f4432c8661 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8baf5d75cae25c7df6d1e0d26c52d19ee848301a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x28561b8a2360f463011c16b6cc0b0cbef8dbbcad - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0fd10b9899882a6f2fcb5c371e17e70fdee00c38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7a58c0be72be218b41c608b7fe7c5bb630736c71 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xddaf27167929cd045a7d97d09a4fa1046ece3d89 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x375e104af98872e5b4fe951919e504a47db1757c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5408d3883ec28c2de205064ae9690142b035fed2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1bb4afbf2ce0c9ec86e6414ad4ba4d9aab1c0de4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7391425ca7cee3ee03e09794b819291a572af83e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x38e382f74dfb84608f3c1f10187f6bef5951de93 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbea269038eb75bdab47a9c04d0f5c572d94b93d5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf41a7b7c79840775f70a085c1fc5a762bbc6b180 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x13654df31871b5d01e5fba8e6c21a5d0344820f5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4d840b741bc05fde325d4ec0b4cfcd0cea237e4e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x49b1be61a8ca3f9a9f178d6550e41e00d9162159 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf5bc3439f53a45607ccad667abc7daf5a583633f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0a953dd9fc813fefaf6015b804c9dfa0624690c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x44ec807ce2f4a6f2737a92e985f318d035883e47 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xfb6115445bff7b52feb98650c87f44907e58f802 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x117a123ded97cd125837d9ac19592b77d806fa88 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd9fcd98c322942075a5c3860693e9f4f03aae07b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x240cd7b53d364a208ed41f8ced4965d11f571b7a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb8d6196d71cdd7d90a053a7769a077772aaac464 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcbde0453d4e7d748077c1b0ac2216c011dd2f406 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x786f112c9a6bc840cdc07cfd840105efd6ef2d4b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0bffdd787c83235f6f0afa0faed42061a4619b7a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1c43cd666f22878ee902769fccda61f401814efb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1b54a6fa1360bd71a0f28f77a1d6fba215d498c3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb528edbef013aff855ac3c50b381f253af13b997 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x888888ae2c4a298efd66d162ffc53b3f2a869888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4cd27e18757baa3a4fe7b0ab7db083002637a6c5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x240d6faf8c3b1a7394e371792a3bf9d28dd65515 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x41b1f9dcd5923c9542b6957b9b72169595acbc5c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd1f2586790a5bd6da1e443441df53af6ec213d83 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8de5b80a0c1b02fe4976851d030b36122dbb8624 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x391cf4b21f557c935c7f670218ef42c21bd8d686 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8bd35250918ed056304fa8641e083be2c42308bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc3960227e41c3f54e9b399ce216149dea5315c34 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x59062301fb510f4ea2417b67404cb16d31e604ba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x75ec618a817eb0a4a7e44ac3dfc64c963daf921a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x7e7a7c916c19a45769f6bdaf91087f93c6c12f78 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x21ccbc5e7f353ec43b2f5b1fb12c3e9d89d30dca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x87eee96d50fb761ad85b1c982d28a042169d61b1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x3c720206bfacb2d16fa3ac0ed87d2048dbc401fc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x8d60fb5886497851aac8c5195006ecf07647ba0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xcb327b99ff831bf8223cced12b1338ff3aa322ff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf544251d25f3d243a36b07e7e7962a678f952691 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa7296cefae8477a81e23230ca5d3a3d6f49d3764 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x051fb509e4a775fabd257611eea1efaed8f91359 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xae2bddbcc932c2d2cf286bad0028c6f5074c77b5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1dd2d631c92b1acdfcdd51a0f7145a50130050c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd3c68968137317a57a9babeacc7707ec433548b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7f6f6720a73c0f54f95ab343d7efeb1fa991f4f7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf3527ef8de265eaa3716fb312c12847bfba66cef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x8888888888f004100c0353d657be6300587a6ccd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xe2a59d5e33c6540e18aaa46bf98917ac3158db0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xfa2ad87e35fc8d3c9f57d73c4667a4651ce6ad2f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xec53bf9167f50cdeb3ae105f56099aaab9061f83 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb3912b20b3abc78c15e85e13ec0bf334fbb924f7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x16a3543fa6b32cac3b0a755f64a729e84f89a75c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf1c9acdc66974dfb6decb12aa385b9cd01190e38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0da2082905583cedfffd4847879d0f1cf3d25c36 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb0ffa8000886e57f86dd5264b9582b2ad87b2b91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xec9333e7dadeebf82d290d6cb12e66cc30ce46b0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x898843fb909e3562c82f2b96f4e3d0693af041df - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xaf05ce8a2cef336006e933c02fc89887f5b3c726 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x13e4b8cffe704d3de6f19e52b201d92c21ec18bd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xaeb3607ec434454ceb308f5cd540875efb54309a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2a3bff78b79a009976eea096a51a948a3dc00e34 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4298e4ad48be89bf63a6fdc470a4b4fe9ce633b1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa117000000f279d81a1d3cc75430faa017fa5a2e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x339058ca41e17b55b6dd295373c5d3cbe8000cd9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa3d4bee77b05d4a0c943877558ce21a763c4fa29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x362bc847a3a9637d3af6624eec853618a43ed7d2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7a65cb87f596caf31a4932f074c59c0592be77d7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa21af1050f7b26e0cff45ee51548254c41ed6b5c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x284b25d8f199125da962abc9ee6e6b1b6715cae3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8fac8031e079f409135766c7d5de29cf22ef897c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf280b16ef293d8e534e370794ef26bf312694126 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x69af81e73a73b40adf4f3d4223cd9b1ece623074 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x888c1a341ce9d9ae9c2d2a75a72a7f0d2551a2dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x465dbc39f46f9d43c581a5d90a43e4a0f2a6ff2d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x44e18207b6e98f4a786957954e462ed46b8c95be - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x70c29e99ca32592c0e88bb571b87444bb0e08e33 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8c7ac134ed985367eadc6f727d79e8295e11435c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6aa56e1d98b3805921c170eb4b3fe7d4fda6d89b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x81db1949d0e888557bc632f7c0f6698b1f8c9106 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2de1218c31a04e1040fc5501b89e3a58793b3ddf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x30ae41d5f9988d359c733232c6c693c0e645c77e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1fc01117e196800f416a577350cb1938d10501c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x3212dc0f8c834e4de893532d27cc9b6001684db0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xd0cf4de352ac8dcce00bd6b93ee73d3cb272edc3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x75e6b648c91d222b2f6318e8ceeed4b691d5323f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2a06a17cbc6d0032cac2c6696da90f29d39a1a29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6668d4a6605a27e5ee51eda040581155eddc6666 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2dc90fa3a0f178ba4bee16cac5d6c9a5a7b4c6cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x9c7beba8f6ef6643abd725e45a4e8387ef260649 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x0cf8e180350253271f4b917ccfb0accc4862f262 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x42069026eac8eee0fd9b5f7adfa4f6e6d69a2b39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x340d2bde5eb28c1eed91b2f790723e3b160613b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xec21890967a8ceb3e55a3f79dac4e90673ba3c2e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6900f7b42fb4abb615c938db6a26d73a9afbed69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4c44a8b7823b80161eb5e6d80c014024752607f2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x103143acf2e717acf8f021823e86a1dbfe944fb5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6969f3a3754ab674b48b7829a8572360e98132ba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x562e362876c8aee4744fc2c6aac8394c312d215d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd0ebfe04adb5ef449ec5874e450810501dc53ed5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2597342ff387b63846eb456419590781c4bfcdaf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4e6221c07dae8d3460a46fa01779cf17fdd72ad8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb612bfc5ce2fb1337bd29f5af24ca85dbb181ce2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc0e10854ab40b2e59a5519c481161a090f1162a0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa7f4195f10f1a62b102bd683eab131d657a6c6e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7e7ef0ee0305c1c195fcae22fd7b207a813eef86 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb6212b633c941e9be168c4b9c2d9e785f1cd42fb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x139052115f8b1773cf7dcba6a553f922a2e54f69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x3f94618ad346f34f43e27f0cf46decbb0d396b1b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf56b3b3972f2f154555a0b62ff5a22b7b2a3c90b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc08cd26474722ce93f4d0c34d16201461c10aa8c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x080c169cd58122f8e1d36713bf8bcbca45176905 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x50da645f148798f68ef2d7db7c1cb22a6819bb2c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xea1d649ddc8e2a6e6ee40b89b2997518476cafa5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa4080f1778e69467e905b8d6f72f6e441f9e9484 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb60acd2057067dc9ed8c083f5aa227a244044fd6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd0dfca0b404e866dc9a3038bd2a545c6735d9fa9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x18a8d75f70eaead79b5a55903d036ce337f623a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xebb66a88cedd12bfe3a289df6dfee377f2963f12 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9343e24716659a3551eb10aff9472a2dcad5db2d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfa3e941d1f6b7b10ed84a0c211bfa8aee907965e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x85bea4ee627b795a79583fcede229e198aa57055 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0c03ce270b4826ec62e7dd007f0b716068639f7b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x19706c142d33376240e418d6385f05691a5fa8e2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb3e41d6e0ea14b43bc5de3c314a408af171b03dd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x634769eb87542eaf41c0008c05d5d8f5d8bec3a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd3c5bdbc6de5ea3899a28f6cd419f29c09fa749f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9dfad1b7102d46b1b197b90095b5c4e9f5845bba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc8f69a9b46b235de8d0b77c355fff7994f1b090f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5200b34e6a519f289f5258de4554ebd3db12e822 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x69fd9281a920717ee54193a1c130b689ef341933 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5d56b6581d2e7e7574adce2dc593f499a53d7505 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x168168db04def453b7e8bfaff1e0102a3e810485 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1f19d846d99a0e75581913b64510fe0e18bbc31f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x571d9b73dc04ed88b4e273e048c8d4848f83b779 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xca5ca9083702c56b481d1eec86f1776fdbd2e594 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x99f40b01ba9c469193b360f72740e416b17ac332 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xc6bdfc4f2e90196738873e824a9efa03f7c64176 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x06480acaae64bcfa6da8fd176f60982584385090 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0c5142bc58f9a61ab8c3d2085dd2f4e550c5ce0b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc734635cd30e882037c3f3de1ebccf9fa9d27d9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x65e570b560027f493f2b1907e8e8e3b9546053bd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd1917629b3e6a72e6772aab5dbe58eb7fa3c2f33 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9e81f6495ba29a6b4d48bddd042c0598fa8abc9f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2075f6e2147d4ac26036c9b4084f8e28b324397d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x01aac2b594f7bdbec740f0f1aa22910ebb4b74ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xef433ebb8ba7a486ce21b854f093b9a3f4e696bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2bb84fd8f7ed0ffae3da36ad60d4d7840bdeeada - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xad86b91a1d1db15a4cd34d0634bbd4ecacb5b61a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4d224452801aced8b2f0aebe155379bb5d594381 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf63e309818e4ea13782678ce6c31c1234fa61809 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe0151763455a8a021e64880c238ba1cff3787ff0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x44ff8620b8ca30902395a7bd3f2407e1a091bf73 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5640e0560e6afd6a9f4ddb41230d0201d181fea7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x88ee7a3537667958d040216d9dc1752d1274d838 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x39d5313c3750140e5042887413ba8aa6145a9bd2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xba2a3dad197d6fee75471215efd5c30c8c854e11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3dd77d53f4fa9b3435b3a2ff6bb408771e6800e6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xf929de51d91c77e42f5090069e0ad7a09e513c73 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x74885b4d524d497261259b38900f54e6dbad2210 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xc55e93c62874d8100dbd2dfe307edc1036ad5434 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x9c9e5fd8bbc25984b178fdce6117defa39d2db39 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xaa53b93608c88ee55fad8db4c504fa20e52642ad - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x55cd6469f597452b5a7536e2cd98fde4c1247ee4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfe550bffb51eb645ea3b324d772a19ac449e92c5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x314d7f9e2f55b430ef656fbb98a7635d43a2261e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3b54eb78fc8103462f86976b06916fa46078b124 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1d4731111bd2a50ab3dd5178574e6f3698270ffc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7a2c5e7788e55ec0a7ba4aeec5b3da322718fb5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x814fe70e85025bec87d4ad3f3b713bdcaac0579b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9b69667f602f15ef2d09a9a18489c788e327461e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8808434a831efea81170a56a9ddc57cc9e6de1d8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe0c8b298db4cffe05d1bea0bb1ba414522b33c1b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x230ea9aed5d08afdb22cd3c06c47cf24ad501301 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x35d8949372d46b7a3d5a56006ae77b215fc69bc0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x535887989b9edffb63b1fd5c6b99a4d45443b49a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9ee8c380e1926730ad89e91665ff27063b13c90a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb8a914a00664e9361eae187468eff94905dfbc15 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xda2e903b0b67f30bf26bd3464f9ee1a383bbbe5f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xd6cf874e24a9f5f43075142101a6b13735cdd424 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x8c92e38eca8210f4fcbf17f0951b198dd7668292 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9a33406165f562e16c3abd82fd1185482e01b49a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7f65323e468939073ef3b5287c73f13951b0ff5b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5597ce42b315f29e42071d231dcd0158da35b77b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0a14ef61afb32e5ca672e021784f71705ac14908 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0f1cfd0bb452db90a3bfc0848349463010419ab2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf3708859c178709d5319ad5405bc81511b72b9e9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xadf734e8d910d01e6528240898d895af6c22e2de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x78a087d713be963bf307b18f2ff8122ef9a63ae9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4287105ffac106eb98a71cab46586906181e35ff - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb8e564b206032bbcda2c3978bc371da52152f72e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3ecced5b416e58664f04a39dd18935eb71d33b15 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x71e26d0e519d14591b9de9a0fe9513a398101490 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0x105d4a9306d2e55a71d2eb95b81553ae1dc20d7b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x420110d74c4c3ea14043a09e81fad53e1932f54c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd6203889c22d9fe5e938a9200f50fdffe9dd8e02 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0a6e7ba5042b38349e437ec6db6214aec7b35676 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6f40d4a6237c257fff2db00fa0510deeecd303eb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3b991130eae3cca364406d718da22fa1c3e7c256 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x555907a0b5c32df0feb35401187aed60a9191d74 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4947b72fed037ade3365da050a9be5c063e605a7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe9732d4b1e7d3789004ff029f032ba3034db059c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x556c3cbdca77a7f21afe15b17e644e0e98e64df4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x81f8f0bb1cb2a06649e51913a151f0e7ef6fa321 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe69ccaaaea33ebfe5b76e0dd373cd9a1a31fd410 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9aab071b4129b083b01cb5a0cb513ce7eca26fa5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5ff0d2de4cd862149c6672c99b7edf3b092667a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x96a5399d07896f757bd4c6ef56461f58db951862 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x0d5105ec5bbbf17dba7a87e1aed2c2c15394a9e2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x00000000ea00f3f4000e7ed5ed91965b19f1009b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x5117f4ad0bc70dbb3b05bf39a1ec1ee40dd67654 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4be87c766a7ce11d5cc864b6c3abb7457dcc4cc9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x16a500aec6c37f84447ef04e66c57cfc6254cf92 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x9f6abbf0ba6b5bfa27f4deb6597cc6ec20573fda - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x5465145a47260d5e715733997333a175d97285bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x93890f346c5d02c3863a06657bc72555dc72c527 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x1d1498166ddceee616a6d99868e1e0677300056f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x1d734a02ef1e1f5886e66b0673b71af5b53ffa94 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x4c3bf0a3de9524af68327d1d2558a3b70d17d42a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x36912b5cf63e509f18e53ac98b3012fa79e77bf5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x858c50c3af1913b0e849afdb74617388a1a5340d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x92dc4ab92eb16e781559e612f349916988013d5a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x548f93779fbc992010c07467cbaf329dd5f059b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd1412d909f67b8db7505ddfcf26cf2303f4b1bb4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfb1aaba03c31ea98a3eec7591808acb1947ee7ac - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9562e2063122eaa4d7c2d786e7ca2610d70ca8b8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x699ec925118567b6475fe495327ba0a778234aaa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x37d299d9900209c3566254cfe59bfe6ff8f8c295 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x128f3e482f5bd5f08fe1b216e60ec0a6013deab9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x554fb3b6c1cf4a3cef49779ced321ca51c667d7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8578a8716013c390b95db73065922f512783e2cf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf5809f3348ff40906bb509f936aba43e6d1961ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x11920f139a3121c2836e01551d43f95b3c31159c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x45940000009600102a1c002f0097c4a500fa00ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4debfb9ed639144cf1e401674af361ffffcefb58 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x0cfc9a713a5c17bc8a5ff0379467f6558bacd0e0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0fd7a301b51d0a83fcaf6718628174d527b373b6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1bc0c42215582d5a085795f4badbac3ff36d1bcb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3c4b6cd7874edc945797123fce2d9a871818524b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x64cb1bafc59bf93aeb90676885c63540cf4f4106 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe8aae6251c6cf39927b0ff31399030c60bec798f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3d1d651761d535df881740ab50ba4bd8a2ec2c00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8216e8143902a8fe0b676006bc25eb23829c123d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xce7de646e7208a4ef112cb6ed5038fa6cc6b12e3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x947950bcc74888a40ffa2593c5798f11fc9124c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x9f9bb3d5af7cc774f9b6adf66e32859b5a998952 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xba41ddf06b7ffd89d1267b5a93bfef2424eb2003 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7a56e1c57c7475ccf742a1832b028f0456652f97 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x58d97b57bb95320f9a05dc918aef65434969c2b2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc785698504a70be37d0e939a4c5326f8eddd5beb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4955f6641bf9c8c163604c321f4b36e988698f75 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x473f4068073cd5b2ab0e4cc8e146f9edc6fb52cc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x16c22a91c705ec3c2d5945dbe2aca37924f1d2ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xac1d3d7a8878e655cbb063d58e453540641f4117 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb72e76ccf005313868db7b48070901a44629da98 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xa71e2738704e367798baa2755af5a10499634953 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x8697841b82c71fcbd9e58c15f6de68cd1c63fd02 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x7dff72693f6a4149b17e7c6314655f6a9f7c8b33 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x650af3c15af43dcb218406d30784416d64cfb6b2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x3c8b650257cfb5f272f799f5e2b4e65093a11a05 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xe4feab21b42919c5c960ed2b4bdffc521e26881f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xc3ec80343d2bae2f8e680fdadde7c17e71e114ea - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x9c2c5fd7b07e95ee044ddeba0e97a665f142394f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0db510e79909666d6dec7f5e49370838c16d950f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x599f07567656e6961e20fa6a90685d393808c192 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4f9fd6be4a90f2620860d680c0d4d5fb53d1a825 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1185cb5122edad199bdbc0cbd7a0457e448f23c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbaa5cc21fd487b8fcc2f632f3f4e8d37262a0842 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb488fcb23333e7baa28d1dfd7b69a5d3a8bfeb3a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2c8c89c442436cc6c0a77943e09c8daf49da3161 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x91ad1b44913cd1b8241a4ff1e2eaa198da6bf4c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa0a2e84f6f19c09a095d4a83ac8de5a32d303a13 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1db0c569ebb4a8b57ac01833b9792f526305e062 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8a638ea79f71f3b91bdc96bbdf9fb27c93013d60 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x731814e491571a2e9ee3c5b1f7f3b962ee8f4870 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2c002ffec41568d138acc36f5894d6156398d539 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x33d13d537609841ce6c42d6fd775dc33e3833411 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x11d41056ff636107dd710ec4ea772490a710cdb7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x2859e4544c4bb03966803b044a93563bd2d0dd4d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x77777feddddffc19ff86db637967013e6c6a116c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfd418e42783382e86ae91e445406600ba144d162 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4f7d2d728ce137dd01ec63ef7b225805c7b54575 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2e44f3f609ff5aa4819b323fd74690f07c3607c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x19e1f2f837a3b90ebd0730cb6111189be0e1b6d6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd55210bb6898c021a19de1f58d27b71f095921ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x823556202e86763853b40e9cde725f412e294689 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4d1c297d39c5c1277964d0e3f8aa901493664530 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x864cb5194722d5a1596f4be8b899916d30dad8d8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x4a24b101728e07a52053c13fb4db2bcf490cabc3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x79ead7a012d97ed8deece279f9bc39e264d7eef9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd566c529b33ecf15170f600d4b1ab12468c8efc6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x3b7e1ce09afe2bb3a23919afb65a38e627cfbe97 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xaa404804ba583c025fa64c9a276a6127ceb355c6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x1a3acf6d19267e2d3e7f898f42803e90c9219062 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6b2504a03ca4d43d0d73776f6ad46dab2f2a4cfd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x98d59767cd1335071a4e9b9d3482685c915131e8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x354d6890caa31a5e28b6059d46781f40880786a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x30121d81f4407474a6d93f5c3060f14aaa098a61 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5d9c2457a10d455e0ad8e28e40cc28eacf27a06a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xac27fa800955849d6d17cc8952ba9dd6eaa66187 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe2b1dc2d4a3b4e59fdf0c47b71a7a86391a8b35a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd2699f9fddc04d262a819808f561c153098c2408 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x23a96680ccde03bd4bdd9a3e9a0cb56a5d27f7c9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3c5fdf0ee37d62c774025599e3b692d027746e24 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf31e6d62bfc485857af2186eb3d8ee94b4379fed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcf3c8be2e2c42331da80ef210e9b1b307c03d36a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x306227d964511a260d14563fbfa82aa75db404b2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc00e94cb662c3520282e6f5717214004a7f26888 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0c4785ee3ca8bf1fb90c772703210bd346aa3413 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe41d2489571d322189246dafa5ebde1f4699f498 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7fd4d7737597e7b4ee22acbf8d94362343ae0a79 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3567aa22cd3ab9aef23d7e18ee0d7cf16974d7e6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf107edabf59ba696e38de62ad5327415bd4d4236 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x06450dee7fd2fb8e39061434babcfc05599a6fb8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9cf0ed013e67db12ca3af8e7506fe401aa14dad6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x33333333fede34409fb7f67c6585047e1f653333 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xc760f9782f8cea5b06d862574464729537159966 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1b7ad346b6ff2d196daa8e78aed86baa6d7e3b02 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xff733b2a3557a7ed6697007ab5d11b79fdd1b76b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x05f52cc483c50c2a7e25a13dac17d736fa50f259 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xafb755c5f2ea2aadbae693d3bf2dc2c35158dc04 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x101a023270368c0d50bffb62780f4afd4ea79c35 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x3562ddf1f5ce2c02ef109e9d5a72e2fdb702711d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x20d704099b62ada091028bcfc44445041ed16f09 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x161e113b8e9bbaefb846f73f31624f6f9607bd44 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa6f774051dfb6b54869227fda2df9cb46f296c09 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x50ce4129ca261ccde4eb100c170843c2936bc11b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbdf317f9c153246c429f23f4093087164b145390 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x08c81699f9a357a9f0d04a09b353576ca328d60d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x589864a9892b1a736ae70a91824ab4dc591fd8cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd98832e8a59156acbee4744b9a94a9989a728f36 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x478e03d45716dda94f6dbc15a633b0d90c237e2f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2676e4e0e2eb58d9bdb5078358ff8a3a964cedf5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1c4cca7c5db003824208adda61bd749e55f463a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x749e5334752466cda899b302ed4176b8573dc877 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x63cb9a22cbc00bf9159429e9dede4b88c3dba8ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2f20cf3466f80a5f7f532fca553c8cbc9727fef6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2c24497d4086490e7ead87cc12597fb50c2e6ed6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4f81837c2f4a189a0b69370027cc2627d93785b4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x252d223d0550bc6c137b003d90bc74f5341a2818 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x72ff5742319ef07061836f5c924ac6d72c919080 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x2ab0e9e4ee70fff1fb9d67031e44f6410170d00e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x8c907e0a72c3d55627e853f4ec6a96b0c8771145 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xc748673057861a797275cd8a068abb95a902e8de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x62d7c4e3566f7f4033fc8e01b4d8e9bbc01c0760 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x109ba5f0230b7b39e4a8ab56e7361db89fa0e108 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x82a605d6d9114f4ad6d5ee461027477eeed31e34 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0bb217e40f8a5cb79adf04e1aab60e5abd0dfc1e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf411903cbc70a74d22900a5de66a2dda66507255 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xdd3b11ef34cd511a2da159034a05fcb94d806686 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4ec1b60b96193a64acae44778e51f7bff2007831 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x78965b1c638a7ff408d1697a96d7b8e47bb7c75f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x60222751504796934bddee8218f9725f0c95d2c1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1ccb4b14a11e0f2994a7ecbbd4cc69632f4c7c76 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc7dcca0a3e69bd762c8db257f868f76be36c8514 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9cdf242ef7975d8c68d5c1f5b6905801699b1940 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xadd39272e83895e7d3f244f696b7a25635f34234 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0000000000c5dc95539589fbd24be07c6c14eca4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcab254f1a32343f11ab41fbde90ecb410cde348a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x95ed629b028cf6aadd1408bb988c6d1daabe4767 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa6c0c097741d55ecd9a3a7def3a8253fd022ceb9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd888a5460fffa4b14340dd9fe2710cbabd520659 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x683989afc948477fd38567f8327f501562c955ac - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x946fb08103b400d1c79e07acccdef5cfd26cd374 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7076de6ff1d91e00be7e92458089c833de99e22e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xadf7c35560035944e805d98ff17d58cde2449389 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x62b9c7356a2dc64a1969e19c23e4f579f9810aa7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd19b72e027cd66bde41d8f60a13740a26c4be8f3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x66a1e37c9b0eaddca17d3662d6c05f4decf3e110 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf477ac7719e2e659001455cdda0cc8f3ad10b604 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xe16e2548a576ad448fb014bbe85284d7f3542df5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x330bd769382cfc6d50175903434ccc8d206dcae5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xb08d8becab1bf76a9ce3d2d5fa946f65ec1d3e83 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x8b21e9b7daf2c4325bf3d18c1beb79a347fe902a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd9a9b4d466747e1ebcb7aeb42784452f40452367 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x306fd3e7b169aa4ee19412323e1a5995b8c1a1f4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x9cb74c8032b007466865f060ad2c46145d45553d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x333333c465a19c85f85c6cfbed7b16b0b26e3333 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb9a5f238dc61eebe820060226c8143cd24624771 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc48cddc6f2650bdb13dcf6681f61ba07209b5299 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfb42da273158b0f642f59f2ba7cc1d5457481677 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6f35720b272bf23832852b13ae9888c706e1a379 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4498cd8ba045e00673402353f5a4347562707e7d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1035ae3f87a91084c6c5084d0615cc6121c5e228 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3639e6f4c224ebd1bf6373c3d97917d33e0492bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8bfac1b375bf2894d6f12fb2eb48b1c1a7916789 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd27c288fd69f228e0c02f79e5ecadff962e05a2b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x62ff28a01abd2484adb18c61f78f30fb2e4a6fdb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x315b8c9a1123c10228d469551033440441b41f0b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1d008f50fb828ef9debbbeae1b71fffe929bf317 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xeec468333ccc16d4bf1cef497a56cf8c0aae4ca3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x20dd04c17afd5c9a8b3f2cdacaa8ee7907385bef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x25e0a7767d03461eaf88b47cd9853722fe05dfd3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x22af33fe49fd1fa80c7149773dde5890d3c76f3b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbc1852f8940991d91bd2b09a5abb5e7b8092a16c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x06a63c498ef95ad1fa4fff841955e512b4b2198a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xeb6d78148f001f3aa2f588997c5e102e489ad341 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa4dc5a82839a148ff172b5b8ba9d52e681fd2261 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xef22cb48b8483df6152e1423b19df5553bbd818b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf83759099dc88f75fc83de854c41e0d9e83ada9b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x844c03892863b0e3e00e805e41b34527044d5c72 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xc9de725a4be9ab74b136c29d4731d6bebd7122e8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x59e69094398afbea632f8bd63033bdd2443a3be1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x15f9eb4b9beafa9db35341c5694c0b6573809808 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x29cbd0510eec0327992cd6006e63f9fa8e7f33b7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcb1592591996765ec0efc1f92599a19767ee5ffa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x26e550ac11b26f78a04489d5f20f24e3559f7dd9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x50327c6c5a14dcade707abad2e27eb517df87ab5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc575bd129848ce06a460a19466c30e1d0328f52c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x47000a7b27a75d44ffadfe9d0b97fa04d569b323 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2196b84eace74867b73fb003aff93c11fce1d47a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc4441c2be5d8fa8126822b9929ca0b81ea0de38e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x421b05cf5ce28cb7347e73e2278e84472f0e4a88 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcdbddbdefb0ee3ef03a89afcd714aa4ef310d567 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf4308b0263723b121056938c2172868e408079d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8cedb0680531d26e62abdbd0f4c5428b7fdc26d5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x49fb8ad7578148e17c3ef0c344ce23a66ed372c4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x40e3d1a4b2c47d9aa61261f5606136ef73e28042 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x60d95823f795f1972dbdbcd886955095e36e04cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x761a3557184cbc07b7493da0661c41177b2f97fa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x64c5cba9a1bfbd2a5faf601d91beff2dcac2c974 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x02f92800f57bcd74066f5709f1daa1a4302df875 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x291a50e611035b6562a2374b8b44de70aa8d7896 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x999faf0af2ff109938eefe6a7bf91ca56f0d07e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1a5b0aaf478bf1fda7b934c76e7692d722982a6d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x680447595e8b7b3aa1b43beb9f6098c79ac2ab3f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xc1eb7689147c81ac840d4ff0d298489fc7986d52 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x60bf4e7cf16ff34513514b968483b54beff42a81 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x5df7abe3c51c01dcf6d1f1f9a0ab4dc3759869b9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xfe049f59963545bf5469f968e04c9646d6e2c2c5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc0041ef357b183448b235a8ea73ce4e4ec8c265f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x12e377989a87da0f9b9166f0f875c9069eaa776c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfc48314ad4ad5bd36a84e8307b86a68a01d95d9c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0521aaa7c96e25afee79fdd4f1bb48f008ae4eac - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7a5f5ccd46ebd7ac30615836d988ca3bd57412b3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x20ef84969f6d81ff74ae4591c331858b20ad82cd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4d70f1058b73198f12a76c193aef5db5dd75babd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9239e9f9e325e706ef8b89936ece9d48896abbe3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xca73ed1815e5915489570014e024b7ebe65de679 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x79dacb99a8698052a9898e81fdf883c29efb93cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2f6c17fa9f9bc3600346ab4e48c0701e1d5962ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xba5e66fb16944da22a62ea4fd70ad02008744460 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2b0772bea2757624287ffc7feb92d03aeae6f12d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc841b4ead3f70be99472ffdb88e5c3c7af6a481a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf8f97a79a3fa77104fab4814e3ed93899777de0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf0d7cb351589c4b1520bf8d31afc87f7fb839c85 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x511ef9ad5e645e533d15df605b4628e3d0d0ff53 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x625bb9bb04bdca51871ed6d07e2dd9034e914631 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x291a8da3c42b7d7f00349d6f1be3c823a2b3fca4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf09034487c84954d49ae04bf6817148ffc2edb83 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1e2093ab84768948c6176db5ad98c909ce97f368 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x64fcc3a02eeeba05ef701b7eed066c6ebd5d4e51 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc796e499cc8f599a2a8280825d8bda92f7a895e0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbdb0e1c40a76c5113a023d685b419b90b01e3d61 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x76c71f1703fbf19ffdcf3051e1e684cb9934510f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc655c331d1aa7f96c252f1f40ce13d80eac53504 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x85645b86243886b7c7c1da6288571f8bea6fc035 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x623cd3a3edf080057892aaf8d773bbb7a5c9b6e9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xab964f7b7b6391bd6c4e8512ef00d01f255d9c0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6112b8714221bbd96ae0a0032a683e38b475d06c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc2eeca228ebac45c339cc5e522dd3a10638155f1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x018dd3a0dd7f213cc822076b3800816d3ce1ed86 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x446c9033e7516d820cc9a2ce2d0b7328b579406f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x73a15fed60bf67631dc6cd7bc5b6e8da8190acf5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4448726b23483927c492f09c1dbfdffd3967b452 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xeeacc51af745846ddf46012b46c6910ea9b12898 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x25931894a86d47441213199621f1f2994e1c39aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x94a8b4ee5cd64c79d0ee816f467ea73009f51aa0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1a4e7febd24b6689704b10685857d8b30885f05e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8248270620aa532e4d64316017be5e873e37cc09 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xca7af58da871736994ce360f51ec6cd28351a3df - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xca4f53e6117623992126a9a45ce61682fe8678df - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x79bbf4508b1391af3a0f4b30bb5fc4aa9ab0e07c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd2a530170d71a9cfe1651fb468e2b98f7ed7456b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xbf7970d56a150cd0b60bd08388a4a75a27777777 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xb87904db461005fc716a6bf9f2d451c33b10b80b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x708383ae0e80e75377d664e4d6344404dede119a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x2c89bbc92bd86f8075d1decc58c7f4e0107f286b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x288f4eb27400fa220d14b864259ad1b7f77c1594 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2d57c47bc5d2432feeedf2c9150162a9862d3ccf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x91da780bc7f4b7cf19abe90411a2a296ec5ff787 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb89d354ad1b0d95a48b3de4607f75a8cd710c1ba - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x79bbf4508b1391af3a0f4b30bb5fc4aa9ab0e07c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6263ad921e11ab47ae85f1daa725b8b3581baed3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x57edc3f1fd42c0d48230e964b1c5184b9c89b2ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa608512bbc9934e4b1ddecf0f5fb38b6ad93308d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9e6a46f294bb67c20f1d1e7afb0bbef614403b55 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x767a739d1a152639e9ea1d8c1bd55fdc5b217d7f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xfbecd19292b1effeaa7b2e61f5101ddb6744a1fb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf04d220b8136e2d3d4be08081dbb565c3c302ffd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xce1eab31756a48915b7e7bb79c589835aac6242d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd1d7aa941c71fd95e9d31bbd81937b3e71bd6231 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x53ae20d42e16626dc41c7842d9ce876358082370 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5ab3d4c385b400f3abb49e80de2faf6a88a7b691 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc438b0c0e80a8fa1b36898d1b36a3fc2ec371c54 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbe35071605277d8be5a52c84a66ab1bc855a758d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x8899ec96ed8c96b5c86c23c3f069c3def75b6d97 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa2cd3d43c775978a96bdbf12d733d5a1ed94fb18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb6aaa0efdfac186652e3b31a6f07a9a74d1b5a75 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6b0b3a982b4634ac68dd83a4dbf02311ce324181 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x32f4768fc4a238a58fc9da408d9a0da4333012e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x0721b3c9f19cfef1d622c918dcd431960f35e060 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x0caadd427a6feb5b5fc1137eb05aa7ddd9c08ce9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x77b7787a09818502305c95d68a2571f090abb135 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x9f07f8a82cb1af1466252e505b7b7ddee103bc91 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xdb298285fe4c5410b05390ca80e8fbe9de1f259b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x4cfe63294dac27ce941d42a778a37f2b35fea21b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xdf7837de1f2fa4631d716cf2502f8b230f1dcc32 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xce899f26928a2b21c6a2fddd393ef37c61dba918 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x21e00ff5374a0b803e0dc13a72800aca95b4b09e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe4a7b54c0a30da69c04dc54b89868c185ff382bc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf857b2764095b9a5f57c3e71f82f297fe4e45334 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9c632e6aaa3ea73f91554f8a3cb2ed2f29605e0c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x097b1b242d3ed90e191c5f83a62f41abe16f6ceb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x504a26cf29674bc77a9341e73f88ccecc864034c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0c1dc73159e30c4b06170f2593d3118968a0dca5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x42e07fa3d31190731368ca2f88d12d80139dca42 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3a46ed8fceb6ef1ada2e4600a522ae7e24d2ed18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4b361e60cf256b926ba15f157d69cac9cd037426 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xdd3acdbdc7b358df453a6cb6bca56c92aa5743aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x78ec15c5fd8efc5e924e9eebb9e549e29c785867 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x164ffdae2fe3891714bc2968f1875ca4fa1079d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x98f4779fccb177a6d856dd1dfd78cd15b7cd2af5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xabe8e5cabe24cb36df9540088fd7ce1175b9bc52 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x80a78a9b6b1272fdb612b39181bf113706024875 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7da2641000cbb407c329310c461b2cb9c70c3046 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfeac2eae96899709a43e252b6b92971d32f9c0f9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0f51bb10119727a7e5ea3538074fb341f56b09ad - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x15700b564ca08d9439c58ca5053166e8317aa138 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x59a529070fbb61e6d6c91f952ccb7f35c34cf8aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1010107b4757c915bc2f1ecd08c85d1bb0be92e0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3845badade8e6dff049820680d1f14bd3903a5d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6f365eb3686ee95bdefbae71f1728d62c0af7ab1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x85f17cf997934a597031b2e18a9ab6ebd4b9f6a4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcc4304a31d09258b0029ea7fe63d032f52e44efe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1196c6704789620514fd25632abe15f69a50bc4f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7475fa4c36344f1d633964f02564f37162299194 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x543ba622733bc9a7bfadd1d07b6c35ae1f9659d9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x20fd4c5396f7d9686f9997e0f10991957f7112fc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x0b3ae50babe7ffa4e1a50569cee6bdefd4ccaee0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf1bb41f9ed87e6c7e1f70e921b7b4bee1df7ae9c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x97ad75064b20fb2b2447fed4fa953bf7f007a706 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xad0d1436dd45dbd6d8e50ac82240b72f52d7ea89 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa0ef786bf476fe0810408caba05e536ac800ff86 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb5130f4767ab0acc579f25a76e8f9e977cb3f948 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbb0e17ef65f82ab018d8edd776e8dd940327b28b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8a60e489004ca22d775c5f2c657598278d17d9c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4c1746a800d224393fe2470c70a35717ed4ea5f1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf34960d9d60be18cc1d5afc1a6f012a723a28811 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9be89d2a4cd102d8fecc6bf9da793be995c22541 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4eddb15a0abfa2c349e8065af9214e942d9a6d36 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x37a645648df29205c6261289983fb04ecd70b4b3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x1cd9a56c8c2ea913c70319a44da75e99255aa46f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x6b43732a9ae9f8654d496c0a075aa4aa43057a0b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xfea7a6a0b346362bf88a9e4a88416b77a57d6c2a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x6d7187220f769bde541ff51dd37ee07416f861d2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x2f3e306d9f02ee8e8850f9040404918d0b345207 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xecdcb5b88f8e3c15f95c720c51c71c9e2080525d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa1832f7f4e534ae557f9b5ab76de54b1873e498b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x514d8e8099286a13486ef6c525c120f51c239b52 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4837b18a6d7af6159c8665505b90a2ed393255e0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6bfdb6f4e65ead27118592a41eb927cea6956198 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd7efb00d12c2c13131fd319336fdf952525da2af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf1a7000000950c7ad8aff13118bb7ab561a448ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x56cfc19d8cbf7d417d370844249be9cb2d2e19a1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x65c101e95d7dd475c7966330fa1a803205ff92ab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x0c5fa0e07949f941a6c2c29a008252db1527d6ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xfdb794692724153d1488ccdbe0c56c252596735f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xf7e78d9c4c74df889a83c8c8d6d05bf70ff75876 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x8fa62d23fb6359c1e1685dbfa9b63ef27ecdb612 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf1a7000000950c7ad8aff13118bb7ab561a448ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xacfe6019ed1a7dc6f7b508c02d1b04ec88cc21bf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9704d2adbc02c085ff526a37ac64872027ac8a50 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3bf9a2a798c9b122747344da0276d30a267a80dc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x64baa63f3eedf9661f736d8e4d42c6f8aa0cda71 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xbe0d3526fc797583dada3f30bc390013062a048b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf5d8015d625be6f59b8073c8189bd51ba28792e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0ff6ffcfda92c53f615a4a75d982f399c989366b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x841a3083074b1a40b644bf2ba2491a731b6da277 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x667102bd3413bfeaa3dffb48fa8288819e480a88 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3073f7aaa4db83f95e9fff17424f71d4751a3073 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x32b86b99441480a7e5bd3a26c124ec2373e3f015 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbf2e353f5db1a01e4e7f051222c666afc81b2574 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x960fce8724aa127184b6d13af41a711755236c77 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9ea59db651a3c79a8d52a394a49da8e9a214d6ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x95987b0cdc7f65d989a30b3b7132a38388c548eb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x707f635951193ddafbb40971a0fcaab8a6415160 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xcafcd85d8ca7ad1e1c6f82f651fa15e33aefd07b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xdd1ddd4d978ac0baef4bfa9c7e91853bfce90f11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x2ab445c24c96db13383bb34678adae50c43b4baa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xc157ee77518769b8009642f68a8d6a500ff59d53 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x90ec58ef4cc9f37b96de1e203b65bd4e6e79580e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd47f3e45b23b7594f5d5e1ccfde63237c60be49e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb3b32f9f8827d4634fe7d973fa1034ec9fddb3b3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd758916365b361cf833bb9c4c465ecd501ddd984 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbc7755a153e852cf76cccddb4c2e7c368f6259d8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb5130f4767ab0acc579f25a76e8f9e977cb3f948 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x39d5313c3750140e5042887413ba8aa6145a9bd2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x86bb94ddd16efc8bc58e6b056e8df71d9e666429 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa4c3497b57c8b6d510f3707a1e9694fd791f45fb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x1f3af095cda17d63cad238358837321e95fc5915 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x0688977ae5b10075f46519063fd2f03adc052c1f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xccb365d2e11ae4d6d74715c680f56cf58bf4bf10 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x643c4e15d7d62ad0abec4a9bd4b001aa3ef52d66 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8e729198d1c59b82bd6bba579310c40d740a11c2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xd4fe6e1e37dfcf35e9eeb54d4cca149d1c10239f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xaeac3b55c3522157ecda7ec8fcb86c832faa28af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x46777c76dbbe40fabb2aab99e33ce20058e76c59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xa78d8321b20c4ef90ecd72f2588aa985a4bdb684 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xaef5bbcbfa438519a5ea80b4c7181b4e78d419f2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x82e64f49ed5ec1bc6e43dad4fc8af9bb3a2312ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x484c2d6e3cdd945a8b2df735e079178c1036578c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x46777c76dbbe40fabb2aab99e33ce20058e76c59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0x1e925de1c68ef83bd98ee3e130ef14a50309c01b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x300211def2a644b036a9bdd3e58159bb2074d388 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x98d0baa52b2d063e780de12f615f963fe8537553 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x67543cf0304c19ca62ac95ba82fd4f4b40788dc1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x938171227ece879267122a36847b219cbd3b9d47 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6967f0974d76d34e140cae27efea32cdf546b58e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4b12507e171970b3acd48edfeb5bd1c676e61280 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb4d4ff3fcbd6965962a79229aa94631d394217cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xeb476e9ab6b1655860b3f40100678d0c1cedb321 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xd5eaaac47bd1993d661bc087e15dfb079a7f3c19 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x6d5ad1592ed9d6d1df9b93c793ab759573ed6714 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa18bbdcd86e4178d10ecd9316667cfe4c4aa8717 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf78d2e7936f5fe18308a3b2951a93b6c4a41f5e2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7ff7fa94b8b66ef313f7970d4eebd2cb3103a2c0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x40e3eddf6d253bb734381a309437428f121c594b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x56072c95faa701256059aa122697b133aded9279 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x881ba05de1e78f549cc63a8f6cabb1d4ad32250d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6c3ea9036406852006290770bedfcaba0e23a0e8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6fb3e0a217407efff7ca062d46c26e5d60a14d69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xd89cc9d79ad3c49e2cd477a8bbc8e63dee53f82e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x9d0bfe46891573c12021942c7d28f15ebb641988 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x46777c76dbbe40fabb2aab99e33ce20058e76c59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1f1c695f6b4a3f8b05f2492cef9474afb6d6ad69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3c8cd0db9a01efa063a7760267b822a129bc7dca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x060cb087a9730e13aa191f31a6d86bff8dfcdcc0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbf1aea8670d2528e08334083616dd9c5f3b087ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9b8df6e244526ab5f6e6400d331db28c8fdddb55 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4b6104755afb5da4581b81c552da3a25608c73b8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x12b4356c65340fb02cdff01293f95febb1512f3b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x08e8ac8c4bca64503f774d2c40c911e8a3ffcc12 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x47a1eb0b825b73e6a14807beaecafef199d5477c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x103071da56e7cd95b415320760d6a0ddc4da1ca5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc43c6bfeda065fe2c4c11765bf838789bd0bb5de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5732046a883704404f284ce41ffadd5b007fd668 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8292bb45bf1ee4d140127049757c2e0ff06317ed - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2d8ea194902bc55431420bd26be92b0782dce91d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0xd1f9c58e33933a993a3891f8acfe05a68e1afc05 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x17d70172c7c4205bd39ce80f7f0ee660b7dc5a23 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x624e2e7fdc8903165f64891672267ab0fcb98831 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbf388570ebd5b88bfc7cd21ec469813c15f453a3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x44551ca46fa5592bb572e20043f7c3d54c85cad7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2133031f5acbc493572c02f271186f241cd8d6a5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xcb6ab91007fe165c81b1e672007361cce9995fd1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb4fde59a779991bfb6a52253b51947828b982be3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x09be1692ca16e06f536f0038ff11d1da8524adb1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xc0634090f2fe6c6d75e61be2b949464abb498973 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd51827754a56860f04acd1d2699b049b026a5925 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa2e3356610840701bdf5611a53974510ae27e2e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x944824290cc12f31ae18ef51216a223ba4063092 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x10dea67478c5f8c5e2d90e5e9b26dbe60c54d800 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2f42b7d686ca3effc69778b6ed8493a7787b4d6e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc669928185dbce49d2230cc9b0979be6dc797957 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x5485a469faea1492191cfce7528ab6e58135aa4d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8e0e57dcb1ce8d9091df38ec1bfc3b224529754a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1151cb3d861920e07a38e03eead12c32178567f6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0305f515fa978cf87226cf8a9776d25bcfb2cc0b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x88800092ff476844f74dc2fc427974bbee2794ae - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe76c6c83af64e4c60245d8c7de953df673a7a33d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x90685e300a4c4532efcefe91202dfe1dfd572f47 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa849eaae994fb86afa73382e9bd88c2b6b18dc71 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbb2a93afcf5d3af8ae28dd50d6c18556ea532c5a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbad6c59d72d44512616f25b3d160c79db5a69ddf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4604151d4c98d1eea200b0d6bffb79a2613182aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa93d86af16fe83f064e3c0e2f3d129f7b7b002b0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x004e9c3ef86bc1ca1f0bb5c7662861ee93350568 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x68749665ff8d2d112fa859aa293f07a622782f38 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4da27a545c0c5b758a6ba100e3a049001de870f5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x820802fa8a99901f52e39acd21177b0be6ee2974 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x3ec2156d4c0a9cbdab4a016633b7bcf6a8d68ea2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x937a1cfaf0a3d9f5dc4d0927f72ee5e3e5f82a00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2e2cc4dfce60257f091980631e75f5c436b71c87 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x65553f5c85c78b977668cec098ec09475099aa61 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x04175b1f982b8c8444f238ac0aae59f029e21099 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1c93d155bd388241f9ab5df500d69eb529ce9583 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x45d9c101a3870ca5024582fd788f4e1e8f7971c3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1986cc18d8ec757447254310d2604f85741aa732 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xb3621cd34803cf7065dcb0d5bfb0f56c1834a063 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x570b1533f6daa82814b25b62b5c7c4c55eb83947 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x43c5034469bce262d32f64c5e7f9f359f5b1495f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbdf43ecadc5cef51b7d1772f722e40596bc1788b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x80ac24aa929eaf5013f6436cda2a7ba190f5cc0b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9813037ee2218799597d83d4a5b6f3b6778218d9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x88909d489678dd17aa6d9609f89b0419bf78fd9a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4274cd7277c7bb0806bd5fe84b9adae466a8da0a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa0246c9032bc3a600820415ae600c6388619a14d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xed04915c23f00a313a544955524eb7dbd823143d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0xaf7e3f16d747e77e927dc94287f86eb95a64d83d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x290f057a2c59b95d8027aa4abf31782676502071 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x46777c76dbbe40fabb2aab99e33ce20058e76c59 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9e12735d77c72c5c3670636d428f2f3815d8a4cb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6bb7a212910682dcfdbd5bcbb3e28fb4e8da10ee - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xf2e244d4020c182e8e2c936d4055e3f0e578064f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe3cf8dbcbdc9b220ddead0bd6342e245daff934d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xebcda5b80f62dd4dd2a96357b42bb6facbf30267 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xdc06717f367e57a16e06cce0c4761604460da8fc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xd5369a3cac0f4448a9a96bb98af9c887c92fc37b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x3199a64bc8aabdfd9a3937a346cc59c3d81d8a9a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xff7d6a96ae471bbcd7713af9cb1feeb16cf56b41 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/polygon/0x0000000000000000000000000000000000001010 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x68037790a0229e9ce6eaa8a99ea92964106c4703 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x28d38df637db75533bd3f71426f3410a82041544 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x437cc33344a0b27a429f795ff6b469c72698b291 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x30c7235866872213f68cb1f08c37cb9eccb93452 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xdc035d45d973e3ec169d2276ddab16f1e407384f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x078d782b760474a361dda0af3839290b0ef57ad6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x9151434b16b9763660705744891fa906f660ecc5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xdb99b0477574ac0b2d9c8cec56b42277da3fdb82 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd769d56f479e9e72a77bb1523e866a33098feec5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x927b51f251480a681271180da4de28d44ec4afb8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0xc02fe7317d4eb8753a02c35fe019786854a92001 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x7dcc39b4d1c53cb31e1abc0e358b43987fef80f7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x2416092f143378750bb29b79ed961ab195cceea5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfa2b947eec368f42195f24f36d2af29f7c24cec2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0xc3eacf0612346366db554c991d7858716db09f58 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x85e90a5430af45776548adb82ee4cd9e33b08077 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/0xef4461891dfb3ac8572ccf7c794664a8dd927945 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0f81001ef0a83ecce5ccebf63eb302c70a39a654 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x1111111111166b7fe7bd91427724b487980afc69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x8f187aa05619a017077f5308904739877ce9ea21 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/0x152b9d0fdc40c096757f570a51e494bd4b943e50 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5a70be406ce7471a44f0183b8d7091f4ad751db5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xbc396689893d065f41bc2c6ecbee5e0085233447 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7eb4db4dddb16a329c5ade17a8a0178331267e28 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/worldchain/NATIVE - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/worldchain/0x79a02482a880bce3f13e09da970dc34db4cd24d1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf816507e690f5aa4e29d164885eb5fa7a5627860 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd3f68c6e8aee820569d58adf8d85d94489315192 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4d0528598f916fd1d8dc80e5f54a8feedcfd4b18 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa0c56a8c0692bd10b3fa8f8ba79cf5332b7107f9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x8d0d000ee44948fc98c9b98a4fa4921476f08b0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xecca809227d43b895754382f1fd871628d7e51fb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x8d0d000ee44948fc98c9b98a4fa4921476f08b0d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9cb41fd9dc6891bae8187029461bfaadf6cc0c69 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/worldchain/0x2cfc85d8e48f8eab294be644d9e25c3030863003 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xe6df05ce8c8301223373cf5b969afcb1498c5528 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x0555e30da8f98308edb960aa94c0db47230d2b9c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4d4574f50dd8b9dbe623cf329dcc78d76935e610 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7d6fcb3327d7e17095fa8b0e3513ac7a3564f5e1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x7300b37dfdfab110d83290a29dfb31b1740219fe - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xbf8566956b4e2d8beb90c4c19dbb8c67a9290c36 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x87d00066cf131ff54b72b134a217d5401e5392b6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xdac991621fd8048d9f235324780abd6c3ad26421 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xe50e3d1a46070444f44df911359033f2937fcc13 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa776a95223c500e81cb0937b291140ff550ac3e4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x4f1aac70b303818ddd0823570af3bb46681d9bd8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x95034f653d5d161890836ad2b6b8cc49d14e029a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x3f160760535eb715d5809a26cf55408a2d9844c1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xde04da55b74435d7b9f2c5c62d9f1b53929b09aa - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x595e21b20e78674f8a64c1566a20b2b316bc3511 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe6f98920852a360497dbcc8ec895f1bb1f7c8df4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xd6b48ccf41a62eb3891e58d0f006b19b01d50cca - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x926759a8eaecfadb5d8bdc7a9c7b193c5085f507 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xd86e6ef14b96d942ef0abf0720c549197ea8c528 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x30c60b20c25b2810ca524810467a0c342294fc61 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6444c6c2d527d85ea97032da9a7504d6d1448ecf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x0f7dc5d02cc1e1f5ee47854d534d332a1081ccc8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x3fefe29da25bea166fb5f6ade7b5976d2b0e586b - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xc9ccbd76c2353e593cc975f13295e8289d04d3bb - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x39702843a6733932ec7ce0dde404e5a6dbd8c989 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x4fa7c69a7b69f8bc48233024d546bc299d6b03bf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xe0cd4cacddcbf4f36e845407ce53e87717b6601d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe0b7ad7f8f26e2b00c8b47b5df370f15f90fcf48 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6a72d3a87f97a0fee2c2ee4233bdaebc32813d7a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xe868084cf08f3c3db11f4b73a95473762d9463f7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xb035723d62e0e2ea7499d76355c9d560f13ba404 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xad8c787992428cd158e451aab109f724b6bc36de - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc20059e0317de91738d13af027dfc4a50781b066 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf39e4b21c84e737df08e2c3b32541d856f508e48 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x30a538effd91acefb1b12ce9bc0074ed18c9dfc9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x95af4af910c28e8ece4512bfe46f1f33687424ce - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x9ab778f84b2397c7015f7e83d12eee47d4c26694 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x45e02bc2875a2914c4f585bbf92a6f28bc07cb70 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcab84bc21f9092167fcfe0ea60f5ce053ab39a1e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x2f299be3b081e8cd47dc56c1932fcae7a91b5dcd - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x5f5668d7c748fc1a17540c3a7f9245d8cea10c29 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xf9902edfca4f49dcaebc335c73aebd82c79c2886 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc96de26018a54d51c097160568752c4e3bd6c364 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x0e7779e698052f8fe56c415c3818fcf89de9ac6d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x3e05284ff11a92d66e44b6b3ea70533729670257 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x6b785a0322126826d8226d77e173d75dafb84d11 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x226a2fa2556c48245e57cd1cba4c6c9e67077dd2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x1d58e204ca59328007469a614522903d69dc0a4c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xea87148a703adc0de89db2ac2b6b381093ae8ee0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xcaa1e525acb44aec4e0d17a0e2467aa3ea7ee3a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa4a2e2ca3fbfe21aed83471d28b6f65a233c6e00 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/celo/0xd221812de1bd094f35587ee8e174b07b6167d9af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x7b10d50b5885be4c7985a88408265c109bd1eec8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x4a0aaf171446dda0ed95295c46820e2015a28b07 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x00c83aecc790e8a4453e5dd3b0b4b3680501a7a7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x07f1508a8fe276fb96b9fcf80ace41eb2abddf81 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x6555255b8ded3c538cb398d9e36769f45d7d3ea7 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc299004a310303d1c0005cb14c70ccc02863924d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xe3225e11cab122f1a126a28997788e5230838ab9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x10ee9f68ee4e4d311e854ae14c53f5b25a917f85 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xaccfd598ef801178ed6c816c234b16ec51ae9f32 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x1aecab957bad4c6e36dd29c3d3bb470c4c29768a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x75231f58b43240c9718dd58b4967c5114342a86c - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x61fac5f038515572d6f42d4bcb6b581642753d50 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xd5c3a723e63a0ecab81081c26c6a3c4b2634bf85 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xf8f331dfa811132c43c308757cd802ca982b7211 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x2c3a8ee94ddd97244a93bc48298f97d2c412f7db - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x642ee4b1f054da33bc6003ad0278f9a27478caf5 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xb994882a1b9bd98a71dd6ea5f61577c42848b0e8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xda5e1988097297dcdc1f90d4dfe7909e847cbef6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xdac32deb60c817ee1cd8fc2bb2a7cda2ce1732d0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xd955c9ba56fb1ab30e34766e252a97ccce3d31a6 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x89824f7609107efe6f8088cd89a620c8edbeed6e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xa9616e5e23ec1582c2828b025becf3ef610e266f - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0xbde8a5331e8ac4831cf8ea9e42e229219eafab97 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x000ae314e2a2172a039b26378814c252734f556a - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xb4c6fedd984bc983b1a758d0875f1ea34f81a6af - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xc50673edb3a7b94e8cad8a7d4e0cd68864e33edf - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x2fd23a21c52fff0535328a7177da1fb31b8a819e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/0x15d0e0c55a3e7ee67152ad7e89acf164253ff68d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x11dc28d01984079b7efe7763b533e6ed9e3722b9 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x1789e0043623282d5dcc7f213d703c6d8bafbb04 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xfa35e2250e376c23955247383dc32c79082e7fcc - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/unichain/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x405fbc9004d857903bfd6b3357792d71a50726b0 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/optimism/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x696f9436b67233384889472cd7cd58a6fb5df4f1 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/worldchain/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0x8dedf84656fa932157e27c060d8613824e7979e3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/bnb/0xacf5a368ec5bb9e804c8ac0b508daa5a21c92e13 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0xa64a1b5b0a5ce578a8c6bca10cbe36d83713d170 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/avalanche/0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/arbitrum/0x0a1a1a107e45b7ced86833863f482bc5f4ed82ef - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0x52a8845df664d76c69d2eea607cd793565af42b8 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x9c4315eb7ba91b7bdc1727ad5f76ea4ac8875506 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/AzVCECC5zr21ikYQne4Xtoh3SVg848duPHUHb3M7bonk - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/8avjtjHAHFqp4g2RR9ALAGBpSTqKPZR8nRbzSTwZERA - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/8ZHE4ow1a2jjxuoMfyExuNamQNALv5ekZhsBn5nMDf5e - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/native - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/HUSTLFV3U5Km8u66rMQExh4nLy7unfKHedEXVK1WgSAG - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/3uXACfojUrya7VH51jVC1DCHq3uzK4A7g469Q954LABS - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/4NGbC4RRrUjS78ooSN53Up7gSg4dGrj6F6dxpMWHbonk - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/goodX4LG92UAcRdFRUykfP2fAfzQAVttrToEPxtxSkp - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/7kN5FQMD8ja4bzysEgc5FXmryKd6gCgjiWnhksjHCFb3 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/FViMp5phQH2bX81S7Yyn1yXjj3BRddFBNcMCbTH8FCze - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/QdyjMr627PR7NtWdcEcgFmDm5haBVUWEcj4jdM4boop - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/3xypwTgs9nWgjc6nUBiHmMb36t2PwL3SwCZkEQvW8FTX - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xaca92e438df0b2401ff60da7e4337b687a2435da - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/KMNo3nJsBXfcpJTVhZcXLW7RmTwTt4GVFE7suUBo9sS - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/UwU8RVXB69Y6Dcju6cN2Qef6fykkq6UUNpB15rZku6Z - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/AvZZF1YaZDziPY2RCK4oJrRVrbN3mTD9NL24hPeaZeUj - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/9tqjeRS1swj36Ee5C1iGiwAxjQJNGAVCzaTLwFY8bonk - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/CASHx9KJUStyftLFWGvEVf59SGeG9sh5FfcnZMVPCASH - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/76rTxzztXjJe7AUaBi7jQ5J61MFgpQgB4Cc934sWbonk - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xa37f4d2dc3d3c136b68fcf52be3afa09d4dfc38d - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/ethereum/0xfab99fcf605fd8f4593edb70a43ba56542777777 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/base/0x8a910ea80fc09d5b5a2120521a39b67980df0bc4 - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/Ey59PH7Z4BFU4HjyKnyMdWt5GGN76KazTAwQihoUXRnk - 2025-10-17T22:04:33.647Z - 0.8 - - - https://app.uniswap.org/explore/tokens/solana/CARDSccUMFKoPRZxt5vt3ksUbxEFEcnZ3H2pd3dKxYjp - 2025-10-17T22:04:33.647Z + https://app.uniswap.org/explore/tokens/base/0x290f057a2c59b95d8027aa4abf31782676502071 + 2025-03-20T21:28:30.528Z 0.8 \ No newline at end of file diff --git a/apps/web/scripts/start-anvil.sh b/apps/web/scripts/start-anvil.sh new file mode 100755 index 00000000000..1abc48436c7 --- /dev/null +++ b/apps/web/scripts/start-anvil.sh @@ -0,0 +1,14 @@ +#!/bin/bash +FORK_URL=$1 +PORT=${2:-8545} +EXTRA_FLAGS=${3:-} + +RUST_LOG=debug anvil \ + --print-traces \ + --disable-code-size-limit \ + --disable-min-priority-fee \ + --no-rate-limit \ + --hardfork prague \ + --fork-url "$FORK_URL" \ + --port "$PORT" \ + ${EXTRA_FLAGS} diff --git a/apps/web/src/appGraphql/data/apollo/client.ts b/apps/web/src/appGraphql/data/apollo/client.ts index bbd4eba5d0c..2d129fd1d35 100644 --- a/apps/web/src/appGraphql/data/apollo/client.ts +++ b/apps/web/src/appGraphql/data/apollo/client.ts @@ -1,3 +1,4 @@ +import { getRetryLink } from 'appGraphql/data/apollo/retryLink' import { ApolloClient, from, HttpLink } from '@apollo/client' import { setupSharedApolloCache } from 'uniswap/src/data/cache' import { getDatadogApolloLink } from 'utilities/src/logger/datadog/datadogLink' @@ -9,10 +10,11 @@ if (!API_URL) { const httpLink = new HttpLink({ uri: API_URL }) const datadogLink = getDatadogApolloLink() +const retryLink = getRetryLink() export const apolloClient = new ApolloClient({ connectToDevTools: true, - link: from([datadogLink, httpLink]), + link: from([datadogLink, retryLink, httpLink]), headers: { 'Content-Type': 'application/json', Origin: 'https://app.uniswap.org', diff --git a/apps/web/src/appGraphql/data/apollo/retryLink.ts b/apps/web/src/appGraphql/data/apollo/retryLink.ts new file mode 100644 index 00000000000..3a3400ba187 --- /dev/null +++ b/apps/web/src/appGraphql/data/apollo/retryLink.ts @@ -0,0 +1,43 @@ +import { RetryLink } from '@apollo/client/link/retry' + +/** + * Operations that should retry on network failure. + * These are queries used by useUpdateManualOutage hooks that power the outage banner. + */ +const RETRY_OPERATIONS = new Set([ + // Transaction queries + 'V4TokenTransactions', + 'V3TokenTransactions', + 'V2TokenTransactions', + 'V4Transactions', + 'V3Transactions', + 'V2Transactions', + // Pool queries + 'TopV4Pools', + 'TopV3Pools', + 'TopV2Pairs', +]) + +/** + * Creates an Apollo RetryLink that retries specific operations on network failure. + * Uses exponential backoff with jitter to avoid thundering herd. + */ +export function getRetryLink(): RetryLink { + return new RetryLink({ + delay: { + initial: 1000, + max: 10000, + jitter: true, + }, + attempts: { + max: 3, + retryIf: (error, operation) => { + if (!RETRY_OPERATIONS.has(operation.operationName)) { + return false + } + // Only retry on network errors, not GraphQL errors (validation, auth, etc.) + return !!error?.networkError + }, + }, + }) +} diff --git a/apps/web/src/appGraphql/data/pools/usePoolData.test.ts b/apps/web/src/appGraphql/data/pools/usePoolData.test.ts new file mode 100644 index 00000000000..5c3832377af --- /dev/null +++ b/apps/web/src/appGraphql/data/pools/usePoolData.test.ts @@ -0,0 +1,266 @@ +import { usePoolData } from 'appGraphql/data/pools/usePoolData' +import { FeeAmount } from '@uniswap/v3-sdk' +import { GraphQLApi } from '@universe/api' +import { validBEPoolToken0, validBEPoolToken1 } from 'test-utils/pools/fixtures' +import { renderHook } from 'test-utils/render' +import { V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools' +import { GQL_MAINNET_CHAINS } from 'uniswap/src/features/chains/chainInfo' +import { UniverseChainId } from 'uniswap/src/features/chains/types' + +const { mockV4Query, mockV3Query, mockV2Query, mockUseEnabledChains } = vi.hoisted(() => { + const mockV4Query = vi.fn() + const mockV3Query = vi.fn() + const mockV2Query = vi.fn() + const mockUseEnabledChains = vi.fn() + return { mockV4Query, mockV3Query, mockV2Query, mockUseEnabledChains } +}) + +vi.mock('@universe/api', async () => { + const actual = await vi.importActual('@universe/api') + return { + ...actual, + GraphQLApi: { + ...(actual.GraphQLApi || {}), + useV4PoolQuery: mockV4Query, + useV3PoolQuery: mockV3Query, + useV2PairQuery: mockV2Query, + }, + } +}) + +vi.mock('uniswap/src/features/chains/hooks/useEnabledChains', async () => { + const actual = await vi.importActual('uniswap/src/features/chains/hooks/useEnabledChains') + return { + ...actual, + useEnabledChains: mockUseEnabledChains, + } +}) + +const mockV4PoolData = { + v4Pool: { + poolId: '0xv4pool123', + protocolVersion: GraphQLApi.ProtocolVersion.V4, + feeTier: 500, + isDynamicFee: false, + tickSpacing: 55, + hook: { + id: '0xhook123', + address: '0xhook123', + }, + rewardsCampaign: { + id: 'campaign1', + boostedApr: 5.5, + startTimestamp: 1234567890, + endTimestamp: 1234567990, + }, + token0: validBEPoolToken0, + token0Supply: 100000, + token1: validBEPoolToken1, + token1Supply: 50000, + txCount: 1000, + volume24h: { + value: 500000, + }, + historicalVolume: [ + { value: 100000, timestamp: Date.now() / 1000 - 3600 }, + { value: 200000, timestamp: Date.now() / 1000 - 7200 }, + ], + totalLiquidity: { + value: 1000000, + }, + totalLiquidityPercentChange24h: { + value: 2.5, + }, + }, +} + +const mockV3PoolData = { + v3Pool: { + id: '0xv3pool456', + protocolVersion: GraphQLApi.ProtocolVersion.V3, + address: '0xv3pool456', + feeTier: FeeAmount.MEDIUM, + token0: validBEPoolToken0, + token0Supply: 80000, + token1: validBEPoolToken1, + token1Supply: 40000, + txCount: 800, + volume24h: { + value: 400000, + }, + historicalVolume: [ + { value: 80000, timestamp: Date.now() / 1000 - 3600 }, + { value: 160000, timestamp: Date.now() / 1000 - 7200 }, + ], + totalLiquidity: { + value: 800000, + }, + totalLiquidityPercentChange24h: { + value: 1.8, + }, + }, +} + +const mockV2PairData = { + v2Pair: { + id: '0xv2pair789', + protocolVersion: GraphQLApi.ProtocolVersion.V2, + address: '0xv2pair789', + token0: validBEPoolToken0, + token0Supply: 60000, + token1: validBEPoolToken1, + token1Supply: 30000, + txCount: 600, + volume24h: { + value: 300000, + }, + historicalVolume: [ + { value: 60000, timestamp: Date.now() / 1000 - 3600 }, + { value: 120000, timestamp: Date.now() / 1000 - 7200 }, + ], + totalLiquidity: { + value: 600000, + }, + totalLiquidityPercentChange24h: { + value: 1.2, + }, + }, +} + +describe('usePoolData', () => { + beforeEach(() => { + vi.clearAllMocks() + + mockUseEnabledChains.mockReturnValue({ + isTestnetModeEnabled: false, + defaultChainId: UniverseChainId.Mainnet, + chains: [UniverseChainId.Mainnet], + gqlChains: GQL_MAINNET_CHAINS, + }) + + mockV4Query.mockReturnValue({ loading: false, error: undefined, data: undefined }) + mockV3Query.mockReturnValue({ loading: false, error: undefined, data: undefined }) + mockV2Query.mockReturnValue({ loading: false, error: undefined, data: undefined }) + }) + + it('should return v4 pool data with hook address and rewards campaign', () => { + mockV4Query.mockReturnValue({ + loading: false, + error: undefined, + data: mockV4PoolData, + }) + + const { result } = renderHook(() => + usePoolData({ + poolIdOrAddress: '0xv4pool123', + chainId: UniverseChainId.Mainnet, + isPoolAddress: false, + }), + ) + + expect(result.current.loading).toBe(false) + expect(result.current.error).toBe(false) + expect(result.current.data).toBeDefined() + + expect(result.current.data?.idOrAddress).toBe('0xv4pool123') + expect(result.current.data?.protocolVersion).toBe(GraphQLApi.ProtocolVersion.V4) + expect(result.current.data?.feeTier?.feeAmount).toBe(500) + expect(result.current.data?.feeTier?.tickSpacing).toBe(55) + expect(result.current.data?.feeTier?.isDynamic).toBe(false) + expect(result.current.data?.hookAddress).toBe('0xhook123') + expect(result.current.data?.rewardsCampaign).toEqual({ + id: 'campaign1', + boostedApr: 5.5, + startTimestamp: 1234567890, + endTimestamp: 1234567990, + }) + + expect(result.current.data?.token0).toEqual(validBEPoolToken0) + expect(result.current.data?.token1).toEqual(validBEPoolToken1) + expect(result.current.data?.tvlToken0).toBe(100000) + expect(result.current.data?.tvlToken1).toBe(50000) + + expect(result.current.data?.volumeUSD24H).toBe(500000) + expect(result.current.data?.tvlUSD).toBe(1000000) + expect(result.current.data?.tvlUSDChange).toBe(2.5) + expect(result.current.data?.txCount).toBe(1000) + }) + + it('should return v3 pool data with calculated fee tier', () => { + mockV3Query.mockReturnValue({ + loading: false, + error: undefined, + data: mockV3PoolData, + }) + + const { result } = renderHook(() => + usePoolData({ + poolIdOrAddress: '0xv3pool456', + chainId: UniverseChainId.Mainnet, + isPoolAddress: true, + }), + ) + + expect(result.current.loading).toBe(false) + expect(result.current.error).toBe(false) + expect(result.current.data).toBeDefined() + + expect(result.current.data?.idOrAddress).toBe('0xv3pool456') + expect(result.current.data?.protocolVersion).toBe(GraphQLApi.ProtocolVersion.V3) + expect(result.current.data?.feeTier?.feeAmount).toBe(FeeAmount.MEDIUM) + expect(result.current.data?.feeTier?.tickSpacing).toBe(60) + expect(result.current.data?.feeTier?.isDynamic).toBe(false) + + expect(result.current.data?.hookAddress).toBeUndefined() + expect(result.current.data?.rewardsCampaign).toBeUndefined() + + expect(result.current.data?.token0).toEqual(validBEPoolToken0) + expect(result.current.data?.token1).toEqual(validBEPoolToken1) + expect(result.current.data?.tvlToken0).toBe(80000) + expect(result.current.data?.tvlToken1).toBe(40000) + + expect(result.current.data?.volumeUSD24H).toBe(400000) + expect(result.current.data?.tvlUSD).toBe(800000) + expect(result.current.data?.tvlUSDChange).toBe(1.8) + expect(result.current.data?.txCount).toBe(800) + }) + + it('should return v2 pool data with default fee tier', () => { + mockV2Query.mockReturnValue({ + loading: false, + error: undefined, + data: mockV2PairData, + }) + + const { result } = renderHook(() => + usePoolData({ + poolIdOrAddress: '0xv2pair789', + chainId: UniverseChainId.Mainnet, + isPoolAddress: true, + }), + ) + + expect(result.current.loading).toBe(false) + expect(result.current.error).toBe(false) + expect(result.current.data).toBeDefined() + + expect(result.current.data?.idOrAddress).toBe('0xv2pair789') + expect(result.current.data?.protocolVersion).toBe(GraphQLApi.ProtocolVersion.V2) + expect(result.current.data?.feeTier?.feeAmount).toBe(V2_DEFAULT_FEE_TIER) + expect(result.current.data?.feeTier?.tickSpacing).toBeUndefined() + expect(result.current.data?.feeTier?.isDynamic).toBe(false) + + expect(result.current.data?.hookAddress).toBeUndefined() + expect(result.current.data?.rewardsCampaign).toBeUndefined() + + expect(result.current.data?.token0).toEqual(validBEPoolToken0) + expect(result.current.data?.token1).toEqual(validBEPoolToken1) + expect(result.current.data?.tvlToken0).toBe(60000) + expect(result.current.data?.tvlToken1).toBe(30000) + + expect(result.current.data?.volumeUSD24H).toBe(300000) + expect(result.current.data?.tvlUSD).toBe(600000) + expect(result.current.data?.tvlUSDChange).toBe(1.2) + expect(result.current.data?.txCount).toBe(600) + }) +}) diff --git a/apps/web/src/appGraphql/data/pools/usePoolData.ts b/apps/web/src/appGraphql/data/pools/usePoolData.ts index e2d1bf91be7..cedee21df58 100644 --- a/apps/web/src/appGraphql/data/pools/usePoolData.ts +++ b/apps/web/src/appGraphql/data/pools/usePoolData.ts @@ -1,8 +1,9 @@ +import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk' import { GraphQLApi } from '@universe/api' import { FeeData } from 'components/Liquidity/Create/types' import ms from 'ms' import { useMemo } from 'react' -import { DEFAULT_TICK_SPACING, V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools' +import { V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' import { UniverseChainId } from 'uniswap/src/features/chains/types' import { toGraphQLChain } from 'uniswap/src/features/chains/utils' @@ -130,7 +131,7 @@ export function usePoolData({ const pool = dataV4?.v4Pool ?? dataV3?.v3Pool ?? dataV2?.v2Pair ?? undefined const feeTier: FeeData = { feeAmount: dataV4?.v4Pool?.feeTier ?? dataV3?.v3Pool?.feeTier ?? V2_DEFAULT_FEE_TIER, - tickSpacing: DEFAULT_TICK_SPACING, + tickSpacing: dataV4?.v4Pool?.tickSpacing ?? TICK_SPACINGS[dataV3?.v3Pool?.feeTier as FeeAmount], isDynamic: dataV4?.v4Pool?.isDynamicFee ?? false, } const poolId = dataV4?.v4Pool?.poolId ?? dataV3?.v3Pool?.address ?? dataV2?.v2Pair?.address ?? poolIdOrAddress diff --git a/apps/web/src/appGraphql/data/util.tsx b/apps/web/src/appGraphql/data/util.tsx index 18e459e39c1..a7ffb3e9d86 100644 --- a/apps/web/src/appGraphql/data/util.tsx +++ b/apps/web/src/appGraphql/data/util.tsx @@ -189,6 +189,12 @@ const PROTOCOL_META: { [source in GraphQLApi.PriceSource]: ProtocolMeta } = { color: '$chain_137', gradient: { start: 'rgba(96, 123, 238, 0.20)', end: 'rgba(55, 70, 136, 0.00)' }, }, + [GraphQLApi.PriceSource.External]: { + // TODO (LP-350): Remove this since this protocol chart does not exist anymore + name: 'external', + color: '$neutral1', + gradient: { start: 'rgba(252, 116, 254, 0.20)', end: 'rgba(252, 116, 254, 0.00)' }, + }, /* [GraphQLApi.PriceSource.UniswapX]: { name: 'UniswapX', color: purple } */ } diff --git a/apps/web/src/assets/images/portfolio-connect-wallet-banner-grid/dark.svg b/apps/web/src/assets/images/portfolio-connect-wallet-banner-grid/dark.svg new file mode 100644 index 00000000000..58541a4f109 --- /dev/null +++ b/apps/web/src/assets/images/portfolio-connect-wallet-banner-grid/dark.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/assets/images/portfolio-connect-wallet-banner-grid/light.svg b/apps/web/src/assets/images/portfolio-connect-wallet-banner-grid/light.svg new file mode 100644 index 00000000000..4a2adf55f55 --- /dev/null +++ b/apps/web/src/assets/images/portfolio-connect-wallet-banner-grid/light.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/assets/images/portfolio-page-disconnected-preview/dark.svg b/apps/web/src/assets/images/portfolio-page-disconnected-preview/dark.svg new file mode 100644 index 00000000000..1ae6e3cb76e --- /dev/null +++ b/apps/web/src/assets/images/portfolio-page-disconnected-preview/dark.svg @@ -0,0 +1,778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/assets/images/portfolio-page-disconnected-preview/light.svg b/apps/web/src/assets/images/portfolio-page-disconnected-preview/light.svg new file mode 100644 index 00000000000..8e9c0579fe4 --- /dev/null +++ b/apps/web/src/assets/images/portfolio-page-disconnected-preview/light.svg @@ -0,0 +1,778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/assets/images/portfolio-page-disconnected-preview/mobile-dark.svg b/apps/web/src/assets/images/portfolio-page-disconnected-preview/mobile-dark.svg new file mode 100644 index 00000000000..843ed723d5d --- /dev/null +++ b/apps/web/src/assets/images/portfolio-page-disconnected-preview/mobile-dark.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/assets/images/portfolio-page-disconnected-preview/mobile-light.svg b/apps/web/src/assets/images/portfolio-page-disconnected-preview/mobile-light.svg new file mode 100644 index 00000000000..9530c38d4e9 --- /dev/null +++ b/apps/web/src/assets/images/portfolio-page-disconnected-preview/mobile-light.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/assets/svg/demo-wallet-emblem.svg b/apps/web/src/assets/svg/demo-wallet-emblem.svg new file mode 100644 index 00000000000..1839d363511 --- /dev/null +++ b/apps/web/src/assets/svg/demo-wallet-emblem.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/src/components/AccountDetails/AddressDisplay.tsx b/apps/web/src/components/AccountDetails/AddressDisplay.tsx index 56d5bc9eb1d..ef1e13367ba 100644 --- a/apps/web/src/components/AccountDetails/AddressDisplay.tsx +++ b/apps/web/src/components/AccountDetails/AddressDisplay.tsx @@ -1,4 +1,4 @@ -import styled from 'lib/styled-components' +import { deprecatedStyled } from 'lib/styled-components' import { EllipsisStyle } from 'theme/components/styles' import { Flex } from 'ui/src' import { Unitag } from 'ui/src/components/icons/Unitag' @@ -7,7 +7,7 @@ import { useENSName } from 'uniswap/src/features/ens/api' import { TestID } from 'uniswap/src/test/fixtures/testIDs' import { shortenAddress } from 'utilities/src/addresses' -const IdentifierText = styled.span` +const IdentifierText = deprecatedStyled.span` ${EllipsisStyle} ` diff --git a/apps/web/src/components/AccountDetails/MultiBlockchainAddressDisplay.tsx b/apps/web/src/components/AccountDetails/MultiBlockchainAddressDisplay.tsx index 33b601988f3..895deef738f 100644 --- a/apps/web/src/components/AccountDetails/MultiBlockchainAddressDisplay.tsx +++ b/apps/web/src/components/AccountDetails/MultiBlockchainAddressDisplay.tsx @@ -49,11 +49,13 @@ function PrimaryAddressDisplay({ ensName, primaryAddress, isMultipleAddresses, + hideAddressInSubtitle, }: { unitag?: string ensName?: string primaryAddress: string isMultipleAddresses: boolean + hideAddressInSubtitle?: boolean }) { const { t } = useTranslation() const shortenedPrimaryAddress = shortenAddress({ address: primaryAddress }) @@ -62,17 +64,18 @@ function PrimaryAddressDisplay({ return ( - {isMultipleAddresses ? ( - - {shortenedPrimaryAddress} {t('common.plusMore', { number: 1 })} - - ) : ( - + {!hideAddressInSubtitle && + (isMultipleAddresses ? ( - {shortenedPrimaryAddress} + {shortenedPrimaryAddress} {t('common.plusMore', { number: 1 })} - - )} + ) : ( + + + {shortenedPrimaryAddress} + + + ))} ) } @@ -138,7 +141,7 @@ function TooltipAccountRow({ account }: { account: AccountItem }) { ) } -export function MultiBlockchainAddressDisplay() { +export function MultiBlockchainAddressDisplay({ hideAddressInSubtitle }: { hideAddressInSubtitle?: boolean }) { const activeAddresses = useActiveAddresses() const evmAddress = activeAddresses.evmAddress const { data: ensName } = useENSName(evmAddress) @@ -193,6 +196,7 @@ export function MultiBlockchainAddressDisplay() { ensName={ensName ?? undefined} primaryAddress={primaryAddress} isMultipleAddresses={isMultipleAddresses} + hideAddressInSubtitle={hideAddressInSubtitle} /> } /> diff --git a/apps/web/src/components/AccountDrawer/AccountDrawer.e2e.test.ts b/apps/web/src/components/AccountDrawer/AccountDrawer.e2e.test.ts index 3d4eab9b0d6..d6a01855fa7 100644 --- a/apps/web/src/components/AccountDrawer/AccountDrawer.e2e.test.ts +++ b/apps/web/src/components/AccountDrawer/AccountDrawer.e2e.test.ts @@ -26,140 +26,155 @@ async function countPortfolioBalancesQueries(page: Page, actions: () => Promise< return portfolioBalanceCount } -test.describe('Mini Portfolio settings', () => { - test.beforeEach(async ({ page }) => { - await page.goto('/swap') - await page.getByTestId(TestID.Web3StatusConnected).click() - await page.getByTestId(TestID.WalletSettings).click() - }) - test('changes theme', async ({ page }) => { - await page.getByTestId(TestID.ThemeDark).click() - await expect(page.locator('html')).toHaveClass('t_dark') - await page.getByTestId(TestID.ThemeLight).click() - await expect(page.locator('html')).toHaveClass('t_light') - }) - - test('changes language', async ({ page }) => { - await page.getByTestId(TestID.LanguageSettingsButton).click() - await page.getByRole('link', { name: 'Spanish (Spain)' }).click() - await expect(page.getByText('Uniswap está disponible en:')).toBeVisible() - await page.reload() - await expect(page.url()).toContain('lng=es-ES') - await expect(page.getByText('Uniswap está disponible en:')).toBeVisible() - }) - - test('toggles testnet', async ({ page }) => { - await page.getByTestId(TestID.TestnetsToggle).click() - await expect(page.getByTestId(TestID.TestnetsToggle)).toHaveAttribute('aria-checked', 'true') - await expect(page.getByText('Swapping on Sepolia')).toBeVisible() - }) - - test('disconnected wallet settings should not be accessible', async ({ page }) => { - await page.goto('/swap?eagerlyConnect=false') - await page.getByLabel('Navigation button').click() - await expect(page.getByTestId(TestID.WalletSettings)).not.toBeVisible() - }) - - test('settings on mobile should be accessible via bottom sheet', async ({ page }) => { - await page.setViewportSize({ width: 375, height: 667 }) - await expect(page.getByTestId(TestID.AccountDrawer).first()).toHaveAttribute('class', /is_Sheet/) - }) -}) - -test.describe('Mini Portfolio account drawer', () => { - test.beforeEach(async ({ page, graphql }) => { - // Set up request interception for portfolio balances - await graphql.intercept('PortfolioBalances', Mocks.PortfolioBalances.hayden) - await graphql.intercept('NftsTab', Mocks.Account.nfts) - await graphql.intercept('ActivityWeb', Mocks.Account.full_activity_history) - await page.goto(`/swap?eagerlyConnectAddress=${HAYDEN_ADDRESS}`) - }) - - test('should fetch balances when the account drawer is opened', async ({ page }) => { - const portfolioBalanceCount = await countPortfolioBalancesQueries(page, async () => { - // Click to open drawer - await page.getByTestId(TestID.Web3StatusConnected).click() - await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() - }) - - expect(portfolioBalanceCount).toBe(1) - }) - - test('should not re-fetch balances on second open', async ({ page }) => { - // First, open drawer and let it fetch data (this should trigger a request) - await page.getByTestId(TestID.Web3StatusConnected).click() - await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() - - // Wait for the portfolio data to actually load - await page.getByTestId(TestID.MiniPortfolioPage).waitFor() - - // Close the drawer - await page.getByTestId(TestID.CloseAccountDrawer).click() - await expect(page.getByTestId(TestID.AccountDrawer)).not.toBeVisible() - - // Now test opening it a second time (should not trigger another request due to caching) - const portfolioBalanceCount = await countPortfolioBalancesQueries(page, async () => { - // Click to open drawer again - await page.getByTestId(TestID.Web3StatusConnected).click() - await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() +test.describe( + 'Account Drawer', + { + tag: '@team:apps-portfolio', + annotation: [ + { type: 'DD_TAGS[team]', description: 'apps-portfolio' }, + { type: 'DD_TAGS[test.type]', description: 'web-e2e' }, + ], + }, + () => { + test.describe('Mini Portfolio settings', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/swap') + await page.getByTestId(TestID.Web3StatusConnected).click() + await page.getByTestId(TestID.WalletSettings).click() + }) + test('changes theme', async ({ page }) => { + await page.getByTestId(TestID.ThemeDark).click() + await expect(page.locator('html')).toHaveClass('t_dark') + await page.getByTestId(TestID.ThemeLight).click() + await expect(page.locator('html')).toHaveClass('t_light') + }) + + test('changes language', async ({ page }) => { + await page.getByTestId(TestID.LanguageSettingsButton).click() + await page.getByRole('link', { name: 'Spanish (Spain)' }).click() + await expect(page.getByText('Uniswap está disponible en:')).toBeVisible() + await page.reload() + await expect(page.url()).toContain('lng=es-ES') + await expect(page.getByText('Uniswap está disponible en:')).toBeVisible() + }) + + test('toggles testnet', async ({ page }) => { + await page.getByTestId(TestID.TestnetsToggle).click() + await expect(page.getByTestId(TestID.TestnetsToggle)).toHaveAttribute('aria-checked', 'true') + // Confirm the info modal appears and then close it + const modalButton = page.getByRole('button', { name: 'Close' }) + await expect(modalButton).toBeVisible() + await modalButton.click() + }) + + test('disconnected wallet settings should not be accessible', async ({ page }) => { + await page.goto('/swap?eagerlyConnect=false') + await page.getByLabel('Navigation button').click() + await expect(page.getByTestId(TestID.WalletSettings)).not.toBeVisible() + }) + + test('settings on mobile should be accessible via bottom sheet', async ({ page }) => { + await page.setViewportSize({ width: 375, height: 667 }) + await expect(page.getByTestId(TestID.AccountDrawer).first()).toHaveAttribute('class', /is_Sheet/) + }) }) - expect(portfolioBalanceCount).toBe(0) - }) - - test('fetches account information', async ({ page }) => { - // Open the mini portfolio - await page.getByTestId(TestID.Web3StatusConnected).click() - - // Wait for the drawer and main content to load - await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() - await page.getByTestId(TestID.MiniPortfolioPage).waitFor() - - // Verify wallet state - wait for tokens tab to load - await expect(page.getByTestId(TestID.MiniPortfolioNavbar)).toContainText('Tokens') - await expect(page.getByTestId(TestID.MiniPortfolioPage)).toContainText('Hidden tokens') - - // Check NFTs section - await page.getByTestId(TestID.MiniPortfolioNavbar).getByText('NFTs').click() - await page.waitForTimeout(15_000) - await expect( - page.getByTestId(`${TestID.MiniPortfolioNftItem}-${'0x3C90502f0CB0ad0A48c51357E65Ff15247A1D88E'}-${21}`), - ).toBeVisible() - - // Check Activity section - await page.getByTestId(TestID.MiniPortfolioNavbar).getByText('Activity').click() - await expect(page.getByTestId(TestID.MiniPortfolioPage)).toContainText('Contract Interaction') - }) - - test('refetches balances when account changes', async ({ page, graphql }) => { - // Open account drawer with first account - await page.getByTestId(TestID.Web3StatusConnected).click() - const drawer = page.getByTestId(TestID.AccountDrawer) - await expect(drawer).toBeVisible() - await page.getByTestId(TestID.MiniPortfolioPage).waitFor() - - // Verify first account address - await expect(drawer.getByText(HAYDEN_ADDRESS.slice(0, 6))).toBeVisible() - - // Set up mock data for second account - await graphql.intercept('PortfolioBalances', Mocks.PortfolioBalances.test_wallet) - - // Count portfolio requests triggered by account change - const portfolioRequestCount = await countPortfolioBalancesQueries(page, async () => { - // Switch to second account (this should trigger new portfolio requests) - await page.goto(`/swap`) - - // Open drawer with new account - await page.getByTestId(TestID.Web3StatusConnected).click() - const newDrawer = page.getByTestId(TestID.AccountDrawer) - await expect(newDrawer).toBeVisible() - await page.getByTestId(TestID.MiniPortfolioPage).waitFor() - - // Verify new account address - await expect(newDrawer.getByText('test0')).toBeVisible() + test.describe('Mini Portfolio account drawer', () => { + test.beforeEach(async ({ page, graphql }) => { + // Set up request interception for portfolio balances + await graphql.intercept('PortfolioBalances', Mocks.PortfolioBalances.hayden) + await graphql.intercept('NftsTab', Mocks.Account.nfts) + await graphql.intercept('ActivityWeb', Mocks.Account.full_activity_history) + await page.goto(`/swap?eagerlyConnectAddress=${HAYDEN_ADDRESS}`) + }) + + test('should fetch balances when the account drawer is opened', async ({ page }) => { + const portfolioBalanceCount = await countPortfolioBalancesQueries(page, async () => { + // Click to open drawer + await page.getByTestId(TestID.Web3StatusConnected).click() + await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() + }) + + expect(portfolioBalanceCount).toBe(1) + }) + + test('should not re-fetch balances on second open', async ({ page }) => { + // First, open drawer and let it fetch data (this should trigger a request) + await page.getByTestId(TestID.Web3StatusConnected).click() + await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() + + // Wait for the portfolio data to actually load + await page.getByTestId(TestID.MiniPortfolioPage).waitFor() + + // Close the drawer + await page.getByTestId(TestID.CloseAccountDrawer).click() + await expect(page.getByTestId(TestID.AccountDrawer)).not.toBeVisible() + + // Now test opening it a second time (should not trigger another request due to caching) + const portfolioBalanceCount = await countPortfolioBalancesQueries(page, async () => { + // Click to open drawer again + await page.getByTestId(TestID.Web3StatusConnected).click() + await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() + }) + + expect(portfolioBalanceCount).toBe(0) + }) + + test('fetches account information', async ({ page }) => { + // Open the mini portfolio + await page.getByTestId(TestID.Web3StatusConnected).click() + + // Wait for the drawer and main content to load + await expect(page.getByTestId(TestID.AccountDrawer)).toBeVisible() + await page.getByTestId(TestID.MiniPortfolioPage).waitFor() + + // Verify wallet state - wait for tokens tab to load + await expect(page.getByTestId(TestID.MiniPortfolioNavbar)).toContainText('Tokens') + await expect(page.getByTestId(TestID.MiniPortfolioPage)).toContainText('Hidden tokens') + + // Check NFTs section + await page.getByTestId(TestID.MiniPortfolioNavbar).getByText('NFTs').click() + await page.waitForTimeout(15_000) + await expect( + page.getByTestId(`${TestID.MiniPortfolioNftItem}-${'0x3C90502f0CB0ad0A48c51357E65Ff15247A1D88E'}-${21}`), + ).toBeVisible() + + // Check Activity section + await page.getByTestId(TestID.MiniPortfolioNavbar).getByText('Activity').click() + await expect(page.getByTestId(TestID.MiniPortfolioPage)).toContainText('Contract Interaction') + }) + + test('refetches balances when account changes', async ({ page, graphql }) => { + // Open account drawer with first account + await page.getByTestId(TestID.Web3StatusConnected).click() + const drawer = page.getByTestId(TestID.AccountDrawer) + await expect(drawer).toBeVisible() + await page.getByTestId(TestID.MiniPortfolioPage).waitFor() + + // Verify first account address + await expect(drawer.getByText(HAYDEN_ADDRESS.slice(0, 6))).toBeVisible() + + // Set up mock data for second account + await graphql.intercept('PortfolioBalances', Mocks.PortfolioBalances.test_wallet) + + // Count portfolio requests triggered by account change + const portfolioRequestCount = await countPortfolioBalancesQueries(page, async () => { + // Switch to second account (this should trigger new portfolio requests) + await page.goto(`/swap`) + + // Open drawer with new account + await page.getByTestId(TestID.Web3StatusConnected).click() + const newDrawer = page.getByTestId(TestID.AccountDrawer) + await expect(newDrawer).toBeVisible() + await page.getByTestId(TestID.MiniPortfolioPage).waitFor() + + // Verify new account address + await expect(newDrawer.getByText('test0')).toBeVisible() + }) + + // Verify that account change triggered portfolio requests + expect(portfolioRequestCount).toBeGreaterThanOrEqual(1) + }) }) - - // Verify that account change triggered portfolio requests - expect(portfolioRequestCount).toBeGreaterThanOrEqual(1) - }) -}) + }, +) diff --git a/apps/web/src/components/AccountDrawer/ActionTile.tsx b/apps/web/src/components/AccountDrawer/ActionTile.tsx index cabbd3f44a3..f0286745cc1 100644 --- a/apps/web/src/components/AccountDrawer/ActionTile.tsx +++ b/apps/web/src/components/AccountDrawer/ActionTile.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react' import { SpinnerSVG } from 'theme/components/icons/spinner' -import { Flex, styled, Text, useSporeColors } from 'ui/src' +import { Flex, FlexProps, styled, Text, useSporeColors } from 'ui/src' const LoadingButtonSpinner = (props: React.ComponentPropsWithoutRef<'svg'>) => ( @@ -39,6 +39,16 @@ const Tile = styled(Flex, { }, }) +export type ActionTileProps = { + dataTestId: string + Icon: ReactNode + name: string + onClick: () => void + loading?: boolean + disabled?: boolean + padding?: FlexProps['p'] +} + export function ActionTile({ dataTestId, Icon, @@ -46,20 +56,14 @@ export function ActionTile({ onClick, loading, disabled, -}: { - dataTestId: string - Icon: ReactNode - name: string - onClick: () => void - loading?: boolean - disabled?: boolean -}) { + padding = '$spacing12', +}: ActionTileProps) { const { accent1 } = useSporeColors() return ( - + {loading ? : Icon} - + {name} diff --git a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.anvil.e2e.test.ts b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.anvil.e2e.test.ts index 38ef1adba12..36604cfc25b 100644 --- a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.anvil.e2e.test.ts +++ b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.anvil.e2e.test.ts @@ -4,23 +4,33 @@ import { HAYDEN_ADDRESS, HAYDEN_ENS, UNITAG_NAME } from 'playwright/fixtures/wal const test = getTest({ withAnvil: true }) -test.describe('AuthenticatedHeader unitag and ENS display', () => { - // Test cases: - // 1. Shows ENS, followed by address, if ENS exists but not Unitag - // 2. Shows Unitag, followed by address, if user has both Unitag and ENS +test.describe( + 'AuthenticatedHeader unitag and ENS display', + { + tag: '@team:apps-growth', + annotation: [ + { type: 'DD_TAGS[team]', description: 'apps-growth' }, + { type: 'DD_TAGS[test.type]', description: 'web-e2e' }, + ], + }, + () => { + // Test cases: + // 1. Shows ENS, followed by address, if ENS exists but not Unitag + // 2. Shows Unitag, followed by address, if user has both Unitag and ENS - const ACCOUNT_WITH_ENS = HAYDEN_ADDRESS + const ACCOUNT_WITH_ENS = HAYDEN_ADDRESS - test('shows ENS, followed by address when ENS exists but not Unitag', async ({ page }) => { - await page.goto(`/swap?eagerlyConnectAddress=${ACCOUNT_WITH_ENS}`) + test('shows ENS, followed by address when ENS exists but not Unitag', async ({ page }) => { + await page.goto(`/swap?eagerlyConnectAddress=${ACCOUNT_WITH_ENS}`) - await openAccountDrawerAndVerify({ page, expectedPrimaryText: HAYDEN_ENS, walletAddress: HAYDEN_ADDRESS }) - }) + await openAccountDrawerAndVerify({ page, expectedPrimaryText: HAYDEN_ENS, walletAddress: HAYDEN_ADDRESS }) + }) - test('shows Unitag when user has both Unitag and ENS', async ({ page }) => { - await mockUnitagResponse({ page, address: HAYDEN_ADDRESS, unitag: UNITAG_NAME }) - await page.goto(`/swap?eagerlyConnectAddress=${HAYDEN_ADDRESS}`) + test('shows Unitag when user has both Unitag and ENS', async ({ page }) => { + await mockUnitagResponse({ page, address: HAYDEN_ADDRESS, unitag: UNITAG_NAME }) + await page.goto(`/swap?eagerlyConnectAddress=${HAYDEN_ADDRESS}`) - await openAccountDrawerAndVerify({ page, expectedPrimaryText: UNITAG_NAME, walletAddress: HAYDEN_ADDRESS }) - }) -}) + await openAccountDrawerAndVerify({ page, expectedPrimaryText: UNITAG_NAME, walletAddress: HAYDEN_ADDRESS }) + }) + }, +) diff --git a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.e2e.test.ts b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.e2e.test.ts index e1348cbf0fd..d9e57ed8817 100644 --- a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.e2e.test.ts +++ b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.e2e.test.ts @@ -4,22 +4,32 @@ import { TEST_WALLET_ADDRESS, UNITAG_NAME } from 'playwright/fixtures/wallets' const test = getTest() -test.describe('AuthenticatedHeader unitag and ENS display', () => { - // Test cases: - // 1. Shows address if no Unitag or ENS exists - // 2. Shows Unitag, followed by address, if Unitag exists but not ENS +test.describe( + 'AuthenticatedHeader unitag and ENS display', + { + tag: '@team:apps-growth', + annotation: [ + { type: 'DD_TAGS[team]', description: 'apps-growth' }, + { type: 'DD_TAGS[test.type]', description: 'web-e2e' }, + ], + }, + () => { + // Test cases: + // 1. Shows address if no Unitag or ENS exists + // 2. Shows Unitag, followed by address, if Unitag exists but not ENS - const ACCOUNT_WITH_NO_USERNAME = '0xF030EaA01aFf57A23483dC8A1c3550d153be69Fb' + const ACCOUNT_WITH_NO_USERNAME = '0xF030EaA01aFf57A23483dC8A1c3550d153be69Fb' - test('shows address if no Unitag or ENS exists', async ({ page }) => { - await page.goto(`/swap?eagerlyConnectAddress=${ACCOUNT_WITH_NO_USERNAME}`) - await openAccountDrawerAndVerify({ page, walletAddress: ACCOUNT_WITH_NO_USERNAME }) - }) + test('shows address if no Unitag or ENS exists', async ({ page }) => { + await page.goto(`/swap?eagerlyConnectAddress=${ACCOUNT_WITH_NO_USERNAME}`) + await openAccountDrawerAndVerify({ page, walletAddress: ACCOUNT_WITH_NO_USERNAME }) + }) - test('shows Unitag, followed by address when Unitag exists but not ENS', async ({ page }) => { - await mockUnitagResponse({ page, address: TEST_WALLET_ADDRESS, unitag: UNITAG_NAME }) - await page.goto('/swap') + test('shows Unitag, followed by address when Unitag exists but not ENS', async ({ page }) => { + await mockUnitagResponse({ page, address: TEST_WALLET_ADDRESS, unitag: UNITAG_NAME }) + await page.goto('/swap') - await openAccountDrawerAndVerify({ page, expectedPrimaryText: UNITAG_NAME, walletAddress: TEST_WALLET_ADDRESS }) - }) -}) + await openAccountDrawerAndVerify({ page, expectedPrimaryText: UNITAG_NAME, walletAddress: TEST_WALLET_ADDRESS }) + }) + }, +) diff --git a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx index 638bf5898ad..a17f7e3d04a 100644 --- a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx +++ b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx @@ -1,45 +1,41 @@ import { NetworkStatus } from '@apollo/client' import { CurrencyAmount, Token } from '@uniswap/sdk-core' +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { MultiBlockchainAddressDisplay } from 'components/AccountDetails/MultiBlockchainAddressDisplay' -import { ActionTile } from 'components/AccountDrawer/ActionTile' import { DisconnectButton } from 'components/AccountDrawer/DisconnectButton' import { DownloadGraduatedWalletCard } from 'components/AccountDrawer/DownloadGraduatedWalletCard' import { EmptyWallet } from 'components/AccountDrawer/MiniPortfolio/EmptyWallet' import { ExtensionDeeplinks } from 'components/AccountDrawer/MiniPortfolio/ExtensionDeeplinks' import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import MiniPortfolio from 'components/AccountDrawer/MiniPortfolio/MiniPortfolio' -import { SendButtonTooltip } from 'components/AccountDrawer/SendButtonTooltip' +import MiniPortfolioV2 from 'components/AccountDrawer/MiniPortfolio/MiniPortfolioV2' +import { ReceiveActionTile } from 'components/ActionTiles/ReceiveActionTile' +import { SendActionTile } from 'components/ActionTiles/SendActionTile/SendActionTile' import { LimitedSupportBanner } from 'components/Banner/LimitedSupportBanner' import DelegationMismatchModal from 'components/delegation/DelegationMismatchModal' import { Settings } from 'components/Icons/Settings' -import { ReceiveModalState } from 'components/ReceiveCryptoModal/types' -import { useOpenReceiveCryptoModal } from 'components/ReceiveCryptoModal/useOpenReceiveCryptoModal' import StatusIcon from 'components/StatusIcon' +import { ExtensionRequestMethods, useUniswapExtensionRequest } from 'components/WalletModal/useWagmiConnectorWithId' import { useAccountsStore } from 'features/accounts/store/hooks' import { useIsUniswapExtensionConnected } from 'hooks/useIsUniswapExtensionConnected' import { useModalState } from 'hooks/useModalState' -import { useTheme } from 'lib/styled-components' -import { useState } from 'react' -import { Trans, useTranslation } from 'react-i18next' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useUserHasAvailableClaim, useUserUnclaimedAmount } from 'state/claim/hooks' -import { Button, Flex, IconButton } from 'ui/src' -import { ArrowDownCircleFilled } from 'ui/src/components/icons/ArrowDownCircleFilled' -import { SendAction } from 'ui/src/components/icons/SendAction' +import { Button, Flex, IconButton, Image, useSporeColors } from 'ui/src' +import { UNISWAP_LOGO } from 'ui/src/assets' import { Shine } from 'ui/src/loading/Shine' +import { iconSizes } from 'ui/src/theme' import AnimatedNumber, { BALANCE_CHANGE_INDICATION_DURATION, } from 'uniswap/src/components/AnimatedNumber/AnimatedNumber' import { TestnetModeBanner } from 'uniswap/src/components/banners/TestnetModeBanner' import { RelativeChange } from 'uniswap/src/components/RelativeChange/RelativeChange' -import { useUniswapContext } from 'uniswap/src/contexts/UniswapContext' import { useConnectionStatus } from 'uniswap/src/features/accounts/store/hooks' import { useEnabledChains } from 'uniswap/src/features/chains/hooks/useEnabledChains' -import { UniverseChainId } from 'uniswap/src/features/chains/types' import { usePortfolioTotalValue } from 'uniswap/src/features/dataApi/balances/balancesRest' import { FiatCurrency } from 'uniswap/src/features/fiatCurrency/constants' import { useAppFiatCurrency, useAppFiatCurrencyInfo } from 'uniswap/src/features/fiatCurrency/hooks' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { useLocalizationContext } from 'uniswap/src/features/language/LocalizationContext' import { Platform } from 'uniswap/src/features/platforms/types/Platform' import { useHasAccountMismatchOnAnyChain } from 'uniswap/src/features/smartWallet/mismatch/hooks' @@ -47,7 +43,6 @@ import { ModalName } from 'uniswap/src/features/telemetry/constants' import i18next from 'uniswap/src/i18n' import { TestID } from 'uniswap/src/test/fixtures/testIDs' import { NumberType } from 'utilities/src/format/types' -import { useEvent } from 'utilities/src/react/hooks' export default function AuthenticatedHeader({ evmAddress, @@ -59,6 +54,7 @@ export default function AuthenticatedHeader({ openSettings: () => void }) { const { t } = useTranslation() + const isPortfolioPageEnabled = useFeatureFlag(FeatureFlags.PortfolioPage) const isSolanaConnected = useConnectionStatus(Platform.SVM).isConnected const multipleWalletsConnected = useAccountsStore((state) => { @@ -67,7 +63,10 @@ export default function AuthenticatedHeader({ return Boolean(evmWalletId && svmWalletId && evmWalletId !== svmWalletId) }) // if different wallets are connected, do not show mini wallet icon - const shouldShowExtensionDeeplinks = useIsUniswapExtensionConnected() && !isSolanaConnected + const isUniswapExtensionConnected = useIsUniswapExtensionConnected() + const uniswapExtensionRequest = useUniswapExtensionRequest() + const shouldShowExtensionDeeplinks = isUniswapExtensionConnected && !isSolanaConnected && !isPortfolioPageEnabled + const shouldShowExtensionButton = isPortfolioPageEnabled && isUniswapExtensionConnected && !isSolanaConnected const { isTestnetModeEnabled } = useEnabledChains() @@ -79,21 +78,6 @@ export default function AuthenticatedHeader({ const accountDrawer = useAccountDrawer() - const openReceiveCryptoModal = useOpenReceiveCryptoModal({ - modalState: ReceiveModalState.DEFAULT, - }) - - const { navigateToSendFlow } = useUniswapContext() - - const isSolanaOnlyWallet = Boolean(svmAddress && !evmAddress) - - const onPressSend = useEvent(() => { - if (!isSolanaOnlyWallet) { - navigateToSendFlow({ chainId: UniverseChainId.Mainnet }) - accountDrawer.close() - } - }) - const { data, networkStatus, loading } = usePortfolioTotalValue({ evmAddress, svmAddress, @@ -116,13 +100,18 @@ export default function AuthenticatedHeader({ const isPermitMismatchUxEnabled = useFeatureFlag(FeatureFlags.EnablePermitMismatchUX) const shouldShowDelegationMismatch = isPermitMismatchUxEnabled && isDelegationMismatch const [displayDelegationMismatchModal, setDisplayDelegationMismatchModal] = useState(false) - const theme = useTheme() + const colors = useSporeColors() const amount = unclaimedAmount?.toFixed(0, { groupSeparator: ',' }) ?? '-' const shouldFadePortfolioDecimals = (currency === FiatCurrency.UnitedStatesDollar || currency === FiatCurrency.Euro) && currencyComponents.symbolAtFront + const handleOpenExtensionSidebar = useCallback(() => { + uniswapExtensionRequest?.(ExtensionRequestMethods.OPEN_SIDEBAR, 'Tokens') + accountDrawer.close() + }, [uniswapExtensionRequest, accountDrawer]) + return ( <> @@ -134,11 +123,23 @@ export default function AuthenticatedHeader({ size={48} /> + {shouldShowExtensionButton && ( + } + borderRadius="$rounded32" + hoverStyle={{ + backgroundColor: '$surface2', + }} + onPress={handleOpenExtensionSidebar} + /> + )} } + icon={} borderRadius="$rounded32" hoverStyle={{ backgroundColor: '$surface2', @@ -190,24 +191,19 @@ export default function AuthenticatedHeader({ ) : ( <> - - } - name={t('common.send.button')} - onClick={onPressSend} - disabled={isSolanaOnlyWallet} - /> - - } - name={t('common.receive')} - onClick={openReceiveCryptoModal} - /> + + + + + + - + {isPortfolioPageEnabled ? ( + + ) : ( + + )} )} {isUnclaimed && ( @@ -217,7 +213,7 @@ export default function AuthenticatedHeader({ onPress={toggleClaimModal} style={{ background: 'linear-gradient(to right, #9139b0 0%, #4261d6 100%)' }} > - + {t('account.authHeader.claimReward', { amount })} )} diff --git a/apps/web/src/components/AccountDrawer/DisconnectButton.tsx b/apps/web/src/components/AccountDrawer/DisconnectButton.tsx index 4cd815d1f91..4b1ebc00bd3 100644 --- a/apps/web/src/components/AccountDrawer/DisconnectButton.tsx +++ b/apps/web/src/components/AccountDrawer/DisconnectButton.tsx @@ -1,21 +1,20 @@ +import { FeatureFlags, useFeatureFlag } from '@universe/gating' import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import { MenuStateVariant, useSetMenu } from 'components/AccountDrawer/menuState' import { Power } from 'components/Icons/Power' import { useAccountsStore, useActiveConnector, useActiveWallet } from 'features/accounts/store/hooks' -import { ExternalWallet } from 'features/accounts/store/types' +import { type ExternalWallet } from 'features/accounts/store/types' import { useDisconnect } from 'hooks/useDisconnect' import { useSignOutWithPasskey } from 'hooks/useSignOutWithPasskey' -import { useTheme } from 'lib/styled-components' -import { PropsWithChildren, useMemo } from 'react' +import { type PropsWithChildren, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch } from 'react-redux' -import { Button, Flex, IconButton, Image, Text, Tooltip } from 'ui/src' +import { Button, Flex, IconButton, Image, Text, Tooltip, useSporeColors } from 'ui/src' import { PlusCircle } from 'ui/src/components/icons/PlusCircle' import { SwitchArrows } from 'ui/src/components/icons/SwitchArrows' -import { AppTFunction } from 'ui/src/i18n/types' +import { type AppTFunction } from 'ui/src/i18n/types' +import { zIndexes } from 'ui/src/theme' import { CONNECTION_PROVIDER_IDS } from 'uniswap/src/constants/web3' -import { FeatureFlags } from 'uniswap/src/features/gating/flags' -import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { Platform } from 'uniswap/src/features/platforms/types/Platform' import { setIsTestnetModeEnabled } from 'uniswap/src/features/settings/slice' import { ElementName } from 'uniswap/src/features/telemetry/constants' @@ -79,14 +78,14 @@ export function DisconnectButton() { } function PowerIconButton({ onPress, pointer }: { onPress?: () => void; pointer: boolean }) { - const theme = useTheme() + const colors = useSporeColors() return ( } + icon={} borderRadius="$rounded32" hoverStyle={{ backgroundColor: '$surface2', @@ -101,14 +100,18 @@ function DisconnectMenuTooltip({ children }: PropsWithChildren) { return ( {children} - + ) } -function DisconnectMenuButtonRow({ children, onPress }: PropsWithChildren<{ onPress: () => void }>) { +function DisconnectMenuButtonRow({ + children, + onPress, + testId, +}: PropsWithChildren<{ onPress: () => void; testId?: string }>) { return ( @@ -265,12 +269,12 @@ function SwitchWalletButtonRow({ variant, platform }: { variant: SwitchButtonVar function InLineDisconnectButton() { const onDisconnect = useOnDisconnect() const { t } = useTranslation() - const theme = useTheme() + const colors = useSporeColors() return ( - - + + {t('common.button.disconnect')} diff --git a/apps/web/src/components/AccountDrawer/LocalCurrencyMenu.tsx b/apps/web/src/components/AccountDrawer/LocalCurrencyMenu.tsx index df10d6598c6..2822cafbbba 100644 --- a/apps/web/src/components/AccountDrawer/LocalCurrencyMenu.tsx +++ b/apps/web/src/components/AccountDrawer/LocalCurrencyMenu.tsx @@ -2,13 +2,13 @@ import { SlideOutMenu } from 'components/AccountDrawer/SlideOutMenu' import { MenuColumn, MenuItem } from 'components/AccountDrawer/shared' import { getLocalCurrencyIcon } from 'constants/localCurrencies' import { useLocalCurrencyLinkProps } from 'hooks/useLocalCurrencyLinkProps' -import styled from 'lib/styled-components' +import { deprecatedStyled } from 'lib/styled-components' import { useMemo } from 'react' import { Trans } from 'react-i18next' import { FiatCurrency, ORDERED_CURRENCIES } from 'uniswap/src/features/fiatCurrency/constants' import { useAppFiatCurrency } from 'uniswap/src/features/fiatCurrency/hooks' -const StyledLocalCurrencyIcon = styled.div` +const StyledLocalCurrencyIcon = deprecatedStyled.div` width: 20px; height: 20px; border-radius: 100%; diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.anvil.e2e.test.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.anvil.e2e.test.ts deleted file mode 100644 index 413b3f15890..00000000000 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.anvil.e2e.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { createExpectSingleTransaction } from 'playwright/anvil/transactions' -import { expect, getTest } from 'playwright/fixtures' -import { stubTradingApiEndpoint } from 'playwright/fixtures/tradingApi' -import { TEST_WALLET_ADDRESS } from 'playwright/fixtures/wallets' -import { Mocks } from 'playwright/mocks/mocks' -import { USDC_MAINNET } from 'uniswap/src/constants/tokens' -import { uniswapUrls } from 'uniswap/src/constants/urls' -import { ElementName } from 'uniswap/src/features/telemetry/constants' -import { TestID } from 'uniswap/src/test/fixtures/testIDs' - -const test = getTest({ withAnvil: true }) - -test.describe('ActivityTab activity history', () => { - test('should deduplicate activity history by nonce', async ({ page, graphql, anvil }) => { - const expectSingleTransaction = createExpectSingleTransaction({ - anvil, - address: TEST_WALLET_ADDRESS, - options: { blocks: 2 }, - }) - - await stubTradingApiEndpoint({ page, endpoint: uniswapUrls.tradingApiPaths.swap }) - await graphql.intercept('ActivityWeb', Mocks.Account.activity_history) - await page.goto(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`) - - // Perform swap and verify transaction was submitted - await expectSingleTransaction(async () => { - await page.getByTestId(TestID.AmountInputIn).click() - await page.getByTestId(TestID.AmountInputIn).fill('1') - await expect(page.getByTestId(TestID.AmountInputIn)).toHaveValue('1') - await page.getByTestId(TestID.ReviewSwap).click() - await page.getByTestId(TestID.Swap).click() - await page.getByTestId(TestID.ActivityPopupCloseIcon).click() - }) - - // Open account drawer and navigate to activity tab - await page.getByTestId(TestID.Web3StatusConnected).click() - await page.getByTestId(ElementName.MiniPortfolioActivityTab).click() - - // Wait for activity content to be visible - await expect(page.getByTestId(TestID.ActivityContent)).toBeVisible() - - // Assert that the local pending transaction is replaced by remote transaction with the same nonce - // The "Swapping" text should not exist as it indicates a pending local transaction - await expect(page.getByText('Swapping')).not.toBeVisible() - - // Verify that we have activity content displayed (from the mocked GraphQL response) - await expect(page.getByTestId(TestID.ActivityContent)).toBeVisible() - }) -}) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.tsx index b0bcf916c64..fe6f8f27853 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityTab.tsx @@ -1,10 +1,11 @@ import { OpenLimitOrdersButton } from 'components/AccountDrawer/MiniPortfolio/Limits/OpenLimitOrdersButton' import { MenuStateVariant, useSetMenu } from 'components/AccountDrawer/menuState' import { useMemo } from 'react' -import { Flex, ScrollView } from 'ui/src' +import { Flex, Loader, ScrollView } from 'ui/src' import { ActivityItem } from 'uniswap/src/components/activity/generateActivityItemRenderer' import { useActivityData } from 'uniswap/src/features/activity/hooks/useActivityData' import { TestID } from 'uniswap/src/test/fixtures/testIDs' +import { useInfiniteScroll } from 'utilities/src/react/useInfiniteScroll' export default function ActivityTab({ evmOwner, @@ -15,17 +16,19 @@ export default function ActivityTab({ }) { const setMenu = useSetMenu() - const { maybeEmptyComponent, renderActivityItem, sectionData } = useActivityData({ - evmOwner, - svmOwner, - ownerAddresses: [evmOwner, svmOwner].filter(Boolean) as string[], - swapCallbacks: { - useLatestSwapTransaction: () => undefined, - useSwapFormTransactionState: () => undefined, - onRetryGenerator: () => () => {}, - }, - fiatOnRampParams: undefined, - skip: false, + const { maybeEmptyComponent, renderActivityItem, sectionData, fetchNextPage, hasNextPage, isFetchingNextPage } = + useActivityData({ + evmOwner, + svmOwner, + ownerAddresses: [evmOwner, svmOwner].filter(Boolean) as string[], + fiatOnRampParams: undefined, + skip: false, + }) + + const { sentinelRef } = useInfiniteScroll({ + onLoadMore: fetchNextPage, + hasNextPage, + isFetching: isFetchingNextPage, }) const ActivityItems = useMemo(() => { @@ -52,6 +55,14 @@ export default function ActivityTab({ )} {ActivityItems} + {/* Show skeleton loading indicator while fetching next page */} + {isFetchingNextPage && ( + + + + )} + {/* Intersection observer sentinel for infinite scroll */} + ) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx index 0defc585b6d..9a5e7402481 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx @@ -6,12 +6,13 @@ import { ColumnCenter } from 'components/deprecated/Column' import Row from 'components/deprecated/Row' import { LoaderV3 } from 'components/Icons/LoadingSpinner' import { DetailLineItem } from 'components/swap/DetailLineItem' -import styled, { useTheme } from 'lib/styled-components' +import { deprecatedStyled } from 'lib/styled-components' +import { useMemo } from 'react' import { Slash } from 'react-feather' import { Trans, useTranslation } from 'react-i18next' import { ThemedText } from 'theme/components' import { ExternalLink } from 'theme/components/Links' -import { Flex, Text } from 'ui/src' +import { Flex, Text, useSporeColors } from 'ui/src' import { Dialog } from 'uniswap/src/components/dialog/Dialog' import { GetHelpHeader } from 'uniswap/src/components/dialog/GetHelpHeader' import { Modal } from 'uniswap/src/components/modals/Modal' @@ -24,11 +25,11 @@ import { UniswapXOrderDetails } from 'uniswap/src/features/transactions/types/tr import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking' import { NumberType } from 'utilities/src/format/types' -const ModalHeader = styled(GetHelpHeader)` +const ModalHeader = deprecatedStyled(GetHelpHeader)` padding: 4px 0px; ` -const Container = styled(ColumnCenter)` +const Container = deprecatedStyled(ColumnCenter)` background-color: ${({ theme }) => theme.surface1}; border-radius: 16px; padding: 16px 24px 24px 24px; @@ -56,7 +57,7 @@ function useCancelOrdersDialogContent( state: CancellationState, orders: UniswapXOrderDetails[], ): { title?: JSX.Element; icon: JSX.Element } { - const theme = useTheme() + const colors = useSporeColors() switch (state) { case CancellationState.REVIEWING_CANCELLATION: return { @@ -66,12 +67,12 @@ function useCancelOrdersDialogContent( ) : ( ), - icon: , + icon: , } case CancellationState.PENDING_SIGNATURE: return { title: , - icon: , + icon: , } case CancellationState.PENDING_CONFIRMATION: return { @@ -98,6 +99,22 @@ export function CancelOrdersDialog(props: CancelOrdersDialogProps) { const { title, icon } = useCancelOrdersDialogContent(cancelState, orders) const cancellationGasFeeInfo = useCancelOrdersGasEstimate(orders) + + const primaryButton = useMemo( + () => ({ + text: t('common.neverMind'), + onPress: onCancel, + variant: 'default' as const, + emphasis: 'secondary' as const, + }), + [t, onCancel], + ) + + const secondaryButton = useMemo( + () => ({ text: t('common.proceed'), onPress: onConfirm, variant: 'critical' as const }), + [t, onConfirm], + ) + if ( [CancellationState.PENDING_SIGNATURE, CancellationState.PENDING_CONFIRMATION, CancellationState.CANCELLED].includes( cancelState, @@ -155,18 +172,10 @@ export function CancelOrdersDialog(props: CancelOrdersDialogProps) { } modalName={ModalName.CancelOrders} - primaryButtonText={t('common.neverMind')} - primaryButtonOnClick={onCancel} - primaryButtonVariant="default" - primaryButtonEmphasis="secondary" - secondaryButtonText={t('common.proceed')} - secondaryButtonOnClick={onConfirm} - secondaryButtonVariant="critical" - buttonContainerProps={{ - flexDirection: 'row', - }} + primaryButton={primaryButton} + secondaryButton={secondaryButton} displayHelpCTA - hasIconBackground + iconBackgroundColor="$surface3" > {/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition */} @@ -185,15 +194,7 @@ function GasEstimateDisplay({ gasEstimateValue, chainId }: { gasEstimateValue?: const gasFeeFormatted = convertFiatAmountFormatted(gasFeeUSD?.toExact(), NumberType.PortfolioBalance) return ( - + , diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/Logos.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/Logos.tsx index 9c5a6081336..1fd9c4c0738 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/Logos.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/Logos.tsx @@ -1,8 +1,9 @@ import { LoaderV3 } from 'components/Icons/LoadingSpinner' -import styled, { css, useTheme } from 'lib/styled-components' +import { css, deprecatedStyled } from 'lib/styled-components' import { FadePresence, FadePresenceAnimationType } from 'theme/components/FadePresence' +import { useSporeColors } from 'ui/src' -export const LogoContainer = styled.div` +export const LogoContainer = deprecatedStyled.div` height: 64px; width: 64px; position: relative; @@ -12,7 +13,7 @@ export const LogoContainer = styled.div` overflow: visible; ` -const LoadingIndicator = styled(LoaderV3)` +const LoadingIndicator = deprecatedStyled(LoaderV3)` stroke: ${({ theme }) => theme.neutral3}; fill: ${({ theme }) => theme.neutral3}; width: calc(100% + 8px); @@ -31,7 +32,7 @@ export function LoadingIndicatorOverlay() { } export function ConfirmedIcon({ className }: { className?: string }) { - const theme = useTheme() + const colors = useSporeColors() return ( @@ -53,7 +54,7 @@ export function ConfirmedIcon({ className }: { className?: string }) { } export function SubmittedIcon({ className }: { className?: string }) { - const theme = useTheme() + const colors = useSporeColors() return ( @@ -79,10 +80,10 @@ const IconCss = css` width: 64px; ` -export const AnimatedEntranceConfirmationIcon = styled(ConfirmedIcon)` +export const AnimatedEntranceConfirmationIcon = deprecatedStyled(ConfirmedIcon)` ${IconCss} ` -export const AnimatedEntranceSubmittedIcon = styled(SubmittedIcon)` +export const AnimatedEntranceSubmittedIcon = deprecatedStyled(SubmittedIcon)` ${IconCss} ` diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx index 01186b48495..28b1df8a0b8 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx @@ -22,14 +22,14 @@ import { useUSDPrice } from 'hooks/useUSDPrice' import { TFunction } from 'i18next' import { atom } from 'jotai' import { useAtomValue, useUpdateAtom } from 'jotai/utils' -import styled, { useTheme } from 'lib/styled-components' +import { deprecatedStyled } from 'lib/styled-components' import { useCallback, useMemo, useState } from 'react' import { ArrowDown } from 'react-feather' import { useTranslation } from 'react-i18next' import { useUniswapXOrderByOrderHash } from 'state/transactions/hooks' import { ThemedText } from 'theme/components' import { Divider } from 'theme/components/Dividers' -import { Button, Flex, TouchableArea } from 'ui/src' +import { Button, Flex, TouchableArea, useSporeColors } from 'ui/src' import { X } from 'ui/src/components/icons/X' import { Modal } from 'uniswap/src/components/modals/Modal' import { InterfaceEventName, ModalName } from 'uniswap/src/features/telemetry/constants' @@ -63,17 +63,17 @@ export function useOpenOffchainActivityModal() { ) } -const Wrapper = styled(AutoColumn).attrs({ gap: 'md', grow: true })` +const Wrapper = deprecatedStyled(AutoColumn).attrs({ gap: 'md', grow: true })` padding: 12px 20px 20px 20px; width: 100%; background-color: ${({ theme }) => theme.surface1}; ` -const OffchainModalDivider = styled(Divider)` +const OffchainModalDivider = deprecatedStyled(Divider)` margin: 28px 0; ` -const InsufficientFundsCopyContainer = styled(Row)` +const InsufficientFundsCopyContainer = deprecatedStyled(Row)` margin-top: 16px; padding: 12px; border: 1.3px solid ${({ theme }) => theme.surface3}; @@ -83,7 +83,7 @@ const InsufficientFundsCopyContainer = styled(Row)` align-items: flex-start; ` -const AlertIconContainer = styled.div` +const AlertIconContainer = deprecatedStyled.div` display: flex; flex-shrink: 0; background-color: ${({ theme }) => theme.deprecated_accentWarning}; @@ -173,7 +173,7 @@ export function OrderContent({ order, onCancel }: { order: UniswapXOrderDetails; const amountsDefined = !!amounts?.inputAmount.currency && !!amounts.outputAmount.currency const fiatValueInput = useUSDPrice(amounts?.inputAmount) const fiatValueOutput = useUSDPrice(amounts?.outputAmount) - const theme = useTheme() + const colors = useSporeColors() const explorerLink = order.hash ? getExplorerLink({ chainId: order.chainId, data: order.hash, type: ExplorerDataType.TRANSACTION }) @@ -241,7 +241,7 @@ export function OrderContent({ order, onCancel }: { order: UniswapXOrderDetails; isLoading={false} headerTextProps={{ fontSize: '24px', lineHeight: '32px' }} /> - + should render limit order text 1`] = ` .c0 { color: #131313; - -webkit-letter-spacing: -0.01em; - -moz-letter-spacing: -0.01em; - -ms-letter-spacing: -0.01em; letter-spacing: -0.01em; } .c1 { cursor: auto; - color: rgba(19,19,19,0.63); + color: rgba(19, 19, 19, 0.63); } .c2 { @@ -19,14 +16,10 @@ exports[`CancelOrdersDialog > should render limit order text 1`] = ` overflow-wrap: break-word; } - + -