前言
首先要了解下tronlink的交易机制,进行trc20如usdt和usdc转账的时候,如果没有冻结能量将会损耗一定的trx,该系统的目的就是减少这部分损耗的trx
相关笔记 tronlink-扫块系统,有需要的可先前往了解
目标
搭建冻结能量系统,例如你有10000trx后,在自己的交易系统里可以根据需求为自己的子钱包进行冻结能量,减少trx的支出
前置条件
python
正式内容
下面会细分说明下每个def的方法用途,以及总的冻结流程如何处理
获取当次可冻结的钱包数
每完成一次流程,需要执行(解冻->质押)*n->投票,计算大概需要的能量判断该次可操作多少个,基础操作目前发现大约300带宽一次
def get_max_unfreeze_count(tronapi):
once = 300
getaccountnet = tronapi.getaccountresource()
freeNetUsed = getaccountnet['freeNetUsed'] if 'freeNetUsed' in getaccountnet else 0
freeNetLimit = getaccountnet['freeNetLimit'] if 'freeNetLimit' in getaccountnet else 0
NetUsed = getaccountnet['NetUsed'] if 'NetUsed' in getaccountnet else 0
NetLimit = getaccountnet['NetLimit'] if 'NetLimit' in getaccountnet else 0
bandwidth = NetLimit + freeNetLimit - NetUsed - freeNetUsed
# 质押->投票 固定操作两次
counts=math.floor((bandwidth-once) / (2 * once))
print("[%s]:当前带宽:%s 可操作:%s个地址" % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),bandwidth,counts))
return counts
从接口或者数据库中查询需要冻结的钱包
本项目的子钱包的表由于需要保存到密钥,因此相对放在另一处服务器保存比较完全
def get_need_freeze_array():
try:
global default_min_energy
url = "api url"
response = requests.get(url)
data = response.json()
if 'status_code' in data:
print("[%s]:请求需冻结钱包失败,返回:[%s]%s" % (
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), data['status_code'], data['message']))
return []
if 'Energy' in data:
default_min_energy=data['Energy']
if 'Data' not in data:
return []
return data['Data']
except Exception as r:
return []
如使用仿照例子则接口返回的格式如下
{"Data":[{"address":"TBbcpKgoEW56RDXDyFvCkNmtNGMnruZDF9"}],"Energy":20000}
计算子钱包归集转账一次需要冻结多少trx
归集的能量如需要20000,但需要换算出具体需要多少trx,而这个值是需要根据全网的情况换算得出,不能固定写死的
def get_min_trx(tronapi):
account = tronapi.getaccount()
account_balance = math.floor(account['balance'] / TRX) if 'balance' in account else 0
time.sleep(1)
getaccountnet = tronapi.getaccountresource()
# 换算计算出当前最低需冻结的trx
min_trx = 10 + math.ceil(
default_min_energy / (getaccountnet['TotalEnergyLimit'] / getaccountnet['TotalEnergyWeight']))
tronPowerLimit = getaccountnet['tronPowerLimit'] if 'tronPowerLimit' in getaccountnet else 0
print( "[%s]:总TRX %s,可用TRX %s,已冻结 %s,单次最低 %s" % (
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), tronPowerLimit + account_balance,
account_balance, tronPowerLimit, min_trx))
return {"min_trx": min_trx}
解冻历史的已冻结过的钱包
need_unfreeze_count 需要解冻的数量, 如目前已冻结了10个钱包,我可能只需要解冻2~3个就满足该次的需求,那么就没必要把全部的钱包都解冻了,这样可以保证在tronlink中的投票收益最大化,而且解冻过程中需要使用主钱包的带宽,用过多的带宽也是会损耗trx的,
def unfreeze_address(tronapi, need_unfreeze_count=0):
excute_unfreeze_address = False
try:
now_unfreeze_count = 0
newJson = []
self_unfreeze = tronapi.unfreeze_balance()
time.sleep(1)
if not self_unfreeze.get('response').get("Error"):
excute_unfreeze_address = True
else:
newJson.append({'address': energy_config.owner_address})
GetDelegatedResourceAccountIndex = tronapi.GetDelegatedResourceAccountIndex()
array = GetDelegatedResourceAccountIndex['toAccounts']
print( "[%s]:已冻结用户:%s 个" % (
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), len(array)))
for address in array:
GetDelegatedResource = tronapi.GetDelegatedResource(keys.to_base58check_address(address))
time.sleep(1)
expire_time = None
resource = "ENERGY"
try:
expire_time = GetDelegatedResource['delegatedResource'][0]['expire_time_for_energy']
resource="ENERGY"
except Exception as r:
expire_time = GetDelegatedResource['delegatedResource'][0]['expire_time_for_bandwidth']
resource="BANDWIDTH"
if expire_time is not None and (int(time.time() * 1000) > expire_time):
if now_unfreeze_count < need_unfreeze_count:
print("[%s]:地址:%s 已解冻" % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
keys.to_base58check_address(address),))
tronapi.unfreeze_balance(receiver=keys.to_base58check_address(address),resource=resource)
time.sleep(1)
excute_unfreeze_address = True
now_unfreeze_count = now_unfreeze_count + 1
else:
newJson.append({'address': keys.to_base58check_address(address)})
else:
print("[%s]:地址:%s 解冻 %s" % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
keys.to_base58check_address(address),
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(
GetDelegatedResource['delegatedResource'][0][
'expire_time_for_energy'] / 1000))
))
newJson.append({'address': keys.to_base58check_address(address)})
return {'newJson': newJson, 'excute_unfreeze_address': excute_unfreeze_address}
except Exception as r:
print("[%s]:解冻流程报错:%s" % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),r))
return {'newJson': [], 'excute_unfreeze_address': False}
主循环程序
流程说明:
- 获取需要新冻结的钱包列表,换算该次执行的单次能量的最小trx,主钱包可冻结的钱包数
- 循环执行冻结
- 投票获取收益
- 睡眠等到下次的执行时间
def now_time_format():
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
tronapi = Tronapi(
network=energy_config.environment,
priv_key=energy_config.owner_key,
owner=energy_config.owner_address
)
while True:
try:
default_min_energy = energy_config.default_min_energy
need_freeze_array = get_need_freeze_array()
resp = get_min_trx(tronapi)
min_trx = resp['min_trx']
time.sleep(1)
print( "[%s]:需冻结%s 个" % (now_time_format(), len(need_freeze_array)))
if len(need_freeze_array) > 0:
#是否需要投票
isNeedvote = False
max_unfreeze_count = get_max_unfreeze_count(tronapi)
if max_unfreeze_count <= 0:
print( "[%s]:当前带宽不足 " % (now_time_format()))
raise BaseException("60")
need_unfreeze_count=len(need_freeze_array) if len(need_freeze_array)<max_unfreeze_count else max_unfreeze_count
newJsonResp = unfreeze_address(
tronapi,
need_unfreeze_count=need_unfreeze_count)
newJson = newJsonResp['newJson']
excute_unfreeze_address = newJsonResp['excute_unfreeze_address']
time.sleep(1)
getaccountnet = tronapi.getaccountresource()
tronPowerLimit = getaccountnet['tronPowerLimit'] if 'tronPowerLimit' in getaccountnet else 0
time.sleep(1)
account = tronapi.getaccount()
account_balance = math.floor(account['balance'] / TRX) if 'balance' in account else 0
print( "[%s]:总TRX%s,可用TRX%s,已冻结%s" % (now_time_format(), tronPowerLimit + account_balance, account_balance,
tronPowerLimit))
address_array = [(item.get('address')) for n, item in enumerate(newJson)]
for i in range(max_unfreeze_count):
target = need_freeze_array.pop()
if target['address'] not in address_array:
freeze_resp = tronapi.freeze_balance(amount=min_trx, receiver=target['address'])
if freeze_resp.get('response').get("Error"):
print( "[%s]:地址 %s,冻结失败%s" % (now_time_format(), target['address'], freeze_resp.get('response').get("Error")))
else:
isNeedvote = True
print("[%s]:地址 %s,冻结能量%s" % (now_time_format(), target['address'], min_trx))
newJson.append({"address": target['address']})
time.sleep(5)
if len(need_freeze_array) == 0:
break
getaccountnet = tronapi.getaccountresource()
tronPowerLimit = getaccountnet['tronPowerLimit'] if 'tronPowerLimit' in getaccountnet else 0
if (tronPowerLimit > 0 and isNeedvote is True) or excute_unfreeze_address is True:
vote_witness_account_resp = tronapi.vote_witness_account(
vote_count=tronPowerLimit,
vote_address=energy_config.vote_address)
print( "[%s]:已冻结 %s,投票 %s" % (now_time_format(), tronPowerLimit, energy_config.vote_address))
print( "[%s]:执行结束" % (now_time_format()))
time.sleep(60 * 30)
else:
time.sleep(60 * 30)
except BaseException as e:
time.sleep(60 * int(str(e)))