OKX withdrawal Address Filler

Automate withdrawal address entry on OKX withdrawal page.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         OKX withdrawal Address Filler
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Automate withdrawal address entry on OKX withdrawal page.
// @author       You
// @match        https://www.okx.com/*/balance/withdrawal-address/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let container;
    let textarea, prefixInput;

    // 创建用户界面
    const createUserInterface = () => {
        container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.top = '50%';
        container.style.left = '100px';
        container.style.transform = 'translateY(-50%)';
        container.style.zIndex = '99999';
        container.style.backgroundColor = '#FFF';
        container.style.padding = '10px';
        container.style.border = '1px solid #CCC';
        container.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
        container.style.borderRadius = '8px';
        container.style.display = 'none'; // 初始设置为隐藏

        textarea = document.createElement('textarea');
        textarea.placeholder = '粘贴地址列表,每行一个';
        textarea.style.width = '300px';
        textarea.style.height = '150px';
        textarea.style.resize = 'none';

        prefixInput = document.createElement('input');
        prefixInput.placeholder = '备注前缀(如 a1, 那么备注会是 a1 到 aN)';
        prefixInput.style.display = 'block';
        prefixInput.style.marginTop = '5px';
        prefixInput.style.width = '290px';


        const fillButton = document.createElement('button');
        fillButton.textContent = '填充地址';
        fillButton.style.backgroundColor = 'black'; // 按钮背景色
        fillButton.style.color = 'white'; // 文字颜色
        fillButton.style.padding = '5px 10px'; // 内边距
        fillButton.style.border = 'none'; // 无边框
        fillButton.style.borderRadius = '15px'; // 边框圆角
        fillButton.style.cursor = 'pointer'; // 鼠标样式
        fillButton.style.marginTop = '10px'; // 顶部外边距
        fillButton.style.float = 'right'; // 右浮动
        fillButton.onclick = () => handleFillButton(textarea.value, prefixInput.value);


        container.appendChild(textarea);
        container.appendChild(prefixInput);
        container.appendChild(fillButton);
        document.body.appendChild(container);
    };

   const resetFormData = () => {
        textarea.value = '';
        prefixInput.value = '';
    };

   const adjustPosition = () => {
        const popup = document.querySelector('.okui-dialog-window');
        if (popup) {
            const rect = popup.getBoundingClientRect();
            container.style.top = `${rect.top + (rect.height / 2) - (container.offsetHeight / 2)}px`; // 纵向居中
            container.style.left = `${rect.left - container.offsetWidth - 10}px`; // 横向放在弹出窗口左侧
        }
    };

    const handleFillButton = async (rawData, prefix) => {
        const data = parseAddresses(rawData, prefix);
        await addAddresses(data.length-1);
        fillForm(0, data);
    };

    // 监听DOM变化
    const observeDOM = (element, callback) => {
        const observer = new MutationObserver(mutations => {
            if (mutations.length) {
                callback();
                adjustPosition(); // 每次DOM变化后调整位置

            }
        });

        observer.observe(element, { childList: true, subtree: true });
    };

    // 检查“新增提现地址”框是否显示
    const checkForPopup = () => {
        const popup = document.querySelector('.balance_okui-dialog-window');
        if (popup && getComputedStyle(popup).display !== 'none') {
            container.style.display = 'block';
        } else {
            container.style.display = 'none';
            resetFormData(); // 当弹出窗口关闭时重置数据
        }
    };

    // 连续点击添加按钮
    const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    const addAddresses = async (count) => {
        for (let i = 0; i < count; i++) {
            document.querySelector('.add-address-form-btn').click();
            await delay(20);
        }
    };

    // 解析地址和前缀
    const parseAddresses = (rawData, prefix) => {
        const lines = rawData.split('\n');
        const baseIndex = parseInt(prefix.substring(1)) || 0;
        return lines.map((line, index) => {
            const address = line.trim();
            const note = `${prefix.substring(0, 1)}${baseIndex + index}`;
            return { address, note };
        });
    };

    // 填充地址
    const fillAddresses = (rawData, prefix) => {
        const data = parseAddresses(rawData, prefix);
        fillForm(0, data);
    };

    function simulateReactInputChange(input, value) {
        const lastValue = input.value;
        input.value = value;

        const event = new Event('input', { bubbles: true });
        event.simulated = true;

        const tracker = input._valueTracker;
        if (tracker) {
            tracker.setValue(lastValue);
        }

        input.dispatchEvent(event);
    }

    function fillForm(index, data) {
        if (index >= data.length) {
            console.log('所有表单已填充完毕。');
            return;
        }

        setTimeout(() => {
            const addressInputs = document.querySelectorAll(
                'input[placeholder*="地址 / .crypto domain"]'
            );
            const noteInputs = document.querySelectorAll(
                'input[placeholder="如:“我的钱包”"]'
            );

            if (addressInputs.length === 0 || noteInputs.length === 0) {
                console.error('找不到输入框,请检查选择器。');
                return;
            }

            if (index >= addressInputs.length || index >= noteInputs.length) {
                console.error('数据超过了输入框的数量。');
                return;
            }

            simulateReactInputChange(addressInputs[index], data[index].address);
            simulateReactInputChange(noteInputs[index], data[index].note);

            console.log(`已填充表单 ${index + 1}。`);

            fillForm(index + 1, data);
        }, 0);
    }

    // 初始化
    window.addEventListener('load', () => {
        createUserInterface();
        observeDOM(document.body, checkForPopup); // 监听body的变化
    });
})();