signin-lumiterra.py 3.3 KB

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