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 __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 __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 __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))
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)
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="
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)
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 = {
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']),
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,
'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)
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:
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")
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
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'])
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()
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
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"]
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"])
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