V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 支持 Solana 登录

你可以在 V2EX 设置中绑定 Solana 地址,然后就可以用 Phantom / Glow 登录 V2EX。

Get V2EX Coin

9raUVuzeWUk53co63M4WXLWPWE4Xc6Lpn7RS9dnkpump

Oah1zO
0.02D
V2EX  ›  Solana

ai 写了个油猴脚本.在个人页中,把地址、sol、v2ex 余额显示出来了..

  •  
  •   Oah1zO · 50 天前 · 725 次点击
    这是一个创建于 50 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题

    // ==UserScript==
    // @name         V2EX Solana Balance Checker (Table Style)
    // @namespace    http://tampermonkey.net/
    // @version      0.5
    // @description  Automatically finds the Solana address on a V2EX user's profile and displays balances in a table below the user info.
    // @author       Gemini
    // @match        https://www.v2ex.com/member/*
    // @grant        GM_xmlhttpRequest
    // @grant        GM_addStyle
    // @connect      api.mainnet-beta.solana.com
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // 1. The Mint Address for the V2EX token
        const v2exTokenMintAddress = '9raUVuzeWUk53co63M4WXLWPWE4Xc6Lpn7RS9dnkpump';
    
        // 2. Solana RPC Endpoint
        const solanaRpcEndpoint = 'https://api.mainnet-beta.solana.com';
    
        /**
         * @function findAddressOnPage
         * @description Scans <script> tags on the page to find and extract the Solana address.
         * @returns {string|null} The found address or null if not found.
         */
        function findAddressOnPage() {
            const scripts = document.querySelectorAll('script');
            for (const script of scripts) {
                if (script.textContent.includes('const address =')) {
                    const match = script.textContent.match(/const address = "([1-9A-HJ-NP-Za-km-z]{32,44})";/);
                    if (match && match[1]) {
                        console.log('Successfully found SOL address on page:', match[1]);
                        return match[1];
                    }
                }
            }
            return null;
        }
    
        // 3. Automatically extract the SOL address from the page
        const userSolanaAddress = findAddressOnPage();
    
        if (!userSolanaAddress) {
            console.log('V2EX Solana Balance Checker: Could not find a Solana address on this page.');
            return;
        }
    
        // 4. Define table styles
        GM_addStyle(`
            .solana-balance-box {
                border-bottom: 1px solid #e2e2e2;
                margin-bottom: 20px;
            }
            .solana-balance-table {
                width: 100%;
                border-collapse: collapse;
                table-layout: fixed;
                margin-bottom: -1px; /* Fix for overlapping borders */
            }
            .solana-balance-table th, .solana-balance-table td {
                padding: 12px;
                text-align: left;
                border-top: 1px solid #e2e2e2;
                font-size: 14px;
                line-height: 1.6;
            }
            .solana-balance-table th {
                font-weight: bold;
                background-color: #f9f9f9;
                color: #555;
            }
            /* --- Column Widths --- */
            .solana-balance-table th:nth-child(1) { width: 60%; } /* Address column */
            .solana-balance-table th:nth-child(2) { width: 20%; } /* SOL Balance column */
            .solana-balance-table th:nth-child(3) { width: 20%; } /* Token Balance column */
    
            .solana-balance-table td {
                font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
                word-wrap: break-word;
                color: #000;
            }
            .solana-balance-table td .loader {
                font-weight: bold;
                color: #999;
            }
        `);
    
        // 5. Create and insert DOM elements
        // Create a container with the 'box' class
        const container = document.createElement('div');
        container.className = 'box solana-balance-box';
    
        // Create the table
        const table = document.createElement('table');
        table.className = 'solana-balance-table';
    
        // Create the header row (Row 1)
        const headerRow = table.insertRow();
        headerRow.innerHTML = '<th>Address</th><th>SOL</th><th>$V2EX</th>';
    
        // Create the data row (Row 2)
        const dataRow = table.insertRow();
        const addressCell = dataRow.insertCell();
        addressCell.textContent = userSolanaAddress;
    
        const solBalanceCell = dataRow.insertCell();
        solBalanceCell.innerHTML = '<span class="loader">Loading...</span>';
    
        const tokenBalanceCell = dataRow.insertCell();
        tokenBalanceCell.innerHTML = '<span class="loader">Loading...</span>';
    
        container.appendChild(table);
    
        // Find the main user info box
        const mainInfoBox = document.querySelector('#Main .box');
        if (mainInfoBox) {
            // Insert the new container right after the main info box
            mainInfoBox.parentNode.insertBefore(container, mainInfoBox.nextSibling);
        }
    
    
        // 6. Fetch data from the Solana RPC
        function getSolBalance() {
            GM_xmlhttpRequest({
                method: 'POST',
                url: solanaRpcEndpoint,
                headers: { 'Content-Type': 'application/json' },
                data: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getBalance', params: [userSolanaAddress] }),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.result) {
                            const solBalance = data.result.value / 1_000_000_000; // Lamports to SOL
                            solBalanceCell.textContent = `${solBalance.toFixed(6)} SOL`;
                        } else {
                            solBalanceCell.textContent = 'Failed to fetch';
                            console.error('Failed to fetch SOL balance:', data.error);
                        }
                    } catch (e) {
                        solBalanceCell.textContent = 'Parse error';
                    }
                },
                onerror: function() {
                    solBalanceCell.textContent = 'Request error';
                }
            });
        }
    
        function getTokenBalance() {
            GM_xmlhttpRequest({
                method: 'POST',
                url: solanaRpcEndpoint,
                headers: { 'Content-Type': 'application/json' },
                data: JSON.stringify({
                    jsonrpc: '2.0', id: 1, method: 'getTokenAccountsByOwner',
                    params: [userSolanaAddress, { mint: v2exTokenMintAddress }, { encoding: 'jsonParsed' }]
                }),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.result && data.result.value.length > 0) {
                            const tokenBalance = data.result.value[0].account.data.parsed.info.tokenAmount.uiAmountString;
                            tokenBalanceCell.textContent = tokenBalance;
                        } else if (data.result) {
                            tokenBalanceCell.textContent = '0';
                        } else {
                            tokenBalanceCell.textContent = 'Failed to fetch';
                            console.error('Failed to fetch token balance:', data.error || 'Token account not found');
                        }
                    } catch(e) {
                        tokenBalanceCell.textContent = 'Parse error';
                    }
                },
                onerror: function() {
                    tokenBalanceCell.textContent = 'Request error';
                }
            });
        }
    
        // Execute the fetch functions
        getSolBalance();
        getTokenBalance();
    
    })();
    
    3 条回复    2025-08-09 19:32:02 +08:00
    MyBules
        1
    MyBules  
       50 天前
    支持
    Oah1zO
        2
    Oah1zO  
    OP
       50 天前
    唔,增加了几个节点和夜间模式的支持..
    ```
    // ==UserScript==
    // @name V2EX Solana Balance Checker 0.7
    // @namespace http://tampermonkey.net/
    // @version 0.7
    // @description Uses JavaScript to read and apply V2EX's native theme colors for perfect integration. Includes auto RPC-node failover.
    // @author Gemini
    // @match https://www.v2ex.com/member/*
    // @match https://v2ex.com/member/*
    // @match https://*.v2ex.com/member/*
    // @grant GM_xmlhttpRequest
    // @grant GM_addStyle
    // @connect api.mainnet-beta.solana.com
    // @connect rpc.ankr.com
    // @connect solana-mainnet.rpc.extrnode.com
    // ==/UserScript==

    (function() {
    'use strict';

    // 1. Configuration
    const v2exTokenMintAddress = '9raUVuzeWUk53co63M4WXLWPWE4Xc6Lpn7RS9dnkpump';
    const RPC_ENDPOINTS = [ 'https://rpc.ankr.com/solana', 'https://api.mainnet-beta.solana.com', 'https://solana-mainnet.rpc.extrnode.com' ];

    function findAddressOnPage() {
    const scripts = document.querySelectorAll('script');
    for (const script of scripts) {
    if (script.textContent.includes('const address =')) {
    const match = script.textContent.match(/const address = "([1-9A-HJ-NP-Za-km-z]{32,44})";/);
    if (match && match[1]) { return match[1]; }
    }
    }
    return null;
    }

    const userSolanaAddress = findAddressOnPage();
    if (!userSolanaAddress) { return; }

    // 2. Add base styles for LAYOUT ONLY.
    GM_addStyle(`
    .solana-balance-box { background-color: var(--box-background-color); border-bottom: 1px solid var(--box-border-color); margin-bottom: 20px; }
    .solana-balance-table { width: 100%; border-collapse: collapse; table-layout: fixed; margin-bottom: -1px; }
    .solana-balance-table th, .solana-balance-table td { padding: 12px; text-align: left; border-top: 1px solid var(--box-border-color); font-size: 14px; line-height: 1.6; }
    .solana-balance-table th { background-color: var(--box-header-background-color); font-weight: bold; }
    .solana-balance-table td { font-family: var(--mono-font); word-wrap: break-word; }
    .solana-balance-table th:nth-child(1) { width: 60%; } .solana-balance-table th:nth-child(2) { width: 20%; } .solana-balance-table th:nth-child(3) { width: 20%; }
    `);

    // 3. Create and insert DOM elements
    const container = document.createElement('div');
    container.className = 'solana-balance-box';
    const table = document.createElement('table');
    table.className = 'solana-balance-table';
    const headerRow = table.insertRow();
    headerRow.innerHTML = '<th>Address</th><th>SOL</th><th>$V2EX</th>';
    const dataRow = table.insertRow();
    const addressCell = dataRow.insertCell();
    const solBalanceCell = dataRow.insertCell();
    const tokenBalanceCell = dataRow.insertCell();
    addressCell.textContent = userSolanaAddress;
    solBalanceCell.textContent = 'Loading...';
    tokenBalanceCell.textContent = 'Loading...';
    container.appendChild(table);
    const mainInfoBox = document.querySelector('#Main .box');
    if (mainInfoBox) {
    mainInfoBox.parentNode.insertBefore(container, mainInfoBox.nextSibling);
    }

    // 4. JavaScript function to READ and APPLY native text colors for BOTH headers and data
    function updateTextColorsForTheme() {
    // Read the actual color values V2EX is currently using
    const nativeHeaderTextColor = getComputedStyle(document.body).getPropertyValue('--box-header-text-color').trim();
    const nativeTextColor = getComputedStyle(document.body).getPropertyValue('--box-foreground-color').trim();
    const nativeFadeColor = getComputedStyle(document.body).getPropertyValue('--color-fade').trim();

    // --- FIX: Apply color to table headers (th) ---
    const headers = table.querySelectorAll('th');
    for (const header of headers) {
    // By changing this to nativeTextColor, the header color will match the data cell color.
    header.style.setProperty('color', nativeTextColor, 'important');
    }

    // --- Apply color to table data (td) ---
    const cells = [addressCell, solBalanceCell, tokenBalanceCell];
    for (const cell of cells) {
    if (cell.textContent === 'Loading...' || cell.textContent === 'Error') {
    cell.style.setProperty('color', nativeFadeColor, 'important');
    } else {
    cell.style.setProperty('color', nativeTextColor, 'important');
    }
    }
    }

    // 5. Observer to detect theme changes in real-time
    const themeObserver = new MutationObserver(() => updateTextColorsForTheme());
    themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
    updateTextColorsForTheme();

    // 6. Fetch data
    function makeRpcRequest(requestPayload, onSuccess, onFailure) {
    let endpointIndex = 0;
    function tryNextEndpoint() {
    if (endpointIndex >= RPC_ENDPOINTS.length) { if (onFailure) onFailure(); return; }
    const currentEndpoint = RPC_ENDPOINTS[endpointIndex++];
    GM_xmlhttpRequest({
    method: 'POST', url: currentEndpoint, headers: { 'Content-Type': 'application/json' },
    data: JSON.stringify(requestPayload), timeout: 8000,
    onload: function(response) {
    try { const data = JSON.parse(response.responseText); if (data.error) { tryNextEndpoint(); } else { onSuccess(data); } }
    catch (e) { tryNextEndpoint(); }
    },
    onerror: tryNextEndpoint, ontimeout: tryNextEndpoint
    });
    }
    tryNextEndpoint();
    }

    function getSolBalance() {
    makeRpcRequest({ jsonrpc: '2.0', id: 1, method: 'getBalance', params: [userSolanaAddress] },
    (data) => { solBalanceCell.textContent = `${(data.result.value / 1e9).toFixed(6)}`; updateTextColorsForTheme(); },
    () => { solBalanceCell.textContent = 'Error'; updateTextColorsForTheme(); }
    );
    }
    function getTokenBalance() {
    makeRpcRequest({ jsonrpc: '2.0', id: 1, method: 'getTokenAccountsByOwner', params: [userSolanaAddress, { mint: v2exTokenMintAddress }, { encoding: 'jsonParsed' }] },
    (data) => { tokenBalanceCell.textContent = data.result.value.length > 0 ? data.result.value[0].account.data.parsed.info.tokenAmount.uiAmountString : '0'; updateTextColorsForTheme(); },
    () => { tokenBalanceCell.textContent = 'Error'; updateTextColorsForTheme(); }
    );
    }

    getSolBalance();
    getTokenBalance();

    })();
    ```
    Oah1zO
        3
    Oah1zO  
    OP
       45 天前
    又更新了一个版本..效果如下图吧..

    安装: https://greasyfork.org/zh-CN/scripts/545123-v2ex-solana-balance-checker

    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5281 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 01:59 · PVG 09:59 · LAX 18:59 · JFK 21:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.