From 7cce783bdd78922be9f5c0e068e6ebfe456b7f61 Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 11:13:19 +0200 Subject: [PATCH 1/7] netowrk-picker --- src/components/connections/index.js | 1 + src/components/connections/networks.js | 232 +++++++++++++++++++++++++ src/index.css | 214 +++++++++++++++++++++++ src/index.js | 2 + 4 files changed, 449 insertions(+) create mode 100644 src/components/connections/networks.js diff --git a/src/components/connections/index.js b/src/components/connections/index.js index 823238c4..f7eaff66 100644 --- a/src/components/connections/index.js +++ b/src/components/connections/index.js @@ -1,2 +1,3 @@ export * from './connections'; export * from './permissions'; +export * from './networks'; diff --git a/src/components/connections/networks.js b/src/components/connections/networks.js new file mode 100644 index 00000000..1bff6c6c --- /dev/null +++ b/src/components/connections/networks.js @@ -0,0 +1,232 @@ +import globalContext from '../..'; + +const NETWORKS = [ + // Main networks + { name: 'Ethereum Mainnet', chainId: '0x1', color: '#627eea', category: 'main' }, + { name: 'Linea', chainId: '0xe708', color: '#000000', category: 'main' }, + { name: 'Base Mainnet', chainId: '0x2105', color: '#0052ff', category: 'main' }, + { name: 'Arbitrum One', chainId: '0xa4b1', color: '#28a0f0', category: 'main' }, + { name: 'Avalanche Network C-Chain', chainId: '0xa86a', color: '#e84142', category: 'main' }, + { name: 'Binance Smart Chain', chainId: '0x38', color: '#f3ba2f', category: 'main' }, + { name: 'OP Mainnet', chainId: '0xa', color: '#ff0420', category: 'main' }, + { name: 'Polygon Mainnet', chainId: '0x89', color: '#8247e5', category: 'main' }, + { name: 'Sei Network', chainId: '0x1a', color: '#ff6b35', category: 'main' }, + { name: 'zkSync Era Mainnet', chainId: '0x144', color: '#8e71c7', category: 'main' }, + + // Test networks + { name: 'Sepolia', chainId: '0xaa36a7', color: '#f6c343', category: 'test' }, + { name: 'Linea Sepolia', chainId: '0xe705', color: '#000000', category: 'test' }, + { name: 'Mega Testnet', chainId: '0x1a4', color: '#ff6b35', category: 'test' }, + { name: 'Monad Testnet', chainId: '0x1a5', color: '#ff6b35', category: 'test' }, +]; + +export function networksComponent(parentContainer) { + parentContainer.insertAdjacentHTML( + 'beforeend', + `
+
+
+

+ Network Picker +

+ +
+
+
`, + ); + + // Create modal dialog + const modal = document.createElement('div'); + modal.className = 'network-modal'; + modal.innerHTML = ` +
+
+
Select Network
+ +
+
+
+
Main Networks
+
+
+
+
Test Networks
+
+
+
+
+ `; + document.body.appendChild(modal); + + const openButton = document.getElementById('openNetworkPicker'); + const closeButton = modal.querySelector('.network-modal-close'); + const currentNetworkName = document.getElementById('currentNetworkName'); + + // Populate network lists + populateNetworkLists(); + + // Event listeners + openButton.addEventListener('click', () => { + modal.style.display = 'flex'; + updateActiveNetworkInModal(); + }); + + closeButton.addEventListener('click', () => { + modal.style.display = 'none'; + }); + + modal.addEventListener('click', (e) => { + if (e.target === modal) { + modal.style.display = 'none'; + } + }); + + // Update current network display when chain changes + document.addEventListener('newNetwork', () => { + updateCurrentNetworkDisplay(); + }); + + // Initial update + updateCurrentNetworkDisplay(); +} + +function populateNetworkLists() { + const mainNetworks = document.getElementById('mainNetworks'); + const testNetworks = document.getElementById('testNetworks'); + + NETWORKS.forEach(network => { + const networkItem = createNetworkItem(network); + + switch (network.category) { + case 'main': + mainNetworks.appendChild(networkItem); + break; + case 'test': + testNetworks.appendChild(networkItem); + break; + } + }); +} + +function createNetworkItem(network) { + const item = document.createElement('div'); + item.className = 'network-modal-item'; + item.dataset.chainId = network.chainId; + item.innerHTML = ` +
+
+
+
${network.name}
+
${network.chainId}
+
+
+ `; + + item.addEventListener('click', async () => { + await switchNetwork(network.chainId); + document.querySelector('.network-modal').style.display = 'none'; + }); + + return item; +} + +async function switchNetwork(chainId) { + if (!globalContext.provider) { + console.error('No provider available'); + return; + } + + try { + await globalContext.provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId }], + }); + } catch (switchError) { + // This error code indicates that the chain has not been added to MetaMask. + if (switchError.code === 4902) { + try { + const network = NETWORKS.find(n => n.chainId === chainId); + await globalContext.provider.request({ + method: 'wallet_addEthereumChain', + params: [ + { + chainId, + chainName: network?.name || 'Unknown Network', + nativeCurrency: { + name: 'ETH', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: ['https://rpc.example.com'], // You might want to add proper RPC URLs + blockExplorerUrls: ['https://explorer.example.com'], // You might want to add proper explorer URLs + }, + ], + }); + } catch (addError) { + console.error('Error adding network:', addError); + } + } else { + console.error('Error switching network:', switchError); + } + } +} + +function updateCurrentNetworkDisplay() { + const currentChainId = globalContext.chainIdInt?.toString(16) || globalContext.chainIdPadded; + const currentNetworkName = document.getElementById('currentNetworkName'); + + if (currentChainId) { + // Convert chainId to proper format for comparison + let normalizedChainId; + if (typeof currentChainId === 'string') { + if (currentChainId.startsWith('0x')) { + normalizedChainId = currentChainId; + } else { + normalizedChainId = `0x${currentChainId}`; + } + } else { + normalizedChainId = `0x${currentChainId.toString(16)}`; + } + + const network = NETWORKS.find(n => n.chainId === normalizedChainId); + + if (network) { + currentNetworkName.textContent = `Current Network: ${network.name}`; + } else { + // Extract the actual chain ID from the padded version if needed + let displayChainId = normalizedChainId; + if (normalizedChainId.length > 10) { + // If it's a padded chain ID, try to extract the actual chain ID + const actualChainId = globalContext.chainIdInt?.toString(16); + if (actualChainId) { + displayChainId = `0x${actualChainId}`; + } + } + currentNetworkName.textContent = `Current Network: Chain ID ${displayChainId}`; + } + } else { + currentNetworkName.textContent = 'Current Network: Not Connected'; + } +} + +function updateActiveNetworkInModal() { + const currentChainId = globalContext.chainIdPadded || globalContext.chainIdInt?.toString(16); + const networkItems = document.querySelectorAll('.network-modal-item'); + + networkItems.forEach(item => { + const itemChainId = item.dataset.chainId; + const isActive = currentChainId && + (itemChainId === currentChainId || + itemChainId === `0x${currentChainId}` || + itemChainId === parseInt(currentChainId, 16).toString(16)); + + if (isActive) { + item.classList.add('active'); + } else { + item.classList.remove('active'); + } + }); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index 6afd3bcb..5a76cae4 100644 --- a/src/index.css +++ b/src/index.css @@ -108,3 +108,217 @@ header { .warning-invisible { display: none; } + +.networks-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.network-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + background: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; + text-align: left; + width: 100%; +} + +.network-item:hover { + background: #e9ecef; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.network-item.active { + background: #007bff; + color: white; + border-color: #007bff; +} + +.network-item.active:hover { + background: #0056b3; +} + +.network-name { + font-weight: 500; + font-size: 14px; +} + +.network-chain-id { + font-size: 12px; + opacity: 0.7; + font-family: monospace; +} + +.network-item.active .network-chain-id { + opacity: 0.9; +} + +.network-modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + justify-content: center; + align-items: center; +} + +.network-modal-content { + background-color: white; + border-radius: 12px; + width: 90%; + max-width: 500px; + max-height: 80vh; + overflow: hidden; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); +} + +.network-modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 24px; + border-bottom: 1px solid #e9ecef; +} + +.network-modal-header h5 { + margin: 0; + font-weight: 600; + color: #212529; +} + +.network-modal-close { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #6c757d; + padding: 0; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background-color 0.2s; +} + +.network-modal-close:hover { + background-color: #f8f9fa; + color: #495057; +} + +.network-modal-body { + padding: 20px 24px; + max-height: 60vh; + overflow-y: auto; +} + +.network-category { + margin-bottom: 24px; +} + +.network-category:last-child { + margin-bottom: 0; +} + +.network-category h6 { + margin: 0 0 12px 0; + font-weight: 600; + color: #495057; + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.network-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.network-modal-item { + display: flex; + align-items: center; + padding: 12px 16px; + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; + border: 1px solid transparent; +} + +.network-modal-item:hover { + background-color: #f8f9fa; + border-color: #dee2e6; +} + +.network-modal-item.active { + background-color: #e3f2fd; + border-color: #2196f3; +} + +.network-modal-item-content { + display: flex; + align-items: center; + width: 100%; +} + +.network-modal-item-icon { + width: 32px; + height: 32px; + border-radius: 50%; + margin-right: 12px; + flex-shrink: 0; +} + +.network-modal-item-info { + flex: 1; +} + +.network-modal-item-name { + font-weight: 500; + font-size: 14px; + color: #212529; + margin-bottom: 2px; +} + +.network-modal-item-chain-id { + font-size: 12px; + color: #6c757d; + font-family: monospace; +} + +.network-modal-item.active .network-modal-item-name { + color: #1976d2; + font-weight: 600; +} + +.network-modal-item.active .network-modal-item-chain-id { + color: #1976d2; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .network-modal-content { + width: 95%; + margin: 20px; + } + + .network-modal-body { + padding: 16px 20px; + } + + .network-modal-header { + padding: 16px 20px; + } +} diff --git a/src/index.js b/src/index.js index 1d6629cd..5f4db336 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ import { NETWORKS_BY_CHAIN_ID } from './onchain-sample-contracts'; import { connectionsComponent, permissionsComponent, + networksComponent, } from './components/connections'; import { sendComponent, @@ -161,6 +162,7 @@ connectionsRow.className = 'row d-flex justify-content-center'; connectionsSection.appendChild(connectionsRow); connectionsComponent(connectionsRow); permissionsComponent(connectionsRow); +networksComponent(connectionsRow); // Connection buttons set up by this file const onboardButton = document.getElementById('connectButton'); From 472e3339064fdad4076c7506fe0f11dff686280c Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 11:36:06 +0200 Subject: [PATCH 2/7] lint fix --- src/components/connections/index.js | 2 +- .../connections/networks-helpers.js | 238 ++++++++++++++++++ src/components/connections/networks.js | 166 +----------- src/index.js | 2 +- 4 files changed, 245 insertions(+), 163 deletions(-) create mode 100644 src/components/connections/networks-helpers.js diff --git a/src/components/connections/index.js b/src/components/connections/index.js index f7eaff66..6fc5d007 100644 --- a/src/components/connections/index.js +++ b/src/components/connections/index.js @@ -1,3 +1,3 @@ export * from './connections'; -export * from './permissions'; export * from './networks'; +export * from './permissions'; diff --git a/src/components/connections/networks-helpers.js b/src/components/connections/networks-helpers.js new file mode 100644 index 00000000..6fc5138d --- /dev/null +++ b/src/components/connections/networks-helpers.js @@ -0,0 +1,238 @@ +import globalContext from '../..'; + +const NETWORKS = [ + // Main networks + { + name: 'Ethereum Mainnet', + chainId: '0x1', + color: '#627eea', + category: 'main', + }, + { + name: 'Linea', + chainId: '0xe708', + color: '#000000', + category: 'main', + }, + { + name: 'Base Mainnet', + chainId: '0x2105', + color: '#0052ff', + category: 'main', + }, + { + name: 'Arbitrum One', + chainId: '0xa4b1', + color: '#28a0f0', + category: 'main', + }, + { + name: 'Avalanche Network C-Chain', + chainId: '0xa86a', + color: '#e84142', + category: 'main', + }, + { + name: 'Binance Smart Chain', + chainId: '0x38', + color: '#f3ba2f', + category: 'main', + }, + { + name: 'OP Mainnet', + chainId: '0xa', + color: '#ff0420', + category: 'main', + }, + { + name: 'Polygon Mainnet', + chainId: '0x89', + color: '#8247e5', + category: 'main', + }, + { + name: 'Sei Network', + chainId: '0x1a', + color: '#ff6b35', + category: 'main', + }, + { + name: 'zkSync Era Mainnet', + chainId: '0x144', + color: '#8e71c7', + category: 'main', + }, + + // Test networks + { + name: 'Sepolia', + chainId: '0xaa36a7', + color: '#f6c343', + category: 'test', + }, + { + name: 'Linea Sepolia', + chainId: '0xe705', + color: '#000000', + category: 'test', + }, + { + name: 'Mega Testnet', + chainId: '0x1a4', + color: '#ff6b35', + category: 'test', + }, + { + name: 'Monad Testnet', + chainId: '0x1a5', + color: '#ff6b35', + category: 'test', + }, +]; + +export function populateNetworkLists() { + const mainNetworks = document.getElementById('mainNetworks'); + const testNetworks = document.getElementById('testNetworks'); + + NETWORKS.forEach((network) => { + const networkItem = createNetworkItem(network); + + switch (network.category) { + case 'main': + mainNetworks.appendChild(networkItem); + break; + case 'test': + testNetworks.appendChild(networkItem); + break; + default: + break; + } + }); +} + +export function createNetworkItem(network) { + const item = document.createElement('div'); + item.className = 'network-modal-item'; + item.dataset.chainId = network.chainId; + item.innerHTML = ` +
+
+
+
${network.name}
+
${network.chainId}
+
+
+ `; + + item.addEventListener('click', async () => { + await switchNetwork(network.chainId); + document.querySelector('.network-modal').style.display = 'none'; + }); + + return item; +} + +export async function switchNetwork(chainId) { + if (!globalContext.provider) { + console.error('No provider available'); + return; + } + + try { + await globalContext.provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId }], + }); + } catch (switchError) { + // This error code indicates that the chain has not been added to MetaMask. + if (switchError.code === 4902) { + try { + const network = NETWORKS.find((n) => n.chainId === chainId); + await globalContext.provider.request({ + method: 'wallet_addEthereumChain', + params: [ + { + chainId, + chainName: network ? network.name : 'Unknown Network', + nativeCurrency: { + name: 'ETH', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: ['https://rpc.example.com'], + blockExplorerUrls: ['https://explorer.example.com'], + }, + ], + }); + } catch (addError) { + console.error('Error adding network:', addError); + } + } else { + console.error('Error switching network:', switchError); + } + } +} + +export function updateCurrentNetworkDisplay() { + const currentChainId = globalContext.chainIdInt + ? globalContext.chainIdInt.toString(16) + : globalContext.chainIdPadded; + const currentNetworkName = document.getElementById('currentNetworkName'); + + if (currentChainId) { + // Convert chainId to proper format for comparison + let normalizedChainId; + if (typeof currentChainId === 'string') { + if (currentChainId.startsWith('0x')) { + normalizedChainId = currentChainId; + } else { + normalizedChainId = `0x${currentChainId}`; + } + } else { + normalizedChainId = `0x${currentChainId.toString(16)}`; + } + + const network = NETWORKS.find((n) => n.chainId === normalizedChainId); + + if (network) { + currentNetworkName.textContent = `Current Network: ${network.name}`; + } else { + // Extract the actual chain ID from the padded version if needed + let displayChainId = normalizedChainId; + if (normalizedChainId.length > 10) { + // If it's a padded chain ID, try to extract the actual chain ID + const actualChainId = globalContext.chainIdInt + ? globalContext.chainIdInt.toString(16) + : null; + if (actualChainId) { + displayChainId = `0x${actualChainId}`; + } + } + currentNetworkName.textContent = `Current Network: Chain ID ${displayChainId}`; + } + } else { + currentNetworkName.textContent = 'Current Network: Not Connected'; + } +} + +export function updateActiveNetworkInModal() { + const currentChainId = + globalContext.chainIdPadded || + (globalContext.chainIdInt ? globalContext.chainIdInt.toString(16) : null); + const networkItems = document.querySelectorAll('.network-modal-item'); + + networkItems.forEach((item) => { + const itemChainId = item.dataset.chainId; + const isActive = + currentChainId && + (itemChainId === currentChainId || + itemChainId === `0x${currentChainId}` || + itemChainId === parseInt(currentChainId, 16).toString(16)); + + if (isActive) { + item.classList.add('active'); + } else { + item.classList.remove('active'); + } + }); +} diff --git a/src/components/connections/networks.js b/src/components/connections/networks.js index 1bff6c6c..a91b71bf 100644 --- a/src/components/connections/networks.js +++ b/src/components/connections/networks.js @@ -1,24 +1,8 @@ -import globalContext from '../..'; - -const NETWORKS = [ - // Main networks - { name: 'Ethereum Mainnet', chainId: '0x1', color: '#627eea', category: 'main' }, - { name: 'Linea', chainId: '0xe708', color: '#000000', category: 'main' }, - { name: 'Base Mainnet', chainId: '0x2105', color: '#0052ff', category: 'main' }, - { name: 'Arbitrum One', chainId: '0xa4b1', color: '#28a0f0', category: 'main' }, - { name: 'Avalanche Network C-Chain', chainId: '0xa86a', color: '#e84142', category: 'main' }, - { name: 'Binance Smart Chain', chainId: '0x38', color: '#f3ba2f', category: 'main' }, - { name: 'OP Mainnet', chainId: '0xa', color: '#ff0420', category: 'main' }, - { name: 'Polygon Mainnet', chainId: '0x89', color: '#8247e5', category: 'main' }, - { name: 'Sei Network', chainId: '0x1a', color: '#ff6b35', category: 'main' }, - { name: 'zkSync Era Mainnet', chainId: '0x144', color: '#8e71c7', category: 'main' }, - - // Test networks - { name: 'Sepolia', chainId: '0xaa36a7', color: '#f6c343', category: 'test' }, - { name: 'Linea Sepolia', chainId: '0xe705', color: '#000000', category: 'test' }, - { name: 'Mega Testnet', chainId: '0x1a4', color: '#ff6b35', category: 'test' }, - { name: 'Monad Testnet', chainId: '0x1a5', color: '#ff6b35', category: 'test' }, -]; +import { + populateNetworkLists, + updateCurrentNetworkDisplay, + updateActiveNetworkInModal, +} from './networks-helpers'; export function networksComponent(parentContainer) { parentContainer.insertAdjacentHTML( @@ -38,7 +22,6 @@ export function networksComponent(parentContainer) { `, ); - // Create modal dialog const modal = document.createElement('div'); modal.className = 'network-modal'; modal.innerHTML = ` @@ -63,7 +46,6 @@ export function networksComponent(parentContainer) { const openButton = document.getElementById('openNetworkPicker'); const closeButton = modal.querySelector('.network-modal-close'); - const currentNetworkName = document.getElementById('currentNetworkName'); // Populate network lists populateNetworkLists(); @@ -92,141 +74,3 @@ export function networksComponent(parentContainer) { // Initial update updateCurrentNetworkDisplay(); } - -function populateNetworkLists() { - const mainNetworks = document.getElementById('mainNetworks'); - const testNetworks = document.getElementById('testNetworks'); - - NETWORKS.forEach(network => { - const networkItem = createNetworkItem(network); - - switch (network.category) { - case 'main': - mainNetworks.appendChild(networkItem); - break; - case 'test': - testNetworks.appendChild(networkItem); - break; - } - }); -} - -function createNetworkItem(network) { - const item = document.createElement('div'); - item.className = 'network-modal-item'; - item.dataset.chainId = network.chainId; - item.innerHTML = ` -
-
-
-
${network.name}
-
${network.chainId}
-
-
- `; - - item.addEventListener('click', async () => { - await switchNetwork(network.chainId); - document.querySelector('.network-modal').style.display = 'none'; - }); - - return item; -} - -async function switchNetwork(chainId) { - if (!globalContext.provider) { - console.error('No provider available'); - return; - } - - try { - await globalContext.provider.request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId }], - }); - } catch (switchError) { - // This error code indicates that the chain has not been added to MetaMask. - if (switchError.code === 4902) { - try { - const network = NETWORKS.find(n => n.chainId === chainId); - await globalContext.provider.request({ - method: 'wallet_addEthereumChain', - params: [ - { - chainId, - chainName: network?.name || 'Unknown Network', - nativeCurrency: { - name: 'ETH', - symbol: 'ETH', - decimals: 18, - }, - rpcUrls: ['https://rpc.example.com'], // You might want to add proper RPC URLs - blockExplorerUrls: ['https://explorer.example.com'], // You might want to add proper explorer URLs - }, - ], - }); - } catch (addError) { - console.error('Error adding network:', addError); - } - } else { - console.error('Error switching network:', switchError); - } - } -} - -function updateCurrentNetworkDisplay() { - const currentChainId = globalContext.chainIdInt?.toString(16) || globalContext.chainIdPadded; - const currentNetworkName = document.getElementById('currentNetworkName'); - - if (currentChainId) { - // Convert chainId to proper format for comparison - let normalizedChainId; - if (typeof currentChainId === 'string') { - if (currentChainId.startsWith('0x')) { - normalizedChainId = currentChainId; - } else { - normalizedChainId = `0x${currentChainId}`; - } - } else { - normalizedChainId = `0x${currentChainId.toString(16)}`; - } - - const network = NETWORKS.find(n => n.chainId === normalizedChainId); - - if (network) { - currentNetworkName.textContent = `Current Network: ${network.name}`; - } else { - // Extract the actual chain ID from the padded version if needed - let displayChainId = normalizedChainId; - if (normalizedChainId.length > 10) { - // If it's a padded chain ID, try to extract the actual chain ID - const actualChainId = globalContext.chainIdInt?.toString(16); - if (actualChainId) { - displayChainId = `0x${actualChainId}`; - } - } - currentNetworkName.textContent = `Current Network: Chain ID ${displayChainId}`; - } - } else { - currentNetworkName.textContent = 'Current Network: Not Connected'; - } -} - -function updateActiveNetworkInModal() { - const currentChainId = globalContext.chainIdPadded || globalContext.chainIdInt?.toString(16); - const networkItems = document.querySelectorAll('.network-modal-item'); - - networkItems.forEach(item => { - const itemChainId = item.dataset.chainId; - const isActive = currentChainId && - (itemChainId === currentChainId || - itemChainId === `0x${currentChainId}` || - itemChainId === parseInt(currentChainId, 16).toString(16)); - - if (isActive) { - item.classList.add('active'); - } else { - item.classList.remove('active'); - } - }); -} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 5f4db336..2082b36a 100644 --- a/src/index.js +++ b/src/index.js @@ -10,8 +10,8 @@ import { NETWORKS_BY_CHAIN_ID } from './onchain-sample-contracts'; import { connectionsComponent, - permissionsComponent, networksComponent, + permissionsComponent, } from './components/connections'; import { sendComponent, From 2ca7b32d68364962d428f3b0ab841ee470258aec Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 11:51:50 +0200 Subject: [PATCH 3/7] err handling --- .../connections/networks-helpers.js | 59 ++++++++++++------- src/components/connections/networks.js | 3 + 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/components/connections/networks-helpers.js b/src/components/connections/networks-helpers.js index 6fc5138d..ff389b86 100644 --- a/src/components/connections/networks-helpers.js +++ b/src/components/connections/networks-helpers.js @@ -125,6 +125,7 @@ export function createNetworkItem(network) { `; item.addEventListener('click', async () => { + hideNetworkError(); // Hide any existing error before attempting to switch await switchNetwork(network.chainId); document.querySelector('.network-modal').style.display = 'none'; }); @@ -132,6 +133,39 @@ export function createNetworkItem(network) { return item; } +export function showNetworkError(message) { + // Remove any existing error message + hideNetworkError(); + + // Create error message element + const errorDiv = document.createElement('div'); + errorDiv.id = 'networkError'; + errorDiv.className = 'error-message'; + errorDiv.style.marginTop = '10px'; + errorDiv.style.width = '100%'; + errorDiv.innerHTML = ` + +
${message}
+ `; + + // Find the network picker button and insert error after it + const networkButton = document.getElementById('openNetworkPicker'); + const cardBody = networkButton.closest('.card-body'); + cardBody.appendChild(errorDiv); + + // Auto-hide after 5 seconds + setTimeout(() => { + hideNetworkError(); + }, 5000); +} + +export function hideNetworkError() { + const existingError = document.getElementById('networkError'); + if (existingError) { + existingError.remove(); + } +} + export async function switchNetwork(chainId) { if (!globalContext.provider) { console.error('No provider available'); @@ -146,29 +180,12 @@ export async function switchNetwork(chainId) { } catch (switchError) { // This error code indicates that the chain has not been added to MetaMask. if (switchError.code === 4902) { - try { - const network = NETWORKS.find((n) => n.chainId === chainId); - await globalContext.provider.request({ - method: 'wallet_addEthereumChain', - params: [ - { - chainId, - chainName: network ? network.name : 'Unknown Network', - nativeCurrency: { - name: 'ETH', - symbol: 'ETH', - decimals: 18, - }, - rpcUrls: ['https://rpc.example.com'], - blockExplorerUrls: ['https://explorer.example.com'], - }, - ], - }); - } catch (addError) { - console.error('Error adding network:', addError); - } + const network = NETWORKS.find((n) => n.chainId === chainId); + const networkName = network ? network.name : `Chain ID ${chainId}`; + showNetworkError(`${networkName} is not available in your wallet`); } else { console.error('Error switching network:', switchError); + showNetworkError('Failed to switch network'); } } } diff --git a/src/components/connections/networks.js b/src/components/connections/networks.js index a91b71bf..b04d7891 100644 --- a/src/components/connections/networks.js +++ b/src/components/connections/networks.js @@ -2,6 +2,7 @@ import { populateNetworkLists, updateCurrentNetworkDisplay, updateActiveNetworkInModal, + hideNetworkError, } from './networks-helpers'; export function networksComponent(parentContainer) { @@ -58,11 +59,13 @@ export function networksComponent(parentContainer) { closeButton.addEventListener('click', () => { modal.style.display = 'none'; + hideNetworkError(); }); modal.addEventListener('click', (e) => { if (e.target === modal) { modal.style.display = 'none'; + hideNetworkError(); } }); From 21b69e02550855a1e62d02145bfec16108e58190 Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 12:25:15 +0200 Subject: [PATCH 4/7] simplify logic --- .../connections/networks-helpers.js | 51 +++++-------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/src/components/connections/networks-helpers.js b/src/components/connections/networks-helpers.js index ff389b86..32235618 100644 --- a/src/components/connections/networks-helpers.js +++ b/src/components/connections/networks-helpers.js @@ -191,41 +191,24 @@ export async function switchNetwork(chainId) { } export function updateCurrentNetworkDisplay() { - const currentChainId = globalContext.chainIdInt - ? globalContext.chainIdInt.toString(16) - : globalContext.chainIdPadded; const currentNetworkName = document.getElementById('currentNetworkName'); - if (currentChainId) { - // Convert chainId to proper format for comparison - let normalizedChainId; - if (typeof currentChainId === 'string') { - if (currentChainId.startsWith('0x')) { - normalizedChainId = currentChainId; - } else { - normalizedChainId = `0x${currentChainId}`; - } - } else { - normalizedChainId = `0x${currentChainId.toString(16)}`; - } - - const network = NETWORKS.find((n) => n.chainId === normalizedChainId); + if ( + globalContext.chainIdInt !== undefined && + globalContext.chainIdInt !== null + ) { + const network = NETWORKS.find((n) => { + const networkChainId = parseInt(n.chainId, 16); + return networkChainId === globalContext.chainIdInt; + }); if (network) { currentNetworkName.textContent = `Current Network: ${network.name}`; } else { - // Extract the actual chain ID from the padded version if needed - let displayChainId = normalizedChainId; - if (normalizedChainId.length > 10) { - // If it's a padded chain ID, try to extract the actual chain ID - const actualChainId = globalContext.chainIdInt - ? globalContext.chainIdInt.toString(16) - : null; - if (actualChainId) { - displayChainId = `0x${actualChainId}`; - } - } - currentNetworkName.textContent = `Current Network: Chain ID ${displayChainId}`; + // Fallback to chain ID if network not found + currentNetworkName.textContent = `Current Network: Chain ID 0x${globalContext.chainIdInt.toString( + 16, + )}`; } } else { currentNetworkName.textContent = 'Current Network: Not Connected'; @@ -233,18 +216,12 @@ export function updateCurrentNetworkDisplay() { } export function updateActiveNetworkInModal() { - const currentChainId = - globalContext.chainIdPadded || - (globalContext.chainIdInt ? globalContext.chainIdInt.toString(16) : null); const networkItems = document.querySelectorAll('.network-modal-item'); networkItems.forEach((item) => { const itemChainId = item.dataset.chainId; - const isActive = - currentChainId && - (itemChainId === currentChainId || - itemChainId === `0x${currentChainId}` || - itemChainId === parseInt(currentChainId, 16).toString(16)); + const itemChainIdInt = parseInt(itemChainId, 16); + const isActive = itemChainIdInt === globalContext.chainIdInt; if (isActive) { item.classList.add('active'); From 2b1f4e95efe7848b0ed21283b0b09aa7b2f99206 Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 12:37:01 +0200 Subject: [PATCH 5/7] rmv icon --- src/components/connections/networks-helpers.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/connections/networks-helpers.js b/src/components/connections/networks-helpers.js index 32235618..741494d2 100644 --- a/src/components/connections/networks-helpers.js +++ b/src/components/connections/networks-helpers.js @@ -143,10 +143,7 @@ export function showNetworkError(message) { errorDiv.className = 'error-message'; errorDiv.style.marginTop = '10px'; errorDiv.style.width = '100%'; - errorDiv.innerHTML = ` - -
${message}
- `; + errorDiv.innerHTML = `
${message}
`; // Find the network picker button and insert error after it const networkButton = document.getElementById('openNetworkPicker'); From 81d3b064d733f96d42f3679750cb859ca2fc202e Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 13:08:06 +0200 Subject: [PATCH 6/7] tweaks --- src/components/connections/networks.js | 5 ----- src/index.js | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/connections/networks.js b/src/components/connections/networks.js index b04d7891..8b9cb569 100644 --- a/src/components/connections/networks.js +++ b/src/components/connections/networks.js @@ -69,11 +69,6 @@ export function networksComponent(parentContainer) { } }); - // Update current network display when chain changes - document.addEventListener('newNetwork', () => { - updateCurrentNetworkDisplay(); - }); - // Initial update updateCurrentNetworkDisplay(); } diff --git a/src/index.js b/src/index.js index 2082b36a..8f7bab13 100644 --- a/src/index.js +++ b/src/index.js @@ -47,6 +47,10 @@ import { } from './components/interactions'; import { sendFormComponent } from './components/forms/send-form'; import { eip5792Component } from './components/transactions/eip5792'; +import { + updateCurrentNetworkDisplay, + updateActiveNetworkInModal, +} from './components/connections/networks-helpers'; const { hstBytecode, @@ -435,6 +439,8 @@ const handleNewChain = (chainId) => { if (!scrollToHandled) { handleScrollTo({ delay: true }); } + updateCurrentNetworkDisplay(); + updateActiveNetworkInModal(); }; function handleNewNetwork(networkId) { From d6fbc25c89a0620d36748b36c89d4d97fb46ff38 Mon Sep 17 00:00:00 2001 From: seaona Date: Tue, 19 Aug 2025 17:40:26 +0200 Subject: [PATCH 7/7] address review comments --- .../connections/networks-helpers.js | 28 +++++++------------ src/components/connections/networks.js | 3 -- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/components/connections/networks-helpers.js b/src/components/connections/networks-helpers.js index 741494d2..c57427f3 100644 --- a/src/components/connections/networks-helpers.js +++ b/src/components/connections/networks-helpers.js @@ -190,26 +190,18 @@ export async function switchNetwork(chainId) { export function updateCurrentNetworkDisplay() { const currentNetworkName = document.getElementById('currentNetworkName'); - if ( - globalContext.chainIdInt !== undefined && - globalContext.chainIdInt !== null - ) { - const network = NETWORKS.find((n) => { - const networkChainId = parseInt(n.chainId, 16); - return networkChainId === globalContext.chainIdInt; - }); - - if (network) { - currentNetworkName.textContent = `Current Network: ${network.name}`; - } else { - // Fallback to chain ID if network not found - currentNetworkName.textContent = `Current Network: Chain ID 0x${globalContext.chainIdInt.toString( - 16, - )}`; - } - } else { + if (!globalContext.chainIdInt) { currentNetworkName.textContent = 'Current Network: Not Connected'; + return; } + const network = NETWORKS.find((n) => { + const networkChainId = parseInt(n.chainId, 16); + return networkChainId === globalContext.chainIdInt; + }); + // Fallback to chain ID if network not found + currentNetworkName.textContent = network + ? `Current Network: ${network.name}` + : `Current Network: Chain ID 0x${globalContext.chainIdInt.toString(16)}`; } export function updateActiveNetworkInModal() { diff --git a/src/components/connections/networks.js b/src/components/connections/networks.js index 8b9cb569..8c69ccb6 100644 --- a/src/components/connections/networks.js +++ b/src/components/connections/networks.js @@ -48,10 +48,8 @@ export function networksComponent(parentContainer) { const openButton = document.getElementById('openNetworkPicker'); const closeButton = modal.querySelector('.network-modal-close'); - // Populate network lists populateNetworkLists(); - // Event listeners openButton.addEventListener('click', () => { modal.style.display = 'flex'; updateActiveNetworkInModal(); @@ -69,6 +67,5 @@ export function networksComponent(parentContainer) { } }); - // Initial update updateCurrentNetworkDisplay(); }