Spaces:
Sleeping
Sleeping
| # EthPen.com | |
| # 最后更新日期:2023 年 9 月 5 日 | |
| # 导入运行代码所需要的库 | |
| import streamlit as st # streamlit app | |
| from web3 import Web3 # 与以太坊交互的库 | |
| import hashlib # 用于数据哈希 | |
| import requests # 用于发送网络请求 | |
| import re # 用于正则表达式 | |
| import time # 用于时间相关 | |
| import os # 用于操作系统文件 | |
| # 许可使用开关 | |
| approved_use = False | |
| # 检查 ETH 地址是否有效 | |
| def is_valid_eth_address(address): | |
| if re.match("^0x[0-9a-fA-F]{40}$", address): | |
| return True | |
| return False | |
| # 检查 Ethereum 私钥是否有效 | |
| def is_valid_eth_private_key(private_key): | |
| if re.match("^[0-9a-fA-F]{64}$", private_key): | |
| return True | |
| return False | |
| # 验证输入的铭文文本是否含有空格和换行符,而且字母全部为小写 | |
| def validate_input(data_str): | |
| if re.search(r'[A-Z\s\n]', data_str): # 查找大写字母、空格或换行符 | |
| return False | |
| return True | |
| # 把文字转换成 16 进制 | |
| def text_to_hex(text): | |
| return ''.join(format(byte, '02x') for byte in text.encode('utf-8')) | |
| # 使用sha256算法计算哈希 | |
| def sha256(input_string): | |
| sha256 = hashlib.sha256() | |
| sha256.update(input_string.encode('utf-8')) | |
| return sha256.hexdigest() | |
| # 使用 Ethscriptions API(主网)检查某个铭文是否已题写 | |
| def check_content_exists(sha): | |
| # 定义请求的网址 | |
| endpoint = f"/ethscriptions/exists/{sha}" | |
| response = requests.get('https://mainnet-api.ethscriptions.com/api' + endpoint) | |
| # 如果返回状态码是200,说明请求成功,然后返回结果(Ture or False) | |
| if response.status_code == 200: | |
| return response.json()['result'] | |
| # 发送自己到自己 0ETH 的交易 | |
| def send_transaction(w3, account_address, private_key, chain_id, gas_price, input_data, current_nonce): | |
| # 设置交易的相关信息 | |
| tx = { | |
| 'chainId': chain_id, # 网络 ID | |
| 'gas': 25000, # 如果交易 gas 过低,可适当调高 | |
| 'gasPrice': gas_price, # gas 的价格 | |
| 'nonce': current_nonce, | |
| 'to': account_address, # 接收地址为自己 | |
| 'value': 0, # 金额为 0ETH | |
| 'data': text_to_hex(input_data), # 铭文内容 | |
| } | |
| # 用私钥签名这个交易 | |
| signed_tx = w3.eth.account.sign_transaction(tx, private_key) | |
| # 发送签名后的交易并获取交易哈希 | |
| tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) | |
| # 打印结果信息 | |
| st.toast(f'{input_data}', icon='✅') | |
| # 返回铭文还有交易哈希 | |
| return input_data, tx_hash.hex() | |
| # 网页前端显示 | |
| st.set_page_config(page_title="EthPen - 批量题写代币铭文", page_icon="🪙", layout='centered', initial_sidebar_state='auto') | |
| # 网页标题 | |
| st.subheader(r'🪙 :rainbow[EthPen - 代币铭文批量题写]', anchor=False, divider='rainbow') | |
| # 提醒 | |
| st.markdown('### 在开始使用前,请仔细阅读相关说明,并在确认无误后打上 ✅。感谢您的理解与配合。') | |
| open_source = st.checkbox('我们已将网站代码完全开源。您可以访问并仔细阅读此页面的源码:[3_🪙_ 批量题写代币铭文.py](https://huggingface.co/spaces/Ethscriptions/eths/tree/main/pages)',) | |
| ask_ai = st.checkbox('如果你对此代码存有疑虑,建议你利用如 [OpenAI - ChetGPT](https://chat.openai.com/)、[Google - Bard](https://bard.google.com/)、[Anthropic - Claude](https://claude.ai/)、[抖音 - 豆包](https://www.doubao.com/)、[百度 - 文心一言](https://yiyan.baidu.com/)、[阿里 - 通义千问](https://qianwen.aliyun.com/) 等知名 AI 平台进行问询,这可以帮助你判断代码是否含有恶意内容。',) | |
| huggingface = st.checkbox('复制我们的代码,你同样可以在 [HuggingFace](https://HuggingFace.co) 上搭建专属于你的域名铭文批量题写工具。',) | |
| site = st.checkbox('请务必确保你正在访问的是 **[EthPen.com](https://ethpen.com)** 网站。我们保证站内代码不包含窃取私钥或其他恶意行为,你可以安心使用。',) | |
| use = st.checkbox('在使用过程中,请按照提示准确地填写信息,这样可以确保程序的顺畅运行。',) | |
| wallet = st.checkbox('为了安全起见,我们建议您使用备用账号(小号)并确保账号中不存放大额资金。',) | |
| test = st.checkbox('首次使用时,我们建议您先在测试网络中操作。确认一切无误后,再切换至主网络使用。',) | |
| feedback = st.checkbox('若您在使用过程中遇到问题,请及时向我们反馈。我们明白程序可能存在不完善之处,并且我们的能力也有限。真心希望得到各位程序员大佬的指导和交流。',) | |
| if open_source and ask_ai and huggingface and site and use and wallet and test and feedback: | |
| approved_use = True | |
| else: | |
| approved_use = False | |
| if approved_use: | |
| st.markdown('## 批量题写代币铭文') | |
| # 连接的网络 ID。比如说,1 代表 Mainnet,5 代表 Goerli 测试网络,11155111 代表 Sepolia 测试网络,如果你不放心,可以先用测试网试试。 | |
| net_options = { | |
| '1': 'Mainnet', | |
| '5': 'Goerli', | |
| '11155111': 'Sepolia' | |
| } | |
| selected_option = st.selectbox( | |
| '**网络 ID**', | |
| list(net_options.keys()), | |
| format_func=lambda x: f"{x} ({net_options[x]})" | |
| ) | |
| chain_id = int(selected_option) | |
| # 这里配置 Ethereum PRC URL,如果你没有,请到 infura.io 或者 alchemy.com 申请一个免费的 API | |
| token_eth_prc_url = st.text_input( | |
| f'**Ethereum PRC 链接**:选填,你可以去 [infura.io](https://app.infura.io/) 或者 [alchemy.com](https://alchemy.com/) 免费申请一个', | |
| f'https://{net_options[str(chain_id)]}.infura.io/v3/eab7f935b9af45e1a54f7d7ed06c5206') | |
| w3 = Web3(Web3.HTTPProvider(f'{token_eth_prc_url}')) | |
| # 初始化当前账户索引为 0 | |
| current_account_index = 0 | |
| # 收集和显示所有交易的结果 | |
| transaction_results = [] | |
| # 创建账户列表 | |
| accounts = [] | |
| # 使用字典来跟踪每个地址的nonce | |
| nonces = {} | |
| # 启用多账户操作 | |
| multipl_accounts = st.toggle('启用**多账户**操作') | |
| if multipl_accounts: | |
| # 多账户的文本框 | |
| account_list = st.text_area(f'输入多个 **ETH 地址及其对应的私钥**,用英文逗号分隔(,),如下:地址,私钥') | |
| if account_list: # 如果账户列表有内容 | |
| for line in account_list.split('\n'): # 根据换行符划分账户 | |
| if ',' in line: # 检查是否包含逗号 | |
| address, key = line.split(',') # 分开地址和私钥 | |
| if is_valid_eth_address(address) and is_valid_eth_private_key(key): # 验证地址和私钥 | |
| current_nonce = w3.eth.get_transaction_count(address) # 获取地址的 nonce | |
| nonces[address] = current_nonce # 记录地址的 nonce | |
| accounts.append((address.strip(), key.strip())) # 保存地址和私钥还有 nonce | |
| else: | |
| st.error(f"地址 {address} 或私钥 {key} 无效,请检查!") | |
| st.stop() | |
| else: | |
| st.error(f"输入格式错误,请确保每行包含一个地址和一个私钥,并使用英文逗号分隔(,)。错误行:**{line}**") | |
| st.stop() | |
| else: | |
| account_address = st.text_input('填写你的 **ETH 地址**:') | |
| private_key = st.text_input('填写你的 **ETH 地址对应的私钥**:', type="password") | |
| if account_address and private_key: # 如果地址和私钥有内容 | |
| if is_valid_eth_address(account_address) and is_valid_eth_private_key(private_key): # 验证地址和私钥 | |
| current_nonce = w3.eth.get_transaction_count(account_address) # 获取地址的 nonce | |
| nonces[account_address] = current_nonce # 记录地址的 nonce | |
| accounts.append((account_address.strip(), private_key.strip())) # 保存地址和私钥还有 nonce | |
| else: | |
| st.error("地址或私钥无效,请检查!") | |
| st.stop() | |
| # 配置铭文文本 | |
| token_protocol = st.text_input('填写需要题写代币铭文协议 **Protocol(p)**:', 'erc-20') | |
| token_operation = st.text_input('填写需要题写代币铭文操作 **Operation(op)**:', 'mint') | |
| token_ticker = st.text_input('填写需要题写代币铭文简称 **Ticker(tick)**:') | |
| token_min_id = st.number_input('填写需要题写代币铭文范围的**最小 ID(id)**:', min_value=1, value=1, step=1) | |
| token_max_id = st.number_input('填写需要题写代币铭文范围的**最大 ID(id)**:', value=21000, step=1) | |
| token_amount = st.number_input('填写需要题写代币铭文数量 **Amount(amt)**:', min_value=1, value=1000, step=1) | |
| st.markdown('###### 预览代币铭文:') | |
| st.code( | |
| f'data:,{{"p":"{token_protocol}","op":"{token_operation}","tick":"{token_ticker}","id":"{token_min_id}","amt":"{token_amount}"}}', | |
| language="json", line_numbers=False) | |
| # 判断铭文文本里是否包含空格、换行符,而且所有的字母都必须为小写。 | |
| if not validate_input( | |
| f'data:,{{"p":"{token_protocol}","op":"{token_operation}","tick":"{token_ticker}","id":"{token_min_id}","amt":"{token_amount}"}}'): | |
| st.warning("**请注意**:通常代币铭文文本里不能包含空格、换行符,而且所有的字母都必须为小写。") | |
| token_check = st.toggle('题写前**检查是否被题写** `主网` `查重`') | |
| # 题写铭文之前检查该铭文有没有被题写 | |
| if token_check: | |
| token_check = True | |
| else: | |
| token_check = False | |
| sleep_3s = st.toggle('每次完成交易后暂停 3 秒') | |
| # 每次交易成功后暂停 3 秒 | |
| if sleep_3s: | |
| sleep_3s = True | |
| else: | |
| sleep_3s = False | |
| # 点击发送交易开始 | |
| if st.button(f'开始**发送交易**'): | |
| if not accounts or not token_protocol or not token_operation or not token_ticker: # 检查是否留空 | |
| st.error(f'请正确谨慎地填写内容,每一项都**不应留空**。') | |
| st.stop() | |
| else: | |
| st.toast('看起来你输入的内容均无没有问题!', icon='🥳') | |
| st.toast(f'开始任务,需要题写的铭文总量为:{token_max_id - token_min_id + 1}', icon='😎') | |
| # 对代币铭文 id 进行循环 | |
| for the_id in range(token_min_id, token_max_id + 1): | |
| # 使用当前账户发送交易,获取当前账户的 nonce | |
| address, key = accounts[current_account_index] | |
| # 得到完整的铭文文本 | |
| input_data = f'data:,{{"p":"{token_protocol}","op":"{token_operation}","tick":"{token_ticker}","id":"{the_id}","amt":"{token_amount}"}}' | |
| # 获取 gas | |
| gas_price = w3.eth.gas_price | |
| # 根据是否检查的开关进行 | |
| if token_check: | |
| # 这里是开了检查后请求 Ethscriptions API | |
| if check_content_exists(sha256(input_data)): | |
| # 返回数据为 Ture,说明该铭文已经被题写,打印信息 | |
| st.toast(f'{input_data} 已经被题写!', icon='☹️') | |
| else: | |
| # 返回数据为 False,说明该铭文还没被题写,发送交易 | |
| # 使用 current_nonce 发送交易 | |
| data, tx_hash = send_transaction(w3, address, key, chain_id, gas_price, input_data, nonces[address]) | |
| # 记录最后输出的结果 | |
| transaction_results.append(f"{address} | {data} | Transaction Hash: {tx_hash}") | |
| # 交易成功后,手动增加 nonce 值 | |
| nonces[address] += 1 | |
| else: | |
| # 这里是未开检查后直接发送交易 | |
| # 使用 current_nonce 发送交易 | |
| data, tx_hash = send_transaction(w3, address, key, chain_id, gas_price, input_data, nonces[address]) | |
| # 记录最后输出的结果 | |
| transaction_results.append(f"{address} | {data} | Transaction Hash: {tx_hash}") | |
| # 交易成功后,手动增加 nonce 值 | |
| nonces[address] += 1 | |
| # 更新当前账户索引,确保索引始终在账户列表的范围内 | |
| current_account_index = (current_account_index + 1) % len(accounts) | |
| # 暂停 3 秒 | |
| if sleep_3s: | |
| time.sleep(3) # 暂停三秒 | |
| st.toast(f'所有任务已经完成。', icon='🎉') | |
| # 庆祝动画 | |
| st.balloons() | |
| # 显示所有交易的结果 | |
| st.code('\n'.join(transaction_results)) | |
| else: | |
| st.error('# 阅读并打勾 ✅ 后方可使用。') | |