jack 4 kuukautta sitten
vanhempi
sitoutus
7b928135f6
2 muutettua tiedostoa jossa 71 lisäystä ja 46 poistoa
  1. 1 1
      project/monad/AccountList.txt
  2. 70 45
      project/monad/signin-lumiterra.py

+ 1 - 1
project/monad/AccountList.txt

@@ -1 +1 @@
-64f548051325022a7aa9e32c5756161d227fd5fb641c3f4623558cb574775254
+aa6fc003745793d7c192b2c404e332770b501ac0b81879d7538d6659ca84b7cf

+ 70 - 45
project/monad/signin-lumiterra.py

@@ -1,14 +1,15 @@
 # -*- coding: utf-8 -*-
 """
 Monad 测试网 talentum 批量签到脚本
-网址: monad.talentum.id
-2. checkIn() → 0x9E1DDC2a90bc0bec6325dF48Cbb3125Ce2879F7C  方法 0x9e4cda43
+网址: https://monad.talentum.id
+合约: 0x9E1DDC2a90bc0bec6325dF48Cbb3125Ce2879F7C  方法: checkIn() → 0x9e4cda43
 """
 
 import asyncio
 import os
 import random
 from typing import List
+import traceback
 
 from web3 import Web3
 from web3.types import TxReceipt
@@ -16,9 +17,7 @@ from eth_account import Account
 
 # ------------------------ 用户配置区 ------------------------
 RPC_URL = "https://testnet-rpc.monad.xyz"
-
 CHECK_IN_CONTRACT = "0x9E1DDC2a90bc0bec6325dF48Cbb3125Ce2879F7C"
-
 KEY_FILE = "AccountList.txt"
 MIN_KEEP_BALANCE = 0.02
 MIN_DELAY = 15
@@ -31,15 +30,12 @@ CHECK_IN_ABI = [{
     "stateMutability": "nonpayable",
     "type": "function"
 }]
-
 # -----------------------------------------------------------
 
 w3 = Web3(Web3.HTTPProvider(RPC_URL))
 if not w3.is_connected():
     raise RuntimeError("无法连接 RPC")
 
-CHECK_IN_SELECTOR = "0x9e4cda43"
-
 def load_private_keys(path: str) -> List[str]:
     if not os.path.isfile(path):
         raise FileNotFoundError(f"找不到私钥文件: {path}")
@@ -47,16 +43,32 @@ def load_private_keys(path: str) -> List[str]:
         return [line.strip() for line in f if line.strip()]
 
 def random_gas_limit(base: int = 100_000) -> int:
-    return base + random.randint(-5_000, 10_000)
+    limit = base + random.randint(-5_000, 10_000)
+    return max(21_000, limit)  # 保证不会小于最低gas
 
-async def wait_tx(tx_hash: str) -> TxReceipt:
+async def wait_tx(tx_hash: str, tx_params: dict):
     print(f"⏳ 等待交易确认: {tx_hash}")
-    receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
-    if receipt.status == 1:
-        print("✅ 成功")
-    else:
-        print("❌ 失败")
-    return receipt
+    try:
+        receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
+        if receipt.status == 1:
+            print("✅ 成功")
+        else:
+            print("❌ 失败(链上执行返回 status=0),尝试获取失败原因...")
+            # 用 eth_call 复现错误,获取revert reason
+            try:
+                w3.eth.call(tx_params, block_identifier="pending")
+                print("但 eth_call 没报错,说明没有 revert reason(或已被修复)")
+            except Exception as call_err:
+                # eth_call 抛出的异常中通常包含 revert reason
+                err_msg = str(call_err)
+                print(f"失败原因(revert reason): {err_msg}")
+            print("-" * 40)
+        return receipt
+    except Exception as e:
+        print(f"❌ 等待交易时出错: {e}")
+        import traceback
+        traceback.print_exc()
+        return None
 
 async def send_contract_call(private_key: str,
                              contract_addr: str,
@@ -66,41 +78,52 @@ async def send_contract_call(private_key: str,
     addr = acct.address
     balance = w3.eth.get_balance(addr)
     if w3.from_wei(balance, "ether") < MIN_KEEP_BALANCE:
-        print(f"⚠️  {addr} 余额不足,跳过")
+        print(f"⚠️  {addr} 余额不足({w3.from_wei(balance, 'ether')} ETH),跳过")
         return
 
     nonce = w3.eth.get_transaction_count(addr)
-
     contract = w3.eth.contract(address=Web3.to_checksum_address(contract_addr), abi=contract_abi)
-    data = contract.encodeABI(fn_name='checkIn')
-
-    tx = {
-        "to": Web3.to_checksum_address(contract_addr),
-        "value": 0,
-        "data": data,
-        "gas": random_gas_limit(),
-        "gasPrice": w3.eth.gas_price,
-        "nonce": nonce,
-        "chainId": w3.eth.chain_id,
-    }
-
-    signed = Account.sign_transaction(tx, private_key)
-    tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction).hex()
-    print(f"{desc} tx_hash: {tx_hash}")
-    await wait_tx(tx_hash)
+    try:
+        data = contract.encodeABI(fn_name='checkIn')
+        tx = {
+            "to": Web3.to_checksum_address(contract_addr),
+            "value": 0,
+            "data": data,
+            "gas": random_gas_limit(),
+            "gasPrice": w3.eth.gas_price,
+            "nonce": nonce,
+            "chainId": w3.eth.chain_id,
+        }
+        signed = Account.sign_transaction(tx, private_key)
+        try:
+            tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction).hex()
+            print(f"{desc} {addr} 提交签到交易: {tx_hash}")
+            # 传入 tx_params 方便 wait_tx 用 eth_call 复现
+            await wait_tx(tx_hash, {
+                "from": addr,
+                "to": tx["to"],
+                "data": tx["data"],
+                "value": tx["value"],
+                "gas": tx["gas"]
+            })
+        except Exception as send_e:
+            print(f"❌ {addr} 广播交易时遇到异常:{send_e}")
+            import traceback
+            traceback.print_exc()
+    except Exception as e:
+        print(f"❌ {addr} 构造或签名交易时出错: {e}")
+        import traceback
+        traceback.print_exc()
 
 async def process_account(private_key: str):
     acct = Account.from_key(private_key)
     print(f"\n========== 开始处理 {acct.address} ==========")
-    try:
-        await send_contract_call(
-            private_key,
-            CHECK_IN_CONTRACT,
-            CHECK_IN_ABI,
-            "SignIn"
-        )
-    except Exception as e:
-        print(f"执行出错: {e}")
+    await send_contract_call(
+        private_key,
+        CHECK_IN_CONTRACT,
+        CHECK_IN_ABI,
+        "SignIn"
+    )
 
 async def main():
     keys = load_private_keys(KEY_FILE)
@@ -109,10 +132,12 @@ async def main():
         return
 
     print(f"共读取到 {len(keys)} 个钱包")
-    for pk in keys:
+    for idx, pk in enumerate(keys):
         await process_account(pk)
-        if pk != keys[-1]:
-            await asyncio.sleep(random.uniform(MIN_DELAY, MAX_DELAY))
+        if idx != len(keys) - 1:
+            wait_sec = random.uniform(MIN_DELAY, MAX_DELAY)
+            print(f"⏳ 随机等待 {wait_sec:.2f} 秒,防批量风控")
+            await asyncio.sleep(wait_sec)
 
 if __name__ == "__main__":
     asyncio.run(main())