of_apr_io_daily.py 3.6 KB

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