File size: 13,002 Bytes
9c1300a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# 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


# 分隔文本函数
def split_and_append(ethscriptions_str, name_selected_option):
    separators = [' ', '\n', ',']
    split_texts = [ethscriptions_str]  # 初始时只有一个完整文本

    for sep in separators:
        pieces = []
        for text in split_texts:
            pieces.extend(text.split(sep))
        split_texts = pieces

    # 移除空字符串
    split_texts = [text.strip() + name_selected_option for text in split_texts if text.strip() != '']

    return split_texts


# 把文字转换成 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('我们已将网站代码完全开源。您可以访问并仔细阅读此页面的源码:[2_🆔_批量题写域名铭文.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 代表主网络,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()

    # 配置铭文文本
    ethscriptions_str = st.text_area(f'输入**多个域名铭文或其他**,可以用`空格`、`换行符`、`英文逗号(,)`分开,不要带 `data:,` 前缀,不要带`域名后缀`:')

    name_selected_option = st.radio("选择域名后缀:", ['🎨 自定义', '🆔.eths', '🆔.eth', '🌲.tree', '🦛.honk', '🔄.etch', '🌐.com'], index=1, horizontal=True)
    if name_selected_option == '🎨 自定义':
        name_selected_option = st.text_input('输入**域名后缀**:')

    # 以空格、换行符、英文逗号分隔文本并加上后缀
    ethscription_list = split_and_append(ethscriptions_str, name_selected_option)

    # 判断铭文文本里是否包含空格、换行符,而且所有的字母都必须为小写。
    if not validate_input(''.join(ethscription_list)):
        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 ethscriptions_str or not name_selected_option:  # 检查是否留空
            st.error(f'请正确谨慎地填写内容,每一项都**不应留空**。')
            st.stop()
        else:
            st.toast('看起来你输入的内容均无没有问题!', icon='🥳')

        st.toast(f'开始任务,需要题写的铭文总量为:{len(ethscription_list)}', icon='😎')

        # 对代币铭文 id 进行循环
        for name_str in ethscription_list:
            # 使用当前账户发送交易,获取当前账户的 nonce
            address, key = accounts[current_account_index]
            # 获取 gas
            gas_price = w3.eth.gas_price
            # 得到完整的铭文文本
            if not name_str.startswith('data:,'):
                input_data = f'data:,{name_str}'

            # 根据是否检查的开关进行
            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('# 阅读并打勾 ✅ 后方可使用。')