signin-of_apr_io_daily.py 3.5 KB

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