import { forEachAsync, generateRandomString, getAxiosClient, getProxyAgent, } from '../utils' import { getAltchaPayload, getRecaptcha, solveHCaptcha } from './captcha' import { DBClient } from '../singletons' import { Status } from '../models/Status' import polly from 'polly-js' import { MnemonicKey } from '@initia/initia.js' import { ethers } from 'ethers' import { v4 as uuidv4 } from 'uuid' import axios from 'axios' import xrpl from 'xrpl' export async function faucetAccount(address: string) { const client = getAxiosClient(true) const altcha = await getAltchaPayload(client) const hCaptcha = await solveHCaptcha(address) try { const res = await client.post( `https://faucet-api.initiation-1.initia.xyz/claim`, { address: address, altcha_payload: altcha, denom: 'uinit', h_captcha: hCaptcha, }, ) console.log(JSON.stringify(res.data)) } catch (e) { console.log(e) throw e } } async function startFaucet(concurrency, index) { const accountsRaw = await DBClient.instance.account.findMany({ where: { status: Status.Inited, }, }) const accounts = accountsRaw.filter(account => account.id % 10 === index) await forEachAsync(accounts, concurrency, async (account, index) => { console.log(`${index}/${accounts.length}: processing ${account.address}`) try { await faucetAccount(account.address) await DBClient.instance.account.update({ where: { id: account.id }, data: { status: Status.Fauceted }, }) } catch (e) { console.log(e) await DBClient.instance.account.update({ where: { id: account.id }, data: { status: Status.FaucetFailed, message: e.message }, }) } }) } function getAndroidModel() { const models = [ 'Mi 9', 'LIO-AN00', 'PCRT00', 'V1824A', 'SM-N9760', 'HD1900', 'SKW-A0', 'NX629J', 'M973Q', 'PCLM10', 'SM-N9810', 'SM-N9860', 'SM-F9160', 'SM-A7009', 'SM-A6060', 'SM-W2018', 'SM-P610', 'VTR-L29', 'M1903F2G', 'XQ-BE72', 'RMX3709', 'SM-E5260', 'SM-W2020', 'SHARK KSR-A0', 'G8HNN', 'SM-G973U', 'SM-G973U1', 'SM-G9738', 'SM-G981B', 'SM-G988U', 'SM-G9880', 'M2012K11G', 'M2006C3LG', '21061119BI', '22127RK46C', 'M2102J20SI', 'MCE91', 'L16', 'L10', 'L12', 'L13', 'M11A', 'N11', 'N12', ] return models[Math.floor(Math.random() * models.length)] } function getAndroidOS() { const oses = ['9', '10', '11', '12', '12.1', '13'] return `Android ${oses[Math.floor(Math.random() * oses.length)]}` } function getIOS() { const iOSes = [ '14', '16.7.8', '17.5', '17.4.1', '16.7.7', '15.8.2', '17.3.1', '15.0.2', '15.1.1', '16.0.3', '16.4.1', ] return `iOS ${iOSes[Math.floor(Math.random() * iOSes.length)]}` } function formatDateTime(date) { function pad(number, length) { return number.toString().padStart(length, '0') } const offset = -date.getTimezoneOffset() const offsetSign = offset >= 0 ? '+' : '-' const offsetHours = pad(Math.floor(Math.abs(offset) / 60), 2) const offsetMinutes = pad(Math.abs(offset) % 60, 2) return ( date.getFullYear() + '-' + pad(date.getMonth() + 1, 2) + '-' + pad(date.getDate(), 2) + 'T' + pad(date.getHours(), 2) + ':' + pad(date.getMinutes(), 2) + ':' + pad(date.getSeconds(), 2) + '.' + pad(date.getMilliseconds(), 3) + offsetSign + offsetHours + offsetMinutes ) } async function getDeviceId(model: string, httpsAgent): Promise { const client = axios.create({ httpsAgent: httpsAgent, headers: { 'X-Android-Package': 'xyz.lunchlunch.app', 'x-firebase-client': 'H4sIAAAAAAAAAKtWykhNLCpJSk0sKVayio7VUSpLLSrOzM9TslIyUqoFAFyivEQfAAAA', 'X-Android-Cert': '841CB3E1F4FC6CD420202DD419E02D4EE2E2099B', 'x-goog-api-key': 'AIzaSyA1PYciMbJ03xonKRM3JBr4yTReQ67GeuU', 'User-Agent': `Dalvik/2.1.0 (Linux; U; Android 9; ${model} Build/PQ3B.190801.05281822)`, }, }) const deviceId = generateRandomString(22) await client.post( 'https://firebaseinstallations.googleapis.com/v1/projects/lunchlunch-fb95e/installations', { fid: deviceId, appId: '1:383319257117:android:1a7c776df5c7048bddd6f4', authVersion: 'FIS_v2', sdkVersion: 'a:18.0.0', }, ) return deviceId } async function getRawMessage(address: string, message: string, duid: string) { const resp = await axios.post('https://rem.mtdao.io/create', { address, message, duid, }) return resp.data } async function faucetLunch(concurrency: number = 8) { const now = new Date() const accounts = await DBClient.instance.account.findMany({ where: { lastRun: { lt: new Date(now.getTime() - 24 * 60 * 60 * 1000), }, status: 0, }, orderBy: { id: 'desc', }, }) await forEachAsync(accounts, concurrency, async (account, index) => { console.log(`${index}/${accounts.length}: processing ${account.address}`) try { if (!account.gmail) { account.gmail = `${generateRandomString(8)}@gmail.com` } if (!account.xrpPrivateKey) { account.xrpPrivateKey = xrpl.Wallet.generate().seed } const key = new MnemonicKey({ mnemonic: account.mnemonic }) const evmWallet = new ethers.Wallet(key.privateKey.toString('hex')) const message = JSON.stringify({ signedAt: formatDateTime(new Date()), }) const evmSignedMessage = await evmWallet.signMessage(message) const proxyAgent = getProxyAgent() let isAndroid = Math.random() > 0.5 if (account.duid) { isAndroid = !account.duid.includes('-') } if (isAndroid && !account.deviceModel) { account.deviceModel = getAndroidModel() } if (!account.duid) { account.duid = isAndroid ? await getDeviceId(account.deviceModel, proxyAgent) : uuidv4().toUpperCase() } if (!account.deviceOS) { account.deviceOS = isAndroid ? getAndroidOS() : getIOS() } const rawMessage = await getRawMessage( evmWallet.address, message, account.duid, ) const client = isAndroid ? axios.create({ headers: { 'Lunch-Language': 'EN', 'lunch-fiat-currency': 'USD', 'lunch-app-duid': account.duid, 'lunch-app-version': '0.17.3', 'lun-app-build': 46, 'Lunch-Device-Model': account.deviceModel, 'Lunch-Device-OS': account.deviceOS, 'Lunch-Platform': 'Android', 'user-agent': `lunch/0.17.3(46) (Linux; ${account.deviceOS}; ${account.deviceModel} Build/PQ3B.190801.05281822)`, }, httpsAgent: proxyAgent, }) : axios.create({ headers: { 'lunch-language': 'en', 'lunch-app-platform': 'iOS', 'lunch-app-version': '44', 'lunch-fiat-currency': 'USD', 'lunch-app-duid': account.duid, 'user-agent': `Lunch/1.0 (xyz.lunchlunch.app; build:44; ${account.deviceOS}) Alamofire/5.8.0`, }, }) const resp = await client.post( 'https://api.lunchlunch.xyz/v1/auth/sign-in', { walletAddress: evmWallet.address, signedMessage: evmSignedMessage, rawMessage: rawMessage, }, ) console.log(`${index}: sign-in success.`) client.defaults.headers[ 'authorization' ] = `Bearer ${resp.data.accessToken}` const xrplWallet = xrpl.Wallet.fromSeed(account.xrpPrivateKey) let needRegister = false try { const memberResp = await client.get( 'https://api.lunchlunch.xyz/v1/member', ) } catch (e) { if (e.response.status === 404) { needRegister = true } } if (needRegister) { const register = await client.post( 'https://api.lunchlunch.xyz/v1/member/register', { evmWalletAddress: evmWallet.address, initiaWalletAddress: key.accAddress, isImportedWallet: true, firstInitiaWalletAddress: key.accAddress, email: account.gmail, xrplWalletAddress: xrplWallet.address, }, ) console.log(`${index}: register done`) } await polly() .waitAndRetry([2000, 2000, 3000, 3000, 3000]) .executeForPromise(async () => { await client.post( 'https://api.lunchlunch.xyz/v1/dish/submit-action/airdrop', { dishId: 43, }, ) console.log(`${index}: airdrop done`) }) await DBClient.instance.account.update({ where: { id: account.id }, data: { status: Status.Fauceted, lastRun: new Date(), xrpPrivateKey: account.xrpPrivateKey, gmail: account.gmail, duid: account.duid, }, }) } catch (e) { console.log(e) await DBClient.instance.account.update({ where: { id: account.id }, data: { status: Status.FaucetFailed, message: e.message, lastRun: new Date(), xrpPrivateKey: account.xrpPrivateKey, gmail: account.gmail, duid: account.duid, }, }) } }) } await faucetLunch(8) // const phase = // 'leave bone supply chair brain thunder giant fatigue winter shrimp father stairs' // const wallet = ethers.Wallet.fromPhrase(phase) // console.log(wallet.address) // const key = new MnemonicKey({ // mnemonic: phase, // }) // const rawMessage = // '809a55a47f136226b26cd6f12e61c4b641895e95f7eb43851005884cd8102bac2e231d14775fa9034745491d08910a1c0d0799ddf6926397dd05e733f62bcbb4' // const signedAmino = key.createSignatureAmino()