signin-talentum.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. # -*- coding: utf-8 -*-
  2. """
  3. Monad 测试网 talentum 批量签到脚本
  4. 网址: monad.talentum.id
  5. 2. checkIn() → 0x703e753E9a2aCa1194DED65833EAec17dcFeAc1b 方法 0x183ff085
  6. """
  7. import asyncio
  8. import os
  9. import random
  10. from typing import List
  11. from web3 import Web3
  12. from web3.types import TxReceipt
  13. from eth_account import Account
  14. # ------------------------ 用户配置区 ------------------------
  15. RPC_URL = "https://testnet-rpc.monad.xyz"
  16. CHECK_IN_CONTRACT = "0xcBE623D259261FFa0CFAff44484bFF46c1b7D6c2"
  17. KEY_FILE = "AccountList.txt"
  18. MIN_KEEP_BALANCE = 0.02
  19. MIN_DELAY = 15
  20. MAX_DELAY = 30
  21. # -----------------------------------------------------------
  22. w3 = Web3(Web3.HTTPProvider(RPC_URL))
  23. if not w3.is_connected():
  24. raise RuntimeError("无法连接 RPC")
  25. CHECK_IN_SELECTOR = "0x7ab71841"
  26. def load_private_keys(path: str) -> List[str]:
  27. if not os.path.isfile(path):
  28. raise FileNotFoundError(f"找不到私钥文件: {path}")
  29. with open(path, "r", encoding="utf-8") as f:
  30. return [line.strip() for line in f if line.strip()]
  31. def random_gas_limit(base: int = 100_000) -> int:
  32. return base + random.randint(-5_000, 10_000)
  33. async def wait_tx(tx_hash: str) -> TxReceipt:
  34. print(f"⏳ 等待交易确认: {tx_hash}")
  35. receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
  36. if receipt.status == 1:
  37. print("✅ 成功")
  38. else:
  39. print("❌ 失败")
  40. return receipt
  41. async def send_contract_call(private_key: str,
  42. contract_addr: str,
  43. selector: str,
  44. desc: str):
  45. acct = Account.from_key(private_key)
  46. addr = acct.address
  47. balance = w3.eth.get_balance(addr)
  48. if w3.from_wei(balance, "ether") < MIN_KEEP_BALANCE:
  49. print(f"⚠️ {addr} 余额不足,跳过")
  50. return
  51. nonce = w3.eth.get_transaction_count(addr)
  52. tx = {
  53. "to": Web3.to_checksum_address(contract_addr),
  54. "value": 0,
  55. "data": selector,
  56. "gas": random_gas_limit(),
  57. "gasPrice": w3.eth.gas_price,
  58. "nonce": nonce,
  59. "chainId": w3.eth.chain_id,
  60. }
  61. signed = Account.sign_transaction(tx, private_key)
  62. # 注意新版为 raw_transaction,不是 rawTransaction
  63. tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction).hex()
  64. print(f"{desc} tx_hash: {tx_hash}")
  65. await wait_tx(tx_hash)
  66. async def process_account(private_key: str):
  67. acct = Account.from_key(private_key)
  68. print(f"\n========== 开始处理 {acct.address} ==========")
  69. try:
  70. await send_contract_call(
  71. private_key,
  72. CHECK_IN_CONTRACT,
  73. CHECK_IN_SELECTOR,
  74. "0x7ab71841"
  75. )
  76. except Exception as e:
  77. print(f"执行出错: {e}")
  78. async def main():
  79. keys = load_private_keys(KEY_FILE)
  80. if not keys:
  81. print("私钥文件为空")
  82. return
  83. print(f"共读取到 {len(keys)} 个钱包")
  84. for pk in keys:
  85. await process_account(pk)
  86. if pk != keys[-1]:
  87. await asyncio.sleep(random.uniform(MIN_DELAY, MAX_DELAY))
  88. if __name__ == "__main__":
  89. asyncio.run(main())