Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- README.md +24 -12
- app.py +134 -0
- requirements.txt +3 -0
README.md
CHANGED
|
@@ -1,12 +1,24 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# BSC Address Checker + Optional BEP20 Transfer (Gradio Space)
|
| 2 |
+
|
| 3 |
+
این پروژه یک اپ ساده Gradio است که:
|
| 4 |
+
- موجودی BNB (BSC) و ETH (Ethereum) را برای یک آدرس نمایش میدهد.
|
| 5 |
+
- جستجوی ساده برای دو توکن رایج (BUSD و WETH_BEP20) انجام میدهد.
|
| 6 |
+
- **اختیاری**: انتقال BEP20 را در شبکه BSC با وارد کردن کلید خصوصی انجام میدهد.
|
| 7 |
+
|
| 8 |
+
## هشدارهای امنیتی
|
| 9 |
+
- هرگز کلید خصوصی خود را در یک Space عمومی قرار ندهید.
|
| 10 |
+
- اگر میخواهید عملیات ارسال را فعال کنید، Space را خصوصی نگه دارید یا اپ را محلی اجرا کنید.
|
| 11 |
+
- توصیه میشود از Wallet-based signing یا پلهای امن استفاده کنید.
|
| 12 |
+
|
| 13 |
+
## Deploy در Hugging Face Spaces
|
| 14 |
+
1. ساخت یک Space جدید (Gradio).
|
| 15 |
+
2. آپلود فایلهای `app.py` و `requirements.txt`.
|
| 16 |
+
3. تنظیم متغیرهای محیطی (در Settings -> Secrets):
|
| 17 |
+
- `BSC_RPC` (اختیاری; پیشفرض: https://bsc-dataseed.binance.org/)
|
| 18 |
+
- `ETH_RPC` (اختیاری)
|
| 19 |
+
4. اگر خواستی قابلیت ارسال را ایمن کنی، از Secrets برای قرار دادن `PRIVATE_KEY` استفاده کن، یا بهتر: اجازه نده کاربران کیها را وارد کنند.
|
| 20 |
+
|
| 21 |
+
## اجرا محلی
|
| 22 |
+
1. pip install -r requirements.txt
|
| 23 |
+
2. export BSC_RPC=...
|
| 24 |
+
3. python app.py
|
app.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
# Hugging Face Space (Gradio) for checking BSC/Ethereum balances and optional BEP20 transfers.
|
| 3 |
+
# WARNING: Never paste your private key into public/shared Spaces. This UI allows optional local signing
|
| 4 |
+
# but you must understand the security risks. Prefer using WalletConnect / local signing or set
|
| 5 |
+
# PRIVATE_KEY as a secret in a private Space only if you know what you're doing.
|
| 6 |
+
|
| 7 |
+
from web3 import Web3
|
| 8 |
+
import gradio as gr
|
| 9 |
+
import os
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
+
load_dotenv()
|
| 12 |
+
|
| 13 |
+
BSC_RPC = os.environ.get('BSC_RPC','https://bsc-dataseed.binance.org/')
|
| 14 |
+
ETH_RPC = os.environ.get('ETH_RPC','https://mainnet.infura.io/v3/YOUR_INFURA_KEY')
|
| 15 |
+
w3_bsc = Web3(Web3.HTTPProvider(BSC_RPC))
|
| 16 |
+
w3_eth = Web3(Web3.HTTPProvider(ETH_RPC))
|
| 17 |
+
|
| 18 |
+
COMMON_TOKENS = {
|
| 19 |
+
'BUSD': '0xe9e7cea3dedca5984780bafc599bd69add087d56',
|
| 20 |
+
'WETH_BEP20': '0x2170ed0880ac9a755fd29b2688956bd959f933f8'
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
ERC20_ABI = [
|
| 24 |
+
{"constant":True,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"},
|
| 25 |
+
{"constant":True,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},
|
| 26 |
+
{"constant":True,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},
|
| 27 |
+
{"constant":False,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"type":"function"}
|
| 28 |
+
]
|
| 29 |
+
|
| 30 |
+
def checksum(addr):
|
| 31 |
+
try:
|
| 32 |
+
return w3_bsc.toChecksumAddress(addr)
|
| 33 |
+
except Exception:
|
| 34 |
+
return None
|
| 35 |
+
|
| 36 |
+
def check_address(addr):
|
| 37 |
+
addr = addr.strip()
|
| 38 |
+
c = checksum(addr)
|
| 39 |
+
if not c:
|
| 40 |
+
return ("آدرس نامعتبر است. لطفاً آدرس را با 0x شروع وارد کنید.", "")
|
| 41 |
+
out_lines = []
|
| 42 |
+
try:
|
| 43 |
+
bnb = w3_bsc.fromWei(w3_bsc.eth.get_balance(c), 'ether')
|
| 44 |
+
out_lines.append(f"BNB (BSC) balance: {bnb} BNB")
|
| 45 |
+
except Exception as e:
|
| 46 |
+
out_lines.append(f"خطا در خواندن موجودی BSC: {e}")
|
| 47 |
+
try:
|
| 48 |
+
eth = w3_eth.fromWei(w3_eth.eth.get_balance(c), 'ether')
|
| 49 |
+
out_lines.append(f"ETH (Ethereum) balance: {eth} ETH")
|
| 50 |
+
except Exception:
|
| 51 |
+
out_lines.append("Ethereum RPC not configured or error reading.")
|
| 52 |
+
token_lines = []
|
| 53 |
+
for sym, taddr in COMMON_TOKENS.items():
|
| 54 |
+
try:
|
| 55 |
+
token = w3_bsc.eth.contract(address=w3_bsc.toChecksumAddress(taddr), abi=ERC20_ABI)
|
| 56 |
+
bal = token.functions.balanceOf(c).call()
|
| 57 |
+
dec = token.functions.decimals().call()
|
| 58 |
+
if bal > 0:
|
| 59 |
+
token_lines.append(f"{sym}: {bal / (10**dec)} ({taddr})")
|
| 60 |
+
except Exception:
|
| 61 |
+
pass
|
| 62 |
+
if token_lines:
|
| 63 |
+
out_lines.append("\nTokens on BSC found:")
|
| 64 |
+
out_lines += token_lines
|
| 65 |
+
else:
|
| 66 |
+
out_lines.append("\nNo common tokens (BUSD/WETH_BEP20) found with nonzero balance.")
|
| 67 |
+
bscscan = f"https://bscscan.com/address/{c}"
|
| 68 |
+
out_lines.append(f"\nView on BscScan: {bscscan}")
|
| 69 |
+
return ("\n".join(out_lines), bscscan)
|
| 70 |
+
|
| 71 |
+
def send_bep20(token_addr, to_addr, amount_str, private_key):
|
| 72 |
+
# Security: This function will perform a real on-chain transfer if called.
|
| 73 |
+
# Use only in a private Space or run locally. The app warns the user strongly.
|
| 74 |
+
try:
|
| 75 |
+
token_addr = w3_bsc.toChecksumAddress(token_addr)
|
| 76 |
+
to_addr = w3_bsc.toChecksumAddress(to_addr)
|
| 77 |
+
except Exception as e:
|
| 78 |
+
return f"آدرسها نامعتبر هستند: {e}"
|
| 79 |
+
try:
|
| 80 |
+
token = w3_bsc.eth.contract(address=token_addr, abi=ERC20_ABI)
|
| 81 |
+
decimals = token.functions.decimals().call()
|
| 82 |
+
amt = int(float(amount_str) * (10 ** decimals))
|
| 83 |
+
except Exception as e:
|
| 84 |
+
return f"خطا در آمادهسازی مقدار یا ABI توکن: {e}"
|
| 85 |
+
try:
|
| 86 |
+
acct = w3_bsc.eth.account.from_key(private_key)
|
| 87 |
+
except Exception as e:
|
| 88 |
+
return f"خطا در private key: {e}"
|
| 89 |
+
from_addr = acct.address
|
| 90 |
+
# Check balance
|
| 91 |
+
try:
|
| 92 |
+
bal = token.functions.balanceOf(from_addr).call()
|
| 93 |
+
if bal < amt:
|
| 94 |
+
return f"موجودی توکن کافی نیست. موجودی: {bal / (10**decimals)}"
|
| 95 |
+
except Exception as e:
|
| 96 |
+
return f"خطا در خواندن موجودی: {e}"
|
| 97 |
+
# Build tx
|
| 98 |
+
nonce = w3_bsc.eth.get_transaction_count(from_addr)
|
| 99 |
+
tx = token.functions.transfer(to_addr, amt).buildTransaction({
|
| 100 |
+
'chainId': 56,
|
| 101 |
+
'gas': 200000,
|
| 102 |
+
'gasPrice': w3_bsc.eth.gas_price,
|
| 103 |
+
'nonce': nonce
|
| 104 |
+
})
|
| 105 |
+
try:
|
| 106 |
+
signed = w3_bsc.eth.account.sign_transaction(tx, private_key=private_key)
|
| 107 |
+
tx_hash = w3_bsc.eth.send_raw_transaction(signed.rawTransaction)
|
| 108 |
+
return f"Transaction submitted: {tx_hash.hex()} — View: https://bscscan.com/tx/{tx_hash.hex()}"
|
| 109 |
+
except Exception as e:
|
| 110 |
+
return f"خطا در ارسال تراکنش: {e}"
|
| 111 |
+
|
| 112 |
+
with gr.Blocks() as demo:
|
| 113 |
+
gr.Markdown("# BSC Address Checker & (optional) BEP20 Transfer")
|
| 114 |
+
gr.Markdown("**Warnings:** Do not paste private keys into public/shared spaces. Use this tool **locally** or in a private Hugging Face Space. Prefer wallet-based signing (WalletConnect) or using environment secrets.")
|
| 115 |
+
with gr.Tab("Check Address"):
|
| 116 |
+
addr_in = gr.Textbox(label="Address (0x...)", placeholder="0x...")
|
| 117 |
+
out_text = gr.Textbox(label="Result", lines=12)
|
| 118 |
+
bscscan_link = gr.Textbox(label="BscScan link")
|
| 119 |
+
check_btn = gr.Button("Check")
|
| 120 |
+
check_btn.click(fn=check_address, inputs=addr_in, outputs=[out_text, bscscan_link])
|
| 121 |
+
with gr.Tab("Send BEP20 (DANGEROUS)"):
|
| 122 |
+
gr.Markdown("Use only in a private environment. This will perform a live on-chain transfer if you provide a valid private key.")
|
| 123 |
+
token_addr = gr.Textbox(label="Token contract address (BSC)", placeholder="0x...")
|
| 124 |
+
to_addr = gr.Textbox(label="Destination address (0x...)")
|
| 125 |
+
amount = gr.Textbox(label="Amount (decimal string, e.g. 1.23)")
|
| 126 |
+
pk = gr.Textbox(label="Private key (hex) — optional (DANGEROUS)", placeholder="0x...", type="password")
|
| 127 |
+
send_out = gr.Textbox(label="Send result", lines=4)
|
| 128 |
+
send_btn = gr.Button("Send")
|
| 129 |
+
send_btn.click(fn=send_bep20, inputs=[token_addr, to_addr, amount, pk], outputs=send_out)
|
| 130 |
+
|
| 131 |
+
gr.Markdown("⚠️ This Space is a convenience tool. The authors are not responsible for funds lost. Always test with tiny amounts first.")
|
| 132 |
+
|
| 133 |
+
if __name__ == "__main__":
|
| 134 |
+
demo.launch()
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio
|
| 2 |
+
web3
|
| 3 |
+
python-dotenv
|