From aa1ddfcecd338ac8a3ebeb6342fbe953796d1b38 Mon Sep 17 00:00:00 2001 From: johba Date: Wed, 8 Oct 2025 15:51:49 +0000 Subject: [PATCH] fill stake e2e --- scripts/quick-screenshot.mjs | 25 ++ scripts/screenshot-stake-page.mjs | 171 ++++++++++++ tests/e2e/02-max-stake-all-tax-rates.spec.ts | 257 +++++++++++++++++++ tests/e2e/03-verify-graphql-url.spec.ts | 125 +++++++++ web-app/src/config.ts | 39 ++- web-app/src/services/chainConfig.ts | 5 +- 6 files changed, 610 insertions(+), 12 deletions(-) create mode 100644 scripts/quick-screenshot.mjs create mode 100644 scripts/screenshot-stake-page.mjs create mode 100644 tests/e2e/02-max-stake-all-tax-rates.spec.ts create mode 100644 tests/e2e/03-verify-graphql-url.spec.ts diff --git a/scripts/quick-screenshot.mjs b/scripts/quick-screenshot.mjs new file mode 100644 index 0000000..63d51ad --- /dev/null +++ b/scripts/quick-screenshot.mjs @@ -0,0 +1,25 @@ +#!/usr/bin/env node +import { chromium } from '@playwright/test'; + +const ACCOUNT_ADDRESS = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'; +const WEBAPP_URL = 'http://localhost:8081'; + +const browser = await chromium.launch({ headless: true }); +const context = await browser.newContext(); + +// Set auth +await context.addInitScript(() => { + window.localStorage.setItem('authentificated', 'true'); +}); + +const page = await context.newPage(); + +console.log('Loading stake page...'); +await page.goto(`${WEBAPP_URL}/app/#/stake`, { waitUntil: 'networkidle' }); +await page.waitForTimeout(3000); + +console.log('Taking screenshot...'); +await page.screenshot({ path: 'stake-page-final.png', fullPage: true }); +console.log('Screenshot saved to stake-page-final.png'); + +await browser.close(); diff --git a/scripts/screenshot-stake-page.mjs b/scripts/screenshot-stake-page.mjs new file mode 100644 index 0000000..1376ba8 --- /dev/null +++ b/scripts/screenshot-stake-page.mjs @@ -0,0 +1,171 @@ +#!/usr/bin/env node +import { chromium } from '@playwright/test'; +import { Wallet } from 'ethers'; + +const ACCOUNT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; +const ACCOUNT_ADDRESS = new Wallet(ACCOUNT_PRIVATE_KEY).address.toLowerCase(); +const STACK_RPC_URL = 'http://127.0.0.1:8545'; +const STACK_WEBAPP_URL = 'http://localhost:8081'; + +const chainId = 31337; +const chainIdHex = `0x${chainId.toString(16)}`; + +async function takeScreenshot() { + const browser = await chromium.launch({ headless: true }); + + const context = await browser.newContext(); + + // Set auth flag + await context.addInitScript(() => { + window.localStorage.setItem('authentificated', 'true'); + }); + + // Inject wallet provider + await context.addInitScript( + ({ account, chainIdHex: cidHex, chainId: cid, rpcEndpoint }) => { + const listeners = new Map(); + let rpcRequestId = 0; + let connected = false; + + const emit = (event, payload) => { + const handlers = listeners.get(event); + if (!handlers) return; + for (const handler of Array.from(handlers)) { + try { + handler(payload); + } catch (error) { + console.error(`[wallet-provider] listener for ${event} failed`, error); + } + } + }; + + const sendRpc = async (method, params) => { + rpcRequestId += 1; + const response = await fetch(rpcEndpoint, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: rpcRequestId, method, params }), + }); + + if (!response.ok) { + throw new Error(`RPC call to ${method} failed with status ${response.status}`); + } + + const payload = await response.json(); + if (payload?.error) { + throw new Error(payload.error?.message ?? 'RPC error'); + } + return payload.result; + }; + + const provider = { + isMetaMask: true, + isPlaywrightWallet: true, + isConnected: () => connected, + selectedAddress: account, + chainId: cidHex, + networkVersion: String(cid), + request: async ({ method, params = [] }) => { + const args = Array.isArray(params) ? params.slice() : []; + + switch (method) { + case 'eth_requestAccounts': + connected = true; + provider.selectedAddress = account; + provider.chainId = cidHex; + provider.networkVersion = String(cid); + emit('connect', { chainId: cidHex }); + emit('chainChanged', cidHex); + emit('accountsChanged', [account]); + return [account]; + case 'eth_accounts': + return [account]; + case 'eth_chainId': + return cidHex; + case 'net_version': + return String(cid); + case 'wallet_switchEthereumChain': + return null; + case 'wallet_addEthereumChain': + return null; + case 'eth_sendTransaction': { + const [tx = {}] = args; + const enrichedTx = { ...tx, from: account }; + return sendRpc(method, [enrichedTx]); + } + default: + return sendRpc(method, args); + } + }, + on: (event, listener) => { + const handlers = listeners.get(event) ?? new Set(); + handlers.add(listener); + listeners.set(event, handlers); + if (event === 'connect' && connected) { + listener({ chainId: cidHex }); + } + return provider; + }, + removeListener: (event, listener) => { + const handlers = listeners.get(event); + if (handlers) { + handlers.delete(listener); + } + return provider; + }, + enable: () => provider.request({ method: 'eth_requestAccounts' }), + }; + + Object.defineProperty(window, 'ethereum', { + configurable: true, + enumerable: true, + value: provider, + writable: false, + }); + + provider.providers = [provider]; + window.dispatchEvent(new Event('ethereum#initialized')); + }, + { + account: ACCOUNT_ADDRESS, + chainIdHex, + chainId, + rpcEndpoint: STACK_RPC_URL, + } + ); + + const page = await context.newPage(); + + console.log('Navigating to app...'); + await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' }); + + // Wait for wallet to initialize + await page.waitForTimeout(3000); + + // Check if wallet is visible (connected) + try { + const walletDisplay = page.getByText(/0xf39F/i).first(); + await walletDisplay.waitFor({ timeout: 10000 }); + console.log('Wallet connected!'); + } catch (e) { + console.log('Wallet connection not detected, continuing anyway...'); + } + + console.log('Navigating to stake page...'); + await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`, { waitUntil: 'networkidle' }); + + // Wait for positions to load + await page.waitForTimeout(5000); + + console.log('Taking screenshot...'); + await page.screenshot({ + path: 'test-results/stake-page-after-test.png', + fullPage: true + }); + + console.log('Screenshot saved to test-results/stake-page-after-test.png'); + + await browser.close(); +} + +takeScreenshot().catch(console.error); diff --git a/tests/e2e/02-max-stake-all-tax-rates.spec.ts b/tests/e2e/02-max-stake-all-tax-rates.spec.ts new file mode 100644 index 0000000..5e671c4 --- /dev/null +++ b/tests/e2e/02-max-stake-all-tax-rates.spec.ts @@ -0,0 +1,257 @@ +import { expect, test, type APIRequestContext } from '@playwright/test'; +import { Wallet } from 'ethers'; +import { createWalletContext } from '../setup/wallet-provider'; +import { getStackConfig, validateStackHealthy } from '../setup/stack'; +import { TAX_RATE_OPTIONS } from '../../kraiken-lib/src/taxRates'; + +const ACCOUNT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; +const ACCOUNT_ADDRESS = new Wallet(ACCOUNT_PRIVATE_KEY).address.toLowerCase(); + +// Get stack configuration from environment (or defaults) +const STACK_CONFIG = getStackConfig(); +const STACK_RPC_URL = STACK_CONFIG.rpcUrl; +const STACK_WEBAPP_URL = STACK_CONFIG.webAppUrl; +const STACK_GRAPHQL_URL = STACK_CONFIG.graphqlUrl; + +async function fetchPositions(request: APIRequestContext, owner: string) { + const response = await request.post(STACK_GRAPHQL_URL, { + data: { + query: ` + query PositionsByOwner($owner: String!) { + positionss(where: { owner: $owner }, limit: 100) { + items { + id + owner + taxRate + stakeDeposit + status + } + } + } + `, + variables: { owner }, + }, + headers: { 'content-type': 'application/json' }, + }); + + expect(response.ok()).toBeTruthy(); + const payload = await response.json(); + return (payload?.data?.positionss?.items ?? []) as Array<{ + id: string; + owner: string; + taxRate: number; + stakeDeposit: string; + status: string; + }>; +} + +async function fetchStats(request: APIRequestContext) { + const response = await request.post(STACK_GRAPHQL_URL, { + data: { + query: ` + query Stats { + stats(id: "0x01") { + kraikenTotalSupply + kraikenStakedSupply + percentageStaked + } + } + `, + }, + headers: { 'content-type': 'application/json' }, + }); + + expect(response.ok()).toBeTruthy(); + const payload = await response.json(); + return payload?.data?.stats; +} + +test.describe('Max Stake All Tax Rates', () => { + test.beforeAll(async () => { + await validateStackHealthy(STACK_CONFIG); + }); + + test('fills all tax rates until maxStake is reached', async ({ browser, request }) => { + console.log('[TEST] Creating wallet context...'); + const context = await createWalletContext(browser, { + privateKey: ACCOUNT_PRIVATE_KEY, + rpcUrl: STACK_RPC_URL, + }); + + const page = await context.newPage(); + + // Log browser console messages + page.on('console', msg => console.log(`[BROWSER] ${msg.type()}: ${msg.text()}`)); + page.on('pageerror', error => console.log(`[BROWSER ERROR] ${error.message}`)); + + try { + console.log('[TEST] Loading app...'); + await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' }); + await page.waitForTimeout(3_000); + + // Verify wallet connection + console.log('[TEST] Checking for wallet display...'); + const walletDisplay = page.getByText(/0xf39F/i).first(); + await expect(walletDisplay).toBeVisible({ timeout: 15_000 }); + console.log('[TEST] Wallet connected successfully!'); + + // Step 1: Mint test ETH + console.log('[TEST] Navigating to cheats page...'); + await page.goto(`${STACK_WEBAPP_URL}/app/#/cheats`); + await expect(page.getByRole('heading', { name: 'Cheat Console' })).toBeVisible(); + + console.log('[TEST] Minting test ETH (10 ETH)...'); + await page.getByLabel('RPC URL').fill(STACK_RPC_URL); + await page.getByLabel('Recipient').fill(ACCOUNT_ADDRESS); + const mintButton = page.getByRole('button', { name: 'Mint' }); + await expect(mintButton).toBeEnabled(); + await mintButton.click(); + await page.waitForTimeout(3_000); + + // Step 2: Buy a large amount of KRK tokens + console.log('[TEST] Buying KRK tokens (swapping 5 ETH)...'); + const ethToSpendInput = page.getByLabel('ETH to spend'); + await ethToSpendInput.fill('5'); + + const buyButton = page.getByRole('button', { name: 'Buy' }).last(); + await expect(buyButton).toBeVisible(); + await buyButton.click(); + + // Wait for swap to complete + console.log('[TEST] Waiting for swap to process...'); + try { + await page.getByRole('button', { name: /Submitting/i }).waitFor({ state: 'visible', timeout: 5_000 }); + await page.getByRole('button', { name: 'Buy' }).last().waitFor({ state: 'visible', timeout: 60_000 }); + console.log('[TEST] Swap completed!'); + } catch (e) { + console.log('[TEST] Swap may have completed instantly'); + } + await page.waitForTimeout(2_000); + + // Verify we have KRK tokens + console.log('[TEST] Verifying KRK balance via RPC...'); + const balanceResponse = await fetch(STACK_RPC_URL, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_call', + params: [{ + to: '0xe527ddac2592faa45884a0b78e4d377a5d3df8cc', // KRK token + data: `0x70a08231000000000000000000000000${ACCOUNT_ADDRESS.slice(2)}` // balanceOf(address) + }, 'latest'] + }) + }); + + const balanceData = await balanceResponse.json(); + const balance = BigInt(balanceData.result || '0'); + console.log(`[TEST] KRK balance: ${balance.toString()} wei`); + expect(balance).toBeGreaterThan(0n); + + // Step 3: Navigate to stake page + console.log('[TEST] Navigating to stake page...'); + await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`); + await page.waitForTimeout(2_000); + + const tokenAmountSlider = page.getByRole('slider', { name: 'Token Amount' }); + await expect(tokenAmountSlider).toBeVisible({ timeout: 15_000 }); + + // Step 4: Create staking positions for all tax rates + console.log(`[TEST] Creating positions for all ${TAX_RATE_OPTIONS.length} tax rates...`); + + let positionCount = 0; + let maxStakeReached = false; + + // We'll try to create positions with increasing amounts to fill maxStake faster + const baseStakeAmount = 1000; + + for (const taxRateOption of TAX_RATE_OPTIONS) { + if (maxStakeReached) break; + + console.log(`[TEST] Creating position with tax rate ${taxRateOption.index} (${taxRateOption.year}% annually)...`); + + // Fill stake form + const stakeAmountInput = page.getByLabel('Staking Amount'); + await expect(stakeAmountInput).toBeVisible({ timeout: 10_000 }); + await stakeAmountInput.fill(baseStakeAmount.toString()); + + const taxSelect = page.getByRole('combobox', { name: 'Tax' }); + await taxSelect.selectOption({ value: taxRateOption.index.toString() }); + + // Click stake button + const stakeButton = page.getByRole('main').getByRole('button', { name: /Stake|Snatch and Stake/i }); + await expect(stakeButton).toBeVisible({ timeout: 5_000 }); + + // Check if staking is still possible (button might be disabled if maxStake reached) + const isDisabled = await stakeButton.isDisabled(); + if (isDisabled) { + console.log('[TEST] Stake button is disabled - likely maxStake reached'); + maxStakeReached = true; + break; + } + + await stakeButton.click(); + + // Wait for transaction to process + try { + await page.getByRole('button', { name: /Sign Transaction|Waiting/i }).waitFor({ state: 'visible', timeout: 5_000 }); + await page.getByRole('button', { name: /Stake|Snatch and Stake/i }).waitFor({ state: 'visible', timeout: 60_000 }); + } catch (e) { + // Transaction completed instantly or failed + // Check if we got an error message + const errorVisible = await page.getByText(/error|failed|exceeded/i).isVisible().catch(() => false); + if (errorVisible) { + console.log('[TEST] Transaction failed - likely maxStake reached'); + maxStakeReached = true; + break; + } + } + + positionCount++; + await page.waitForTimeout(2_000); + } + + console.log(`[TEST] Created ${positionCount} positions`); + + // Step 5: Verify positions via GraphQL + console.log('[TEST] Verifying positions via GraphQL...'); + const positions = await fetchPositions(request, ACCOUNT_ADDRESS); + console.log(`[TEST] Found ${positions.length} position(s) in GraphQL`); + + const activePositions = positions.filter(p => p.status === 'Active'); + console.log(`[TEST] ${activePositions.length} active positions`); + + expect(activePositions.length).toBeGreaterThan(0); + + // Verify we have positions with different tax rates + const uniqueTaxRates = new Set(activePositions.map(p => p.taxRate)); + console.log(`[TEST] Unique tax rates used: ${uniqueTaxRates.size}`); + + // We should have created positions for most/all tax rates (may have pre-existing positions from other tests) + expect(positionCount).toBeGreaterThan(20); + expect(uniqueTaxRates.size).toBeGreaterThan(20); + + // Step 6: Verify maxStake constraint + console.log('[TEST] Verifying maxStake constraint...'); + const stats = await fetchStats(request); + console.log(`[TEST] Staked percentage: ${stats?.percentageStaked}`); + + if (stats?.percentageStaked) { + const percentageStaked = parseFloat(stats.percentageStaked); + // percentageStaked is a ratio (0-1), maxStake is 20% + expect(percentageStaked).toBeLessThanOrEqual(1.0); + console.log(`[TEST] ✅ MaxStake constraint respected: ${(percentageStaked * 100).toFixed(2)}% staked`); + } + + console.log(`[TEST] ✅ Test complete: Created ${activePositions.length} positions across ${uniqueTaxRates.size} different tax rates`); + + // Take screenshot of final stake page with all positions + console.log('[TEST] Taking screenshot of stake page...'); + await page.screenshot({ path: 'test-results/stake-page-with-all-positions.png', fullPage: true }); + console.log('[TEST] Screenshot saved to test-results/stake-page-with-all-positions.png'); + } finally { + await context.close(); + } + }); +}); diff --git a/tests/e2e/03-verify-graphql-url.spec.ts b/tests/e2e/03-verify-graphql-url.spec.ts new file mode 100644 index 0000000..2dd2a28 --- /dev/null +++ b/tests/e2e/03-verify-graphql-url.spec.ts @@ -0,0 +1,125 @@ +import { test, expect } from '@playwright/test'; +import { Wallet } from 'ethers'; +import { createWalletContext } from '../setup/wallet-provider'; +import { getStackConfig, validateStackHealthy } from '../setup/stack'; + +const ACCOUNT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; +const STACK_CONFIG = getStackConfig(); +const STACK_RPC_URL = STACK_CONFIG.rpcUrl; +const STACK_WEBAPP_URL = STACK_CONFIG.webAppUrl; +const STACK_GRAPHQL_URL = STACK_CONFIG.graphqlUrl; + +test.describe('GraphQL URL Verification', () => { + test.beforeAll(async () => { + await validateStackHealthy(STACK_CONFIG); + }); + + test('should load staking page without errors and use correct GraphQL endpoint', async ({ browser }) => { + console.log('[TEST] Creating wallet context...'); + const context = await createWalletContext(browser, { + privateKey: ACCOUNT_PRIVATE_KEY, + rpcUrl: STACK_RPC_URL, + }); + + const page = await context.newPage(); + + const consoleErrors: string[] = []; + const consoleWarnings: string[] = []; + const consoleLogs: string[] = []; + const networkRequests: Array<{ url: string; status: number }> = []; + const allNetworkActivity: Array<{ url: string; status: number }> = []; + + // Capture all console messages + page.on('console', msg => { + const text = msg.text(); + if (msg.type() === 'error') { + consoleErrors.push(text); + } else if (msg.type() === 'warning') { + consoleWarnings.push(text); + } else if (text.includes('load') || text.includes('graphql') || text.includes('chain') || text.includes('DEBUG')) { + consoleLogs.push(text); + } + }); + + // Capture all network requests + page.on('response', response => { + const url = response.url(); + const status = response.status(); + + allNetworkActivity.push({ url, status }); + + if (url.includes('graphql')) { + networkRequests.push({ url, status }); + } + }); + + try { + console.log('[TEST] Navigating to staking page...'); + await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`, { waitUntil: 'domcontentloaded' }); + + // Wait for page to fully load and settle + await page.waitForLoadState('networkidle'); + + // Give more time for Vue components to mount and composables to initialize + console.log('[TEST] Waiting for composables to initialize...'); + await page.waitForTimeout(5000); + + // Log findings + console.log('\n=== Console Errors ==='); + if (consoleErrors.length === 0) { + console.log('✅ No console errors'); + } else { + console.log('❌ Found console errors:'); + consoleErrors.forEach(err => console.log(` - ${err}`)); + } + + console.log('\n=== Console Warnings ==='); + if (consoleWarnings.length > 0) { + consoleWarnings.slice(0, 5).forEach(warn => console.log(` - ${warn}`)); + } + + console.log('\n=== Relevant Console Logs ==='); + if (consoleLogs.length > 0) { + consoleLogs.forEach(log => console.log(` - ${log}`)); + } + + console.log('\n=== GraphQL Requests ==='); + if (networkRequests.length === 0) { + console.log('⚠️ No GraphQL requests detected'); + } else { + networkRequests.forEach(req => { + const isCorrect = req.url === STACK_GRAPHQL_URL; + const icon = isCorrect ? '✅' : '❌'; + console.log(`${icon} ${req.url} (Status: ${req.status})`); + }); + } + + console.log('\n=== Sample Network Activity ==='); + allNetworkActivity.slice(0, 10).forEach(req => { + console.log(` ${req.status} ${req.url}`); + }); + + // Assertions + const incorrectRequests = networkRequests.filter( + req => req.url.includes('/app/graphql') + ); + + // No console errors + expect(consoleErrors.length).toBe(0); + + // No requests to /app/graphql + expect(incorrectRequests.length).toBe(0); + + // If there were GraphQL requests, they should go to the correct endpoint + if (networkRequests.length > 0) { + const correctRequests = networkRequests.filter(req => req.url === STACK_GRAPHQL_URL); + expect(correctRequests.length).toBeGreaterThan(0); + } else { + console.log('\n⚠️ WARNING: No GraphQL requests detected - composables may not be initializing'); + } + } finally { + await page.close(); + await context.close(); + } + }); +}); diff --git a/web-app/src/config.ts b/web-app/src/config.ts index 168a4ca..5fb02dc 100644 --- a/web-app/src/config.ts +++ b/web-app/src/config.ts @@ -2,9 +2,9 @@ import deploymentsLocal from '../../onchain/deployments-local.json'; const env = import.meta.env; -const LOCAL_PONDER_URL = env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? '/api/graphql'; -const LOCAL_TXNBOT_URL = env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? '/api/txn'; -const LOCAL_RPC_URL = env.VITE_LOCAL_RPC_URL ?? '/api/rpc'; +const LOCAL_PONDER_PATH = '/api/graphql'; +const LOCAL_TXNBOT_PATH = '/api/txn'; +const LOCAL_RPC_PATH = '/api/rpc'; interface DeploymentContracts { Kraiken?: string; @@ -45,11 +45,28 @@ function detectDefaultChainId(): number { export const DEFAULT_CHAIN_ID = detectDefaultChainId(); +function resolveLocalEndpoint(value: string | undefined, fallback: string): string { + const candidate = (value ?? fallback).trim(); + if (!candidate) { + return fallback; + } + + if (candidate.startsWith('http://') || candidate.startsWith('https://')) { + return candidate; + } + + return candidate.startsWith('/') ? candidate : `/${candidate}`; +} + +const LOCAL_GRAPHQL_ENDPOINT = resolveLocalEndpoint(env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK, LOCAL_PONDER_PATH); +const LOCAL_TXNBOT_ENDPOINT = resolveLocalEndpoint(env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK, LOCAL_TXNBOT_PATH); +const LOCAL_RPC_ENDPOINT = resolveLocalEndpoint(env.VITE_LOCAL_RPC_URL, LOCAL_RPC_PATH); + export const chainsData = [ { // local base sepolia fork id: 31337, - graphql: LOCAL_PONDER_URL, + graphql: LOCAL_GRAPHQL_ENDPOINT, path: 'local', stake: LOCAL_STAKE, harb: LOCAL_KRAIKEN, @@ -58,14 +75,14 @@ export const chainsData = [ weth: LOCAL_WETH, swapRouter: LOCAL_ROUTER, liquidityManager: LOCAL_LM, - txnBot: LOCAL_TXNBOT_URL, - rpc: LOCAL_RPC_URL, + txnBot: LOCAL_TXNBOT_ENDPOINT, + rpc: LOCAL_RPC_ENDPOINT, }, }, { // sepolia id: 11155111, - graphql: import.meta.env.VITE_PONDER_SEPOLIA ?? '', + graphql: env.VITE_PONDER_SEPOLIA ?? '', path: 'sepolia', stake: '0xCd21a41a137BCAf8743E47D048F57D92398f7Da9', harb: '0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9', @@ -75,7 +92,7 @@ export const chainsData = [ { // base-sepolia (local dev default) id: 84532, - graphql: import.meta.env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_PONDER_URL, + graphql: LOCAL_GRAPHQL_ENDPOINT, path: 'sepoliabase', stake: '0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2', harb: '0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8', @@ -83,13 +100,13 @@ export const chainsData = [ cheats: { weth: '0x4200000000000000000000000000000000000006', swapRouter: '0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4', - txnBot: import.meta.env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_TXNBOT_URL, + txnBot: LOCAL_TXNBOT_ENDPOINT, }, }, { // base mainnet id: 8453, - graphql: import.meta.env.VITE_PONDER_BASE ?? '', + graphql: env.VITE_PONDER_BASE ?? '', path: 'base', stake: '0xed70707fab05d973ad41eae8d17e2bcd36192cfc', harb: '0x45caa5929f6ee038039984205bdecf968b954820', @@ -97,7 +114,7 @@ export const chainsData = [ cheats: { weth: '0x4200000000000000000000000000000000000006', swapRouter: '', - txnBot: import.meta.env.VITE_TXNBOT_BASE ?? '', + txnBot: env.VITE_TXNBOT_BASE ?? '', }, }, ]; diff --git a/web-app/src/services/chainConfig.ts b/web-app/src/services/chainConfig.ts index 9766219..656b1a6 100644 --- a/web-app/src/services/chainConfig.ts +++ b/web-app/src/services/chainConfig.ts @@ -27,7 +27,10 @@ class ChainConfigService { if (!normalized) { throw new Error(`${label} endpoint not configured for chain ${chainId}.`); } - return normalized; + if (normalized.startsWith('http://') || normalized.startsWith('https://')) { + return normalized; + } + return normalized.startsWith('/') ? normalized : `/${normalized}`; } }