Отправка крипты с OKX на кошельки и отправка с кошельков на саб-аккаунты

Содержание:

Как использовать код из статьи

Как запускать файлы .py и .ipynb написано в инструкции.

Скачайте репозиторий с Гитхаба. В нем будет 4 скрипта:

  • withdraw-from-okx-to-wallets-var-1.py - для отправки одного вида монет в равном количестве на кошельки. В этом скрипте используются файлы:
  • Список адресов получателей в excel wallet_addresses_var_1.xlsx или txt - wallet_addresses_var_1.txt.
  • Результат работы скрипта записывается в отчет excel - report_table_var_1.xlsx.

  • withdraw-from-okx-to-wallets-var-2.py - настраиваемая отправка: разные монеты в разном количестве на кошельки. В этом скрипте используются файлы:
  • Настройки для отправки (адрес, название монеты, количество, комиссия, сеть) в формате excel sending_settings_var_2.xlsx или txt - sending_settings_var_2.txt.
  • Результат работы скрипта записывается в отчет excel - report_table_var_2.xlsx.

  • withdraw-from-wallets-to-okx-var-1.py - отправка нативных монет с кошельков на саб-аккаунты биржи. В этом скрипте используются файлы:
  • Настройки для отправки (приватный ключ кошелька, адрес okx, название монеты, название сети, количество, url rpc) в формате excel sending_from_wallets_settings_var_1.xlsx или txt - sending_from_wallets_settings_var_1.txt.
  • Результат работы скрипта записывается в отчет excel - report_table_var_sending_from_wallets_1.xlsx.

  • withdraw-from-wallets-to-okx-var-2.py - отправка токенов, например стейблов с кошельков на саб-аккаунты биржи. В этом скрипте используются файлы:
  • Настройки для отправки (приватный ключ кошелька, адрес okx, название монеты, название сети, адрес смарт-контракта, количество, url rpc) в формате excel sending_from_wallets_settings_var_2.xlsx или txt - sending_from_wallets_settings_var_2.txt.
  • ABI для работы отправки ERC20 токенов в формате json ERC20_ABI.json.
  • Результат работы скрипта записывается в отчет excel - report_table_var_sending_from_wallets_2.xlsx.

Также 2 дополнительных скрипта:
  • get-assets-info.py - скрипт для получения информации о монетах, которые в okx: название, название сети, минимальная и максимальная комиссия. Результат экспортируется в assets_settings.xlsx.
  • count-withdrawal-cost-from-okx.py - скрипт для предварительного расчета необходимой суммы на балансе с учетом комиссий, чтобы осуществить отправку в скрипте withdraw-from-okx-to-wallets-var-1.py.

Подготовка к работе с биржей

Создайте ключ в личном кабинете.

Варианты подключения

Api-ключ, секретный ключ, также серкретная фраза, которая использовалась для создания этих ключей:
API_KEY = '8gb2d2f2-274b-455d-967e-9fdcb490f46c'
SECRET_KEY = 'D27A0AA2C55BC18B15055580921E56209'
PASSPHRASE = 'secret_phrase'

Вариант 1 - Подключение с помощью requests

В качестве примера подключения ниже представлен код проверки баланса на Основном аккаунте.
Необходимые библиотеки:
import requests
import json
import datetime
 
import hmac
import base64
Настройки:
OKX_DOMAIN = 'https://www.okx.com'
Ресурс для проверки баланса:
ENDPOINT = '/api/v5/asset/balances'
Функция для отправки даты и времени в формате UTC, например 2020-12-08T09:08:57.715Z
def get_time():
    now = datetime.datetime.utcnow()
    timestamp = now.isoformat("T", "milliseconds")
    return timestamp + "Z"
Функция для создания подписи в нужном формате, указанном в документации:
def signature(timestamp, method, endpoint, body, secret_key):
    if str(body) == '{}' or str(body) == 'None':
        body = ''
    message = str(timestamp) + str.upper(method) + endpoint + str(body)
    mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
    d = mac.digest()
    return base64.b64encode(d)
Функция для создания заголовка:
def get_header():
    body= {}
    method= 'GET'
    header = dict()
    header['CONTENT-TYPE'] = 'application/json'
    header['OK-ACCESS-KEY'] = API_KEY
    header['OK-ACCESS-SIGN'] = signature(get_time(), method, ENDPOINT, body, SECRET_KEY)
    header['OK-ACCESS-TIMESTAMP'] = str(get_time())
    header['OK-ACCESS-PASSPHRASE'] = PASSPHRASE
    return header

Подключение

url = OKX_DOMAIN + ENDPOINT
header = get_header()
response= requests.get(url, headers=header)
Результат:
result = response.json()

Проверка по конкретному ассету

Нужно изменить адрес ресурса - добавить параметр ccy с тикером ассета:
ENDPOINT = '/api/v5/asset/balances?ccy=USDC'
url = OKX_DOMAIN + ENDPOINT
header = get_header()
response= requests.get(url, headers=header)
result = response.json()

Вариант 2 - Подключение с помощью библиотеки Okx

Okx предлагает библиотеку https://pypi.org/project/python-okx/, которая сокращает количество кода и делает работу немного удобнее.

С помощью библиотеки okx выполним аналогичную задачу, которая выше решалась с помощью requests.

import okx.Funding as Funding
Подключение. Необходимо передать api-ключ, секретный ключ и секретную фразу. Еще в документации присутствует bool параметр False, не разобрался зачем он нужен:) И параметр 0 - реальный аккаунт или 1 - демо аккаунт:
funding_api = Funding.FundingAPI(API_KEY, SECRET_KEY, PASSPHRASE, False, '0')
result = funding_api.get_balances()

Проверка по конкретному ассету

Нужно передать в качестве параметра тикер валюты:
result = funding_api.get_balances('USDC')
Все дальнейшие варианты пополнения кошельков через биржу okx буду делать на этой библиотеке.

Часть 1 - Отправка крипты с Okx на кошельки

Подготовка

  • Необходимо добавить адреса для вывода через личный кабинет. Не нашел способ добавить адреса по API :(
    Инструкция https://www.okx.com/ru/help/10270095472397
    Если вы будете добавлять EVM адреса, чтобы в будущем отправлять на любые emv-сети любые активы, при добавлении адреса выбирайте Тип адреса - Адрес EVM. Поставьте галочку "Сохранить адрес как проверенный..."
  • Для отправки монет на кошельки нужно знать обозначение сети, в которую будет отправлен актив и значение комиссии. Как называется сеть можно узнать через личный кабинет - вывод средств.
  • Или получить всю информацию по API:
Файл get-assets-info.py
import pandas as pd
import okx.Funding as Funding
Настройки вашего аккаунта:
API_KEY = '8gb2d2f2-274b-455d-967e-9fdcb490f46c'
SECRET_KEY = 'D27A0AA2C55BC18B15055580921E56209'
PASSPHRASE = 'secret_phrase'
Подключение:
fundingAPI = Funding.FundingAPI(API_KEY, SECRET_KEY, PASSPHRASE, False, '0')

Получение информации об ассетах.

Документация https://www.okx.com/docs-v5/en/?python#funding-account-rest-api-get-currencies

result = fundingAPI.get_currencies()
Запись в список со словарями название монет, названия сетей, в которые можно отправить монету, минимальную комиссию и максимальную:
list_of_dicts = []
for element in range(len(result['data'])):
    dictionary = dict()
    dictionary['asset_name'] = result['data'][element]['ccy']
    dictionary['chain_name'] = result['data'][element]['chain']
    dictionary['min_fee'] = result['data'][element]['minFee']
    dictionary['max_fee'] = result['data'][element]['maxFee']
    list_of_dicts.append(dictionary)
Запись в датафрейм:
df = pd.DataFrame.from_dict(list_of_dicts)
Экспорт в Excel:
writer_kernel = pd.ExcelWriter('assets_settings.xlsx', engine='xlsxwriter')
df.to_excel(writer_kernel, index=False)
writer_kernel.close()

Вариант 1 - отправить один вид монет в равном количестве на кошельки

Файл withdraw-from-okx-to-wallets-var-1.py
Этот вариант скрипта получает список адресов и распыляет общую сумму, указанную в TOTAL_SENDING_AMOUNT, на все кошельки. Также скрипт немного уникализирует каждую транзакцию, создавая уникальное количество монет к отправке и разное время задержки между транзакциями.

Библиотеки

import random
import time
 
import pandas as pd
 
import okx.Funding as Funding

Настройки

Настройки вашего аккаунта:
API_KEY = '8gb2d2f2-274b-455d-967e-9fdcb490f46c'
SECRET_KEY = 'D27A0AA2C55BC18B15055580921E56209'
PASSPHRASE = 'secret_phrase'
Название файла с настройками:
Excel:
SETTINGS_XLSX = 'wallet_addresses_var_1.xlsx'
или TXT:
SETTINGS_TXT = 'wallet_addresses_var_1.txt'
Тикер:
ASSET_NAME = 'USDC'
Название сети:
CHAIN_NAME = 'USDC-Arbitrum One (Bridged)'
Комиссия, использую значение minFee:
FEE = 0.1
Общее количество актива, которое будет раскидано на все кошельки:
TOTAL_SENDING_AMOUNT = 50

Дополнительные настройки

Стоит попытаться немного уникализировать тразакции, можно повлиять на отправляемую сумму и время - задержку между отправками.

Для задержки между транзакциями нужно задать минимальное время задержки и максимальное. Из диапазона этих значений будет выбираться случайное число.

Минимальная задержка (сек):
MIN_TIMESLEEP_BETWEEN_TRANSACTIONS = 240
Максимальная (сек):
MAX_TIMESLEEP_BETWEEN_TRANSACTIONS = 480
Чтобы уникализировать сумму отправки и вписаться в отправляемый бюджет, буду генерировать случайное число и отнимать его от отправляемой суммы. Это число будет отпределяться с помощью функции random.uniform(min_value, max_value), которая будет выдавать случайное число с плавающей точкой. Диапазон будет состоять из минимального значения TOTAL_SENDING_AMOUNT, деленный на MIN_VALUE_IN_RANGE_DIVIDER и максимального - TOTAL_SENDING_AMOUNT, деленный на MAX_VALUE_IN_RANGE_DIVIDER.
MIN_VALUE_IN_RANGE_DIVIDER = 10000
MAX_VALUE_IN_RANGE_DIVIDER = 100
Прежде, чем отправить крипту на кошельки, можете воспользоваться скриптом для предварительного расчета наобходимой суммы на балансе.
Файл count-withdrawal-cost-from-okx.py
Количество кошельков:
WALLETS_QUANTITY = 10

Например, я собираюсь разбросать по 10 кошелькам (WALLETS_QUANTITY) 50 USDC (TOTAL_SENDING_AMOUNT).

Отправляемое количество крипты для каждого кошелька будет определяться так:

общая сумма для всех кошельков делится на количество кошельков, вычитается комиссия, вычитается случайное число.

TOTAL_SENDING_AMOUNT / WALLETS_QUANTITY - FEE - random.uniform(TOTAL_SENDING_AMOUNT / MIN_VALUE_IN_RANGE_DIVIDER,
                                                               TOTAL_SENDING_AMOUNT / MAX_VALUE_IN_RANGE_DIVIDER)
total = []
for number in range(10):
    sent_amount = TOTAL_SENDING_AMOUNT / WALLETS_QUANTITY - FEE - random.uniform(
        TOTAL_SENDING_AMOUNT / MIN_VALUE_IN_RANGE_DIVIDER,
        TOTAL_SENDING_AMOUNT / MAX_VALUE_IN_RANGE_DIVIDER
    )
 
    print('Wallet №:', number, 'sent:', sent_amount)
 
    total.append(sent_amount)
Итоговая отправленная сумма немного меньше TOTAL_SENDING_AMOUNT:
Таким образом можно предварительно посмотреть, сколько крипты будет выведено на кошельки при определенных настройках, и сколько нужно крипты на балансе биржи, чтобы хватило на все с учетом комиссий.
Возвращаемся к настройкам для отправки крипты с Okx на кошельки.
Название файла с отчетом xlsx, который используется в функции ниже:
REPORT_NAME = 'report_table_var_1.xlsx'

Функции для создания отчета

Дополнительно напишу функцию, которая будет создавать отчет и сохраняться его в xlsx. В нем будет записан адрес кошелька, отправленная сумма, поле с кодом и расшифровкой ошибки, если транзакция будет отклонена.

excel_export записывает данные в excel. Принимает таблицу с данными (датафрейм) и название будущего отчета:

def excel_export(table, table_name):
    writer_kernel = pd.ExcelWriter(table_name, engine='xlsxwriter')
    table.to_excel(writer_kernel, index=False)
    writer_kernel.close()
open_table открывает таблицу с отчетом, если таблицы нет, создает ее с нужными полями:
def open_table():
    try:
        table = pd.read_excel(REPORT_NAME)
 
        return table.to_dict('records')
 
    except:
        table_template = pd.DataFrame(index=[0])
        excel_export(table_template, REPORT_NAME)
        created_table = pd.read_excel(REPORT_NAME)
 
        return created_table.to_dict('records')
create_report принимает открытую таблицу, адрес кошелька, название монеты, сеть, отправленную сумму, ошибку, если она возникла, затем записывает данные в соответствующие поля, экспортирует xlsx:
def create_report(report_table, address, asset_name, chain, sent_amount, error):
 
    report_dict = dict()
    report_dict['address'] = address
    report_dict['asset_name'] = asset_name
    report_dict['chain'] = chain
    report_dict['sent_amount'] = sent_amount
    report_dict['error'] = error
 
    report_table.append(report_dict)
 
    result_table = pd.DataFrame.from_dict(report_table)
 
    result_table.dropna(inplace=True)
 
    excel_export(result_table, REPORT_NAME)

Загрузка адресов кошельков

Excel:
wallet_addresses_xlsx = pd.read_excel(SETTINGS_XLSX)
Загрузилась таблица со списком адресов в поле с заголовком address:
Конвертация серии датафрейма в list:
wallet_addresses_list = wallet_addresses_xlsx['address'].to_list()
или загрузите TXT:
with open(SETTINGS_TXT, 'r') as f:
    data = f.readlines()
    wallet_addresses_list = [adress.replace('\n', '') for adress in data]

Отправка

Подключение:
fundingAPI = Funding.FundingAPI(API_KEY, SECRET_KEY, PASSPHRASE, False, '0')

Документация отправки активов с биржи https://www.okx.com/docs-v5/en/?python#funding-account-rest-api-withdrawal

  • ccy - название монеты
  • toAddr - адрес кошелька
  • amt - количество монет
  • fee - комиссия
  • dest - внутренние трансферы или ончейн отправка, нам нужно "ончейн" - параметр 4
  • chain - название сети
Что происходит в скрипте:
  • На каждой итерации цикла берется адрес кошелька из списка wallet_addresses_list.
  • В sending_amount определяется количество монет для отправки - TOTAL_SENDING_AMOUNT делится на количество кошельков в списке и вычитается случайное число.
  • В fundingAPI.withdrawal осуществляется тразакция, ответ записывается в api_response.
  • Если в ответе 'code' равно 0, отправка прошла успешно, записывается отчет в xlsx. Если 'code' не равен нулю, при отправке возникла ошибка, все данные, код ошибки и расшифровка ошибки записывается в отчет xlsx.
  • Скрипт засыпает на какое-то время, выбранное из диапазона.
  • Если в цикле возникла ошибка, в блоке except записывается отчет с ошибкой.
for address in wallet_addresses_list:
    try:    
        sending_amount = (TOTAL_SENDING_AMOUNT / len(wallet_addresses_list) - 
                          FEE -
                          random.uniform(TOTAL_SENDING_AMOUNT / MIN_VALUE_IN_RANGE_DIVIDER,
                                         TOTAL_SENDING_AMOUNT / MAX_VALUE_IN_RANGE_DIVIDER))
 
        print(f'Sending {sending_amount} {ASSET_NAME} to address {address} in chain {CHAIN_NAME}')
 
        api_response = fundingAPI.withdrawal(
            ccy=ASSET_NAME,
            toAddr=address,
            amt=sending_amount,
            fee=FEE,
            dest="4",
            chain=CHAIN_NAME
        )
 
        if api_response['code'] == '0':
            print('Sent')
            print('Creating report', '\n')
            report_table = open_table()
            create_report(report_table, address, ASSET_NAME, CHAIN_NAME, sending_amount, '-')
 
        elif api_response['code'] != '0':
            print('Didnt send')
            print('Creating report', '\n')
            error_code = api_response['code']
            error_message = api_response['msg']
            error_report_string = f'{error_code}-{error_message}'
 
            report_table = open_table()
            create_report(report_table, address, ASSET_NAME, CHAIN_NAME, sending_amount, error_report_string)
 
        time_sleep_value = random.choice(range(MIN_TIMESLEEP_BETWEEN_TRANSACTIONS, 
                                               MAX_TIMESLEEP_BETWEEN_TRANSACTIONS))
        print('Delay', time_sleep_value, '\n')
 
        time.sleep(time_sleep_value)
 
    except Exception as error:
        print('Didnt send')
        print('Creating report', '\n')
        report_table = open_table()
        create_report(report_table, '-', '-', '-', '-', error)
Если не хотите записывать отчет в таблицу, закоментируйте или удалите строки, начиная с 18 (if api_response['code'] == '0') по 32 (create_report...), а также закомментируйте 43 и 44 строки в блоке except (report_table...create_report...).

Вариант 2 - настраиваемая отправка: разные монеты в разном количестве на кошельки

В этом варианте скрипту передается файл с настройками - xlsx или txt, в котором каждому кошельку, на который нужно отправить крипту, задается своя монета, количество монет, комиссия и сеть для отправки.

Библиотеки

import random
import time
 
import pandas as pd
 
import okx.Funding as Funding

Настройки

Настройки вашего аккаунта:
API_KEY = '8gb2d2f2-274b-455d-967e-9fdcb490f46c'
SECRET_KEY = 'D27A0AA2C55BC18B15055580921E56209'
PASSPHRASE = 'secret_phrase'
Название файла с настройками:
Excel:
SETTINGS_XLSX = 'sending_settings_var_2.xlsx'
Таблица содержит поля, в которых нужно будет указать соответствующие параметры:
  • wallet_address - адрес кошелька
  • asset_name - тикер монеты
  • amount - количество, которое необходимо отправить
  • fee - комиссия
  • chain - название сети
Как правильно писать тикер, название сети и корректное значение комисси можно узнать в полученных ранее данных, выгруженных в таблицу assets_settings.
или загрузите TXT:
SETTINGS_TXT = 'sending_settings_var_2.txt'
txt содержит все параметры для кошелька, перечисленные через запятую. Настройки для разных кошельков отделены точкой запятой.

Дополнительные настройки

Для задержки между транзакциями нужно задать минимальное время задержки и максимальное. Из диапазона этих значений будет выбираться случайное число.
Минимальная задержка (сек):
MIN_TIMESLEEP_BETWEEN_TRANSACTIONS = 240
Максимальная (сек):
MAX_TIMESLEEP_BETWEEN_TRANSACTIONS = 480
Название файла с отчетом xlsx, который используется в функции ниже:
REPORT_NAME = 'report_table_var_2.xlsx'

Функции для создания отчета

Такие же функции для создания отчета, которые использовались в первом примере скрипта.

excel_export записывает данные в excel. Принивает таблицу с данными (датафрейм), название будущего отчета:

def excel_export(table, table_name):
    writer_kernel = pd.ExcelWriter(table_name, engine='xlsxwriter')
    table.to_excel(writer_kernel, index=False)
    writer_kernel.close()
open_table открывает таблицу с отчетом, если таблицы нет, создает ее с нужными полями:
def open_table():
    try:
        table = pd.read_excel(REPORT_NAME)
 
        return table.to_dict('records')
 
    except:
        table_template = pd.DataFrame(index=[0])
        excel_export(table_template, REPORT_NAME)
        created_table = pd.read_excel(REPORT_NAME)
 
        return created_table.to_dict('records')
create_report принимает открытую таблицу, адрес кошелька, название монеты, сеть, отправленную сумму, ошибку, если она возникла, затем записывает данные в соответствующие поля, экспортирует xlsx:
def create_report(report_table, address, asset_name, chain, sent_amount, error):
    report_dict = dict()
    report_dict['address'] = address
    report_dict['asset_name'] = asset_name
    report_dict['chain'] = chain
    report_dict['sent_amount'] = sent_amount
    report_dict['error'] = error
 
    report_table.append(report_dict)
 
    result_table = pd.DataFrame.from_dict(report_table)
 
    result_table.dropna(inplace=True)
 
    excel_export(result_table, REPORT_NAME)

Загрузка файлов с настройками

Excel:
assets_settings_for_sending = pd.read_excel(SETTINGS_XLSX)
Для дальнейшей работы, конвертирую датафрейм в список словарей:
list_of_assets_settings_for_sending = assets_settings_for_sending.to_dict('records')
или загрузите TXT:
Обратите внимание, что каждый параметры для транзакции отделяется друг от друга запятой, поэтому не используйте запятую в десятичных числах, сипользуйте точку. Например 0.003, а не 0,003. Если будете использовать запятую, скрипт создаст два значения - 0 и 003.
with open(SETTINGS_TXT, 'r') as file:
    lines = file.readlines()
 
list_of_assets_settings_for_sending = []
for line in lines:
    elements = line.strip().split(';')
 
    wallet_address, asset_name, amount, fee, chain_name = elements[0].split(',')
    data = {
        'wallet_address': wallet_address,
        'asset_name': asset_name,
        'amount': amount,
        'fee': fee,
        'chain_name': chain_name
    }
 
    list_of_assets_settings_for_sending.append(data)

Отправка

Подключение:
fundingAPI = Funding.FundingAPI(API_KEY, SECRET_KEY, PASSPHRASE, False, '0')
Документация отправки активов с биржи https://www.okx.com/docs-v5/en/?python#funding-account-rest-api-withdrawal
  • ccy - название монеты
  • toAddr - адрес кошелька
  • amt - количество монет
  • fee - комиссия
  • dest - внутренние трансферы или ончейн отправка, нам нужно "ончейн" - параметр 4
  • chain - название сети
Что происходит в скрипте:
  • На каждой итерации цикла берется строка из списка словарей list_of_assets_settings_for_sending, из словарей определяются параметры и записываются в соответствующие переменные: wallet_address, asset_name, amount, fee, chain.
  • В fundingAPI.withdrawal осуществляется тразакция, ответ записывается в api_response.
  • Если в ответе 'code' равно 0, отправка прошла успешно, записывается отчет в xlsx. Если 'code' не равен нулю, при отправке возникла ошибка, все данные, код ошибки и расшифровка ошибки записывается в отчет xlsx.
  • Скрипт засыпает на какое-то время, выбранное из диапазона.
  • Если в цикле возникла ошибка, в блоке except записывается отчет с ошибкой.
for row in list_of_assets_settings_for_sending:
    try:
        wallet_address, asset_name, amount, fee, chain = (row['wallet_address'], row['asset_name'], 
                                                          row['amount'], row['fee'], row['chain'])
 
        print(f'Sending {amount} {asset_name} to address {wallet_address} in chain {chain}')
 
        api_response = fundingAPI.withdrawal(
            ccy=asset_name,
            toAddr=wallet_address,
            amt=amount - fee,
            fee=fee,
            dest="4",
            chain=chain
        )
 
        if api_response['code'] == '0':
            print('Sent')
            print('Creating report', '\n')
            report_table = open_table()
            create_report(report_table, wallet_address, asset_name, chain, amount, '-')
 
        elif api_response['code'] != '0':
            print('Didnt send')
            print('Creating report', '\n')
            error_code = api_response['code']
            error_message = api_response['msg']
            error_report_string = f'{error_code}-{error_message}'
 
            report_table = open_table()
            create_report(report_table, wallet_address, asset_name, chain, amount, error_report_string)
 
        time_sleep_value = random.choice(range(MIN_TIMESLEEP_BETWEEN_TRANSACTIONS, 
                                               MAX_TIMESLEEP_BETWEEN_TRANSACTIONS))
        print('Delay', time_sleep_value, '\n')
 
        time.sleep(time_sleep_value)
 
    except Exception as error:
        print('Didnt send')
        print('Creating report', '\n')
        report_table = open_table()
        create_report(report_table, '-', '-', '-', '-', error)
Если не хотите записывать отчет в таблицу, закоментируйте или удалите строки, начиная с 17 (if api_response['code'] == '0') по 31 (create_report...), а также закомментируйте 42 и 43 строки в блоке except (report_table...create_report...).

Часть 2 - отправка крипты с кошельков на саб-аккаунты Okx

Чтобы не создавать связь между кошельками, нужно отправлять крипту на саб-аккаунты. Каждый кошелек соответствует адресу на саб-аккаунте. Так завещали старшие.

Не смог найти в документации api-метода, чтобы получить адреса для депозита на саб-акках. Поэтому придется взять адреса через интерфейс биржи.

Код взят из отличной статьи https://habr.com/ru/articles/674204/

Вариант 1 - отправка нативных монет

Файл withdraw-from-wallets-to-okx-var-1.py
В этом варианте скрипт получает файл с настройками и отправляет нативные монеты сети на адреса саб-акков okx. Настройки содержат:
  • приватный ключ кошелька
  • адрес okx
  • название монеты
  • название сети
  • количество отправляемых монет
  • url rpc сети - узнать можно тут https://chainlist.org/

Библиотеки

import random
import time
 
from web3 import Web3
 
import pandas as pd

Настройки

Excel:
SETTINGS_XLSX = 'sending_from_wallets_settings_var_1.xlsx'
TXT:
SETTINGS_TXT = 'sending_from_wallets_settings_var_1.txt'

Дополнительные настройки

Для задержки между транзакциями нужно задать минимальное время задержки и максимальное. Из диапазона этих значений будет выбираться случайное число.
Минимальная задержка (сек):
MIN_TIMESLEEP_BETWEEN_TRANSACTIONS = 240
Максимальная (сек):
MAX_TIMESLEEP_BETWEEN_TRANSACTIONS = 480
Название файла с отчетом xlsx, который используется в функции ниже:
REPORT_NAME = 'report_table_var_sending_from_wallets_1.xlsx'

Функции для создания отчета

Такие же функции для создания отчета, которые использовались в первом примере скрипта.

excel_export записывает данные в excel. Принивает таблицу с данными (датафрейм), название будущего отчета:

def excel_export(table, table_name):
    writer_kernel = pd.ExcelWriter(table_name, engine='xlsxwriter')
    table.to_excel(writer_kernel, index=False)
    writer_kernel.close()
open_table открывает таблицу с отчетом, если таблицы нет, создает ее:
def open_table():
    try:
        table = pd.read_excel(REPORT_NAME)
 
        return table.to_dict('records')
 
    except:
 
        table_template = pd.DataFrame(index=[0])
 
        excel_export(table_template, REPORT_NAME)
 
        created_table = pd.read_excel(REPORT_NAME)
 
        return created_table.to_dict('records')
create_report принимает открытую таблицу, адрес кошелька, название монеты, сеть, отправленную сумму, hash транзакции, ошибку, если она возникла, затем записывает данные в соответствующие поля, экспортирует xlsx:
def create_report(report_table, wallet_address, okx_address, asset_name, chain, sent_amount, txn_hash, error):
 
    report_dict = dict()
    report_dict['wallet_address'] = wallet_address
    report_dict['okx_address'] = okx_address
    report_dict['asset_name'] = asset_name
    report_dict['chain'] = chain
    report_dict['sent_amount'] = sent_amount
    report_dict['txn_hash'] = txn_hash
    report_dict['error'] = error
 
    report_table.append(report_dict)
 
    result_table = pd.DataFrame.from_dict(report_table)
 
    result_table.dropna(inplace=True)
 
    excel_export(result_table, REPORT_NAME)

Загрузка файлов с настройками

Excel с настройками:
settings_for_sending_from_wallets = pd.read_excel(SETTINGS_XLSX)
list_of_settings_for_sending_from_wallets = settings_for_sending_from_wallets.to_dict('records')
или используйте TXT:
Обратите внимание, что каждый параметры для транзакции отделяется друг от друга запятой, поэтому не используйте запятую в десятичных числах, сипользуйте точку. Например 0.003, а не 0,003. Если будете использовать запятую, скрипт создаст два значения - 0 и 003.
with open(SETTINGS_TXT, 'r') as file:
    lines = file.readlines()
 
list_of_assets_settings_for_sending = []
for line in lines:
    elements = line.strip().split(';')
 
    private_key, okx_sub_address, asset_name, chain_name, amount, chain_rpc = elements[0].split(',')
    data = {
        'private_key': private_key,
        'okx_sub_address': okx_sub_address,
        'asset_name': asset_name,
        'chain_name': chain_name,
        'amount': amount,
        'chain_rpc': chain_rpc
    }
 
    list_of_assets_settings_for_sending.append(data)

Отправка

Что происходит в скрипте:
  • На каждой итерации цикла из файла с настройками определяются все параметры для транзакции.
  • Выполняется подключение по url rpc, которое указано в настройках.
  • Из приватного ключа, который указан в настройках определяется публичный адрес.
  • В amount_wei конвертируется количество монет в wei, указанное в настройках в ether.
  • В gas_price определяется цена газа, в estimated_gas оценка лимита газа для отправки крипты, в fee расчитывается стоимость комиссии.
  • В nonce определяется номер транзакции.
  • В transaction_settings задаются все параметры будущей транзакции. Value рассчитывается из отправляемой суммы минус комиссия.
  • В sent_amount конвертируется из wei в ether отправленная сумма. Для отчета.
  • В signed_transaction подписывается транзакция, используется приватный ключ.
  • send_raw_transaction отправляется подписанную транзакцию, hash записывается в transaction_hash.
  • Записывается отчет.
  • В time_sleep_value определяется случайное время задержки. Скрипт засыпает.
  • Если срабатывает блок except, записывается данные транзакции и ошибка.
for row in list_of_settings_for_sending_from_wallets:
    try:
        private_key, okx_sub_address, asset_name, chain_name, amount, chain_rpc = (row['private_key'],
                                                                                  row['okx_sub_address'],
                                                                                  row['asset_name'],
                                                                                  row['chain_name'],
                                                                                  row['amount'],
                                                                                  row['chain_rpc'])
 
        web3 = Web3(Web3.HTTPProvider(chain_rpc))
 
        from_address = web3.eth.account.from_key(private_key).address
 
        print(f'Sending {amount} {asset_name} on the {chain_name} network from address {from_address}')
 
        to_address = web3.to_checksum_address(okx_sub_address)
 
        print(f'To OKX address {to_address}')
 
        amount_wei = int(web3.to_wei(amount, 'ether'))
 
        gas_price = web3.eth.gas_price
 
        estimated_gas = web3.eth.estimate_gas({
            'to': to_address,
            'value': amount_wei,
        })
 
        fee = gas_price * estimated_gas
 
        nonce = web3.eth.get_transaction_count(from_address)
 
        transaction_settings = {
          'chainId': web3.eth.chain_id,
          'from': from_address,
          'to': to_address,
          'value': amount_wei - fee,
          'nonce': nonce, 
          'gasPrice': gas_price,
          'gas': estimated_gas,
        }
 
        sent_amount = float(web3.from_wei(amount_wei - fee, 'ether'))
 
        signed_transaction = web3.eth.account.sign_transaction(transaction_settings, private_key)
 
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
 
        print('Sent')
        print('Txn hash:', transaction_hash.hex())
 
        print('Creating report')
        report_table = open_table()
        create_report(report_table, from_address, to_address, 
                      asset_name, chain_name, sent_amount, transaction_hash.hex(), '-')
 
        time_sleep_value = random.choice(range(MIN_TIMESLEEP_BETWEEN_TRANSACTIONS, 
                                               MAX_TIMESLEEP_BETWEEN_TRANSACTIONS))
        print('Delay', time_sleep_value, '\n')
 
        time.sleep(time_sleep_value)
 
    except Exception as error:
        print('Didnt send')
        print('Creating report')
        report_table = open_table()
        create_report(report_table, from_address, to_address, 
                      asset_name, chain_name, '-', '-', error)

Вариант 2 - отправка токенов, например стейблов

Файл withdraw-from-wallets-to-okx-var-2.py
В этом варианте скрипт получает файл с настройками и отправляет ERC20 токены на адреса саб-акков okx. Настройки содержат:
  • приватный ключ кошелька
  • адрес okx
  • название монеты
  • название сети
  • адрес смарт-контракта
  • количество отправляемых монет
  • url rpc сети - взять можно тут https://chainlist.org/

Библиотеки

import random
import json
import time
 
from web3 import Web3
import pandas as pd

Настройки

Название json-файла, который содержит универсальный ABI для ERC20 токенов:
JSON_NAME = 'ERC20_ABI.json'
Название таблицы Excel с настройками:
SETTINGS_XLSX = 'sending_from_wallets_settings_var_2.xlsx'
txt аналог:
SETTINGS_TXT = 'sending_from_wallets_settings_var_2.txt'

Дополнительные настройки

Для задержки между транзакциями нужно задать минимальное время задержки и максимальное. Из диапазона этих значений будет выбираться случайное число.
Минимальная задержка (сек):
MIN_TIMESLEEP_BETWEEN_TRANSACTIONS = 240
Максимальная (сек):
MAX_TIMESLEEP_BETWEEN_TRANSACTIONS = 480
Название файла с отчетом xlsx, который используется в функции ниже:
REPORT_NAME = 'report_table_var_sending_from_wallets_2.xlsx'

Функции для создания отчета

Такие же функции для создания отчета, которые использовались в первом примере скрипта.

excel_export записывает данные в excel. Принимает таблицу с данными (датафрейм), название будущего отчета:

def excel_export(table, table_name):
    writer_kernel = pd.ExcelWriter(table_name, engine='xlsxwriter')
    table.to_excel(writer_kernel, index=False)
    writer_kernel.close()
open_table открывает таблицу с отчетом, если таблицы нет, создает ее с нужными полями:
def open_table():
    try:
        table = pd.read_excel(REPORT_NAME)
 
        return table.to_dict('records')
 
    except:
 
        table_template = pd.DataFrame(index=[0])
 
        excel_export(table_template, REPORT_NAME)
 
        created_table = pd.read_excel(REPORT_NAME)
 
        return created_table.to_dict('records')
create_report принимает открытую таблицу, адрес кошелька, название монеты, сеть, отправленную сумму, hash транзакции, ошибку, если она возникла, затем записывает данные в соответствующие поля, экспортирует xlsx:
def create_report(report_table, wallet_address, okx_address, asset_name, chain, sent_amount, txn_hash, error):
 
    report_dict = dict()
    report_dict['wallet_address'] = wallet_address
    report_dict['okx_address'] = okx_address
    report_dict['asset_name'] = asset_name
    report_dict['chain'] = chain
    report_dict['sent_amount'] = sent_amount
    report_dict['txn_hash'] = txn_hash
    report_dict['error'] = error
 
    report_table.append(report_dict)
 
    result_table = pd.DataFrame.from_dict(report_table)
 
    result_table.dropna(inplace=True)
 
    excel_export(result_table, REPORT_NAME)

Загрузка файлов с настройками

Excel с настройками:
settings_for_sending_from_wallets = pd.read_excel(SETTINGS_XLSX)
list_of_settings_for_sending_from_wallets = settings_for_sending_from_wallets.to_dict('records')
или используйте TXT:
Обратите внимание, что каждый параметры для транзакции отделяется друг от друга запятой, поэтому не используйте запятую в десятичных числах, сипользуйте точку. Например 0.003, а не 0,003. Если будете использовать запятую, скрипт создаст два значения - 0 и 003.
with open(SETTINGS_TXT, 'r') as file:
    lines = file.readlines()
 
list_of_assets_settings_for_sending = []
for line in lines:
    elements = line.strip().split(';')
 
    private_key, okx_sub_address, asset_name, chain_name, token_contract, amount, chain_rpc = elements[0].split(',')
    data = {
        'private_key': private_key,
        'okx_sub_address': okx_sub_address,
        'asset_name': asset_name,
        'chain_name': chain_name,
        'token_contract': token_contract,
        'amount': amount,
        'chain_rpc': chain_rpc
    }
 
    list_of_assets_settings_for_sending.append(data)
Загрузка JSON:
with open(JSON_NAME, 'r') as file:
    abi_json = json.load(file)

Отправка

Что происходит в скрипте:
  • На каждой итерации цикла из файла с настройками определяются все параметры для транзакции.
  • Выполняется подключение по url rpc, которое указано в настройках.
  • Из приватного ключа, который указан в настройках определяется публичный адрес.
  • В token_contract происходит инициализация указанного в настройках контракта.
  • В token_decimals определяется количество знаков для токена - формат, в котором он хранится в какой-либо сети.
  • В sending_amount записывается количество токенов для отправки в соответствующем формате.
  • В transaction_settings задаются все параметры будущей транзакции.
  • В transaction создается транзакция.
  • В signed_transaction подписывается транзакция, используется приватный ключ.
  • send_raw_transaction отправляется подписанную транзакцию, hash записывается в transaction_hash.
  • В sent_amount отправленная сумма конвертируется из wei в ether для отчета.
  • Записывается отчет.
  • В time_sleep_value определяется случайное время задержки. Скрипт засыпает.
  • Если срабатывает блок except, записывается данные транзакции и ошибка.
for row in list_of_settings_for_sending_from_wallets:
    try:
        private_key, okx_sub_address, asset_name, chain_name, contract, amount, chain_rpc = (row['private_key'],
                                                                                            row['okx_sub_address'],
                                                                                            row['asset_name'],
                                                                                            row['chain_name'],
                                                                                            row['token_contract'],
                                                                                            row['amount'],
                                                                                            row['chain_rpc'])
        web3 = Web3(Web3.HTTPProvider(chain_rpc))
 
        from_address = web3.eth.account.from_key(private_key).address
 
        print(f'Sending {amount} {asset_name} on the {chain_name} network from address {from_address}')
 
        to_address = web3.to_checksum_address(okx_sub_address)
 
        print(f'To OKX address {to_address}')
 
        token_contract = web3.eth.contract(web3.to_checksum_address(contract), abi=abi_json)
 
        token_decimals = token_contract.functions.decimals().call()
 
        sending_amount = int(amount * 10**token_decimals)
 
        transaction_settings = {
            'chainId': web3.eth.chain_id,
            'from': from_address,
            'gasPrice': web3.eth.gas_price,
            'nonce': web3.eth.get_transaction_count(from_address)
        }
 
        transaction = token_contract.functions.transfer(to_address,
                                                         sending_amount).build_transaction(transaction_settings)
 
        signed_transaction = web3.eth.account.sign_transaction(transaction, private_key)
 
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
 
        sent_amount = float(web3.from_wei(sending_amount, 'ether'))
 
        print('Sent')
        print('Txn hash:', transaction_hash.hex())
 
        print('Creating report')
        report_table = open_table()
        create_report(report_table, from_address, to_address, 
                      asset_name, chain_name, sent_amount, transaction_hash.hex(), '-')
 
        time_sleep_value = random.choice(range(MIN_TIMESLEEP_BETWEEN_TRANSACTIONS, 
                                               MAX_TIMESLEEP_BETWEEN_TRANSACTIONS))
        print('Delay', time_sleep_value, '\n')
 
        time.sleep(time_sleep_value)
 
    except Exception as error:
        print('Didnt send')
        print('Creating report', '\n')
        print(error, '\n')
        report_table = open_table()
        create_report(report_table, from_address, to_address, 
                      asset_name, chain_name, '-', '-', error)
# Теги

Пишу статьи с Python кодом для автоматизации крипты. Телеграм - @rukidablkliki

Последние статьи по теме

© Crypto-Py.com