faucet.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import {
  2. forEachAsync,
  3. generateRandomString,
  4. getAxiosClient,
  5. getProxyAgent,
  6. } from '../utils'
  7. import { getAltchaPayload, getRecaptcha, solveHCaptcha } from './captcha'
  8. import { DBClient } from '../singletons'
  9. import { Status } from '../models/Status'
  10. import polly from 'polly-js'
  11. import { MnemonicKey } from '@initia/initia.js'
  12. import { ethers } from 'ethers'
  13. import { v4 as uuidv4 } from 'uuid'
  14. import axios from 'axios'
  15. import xrpl from 'xrpl'
  16. export async function faucetAccount(address: string) {
  17. const client = getAxiosClient(true)
  18. const altcha = await getAltchaPayload(client)
  19. const hCaptcha = await solveHCaptcha(address)
  20. try {
  21. const res = await client.post(
  22. `https://faucet-api.initiation-1.initia.xyz/claim`,
  23. {
  24. address: address,
  25. altcha_payload: altcha,
  26. denom: 'uinit',
  27. h_captcha: hCaptcha,
  28. },
  29. )
  30. console.log(JSON.stringify(res.data))
  31. } catch (e) {
  32. console.log(e)
  33. throw e
  34. }
  35. }
  36. async function startFaucet(concurrency, index) {
  37. const accountsRaw = await DBClient.instance.account.findMany({
  38. where: {
  39. status: Status.Inited,
  40. },
  41. })
  42. const accounts = accountsRaw.filter(account => account.id % 10 === index)
  43. await forEachAsync(accounts, concurrency, async (account, index) => {
  44. console.log(`${index}/${accounts.length}: processing ${account.address}`)
  45. try {
  46. await faucetAccount(account.address)
  47. await DBClient.instance.account.update({
  48. where: { id: account.id },
  49. data: { status: Status.Fauceted },
  50. })
  51. } catch (e) {
  52. console.log(e)
  53. await DBClient.instance.account.update({
  54. where: { id: account.id },
  55. data: { status: Status.FaucetFailed, message: e.message },
  56. })
  57. }
  58. })
  59. }
  60. function getAndroidModel() {
  61. const models = [
  62. 'Mi 9',
  63. 'LIO-AN00',
  64. 'PCRT00',
  65. 'V1824A',
  66. 'SM-N9760',
  67. 'HD1900',
  68. 'SKW-A0',
  69. 'NX629J',
  70. 'M973Q',
  71. 'PCLM10',
  72. 'SM-N9810',
  73. 'SM-N9860',
  74. 'SM-F9160',
  75. 'SM-A7009',
  76. 'SM-A6060',
  77. 'SM-W2018',
  78. 'SM-P610',
  79. 'VTR-L29',
  80. 'M1903F2G',
  81. 'XQ-BE72',
  82. 'RMX3709',
  83. 'SM-E5260',
  84. 'SM-W2020',
  85. 'SHARK KSR-A0',
  86. 'G8HNN',
  87. 'SM-G973U',
  88. 'SM-G973U1',
  89. 'SM-G9738',
  90. 'SM-G981B',
  91. 'SM-G988U',
  92. 'SM-G9880',
  93. 'M2012K11G',
  94. 'M2006C3LG',
  95. '21061119BI',
  96. '22127RK46C',
  97. 'M2102J20SI',
  98. 'MCE91',
  99. 'L16',
  100. 'L10',
  101. 'L12',
  102. 'L13',
  103. 'M11A',
  104. 'N11',
  105. 'N12',
  106. ]
  107. return models[Math.floor(Math.random() * models.length)]
  108. }
  109. function getAndroidOS() {
  110. const oses = ['9', '10', '11', '12', '12.1', '13']
  111. return `Android ${oses[Math.floor(Math.random() * oses.length)]}`
  112. }
  113. function getIOS() {
  114. const iOSes = [
  115. '14',
  116. '16.7.8',
  117. '17.5',
  118. '17.4.1',
  119. '16.7.7',
  120. '15.8.2',
  121. '17.3.1',
  122. '15.0.2',
  123. '15.1.1',
  124. '16.0.3',
  125. '16.4.1',
  126. ]
  127. return `iOS ${iOSes[Math.floor(Math.random() * iOSes.length)]}`
  128. }
  129. function formatDateTime(date) {
  130. function pad(number, length) {
  131. return number.toString().padStart(length, '0')
  132. }
  133. const offset = -date.getTimezoneOffset()
  134. const offsetSign = offset >= 0 ? '+' : '-'
  135. const offsetHours = pad(Math.floor(Math.abs(offset) / 60), 2)
  136. const offsetMinutes = pad(Math.abs(offset) % 60, 2)
  137. return (
  138. date.getFullYear() +
  139. '-' +
  140. pad(date.getMonth() + 1, 2) +
  141. '-' +
  142. pad(date.getDate(), 2) +
  143. 'T' +
  144. pad(date.getHours(), 2) +
  145. ':' +
  146. pad(date.getMinutes(), 2) +
  147. ':' +
  148. pad(date.getSeconds(), 2) +
  149. '.' +
  150. pad(date.getMilliseconds(), 3) +
  151. offsetSign +
  152. offsetHours +
  153. offsetMinutes
  154. )
  155. }
  156. async function getDeviceId(model: string, httpsAgent): Promise<string> {
  157. const client = axios.create({
  158. httpsAgent: httpsAgent,
  159. headers: {
  160. 'X-Android-Package': 'xyz.lunchlunch.app',
  161. 'x-firebase-client':
  162. 'H4sIAAAAAAAAAKtWykhNLCpJSk0sKVayio7VUSpLLSrOzM9TslIyUqoFAFyivEQfAAAA',
  163. 'X-Android-Cert': '841CB3E1F4FC6CD420202DD419E02D4EE2E2099B',
  164. 'x-goog-api-key': 'AIzaSyA1PYciMbJ03xonKRM3JBr4yTReQ67GeuU',
  165. 'User-Agent': `Dalvik/2.1.0 (Linux; U; Android 9; ${model} Build/PQ3B.190801.05281822)`,
  166. },
  167. })
  168. const deviceId = generateRandomString(22)
  169. await client.post(
  170. 'https://firebaseinstallations.googleapis.com/v1/projects/lunchlunch-fb95e/installations',
  171. {
  172. fid: deviceId,
  173. appId: '1:383319257117:android:1a7c776df5c7048bddd6f4',
  174. authVersion: 'FIS_v2',
  175. sdkVersion: 'a:18.0.0',
  176. },
  177. )
  178. return deviceId
  179. }
  180. async function getRawMessage(address: string, message: string, duid: string) {
  181. const resp = await axios.post('https://rem.mtdao.io/create', {
  182. address,
  183. message,
  184. duid,
  185. })
  186. return resp.data
  187. }
  188. async function faucetLunch(concurrency: number = 8) {
  189. const now = new Date()
  190. const accounts = await DBClient.instance.account.findMany({
  191. where: {
  192. lastRun: {
  193. lt: new Date(now.getTime() - 24 * 60 * 60 * 1000),
  194. },
  195. status: 0,
  196. },
  197. orderBy: {
  198. id: 'desc',
  199. },
  200. })
  201. await forEachAsync(accounts, concurrency, async (account, index) => {
  202. console.log(`${index}/${accounts.length}: processing ${account.address}`)
  203. try {
  204. if (!account.gmail) {
  205. account.gmail = `${generateRandomString(8)}@gmail.com`
  206. }
  207. if (!account.xrpPrivateKey) {
  208. account.xrpPrivateKey = xrpl.Wallet.generate().seed
  209. }
  210. const key = new MnemonicKey({ mnemonic: account.mnemonic })
  211. const evmWallet = new ethers.Wallet(key.privateKey.toString('hex'))
  212. const message = JSON.stringify({
  213. signedAt: formatDateTime(new Date()),
  214. })
  215. const evmSignedMessage = await evmWallet.signMessage(message)
  216. const proxyAgent = getProxyAgent()
  217. let isAndroid = Math.random() > 0.5
  218. if (account.duid) {
  219. isAndroid = !account.duid.includes('-')
  220. }
  221. if (isAndroid && !account.deviceModel) {
  222. account.deviceModel = getAndroidModel()
  223. }
  224. if (!account.duid) {
  225. account.duid = isAndroid
  226. ? await getDeviceId(account.deviceModel, proxyAgent)
  227. : uuidv4().toUpperCase()
  228. }
  229. if (!account.deviceOS) {
  230. account.deviceOS = isAndroid ? getAndroidOS() : getIOS()
  231. }
  232. const rawMessage = await getRawMessage(
  233. evmWallet.address,
  234. message,
  235. account.duid,
  236. )
  237. const client = isAndroid
  238. ? axios.create({
  239. headers: {
  240. 'Lunch-Language': 'EN',
  241. 'lunch-fiat-currency': 'USD',
  242. 'lunch-app-duid': account.duid,
  243. 'lunch-app-version': '0.17.3',
  244. 'lun-app-build': 46,
  245. 'Lunch-Device-Model': account.deviceModel,
  246. 'Lunch-Device-OS': account.deviceOS,
  247. 'Lunch-Platform': 'Android',
  248. 'user-agent': `lunch/0.17.3(46) (Linux; ${account.deviceOS}; ${account.deviceModel} Build/PQ3B.190801.05281822)`,
  249. },
  250. httpsAgent: proxyAgent,
  251. })
  252. : axios.create({
  253. headers: {
  254. 'lunch-language': 'en',
  255. 'lunch-app-platform': 'iOS',
  256. 'lunch-app-version': '44',
  257. 'lunch-fiat-currency': 'USD',
  258. 'lunch-app-duid': account.duid,
  259. 'user-agent': `Lunch/1.0 (xyz.lunchlunch.app; build:44; ${account.deviceOS}) Alamofire/5.8.0`,
  260. },
  261. })
  262. const resp = await client.post(
  263. 'https://api.lunchlunch.xyz/v1/auth/sign-in',
  264. {
  265. walletAddress: evmWallet.address,
  266. signedMessage: evmSignedMessage,
  267. rawMessage: rawMessage,
  268. },
  269. )
  270. console.log(`${index}: sign-in success.`)
  271. client.defaults.headers[
  272. 'authorization'
  273. ] = `Bearer ${resp.data.accessToken}`
  274. const xrplWallet = xrpl.Wallet.fromSeed(account.xrpPrivateKey)
  275. let needRegister = false
  276. try {
  277. const memberResp = await client.get(
  278. 'https://api.lunchlunch.xyz/v1/member',
  279. )
  280. } catch (e) {
  281. if (e.response.status === 404) {
  282. needRegister = true
  283. }
  284. }
  285. if (needRegister) {
  286. const register = await client.post(
  287. 'https://api.lunchlunch.xyz/v1/member/register',
  288. {
  289. evmWalletAddress: evmWallet.address,
  290. initiaWalletAddress: key.accAddress,
  291. isImportedWallet: true,
  292. firstInitiaWalletAddress: key.accAddress,
  293. email: account.gmail,
  294. xrplWalletAddress: xrplWallet.address,
  295. },
  296. )
  297. console.log(`${index}: register done`)
  298. }
  299. await polly()
  300. .waitAndRetry([2000, 2000, 3000, 3000, 3000])
  301. .executeForPromise(async () => {
  302. await client.post(
  303. 'https://api.lunchlunch.xyz/v1/dish/submit-action/airdrop',
  304. {
  305. dishId: 43,
  306. },
  307. )
  308. console.log(`${index}: airdrop done`)
  309. })
  310. await DBClient.instance.account.update({
  311. where: { id: account.id },
  312. data: {
  313. status: Status.Fauceted,
  314. lastRun: new Date(),
  315. xrpPrivateKey: account.xrpPrivateKey,
  316. gmail: account.gmail,
  317. duid: account.duid,
  318. },
  319. })
  320. } catch (e) {
  321. console.log(e)
  322. await DBClient.instance.account.update({
  323. where: { id: account.id },
  324. data: {
  325. status: Status.FaucetFailed,
  326. message: e.message,
  327. lastRun: new Date(),
  328. xrpPrivateKey: account.xrpPrivateKey,
  329. gmail: account.gmail,
  330. duid: account.duid,
  331. },
  332. })
  333. }
  334. })
  335. }
  336. await faucetLunch(8)
  337. // const phase =
  338. // 'leave bone supply chair brain thunder giant fatigue winter shrimp father stairs'
  339. // const wallet = ethers.Wallet.fromPhrase(phase)
  340. // console.log(wallet.address)
  341. // const key = new MnemonicKey({
  342. // mnemonic: phase,
  343. // })
  344. // const rawMessage =
  345. // '809a55a47f136226b26cd6f12e61c4b641895e95f7eb43851005884cd8102bac2e231d14775fa9034745491d08910a1c0d0799ddf6926397dd05e733f62bcbb4'
  346. // const signedAmino = key.createSignatureAmino()