Esempio n. 1
0
    def __init__(self, api_url, pub_key, set_off_tx, missed_blocks=4, sleep_time_ms=1000):
        """
        Args:
            api_url (str): URL for Minter API
            pub_key (str): Pub key of validator under control
            set_off_tx (str): Signed tx, which will be sent to chain
            missed_blocks (int): Amount of missed blocks, when validator
                                 should be offed
            sleep_time_ms (int): Amount of milliseconds between guard eviction
        """
        super().__init__()

        # Set attributes
        self.minterapi = MinterAPI(api_url=api_url)
        self.pub_key = pub_key
        self.set_off_tx = set_off_tx
        self.missed_blocks = int(missed_blocks)
        self.sleep_time_ms = int(sleep_time_ms)

        # Check set off tx to be valid
        tx = MinterTx.from_raw(self.set_off_tx)
        if not isinstance(tx, MinterSetCandidateOffTx):
            raise Exception('Set off tx is not instance of MinterSetCandidateOffTx')

        nonce = self.minterapi.get_nonce(tx.from_mx)
        if tx.nonce != nonce:
            raise Exception('Set off tx has {} nonce, expected {}'.format(
                tx.nonce,
                nonce
            ))
Esempio n. 2
0
 def __init__(self, token=None, min_delegated=0, stop_list=None, node=None):
     self.token = token
     self.min_delegated = min_delegated
     self.stop_list = stop_list or []
     self.API = MinterAPI(node['url'],
                          headers=node['headers'],
                          **node['timeouts']) if node else default_API
Esempio n. 3
0
 def __init__(self, seed, pk=None, node=None):
     self.seed = seed
     self.private_key = pk or MinterWallet.create(
         mnemonic=seed)['private_key']
     self.address = MinterWallet.create(mnemonic=seed)['address']
     self.node = node
     self.API = MinterAPI(node['url'],
                          headers=node['headers'],
                          **node['timeouts']) if node else default_API
Esempio n. 4
0
    def __init__(self,
                 api_urls,
                 pub_key,
                 set_off_tx,
                 missed_blocks=4,
                 sleep_time_ms=1000):
        """
        Args:
            api_urls (list): Minter API URLs
            pub_key (str): Pub key of validator under control
            set_off_tx (str): Signed tx, which will be sent to chain
            missed_blocks (int): Amount of missed blocks, when validator
                                 should be offed
            sleep_time_ms (int): Amount of milliseconds between guard eviction
        """
        super().__init__()

        # Set attributes
        self.minterapis = [MinterAPI(api_url) for api_url in api_urls]
        self.pub_key = pub_key
        self.set_off_tx = set_off_tx
        self.missed_blocks = int(missed_blocks)
        self.sleep_time_ms = int(sleep_time_ms)

        # Check set off tx to be valid
        tx = MinterTx.from_raw(self.set_off_tx)
        if not isinstance(tx, MinterSetCandidateOffTx):
            raise Exception(
                'Set off tx is not instance of MinterSetCandidateOffTx')

        # Get nonce from API
        nonce = None
        for minterapi in self.minterapis:
            try:
                nonce = minterapi.get_nonce(tx.from_mx)
                break
            except Exception as e:
                logger.error('{}: {}'.format(e.__class__.__name__,
                                             e.__str__()))

        if tx.nonce != nonce:
            raise Exception('Set off tx has {} nonce, expected {}'.format(
                tx.nonce, nonce))
Esempio n. 5
0
import settings, wallet, val

import mintersdk
from mintersdk.minterapi import MinterAPI
from mintersdk.sdk.wallet import MinterWallet
from mintersdk.sdk.transactions import MinterTx, MinterSendCoinTx, MinterMultiSendCoinTx

print('Connecting Node...')
API = MinterAPI(settings.NODE_API_URL, **settings.TIMEOUTS)

print('Making wallet data...')
wallet_from = wallet.wallet_from

# Список для мультисенда (list[dict{coin, to, value}])
print('Generating multisend list...')
w_dict = val.multisend_list

def send(wallet_from, wallet_to, coin, value, gas_coin='BIP', payload=''):
    nonce = API.get_nonce(wallet_from['address'])
    send_tx = MinterSendCoinTx(coin, wallet_to, value, nonce=nonce, gas_coin=gas_coin, payload=payload)
    send_tx.sign(wallet_from['private_key'])
    r = API.send_transaction(send_tx.signed_tx)
    print(f'Send TX response:\n{r}')
    return send_tx

# Генерация и отправка Multisend транзакции
def multisend(wallet_from, w_dict, gas_coin='BIP', payload=''):
    nonce = API.get_nonce(wallet_from['address'])
    tx = MinterMultiSendCoinTx(w_dict, nonce=nonce, gas_coin=gas_coin, payload=payload)
    tx.sign(wallet_from['private_key'])
    r = API.send_transaction(tx.signed_tx)
Esempio n. 6
0
import time
import telebot
from telebot import types
import re
from mintersdk.minterapi import MinterAPI
from telebot.types import InlineKeyboardButton
import json
import requests

token = 'ваш токен'
bot = telebot.TeleBot(token)

digits_pattern = re.compile(r'^[0-9]+ [0-9]+$', re.MULTILINE)

minter = MinterAPI(api_url="http://api-02.minter.store:8841")

canal = "@kriptoanarhia"

coin_check = "BLACKBIP"


@bot.message_handler(commands=['start'])
def find_file_ids(message):
    keyboard = types.InlineKeyboardMarkup(row_width=5)
    keyboard.add(InlineKeyboardButton(text="Обновить", callback_data="reset"))
    coin = minter.get_coin_info(coin_check)
    cost = int(
        json.loads(
            requests.get(
                "http://api-02.minter.store:8841" +
                "/estimate_coin_buy?coin_to_sell=BIP&value_to_buy=100000000&coin_to_buy="
Esempio n. 7
0
class Guard(object):
    """
    Guard class
    """

    def __init__(self, api_url, pub_key, set_off_tx, missed_blocks=4, sleep_time_ms=1000):
        """
        Args:
            api_url (str): URL for Minter API
            pub_key (str): Pub key of validator under control
            set_off_tx (str): Signed tx, which will be sent to chain
            missed_blocks (int): Amount of missed blocks, when validator
                                 should be offed
            sleep_time_ms (int): Amount of milliseconds between guard eviction
        """
        super().__init__()

        # Set attributes
        self.minterapi = MinterAPI(api_url=api_url)
        self.pub_key = pub_key
        self.set_off_tx = set_off_tx
        self.missed_blocks = int(missed_blocks)
        self.sleep_time_ms = int(sleep_time_ms)

        # Check set off tx to be valid
        tx = MinterTx.from_raw(self.set_off_tx)
        if not isinstance(tx, MinterSetCandidateOffTx):
            raise Exception('Set off tx is not instance of MinterSetCandidateOffTx')

        nonce = self.minterapi.get_nonce(tx.from_mx)
        if tx.nonce != nonce:
            raise Exception('Set off tx has {} nonce, expected {}'.format(
                tx.nonce,
                nonce
            ))

    def track(self):
        """
        Tracking method
        """

        while True:
            try:
                # Get missed blocks
                response = self.minterapi.get_missed_blocks(
                    public_key=self.pub_key
                )

                # Raise exception on non 404 error (Validator not found)
                if response.get('error'):
                    if response['error']['code'] != 404:
                        raise Exception(response['error'])
                    else:
                        logger.debug("Going for a sleep for {}ms.".format(self.sleep_time_ms))
                        time.sleep(self.sleep_time_ms/1000)
                        continue

                # If response is ok, get missed blocks count
                mb = int(response['result']['missed_blocks_count'])
                logger.debug("Missed block count: {}".format(mb))

                # If missed blocks is greater than limit, set candidate off
                if mb >= self.missed_blocks:
                    # Send set candidate off transaction
                    response = self.minterapi.send_transaction(
                        tx=self.set_off_tx
                    )

                    if response.get('error'):
                        raise Exception(response['error'])

                    # Write log info message abount setting candidate off
                    logger.warning('Set candidate off. Blocks missed: {}'.format(mb))
            except Exception as e:
                logger.error('{}: {}'.format(
                    e.__class__.__name__,
                    e.__str__()
                ))

            # Wait specific time between each loop
            logger.debug("Going for a sleep for {}ms.".format(self.sleep_time_ms))
            time.sleep(self.sleep_time_ms/1000)
Esempio n. 8
0
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from mintersdk.minterapi import MinterAPI
from mintersdk.sdk import wallet
from mintersdk.sdk.transactions import MinterSendCoinTx
from sql import sql

import const
import sqlhelper
from langs import en, ru

minter_wallet = mintersdk.sdk.wallet.MinterWallet
minter = MinterAPI(api_url=const.node_url)
bot = telebot.TeleBot(const.token)

sql_base = sqlhelper.sqlbase(const.path)


def get_address(mnemonic):
    create = minter_wallet.create(mnemonic=mnemonic)
    private_key = create['address']

    return private_key


def get_token():
    PARAMS = {'grant_type': 'client_credentials'}
    HEADERS = {
Esempio n. 9
0
        print('PUB_KEY is not set in config file')
        sys.exit(1)

    # Second arg is always action with node
    action = sys.argv[2]
    if action not in ['on', 'off']:
        print('Specify correct tx action (on/off)')
        sys.exit(1)

    # Get params from user
    seed = getpass.getpass('Provide seed phrase (password like input): ')

    # When all data seems to be set, create txs
    try:
        # Get API and wallet
        minterapi = MinterAPI(api_url)
        wallet = MinterWallet.create(mnemonic=seed)

        if action == 'on':
            # Set candidate on tx
            tx = MinterSetCandidateOnTx(
                pub_key=pub_key,
                nonce=minterapi.get_nonce(address=wallet['address']),
                gas_coin='BIP')
            tx.sign(wallet['private_key'])
            tx_str = 'Set candidate ON tx: {}'.format(tx.signed_tx)
        elif action == 'off':
            # Set candidate off tx
            tx = MinterSetCandidateOffTx(
                pub_key=pub_key,
                nonce=minterapi.get_nonce(address=wallet['address']),
Esempio n. 10
0
import psycopg2
from mintersdk.sdk.wallet import MinterWallet
import qrcode
import os.path
from mintersdk.minterapi import MinterAPI
from mintersdk.sdk.transactions import MinterSendCoinTx
from mintersdk.sdk.deeplink import MinterDeeplink
import requests
from minterbiz.sdk import Wallet
import config
conn = psycopg2.connect(dbname='minter_like',
                        user=config.db_user,
                        password=config.db_password,
                        host='localhost')
cursor = conn.cursor()
api = MinterAPI(api_url="http://api.mtools.network")


def repack(chat_id):
    likes = get_balance(chat_id)
    if likes > 0:
        mnemonic = get_mnemo(chat_id)
        data = {'seed': mnemonic, 'name': 'User'}
        print(mnemonic)
        print(data)
        a = requests.post('https://push.minter-scoring.space/api/new',
                          data=data).json()
        print(a["address"])
        wallet = Wallet(seed=mnemonic)
        wallet.send(to=a["address"],
                    value=likes,
Esempio n. 11
0
            'wallet': 'Mx5cef09065d68561ad9f61a905c7d0aa230117733',
            'percent': 0.85
        },
        'isheldon': {
            'wallet': 'Mxd315211a50c65e036c14117f6949f2ac91bb6170',
            'percent': 0.15
        }
    },
    'taxes': {
        'wallet': 'Mx50d9e92706ce51341c5f8f0c57afe1950a3ea922',
        'percent': 0.1
    }
}


api = MinterAPI(NODE_API_URL)
balances = human_balances(api, wallet)

# Конвертируем кастомки в BIP
convert_all_wallet_coins_to('BIP', api, wallet, private_key, balances)

# Всего BIP на кошельке
bip_total = balances['BIP'] - min_bip
print(f"Total BIP = {bip_total}")

# Считаем кому сколько
after_taxes = write_taxes_and_return_remains(bip_total, payouts)  # Налоги
delegators_to_be_payed = after_taxes * payouts['delegators']['percent']  # Делегаторам
founders_to_be_payed = after_taxes - delegators_to_be_payed  # Основателям
write_founders_values(founders_to_be_payed, payouts)
Esempio n. 12
0
            if len(msg_tokens) < 2 or msg_tokens[1] != auth_key:
                bot.send_message(chat_id=chat_id, text="Auth failed")
                return

            chats.append((bot, chat_id))
            with open(chats_file, 'wb') as handle:
                pickle.dump(chats, handle, protocol=pickle.HIGHEST_PROTOCOL)

            bot.send_message(chat_id=chat_id, text="Successful auth")

        start_handler = CommandHandler('start', start)
        dispatcher.add_handler(start_handler)

        updater.start_polling()

        minterapi = MinterAPI(config['minter_api_url'])
        pub_keys = config['minter_nodes_pub_keys']

        missed_blocks_threshold_to_notify = config[
            'missed_blocks_threshold_to_notify']

        nodes = {
            pub_key: {
                'status': 2,
                'missed_blocks': 0
            }
            for pub_key in pub_keys
        }
        while True:
            try:
                for pub_key in pub_keys:
Esempio n. 13
0
import mintersdk
from mintersdk.sdk.transactions import MinterSendCoinTx  # Импортируем библиотеку для генерации транзакций
from mintersdk.minterapi import MinterAPI
from mintersdk.sdk.wallet import MinterWallet

minter = MinterAPI(api_url='https://api.minter.one')  # API ноды

minter_wallet = mintersdk.sdk.wallet.MinterWallet

create = minter_wallet.create(
    mnemonic=
    'flat regular cook pull hand swift dentist taxi miss used elevator treat')

private_key = ['private_key']

# print(private_key)

# nonce = minter.get_nonce(address='Mxfcbdfb79aead11c003c93cfe88ae2e57fd6c0288') # Адрес откуда отправляется монета
# tx = MinterSendCoinTx(coin='BIP', to='Mxe4b80d500ec39f136833bca4705e8041c24fe417', value=float(1), nonce=nonce, gas_coin='BIP')
#
# tx.sign(private_key='a5045c1cc7569cd01113acc9d41743d1c991569922978d77be196f6963bae4eb')
#
# send = minter.send_transaction(tx=tx.signed_tx) # Отправка транзакции
#
# hash = 'Mt'+send['result']['hash'].lower()

# balance = minter.get_balance(address='Mxe4b80d500ec39f136833bca4705e8041c24fe417')
# print(balance)

# list_trx = minter.get_transactions("tx.from='Mxe4b80d500ec39f136833bca4705e8041c24fe417' "
# #                                    "AND tx.type='01' AND tx.height=4650569")
Esempio n. 14
0
class Delegators:
    def __init__(self, token=None, min_delegated=0, stop_list=None, node=None):
        self.token = token
        self.min_delegated = min_delegated
        self.stop_list = stop_list or []
        self.API = MinterAPI(node['url'],
                             headers=node['headers'],
                             **node['timeouts']) if node else default_API

    def get_delegations(self, by_node='', min_delegated=0, stop_list=None):
        """
        Получаем всех делегаторов монеты self.token
        :param stop_list: list ['Mx...1', 'Mx...2', ...]
        :param by_node: str 'Mp....'
        :param min_delegated: float/int Минимальное количество делегированных токенов
        :return: dict {address: delegated_tokens, ...}
        """

        stop_list = stop_list or [] or self.stop_list
        min_delegated = min_delegated or self.min_delegated

        # Получаем стейки
        stakes = []

        # По отдельной ноде
        if by_node:
            stakes = self.API.get_candidate(by_node)['result']['stakes']
        # По всем нодам
        else:
            validators = self.API.get_validators(limit=256)['result']
            pub_keys = [v['pub_key'] for v in validators]
            print(f"Получаем стейки валидаторов")
            for i, pub_key in enumerate(pub_keys, 1):
                print(f"{i} / {len(validators)}")
                stakes += self.API.get_candidate(pub_key)['result']['stakes']

        # Получаем словарь со всеми делегаторами и суммарное количество заделегированных токенов
        delegators = {}
        for stake in stakes:
            if (stake['coin'] == self.token
                    or self.token is None) and stake['owner'] not in stop_list:
                if stake['owner'] not in delegators.keys():
                    delegators[stake['owner']] = to_bip(stake['value'])
                else:
                    delegators[stake['owner']] += to_bip(stake['value'])

        # Фильтруем делегаторов по минимальной сумме
        if min_delegated > 0:
            delegators = {
                k: v
                for k, v in delegators.items() if v >= min_delegated
            }

        return delegators

    def get_payouts(self,
                    bip_total,
                    by_node="",
                    min_delegated=0,
                    stop_list=None):
        """
        Создает словарь адрес - выплата
        :param stop_list: list ['Mx...1', 'Mx...2', ...]
        :param bip_total: float/int
        :param by_node: str 'Mp....'
        :param min_delegated: float/int Минимальное количество делегированных токенов
        :return: dict {address: bip_value, ...}
        """
        stop_list = stop_list or [] or self.stop_list
        min_delegated = min_delegated or self.min_delegated

        delegators = self.get_delegations(by_node=by_node,
                                          min_delegated=min_delegated,
                                          stop_list=stop_list)

        # Получаем сумму выплаты в BIP для каждого делегатора
        tokens_sum = Decimal(str(sum(delegators.values())))
        for key in delegators:
            delegators[key] = round(
                Decimal(str(bip_total)) * Decimal(str(delegators[key])) /
                Decimal(str(tokens_sum)), 18)

        return delegators
Esempio n. 15
0
from mintersdk.minterapi import MinterAPI

default_node = {
    'url': 'https://mnt.funfasy.dev/',
    'timeouts': {
        'read_timeout': 6,
        'connect_timeout': 7
    },
    'headers': {}
}

default_API = MinterAPI(default_node['url'],
                        headers=default_node['headers'],
                        **default_node['timeouts'])
Esempio n. 16
0
import requests
from mintersdk import MinterConvertor
from mintersdk.sdk.wallet import MinterWallet
from retry import retry
import base64

from mintersdk.minterapi import MinterAPI
from mintersdk.sdk.transactions import MinterSendCoinTx

from models import Blocks, Wallet, Users
from config import BOT_WALLET, OWNER_WALLET, PRIVATE_KEY, EXPLORER_API_URL

# new, should be fast, but bugged
# api = MinterAPI('https://minter-node-1.testnet.minter.network:8841')

api = MinterAPI('https://api.minter.stakeholder.space')


@retry(tries=3, delay=2)
def check_balance(user_or_wallet):
    w = Wallet.get(user=user_or_wallet) if isinstance(user_or_wallet, Users) \
        else user_or_wallet if isinstance(user_or_wallet, Wallet) \
        else Wallet.get(address=user_or_wallet)
    if not w:
        return
    r = api.get_balance(w.address)
    balance = float(
        MinterConvertor.convert_value(r['result']['balance']['BIP'], 'bip'))
    w.balance = balance
    w.updated_dt = datetime.utcnow()
    w.save()
Esempio n. 17
0
        print('PUB_KEY is not set in config file')
        sys.exit(1)

    # Second arg is always action with node
    action = sys.argv[2]
    if action not in ['on', 'off']:
        print('Specify correct tx action (on/off)')
        sys.exit(1)

    # Get params from user
    seed = getpass.getpass('Provide seed phrase (password like input): ')

    # When all data seems to be set, create txs
    try:
        # Get APIs
        minterapis = [MinterAPI(api_url) for api_url in api_urls]

        # Get wallet
        wallet = MinterWallet.create(mnemonic=seed)

        # Get nonce from API
        nonce = None
        for minterapi in minterapis:
            try:
                nonce = minterapi.get_nonce(address=wallet['address'])
                break
            except Exception as e:
                print(e.__str__())

        if nonce is None:
            raise
Esempio n. 18
0
import psycopg2
from mintersdk.sdk.wallet import MinterWallet
import qrcode
import os.path
from mintersdk.minterapi import MinterAPI
from mintersdk.sdk.transactions import MinterSendCoinTx
from mintersdk.sdk.deeplink import MinterDeeplink
import requests
from minterbiz.sdk import Wallet
import config
conn = psycopg2.connect(dbname='minter_like', user=config.db_user, 
                        password=config.db_password, host='localhost')
cursor = conn.cursor()
api = MinterAPI(api_url="http://api.minter.one")

def repack(chat_id):
    likes = get_balance(chat_id)
    if likes > 0:
        mnemonic = get_mnemo(chat_id)
        data = {
          'seed': mnemonic,
          'name': 'User'
        }
        print(mnemonic)
        print(data)
        a = requests.post('https://push.minter-scoring.space/api/new', data=data).json()
        print(a["address"])
        wallet = Wallet(seed=mnemonic)
        wallet.send(to=a["address"],value=likes, coin="LIKE", payload='', include_commission=True)
        if "link" in a:
            return a["link"]
Esempio n. 19
0
from pycoingecko import CoinGeckoAPI
import db
from mintersdk.minterapi import MinterAPI
import time
import requests
import json
cg = CoinGeckoAPI()
api = MinterAPI(api_url="http://api.minter.one")
caches = dict(messages={},push={},tap_mn={},balance={},pricebip=[float(cg.get_price(ids='bip', vs_currencies='usd')["bip"]["usd"]),time.time()],pricelike=[float(api.estimate_coin_buy("BIP", 1, "LIKE", pip2bip=True)["result"]["will_pay"]),time.time()])

def get_tap_minter_push(message):
    if not message.chat.id in caches["push"]:
        mnemo = db.get_mnemo(message.chat.id)
        data = json.dumps(dict(seed=mnemo,coin="LIKE"))
        response = requests.post('https://api.minterpush.com/create', headers={'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaW50ZXJwdXNoLmNvbSIsImF1ZCI6Im1pbnRlcnB1c2guY29tIiwicm9sIjoiYXBpIiwic3ViIjoiUlJQNzRtaDhpb1pDbWh3eWVQMUtUTEJHIiwiZXhwIjoxNTkzMDI5NDcwfQ.poTy3D1fZDcYpLPlRe1pVK6xCiCjbqHL3CNZbpjrMpUEuHDczEz0Q_p4rqQgh0Ia2HYxRQiA-Pn1RWKpJ2Ihkw','Content-Type': 'application/json'}, data=data)
        caches["push"][message.chat.id] = response.json()["data"]["url"]
    return caches["push"][message.chat.id]

def get_tap_mn_push(message):
    if not message.chat.id in caches["tap_mn"]:
        caches["tap_mn"][message.chat.id] = requests.post("https://push.minter-scoring.space/api/new",data=dict(seed=db.get_mnemo(message.chat.id))).json()["link"]
    return caches["tap_mn"][message.chat.id]

def get_price():
    if caches["pricebip"][1] < time.time() - 30 * 60:
        caches["pricebip"][0] = float(requests.get("https://api.bip.dev/api/price").json()["data"]["price"]) / 10000
    return caches["pricebip"][0]

def get_price_like():
    if caches["pricelike"][1] < time.time() - 30 * 60:
        caches["pricelike"][0] = float(api.estimate_coin_buy("BIP", 1, "LIKE", pip2bip=True)["result"]["will_pay"])
Esempio n. 20
0
class Wallet:
    def __init__(self, seed, pk=None, node=None):
        self.seed = seed
        self.private_key = pk or MinterWallet.create(
            mnemonic=seed)['private_key']
        self.address = MinterWallet.create(mnemonic=seed)['address']
        self.node = node
        self.API = MinterAPI(node['url'],
                             headers=node['headers'],
                             **node['timeouts']) if node else default_API

    # ------------------------------------------
    # ОСНОВНЫЕ ФУНКЦИИ
    # ------------------------------------------

    def get_balance(self, in_bip=True):
        """
        Получаем баланс кошелька
        """
        return self.API.get_balance(self.address,
                                    pip2bip=in_bip)['result']['balance']

    def get_bip_balance(self):
        """
        Получаем баланс кошелька в BIP
        """
        return self.API.get_balance(self.address,
                                    pip2bip=True)['result']['balance']['BIP']

    def convert(self, value, from_symbol, to_symbol, gas_coin=None):
        """
        Конвертирует одну монету в другую
        :param gas_coin: str 'SYMBOL' - монета для оплаты комиссии
        :param value: int/float
        :param from_symbol: str (Тикер монеты)
        :param to_symbol: str (Тикер монеты)
        :return:
        """

        from_symbol = from_symbol.upper()
        to_symbol = to_symbol.upper()
        value = Decimal(str(value))
        if gas_coin is None:
            gas_coin = from_symbol

        balances = self.get_balance(in_bip=True)

        if balances[from_symbol] < value:
            print(
                f"На кошельке недостаточно {from_symbol}. Нужно {value}, а есть {balances[from_symbol]}"
            )
            return

        # Генерируем транзакцию
        nonce = self.API.get_nonce(self.address)
        tx = MinterSellCoinTx(coin_to_sell=from_symbol,
                              value_to_sell=value,
                              coin_to_buy=to_symbol,
                              min_value_to_buy=0,
                              nonce=nonce,
                              gas_coin=gas_coin)

        # Проверяем достаточно ли баланса на оплату комиссии
        commission = to_bip(tx.get_fee())
        if gas_coin == from_symbol and balances[from_symbol] < (value +
                                                                commission):
            print(
                f"На кошельке недостаточно {from_symbol} для оплаты комиссии {commission}\n"
                f"Баланс: {round(balances[from_symbol], 2)}\n"
                f"Нужно:  {value + commission} (+{value + commission - round(balances[from_symbol], 2)})"
            )
            return
        elif balances[gas_coin] < commission:
            print(
                f"На кошельке недостаточно {gas_coin} для оплаты комиссии {commission}\n"
                f"Баланс: {round(balances[gas_coin], 2)}\n"
                f"Нужно:  {commission}")
            return

        # Отправляем транзакицю
        tx.sign(private_key=self.private_key)
        r = self.API.send_transaction(tx.signed_tx)

        try:
            if r['result']['code'] == 0:
                print(f'{from_symbol} сконвертирован в {to_symbol}')
                self._wait_for_nonce(
                    nonce
                )  # Ждем nonce, чтобы предотвратить отправку нескольких транзакций в блоке
        except Exception:
            print(
                f'Не удалось сконвертировать {from_symbol} в {to_symbol}\nServer response: {r}'
            )

        return r

    def convert_all_coins_to(self, symbol, gas_coin=None):
        """
        Конвертирует все монеты на кошельке в symbol
        """
        symbol = symbol.upper()
        balances = self.get_balance()
        if gas_coin is None:
            gas_coin = symbol

        if self._only_symbol(balances, symbol):
            return

        for coin in balances:
            if coin == symbol:
                continue

            nonce = self.API.get_nonce(self.address)
            tx = MinterSellAllCoinTx(coin_to_sell=coin,
                                     coin_to_buy=symbol,
                                     min_value_to_buy=0,
                                     nonce=nonce,
                                     gas_coin=gas_coin)
            tx.sign(private_key=self.private_key)
            r = self.API.send_transaction(tx.signed_tx)

            try:
                if r['result']['code'] == 0:
                    print(f'{coin} сконвертирован в {symbol}')
                    self._wait_for_nonce(
                        nonce
                    )  # Ждем nonce, чтобы предотвратить отправку нескольких транзакций в блоке
            except Exception:
                print(
                    f'Не удалось сконвертировать {coin} в {symbol}\nServer response: {r}'
                )

            return r

    def pay(self, payouts, coin="BIP", payload='', include_commission=True):
        """
        Выплата на любое количество адресов
        :param payouts: dict > {'Mp...1: 100', 'Mp...2': 50, ...} - словарь кошелек: сумма
        :param coin: str > 'SYMBOL' - Монета, в которой будет производится выплата
        :param payload: str - комментарий к транзакции
        :param include_commission: bool - Если True, то комиссия за перевод включается в сумму выплаты и выплаты будут пересчитаны с учетом комиссии
        :return: json - ответ от ноды
        """
        return self.multisend(payouts,
                              coin=coin,
                              payload=payload,
                              include_commission=include_commission)

    def pay_token_delegators(self,
                             delegated_token,
                             to_be_payed,
                             by_node='',
                             min_delegated=0,
                             stop_list=None,
                             coin='BIP',
                             payload='',
                             include_commission=True):
        """
        Выплата делегаторам конкретного токена
        :param delegated_token: str > 'SYMBOL' - делгаторы этого токена получают выплату
        :param to_be_payed: int/float - сумма, которая будет выплачена всем делегаторам
        :param by_node: str > 'Mp....' - публичный адрес валидатора . Если заполнить, то выплата будет только делегатором конкретной ноды
        :param min_delegated: int/float - столько минимум должно быть делегировано, чтобы получить выплату
        :param stop_list: list > ['Mx...1', 'Mx...2', ...] кошельки, не участвующие в выплате
        :param coin: str > 'SYMBOL' - монета, в которой будет производится выплата
        :param payload: str - комментарий к транзакции
        :param include_commission: bool - Если True, то комиссия за перевод включается в сумму выплаты и выплаты будут пересчитаны с учетом комиссии
        :return:
        """
        delegators = Delegators(delegated_token, node=self.node)
        payouts = delegators.get_payouts(to_be_payed,
                                         by_node=by_node,
                                         min_delegated=min_delegated,
                                         stop_list=stop_list)
        return self.multisend(payouts,
                              coin=coin,
                              payload=payload,
                              include_commission=include_commission)

    def pay_by_shares(self,
                      shares,
                      to_be_payed,
                      coin="BIP",
                      payload='',
                      include_commission=True):
        """
        Выплаты по пропорциям
        :param shares: dict
        :param to_be_payed: int/float сумма выплаты
        :param coin: str 'SYMBOL'
        :param payload: str
        :param include_commission: bool
        :return: node response
        """
        payouts = self._convert_shares_to_payouts(shares, to_be_payed)
        return self.multisend(payouts,
                              coin=coin,
                              payload=payload,
                              include_commission=include_commission)

    def send(self, to, value, coin="BIP", payload='', include_commission=True):
        value = Decimal(str(value))

        nonce = self.API.get_nonce(self.address)
        tx = MinterSendCoinTx(coin=coin,
                              to=to,
                              value=value,
                              nonce=nonce,
                              gas_coin=coin,
                              payload=payload)

        if include_commission:
            if coin == 'BIP':
                commission = to_bip(tx.get_fee())
            else:
                tx.sign(self.private_key)
                commission = self.API.estimate_tx_commission(
                    tx.signed_tx, pip2bip=True)['result']['commission']

            tx.value = value - commission

        # Проверяем на ошибки
        if tx.value <= 0:
            print(
                f'Ошибка: Комиссия ({to_bip(tx.get_fee())}) превышает сумму выплаты ({value})'
            )
            return
        elif tx.value > self.get_balance(in_bip=True)[coin]:
            print(f'Ошибка: На кошельке недостаточно {coin}')
            return

        tx.sign(private_key=self.private_key)

        r = self.API.send_transaction(tx.signed_tx)

        try:
            if r['result']['code'] == 0:
                print(f'{value} {coin} успешно отпрвлены на адрес {to}')
                self._wait_for_nonce(
                    nonce
                )  # Ждем nonce, чтобы предотвратить отправку нескольких транзакций в блоке
        except Exception:
            print(f'Не удалось отправить {coin}\nServer response: {r}')

        return r

    def multisend(self,
                  to_dict,
                  coin="BIP",
                  payload='',
                  include_commission=True):
        """
        Multisend на любое количество адресов

        :param to_dict: dict {address: value, ...}
        :param coin: str 'SYMBOL'
        :param payload: str 'Комментарий к транзакции'
        :param include_commission: bool Платит с учетом комиссии
        :return:
        """

        # Генерация общего списка транзакций и расчет общей суммы выплаты
        all_txs = []
        total_value = 0
        for d_address, d_value in to_dict.items():
            d_value = Decimal(str(d_value))
            all_txs.append({'coin': coin, 'to': d_address, 'value': d_value})
            total_value += d_value

        # Проверяем хватит ли баланса для совершения транзакции
        balance = self.get_balance(in_bip=True)[coin]
        if total_value > balance:
            print(
                f'Ошибка: На кошельке недостаточно {coin}. Нужно {total_value}, а у нас {balance}'
            )
            return

        # Разбивка на списки по 100 транзакций
        all_txs = self._split_txs(all_txs)

        # Генерируем шаблоны транзакций
        tx_templates = [
            MinterMultiSendCoinTx(txs, nonce=1, gas_coin=coin, payload=payload)
            for txs in all_txs
        ]

        # Считаем общую комиссию за все транзакции
        if coin == 'BIP':
            total_commission = to_bip(sum(tx.get_fee() for tx in tx_templates))
        else:
            [tx.sign(self.private_key) for tx in tx_templates]
            total_commission = sum(
                self.API.estimate_tx_commission(tx.signed_tx, pip2bip=True)
                ['result']['commission'] for tx in tx_templates)

        # Если перевод с учетом комиссии, то пересчитываем выплаты
        if include_commission:
            new_total_value = total_value - total_commission
            if new_total_value <= 0:
                print(
                    f'Ошибка: Комиссия ({total_commission}) превышает сумму выплаты ({total_value})'
                )
                return

            for tx in tx_templates:
                for tx_dict in tx.txs:
                    tx_dict['value'] = new_total_value * Decimal(
                        str(tx_dict['value'])) / Decimal(str(total_value))
        else:
            total_value -= total_commission
            if total_value <= 0:
                print(
                    f'Ошибка: Комиссия ({total_commission}) превышает сумму выплаты ({total_value})'
                )
                return

        r_out = []
        # Делаем multisend
        for tx in tx_templates:
            tx.nonce = self.API.get_nonce(self.address)
            tx.sign(self.private_key)
            r = self.API.send_transaction(tx.signed_tx)

            try:
                if r['result']['code'] == 0:
                    print(
                        f'Multisend для {len(tx.txs)} получателей успешно отправлен'
                    )
                    self._wait_for_nonce(
                        tx.nonce
                    )  # Ждем nonce, чтобы предотвратить отправку нескольких транзакций в блоке

            except Exception:
                print(f'Не удалось отправить multisend\nServer response: {r}')

            r_out.append(r)

        return r_out

    # ------------------------------------------
    # СЛУЖЕБНЫЕ ФУНКЦИИ
    # ------------------------------------------

    @staticmethod
    def _convert_shares_to_payouts(shares, to_be_payed):

        for key in shares:
            shares[key] = Decimal(str(shares[key])) * Decimal(str(to_be_payed))

        return shares

    @staticmethod
    def _split_txs(txs, length=100):
        """
        Делает несколько multisend списков по length транзакций на список
        """
        if length > 100:
            print(
                '[!] Ошибка в Wallet._split_txs: Максимум 100 адресов на 1 multisend транзакцию'
            )
            return

        txs_list = []

        while len(txs) > length:
            txs_list.append(txs[:length])
            txs = txs[length:]
        else:
            txs_list.append(txs)

        return txs_list

    def _wait_for_nonce(self, old_nonce):
        """
        Прерывается, если новый nonce != старый nonce
        """
        while True:
            nonce = self.API.get_nonce(self.address)
            if nonce != old_nonce:
                break

            time.sleep(1)

    @staticmethod
    def _only_symbol(balances, symbol):
        """
        True, если на балансе кошелька только symbol
        """
        if len(balances) > 1:
            return False
        elif symbol in balances:
            return True