瀏覽代碼

lunch faucet

Shawn Lu 1 年之前
父節點
當前提交
740e96b693
共有 6 個文件被更改,包括 426 次插入40 次删除
  1. 5 2
      package.json
  2. 30 27
      prisma/schema.prisma
  3. 113 5
      src/faucet/faucet.ts
  4. 85 3
      src/test.ts
  5. 6 1
      src/utils/index.ts
  6. 187 2
      yarn.lock

+ 5 - 2
package.json

@@ -31,7 +31,7 @@
     "testShawn": "npm run build && node build/src/test.js",
     "faucet": "npm run build && node build/src/faucet/faucet.js",
     "clean": "rimraf coverage build tmp",
-    "prebuild": "yarn prisma generate",
+    "postinstall": "yarn prisma generate",
     "build": "tsc -p tsconfig.json && tsc-alias",
     "build:watch": "tsc -w -p tsconfig.json && tsc-alias",
     "build:release": "npm run clean && tsc -p tsconfig.release.json && tsc-alias",
@@ -56,13 +56,16 @@
     "@prisma/client": "^5.14.0",
     "axios": "^1.6.8",
     "dotenv": "^16.4.5",
+    "ethers": "^6.12.1",
     "https-proxy-agent": "^7.0.4",
     "minify": "^11.1.1",
     "node-cron": "^3.0.3",
     "polly-js": "^1.8.3",
     "prisma": "^5.14.0",
     "shawnlu96-altcha-lib": "0.3.6",
-    "tslib": "~2.6.2"
+    "tslib": "~2.6.2",
+    "uuid": "^9.0.1",
+    "xrpl": "^3.0.0"
   },
   "volta": {
     "node": "18.12.1"

+ 30 - 27
prisma/schema.prisma

@@ -14,37 +14,40 @@ datasource db {
 }
 
 model Account {
-  id       Int     @id @default(autoincrement())
-  mnemonic String  @db.VarChar(255)
-  address  String  @db.VarChar(100)
-  balance  String? @db.VarChar(50)
-  status   Int     @default(0)
-  message  String? @db.Text
+  id            Int      @id @default(autoincrement())
+  mnemonic      String   @db.VarChar(255)
+  address       String   @db.VarChar(100)
+  balance       String?  @db.VarChar(50)
+  xrpPrivateKey String?  @db.VarChar(255)
+  gmail         String?  @db.VarChar(100)
+  status        Int      @default(0)
+  message       String?  @db.Text
+  lastRun       DateTime
+  duid          String?  @db.VarChar(80)
 }
 
 model RandomTask {
-  id        Int    @id @default(autoincrement())
-  mnemonic  String @db.VarChar(255)
-  address   String @db.VarChar(100)
-  toAddress String @db.VarChar(100)
-  task2     Int    @default(0)
-  task3     Int    @default(0)
-  task4     Int    @default(0)
-  task5     Int    @default(0)
-  task6     Int    @default(0)
-  finish    Int    @default(0)
+  id       Int    @id @default(autoincrement())
+  mnemonic String @db.VarChar(255)
+  address  String @db.VarChar(100)
+  task2    Int    @default(0)
+  task3    Int    @default(0)
+  task4    Int    @default(0)
+  task5    Int    @default(0)
+  task6    Int    @default(0)
+  finish   Int    @default(0)
 }
 
 model RandomTask2 {
-  id        Int    @id @default(autoincrement())
-  mnemonic  String @db.VarChar(255)
-  address   String @db.VarChar(100)
-  toAddress String @db.VarChar(100)
-  task1     Int    @default(0)
-  task2     Int    @default(0)
-  task3     Int    @default(0)
-  task4     Int    @default(0)
-  task5     Int    @default(0)
-  task6     Int    @default(0)
-  finish    Int    @default(0)
+  id        Int     @id @default(autoincrement())
+  mnemonic  String  @db.VarChar(255)
+  address   String  @db.VarChar(100)
+  toAddress String? @db.VarChar(100)
+  task1     Int     @default(0)
+  task2     Int     @default(0)
+  task3     Int     @default(0)
+  task4     Int     @default(0)
+  task5     Int     @default(0)
+  task6     Int     @default(0)
+  finish    Int     @default(0)
 }

+ 113 - 5
src/faucet/faucet.ts

@@ -1,7 +1,18 @@
-import { forEachAsync, getAxiosClient } from '../utils'
+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)
@@ -24,14 +35,13 @@ export async function faucetAccount(address: string) {
   }
 }
 
-async function startFaucet(concurrency , index) {
+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)
+  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 {
@@ -51,4 +61,102 @@ async function startFaucet(concurrency , index) {
   })
 }
 
-await startFaucet(8, 0)
+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,
+    },
+  })
+  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
+      if (!account.duid) account.duid = uuidv4().toUpperCase()
+      await polly()
+        .waitAndRetry(3)
+        .executeForPromise(async () => {
+          const key = new MnemonicKey({ mnemonic: account.mnemonic })
+          const evmWallet = new ethers.Wallet(key.privateKey.toString('hex'))
+          const message = JSON.stringify({ signedAt: new Date().toISOString() })
+          const client = axios.create({
+            headers: {
+              'user-agent':
+                'Lunch/1.0 (xyz.lunchlunch.app; build:41; iOS 17.4.1) Alamofire/5.8.0',
+              'lunch-app-version': 41,
+              'lunch-fiat-currency': 'USD',
+              'lunch-app-duid': account.duid,
+            },
+            httpsAgent: getProxyAgent(),
+          })
+          const resp = await client.post(
+            'https://api.lunchlunch.xyz/v1/auth/sign-in',
+            {
+              walletAddress: evmWallet.address,
+              signedMessage: await evmWallet.signMessage(message),
+              rawMessage: message,
+            },
+          )
+          console.log(`${index}: sign-in success.`)
+          client.defaults.headers[
+            'authorization'
+          ] = `Bearer ${resp.data.accessToken}`
+
+          const xrplWallet = xrpl.Wallet.fromSeed(account.xrpPrivateKey)
+          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`)
+          try {
+            const airdropResp = await client.post(
+              'https://api.lunchlunch.xyz/v1/dish/submit-action/airdrop',
+              {
+                dishId: 42,
+              },
+            )
+            console.log(`${index}: airdrop done`)
+          } catch (e) {
+            console.log('airdrop failed', e)
+          }
+        })
+      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(20)

+ 85 - 3
src/test.ts

@@ -1,12 +1,17 @@
 import { faucetAccount } from './faucet/faucet'
 
-import { MnemonicKey } from '@initia/initia.js'
+import { AccAddress, MnemonicKey, RawKey } from '@initia/initia.js'
 import { InitiaClient } from './InitiaClient'
 import polly from 'polly-js'
+import { v4 as uuidv4 } from 'uuid'
 import { DBClient } from './singletons'
 import { solveChallenge } from 'shawnlu96-altcha-lib'
-import { getAxiosClient } from './utils'
+import { forEachAsync, getAxiosClient } from './utils'
 import { getAltchaPayload, solveHCaptcha } from './faucet/captcha'
+import axios from 'axios'
+import toHex = AccAddress.toHex
+import xrpl from 'xrpl'
+import { ethers } from 'ethers'
 
 // await polly()
 //   .waitAndRetry(4)
@@ -31,9 +36,86 @@ async function createAccounts(count: number) {
   await DBClient.instance.account.createMany({ data: accounts })
 }
 
+async function createDummyAddresses() {
+  let total = 0
+  function getTargets() {
+    return DBClient.instance.randomTask2.findMany({
+      where: {
+        toAddress: null,
+      },
+      take: 40,
+    })
+  }
+  let targets = await getTargets()
+  while (targets.length > 0) {
+    await forEachAsync(targets, 15, async task => {
+      const key = new MnemonicKey()
+      task.toAddress = key.accAddress
+      await DBClient.instance.randomTask2.update({
+        where: { id: task.id },
+        data: { toAddress: key.accAddress },
+      })
+    })
+    total += targets.length
+    console.log(total)
+    targets = await getTargets()
+  }
+}
+
+async function testFaucet() {
+  const key = new MnemonicKey()
+  const evmWallet = new ethers.Wallet(key.privateKey.toString('hex'))
+
+  const evmAddress = toHex(key.accAddress)
+  const message = JSON.stringify({ signedAt: new Date().toISOString() })
+
+  const duid = uuidv4().toUpperCase()
+  const client = axios.create({
+    headers: {
+      'user-agent':
+        'Lunch/1.0 (xyz.lunchlunch.app; build:41; iOS 17.4.1) Alamofire/5.8.0',
+      'lunch-app-version': 41,
+      'lunch-fiat-currency': 'USD',
+      'lunch-app-duid': duid,
+    },
+  })
+  const resp = await client.post('https://api.lunchlunch.xyz/v1/auth/sign-in', {
+    walletAddress: evmWallet.address,
+    signedMessage: await evmWallet.signMessage(message),
+    rawMessage: message,
+  })
+  console.log(resp.data)
+  client.defaults.headers['authorization'] = `Bearer ${resp.data.accessToken}`
+  const register = await client.post(
+    'https://api.lunchlunch.xyz/v1/member/register',
+    {
+      evmWalletAddress: evmWallet.address,
+      initiaWalletAddress: key.accAddress,
+      isImportedWallet: true,
+      firstInitiaWalletAddress: key.accAddress,
+      email: 'xsdacds@gmail.com',
+      xrplWalletAddress: xrpl.Wallet.generate().address,
+    },
+  )
+  console.log(register.data)
+  try {
+    const airdropResp = await client.post(
+      'https://api.lunchlunch.xyz/v1/dish/submit-action/airdrop',
+      {
+        dishId: 42,
+      },
+    )
+    console.log(airdropResp.data)
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+await testFaucet()
+// await createDummyAddresses()
 // await createAccounts(10000)
 // await faucetAccount('init1xj0ekwu5n3t4c8aj2xfxr9l94lwr0sssl4rkxy')
-await getAltchaPayload(getAxiosClient(true))
+// await getAltchaPayload(getAxiosClient(true))
 // const client = new InitiaClient(
 //   'scheme address way popular shrug fall rail eyebrow buzz learn cross involve spatial rookie ostrich grocery chest ice glory security slogan summer also comfort',
 // )

+ 6 - 1
src/utils/index.ts

@@ -30,6 +30,11 @@ export function generate2CaptchaProxyAgent() {
   )
 }
 
+export function getProxyAgent() {
+  const pass = `test1234_session-${generateRandomString(8)}_lifetime-1h`
+  return new HttpsProxyAgent(`http://tuxla:${pass}@geo.iproyal.com:12321`)
+}
+
 export function getAxiosClient(useProxy: boolean) {
   const pass = `test1234_session-${generateRandomString(8)}_lifetime-1h`
   const proxyAgent = useProxy
@@ -61,7 +66,7 @@ export async function forEachAsync(
   array: any[],
   concurrency: number,
   asyncFn: (item: any, index: number) => Promise<void>,
-  timeout: number=320000,
+  timeout: number = 320000,
 ) {
   let running = 0 // 当前正在运行的任务数
   let completed = 0 // 完成的任务数

+ 187 - 2
yarn.lock

@@ -2,6 +2,11 @@
 # yarn lockfile v1
 
 
+"@adraffy/ens-normalize@1.10.1":
+  version "1.10.1"
+  resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069"
+  integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==
+
 "@ampproject/remapping@^2.2.0":
   version "2.3.0"
   resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz"
@@ -1209,7 +1214,26 @@
   resolved "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz"
   integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==
 
-"@noble/hashes@^1.2.0":
+"@noble/curves@1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35"
+  integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==
+  dependencies:
+    "@noble/hashes" "1.3.2"
+
+"@noble/curves@^1.0.0", "@noble/curves@~1.4.0":
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6"
+  integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==
+  dependencies:
+    "@noble/hashes" "1.4.0"
+
+"@noble/hashes@1.3.2":
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
+  integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==
+
+"@noble/hashes@1.4.0", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0", "@noble/hashes@~1.4.0":
   version "1.4.0"
   resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz"
   integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
@@ -1344,6 +1368,28 @@
   resolved "https://registry.npmjs.org/@putout/minify/-/minify-3.9.0.tgz"
   integrity sha512-6QUtp0W2fZyjeLpIyuf+RAmfagM6ZGJeLnwsqr6XkAyKp+0KYnhkh0c3j5oTpasdyNI7fdfSuR864I34p82WVw==
 
+"@scure/base@^1.1.3", "@scure/base@~1.1.6":
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d"
+  integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==
+
+"@scure/bip32@^1.3.1":
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67"
+  integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==
+  dependencies:
+    "@noble/curves" "~1.4.0"
+    "@noble/hashes" "~1.4.0"
+    "@scure/base" "~1.1.6"
+
+"@scure/bip39@^1.2.1":
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3"
+  integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==
+  dependencies:
+    "@noble/hashes" "~1.4.0"
+    "@scure/base" "~1.1.6"
+
 "@sinclair/typebox@^0.27.8":
   version "0.27.8"
   resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz"
@@ -1393,6 +1439,11 @@
   resolved "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz"
   integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
 
+"@types/node@18.15.13":
+  version "18.15.13"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469"
+  integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==
+
 "@types/semver@^7.5.0":
   version "7.5.8"
   resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz"
@@ -1501,6 +1552,23 @@
     "@typescript-eslint/types" "6.2.1"
     eslint-visitor-keys "^3.4.1"
 
+"@xrplf/isomorphic@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@xrplf/isomorphic/-/isomorphic-1.0.0.tgz#b9ca265225660c44a7b15bce4a1d9fb717ed3fc1"
+  integrity sha512-IyMsxyjkJK8YWq566KyuFuh/PUiLzQ02RbUO5qa+vEQb6zIAR9MzFwN7wBmBy7wmKkjligcdNDMG5EaBRH8FxQ==
+  dependencies:
+    "@noble/hashes" "^1.0.0"
+    eventemitter3 "5.0.1"
+    ws "^8.13.0"
+
+"@xrplf/secret-numbers@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@xrplf/secret-numbers/-/secret-numbers-1.0.0.tgz#cc19ff84236cc2737b38f2e42a29924f2b8ffc0e"
+  integrity sha512-qsCLGyqe1zaq9j7PZJopK+iGTGRbk6akkg6iZXJJgxKwck0C5x5Gnwlb1HKYGOwPKyrXWpV6a2YmcpNpUFctGg==
+  dependencies:
+    "@xrplf/isomorphic" "^1.0.0"
+    ripple-keypairs "^2.0.0"
+
 JSONStream@^1.0.3:
   version "1.3.5"
   resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz"
@@ -1538,6 +1606,11 @@ acorn@^8.8.2, acorn@^8.9.0:
   resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz"
   integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
 
+aes-js@4.0.0-beta.5:
+  version "4.0.0-beta.5"
+  resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873"
+  integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==
+
 agent-base@^7.0.2:
   version "7.1.1"
   resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz"
@@ -1701,7 +1774,7 @@ bech32@^2.0.0:
   resolved "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz"
   integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==
 
-bignumber.js@^9.1.0:
+bignumber.js@^9.0.0, bignumber.js@^9.1.0:
   version "9.1.2"
   resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz"
   integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==
@@ -2204,6 +2277,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
+cross-fetch@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983"
+  integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==
+  dependencies:
+    node-fetch "^2.6.12"
+
 cross-spawn@^7.0.0, cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
@@ -2532,6 +2612,24 @@ esutils@^2.0.2:
   resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
   integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
 
+ethers@^6.12.1:
+  version "6.12.1"
+  resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.12.1.tgz#517ff6d66d4fd5433e38e903051da3e57c87ff37"
+  integrity sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==
+  dependencies:
+    "@adraffy/ens-normalize" "1.10.1"
+    "@noble/curves" "1.2.0"
+    "@noble/hashes" "1.3.2"
+    "@types/node" "18.15.13"
+    aes-js "4.0.0-beta.5"
+    tslib "2.4.0"
+    ws "8.5.0"
+
+eventemitter3@5.0.1, eventemitter3@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+  integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
 events@^3.0.0, events@^3.3.0:
   version "3.3.0"
   resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
@@ -3449,6 +3547,13 @@ node-cron@^3.0.3:
   dependencies:
     uuid "8.3.2"
 
+node-fetch@^2.6.12:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+  integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+  dependencies:
+    whatwg-url "^5.0.0"
+
 node-gyp-build@^4.2.0:
   version "4.8.1"
   resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz"
@@ -3937,6 +4042,32 @@ ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2:
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+ripple-address-codec@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-5.0.0.tgz#97059f7bba6f9ed7a52843de8aa427723fb529f6"
+  integrity sha512-de7osLRH/pt5HX2xw2TRJtbdLLWHu0RXirpQaEeCnWKY5DYHykh3ETSkofvm0aX0LJiV7kwkegJxQkmbO94gWw==
+  dependencies:
+    "@scure/base" "^1.1.3"
+    "@xrplf/isomorphic" "^1.0.0"
+
+ripple-binary-codec@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-2.0.0.tgz#3af6b9fc471b09d6f9184b8e8a3c0aa06cfabe0c"
+  integrity sha512-zakENc9A5dlW85uzrmQHrJehymhL59ftggboRNrjxFDJdlNJ6DSE210P3ys/9kL0oVtOzFnTrOPFfxHZeOsA/Q==
+  dependencies:
+    "@xrplf/isomorphic" "^1.0.0"
+    bignumber.js "^9.0.0"
+    ripple-address-codec "^5.0.0"
+
+ripple-keypairs@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-2.0.0.tgz#4a1a8142e9a58c07e61b3cc6cfe7317db718d289"
+  integrity sha512-b5rfL2EZiffmklqZk1W+dvSy97v3V/C7936WxCCgDynaGPp7GE6R2XO7EU9O2LlM/z95rj870IylYnOQs+1Rag==
+  dependencies:
+    "@noble/curves" "^1.0.0"
+    "@xrplf/isomorphic" "^1.0.0"
+    ripple-address-codec "^5.0.0"
+
 run-parallel@^1.1.9:
   version "1.2.0"
   resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
@@ -4287,6 +4418,11 @@ to-regex-range@^5.0.1:
   dependencies:
     is-number "^7.0.0"
 
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+  integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
 try-catch@^3.0.0:
   version "3.0.1"
   resolved "https://registry.npmjs.org/try-catch/-/try-catch-3.0.1.tgz"
@@ -4314,6 +4450,11 @@ tsc-alias@1.8.8:
     normalize-path "^3.0.0"
     plimit-lit "^1.2.6"
 
+tslib@2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+  integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
 tslib@^2.0.3, tslib@^2.1.0, tslib@^2.6.2, tslib@~2.6.2:
   version "2.6.2"
   resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
@@ -4451,11 +4592,29 @@ uuid@8.3.2:
   resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
   integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
 
+uuid@^9.0.1:
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+  integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
 vm-browserify@^1.0.0:
   version "1.1.2"
   resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz"
   integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
 
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+  integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+whatwg-url@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+  integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
 which-typed-array@^1.1.14, which-typed-array@^1.1.2:
   version "1.1.15"
   resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz"
@@ -4509,11 +4668,37 @@ wrappy@1:
   resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
+ws@8.5.0:
+  version "8.5.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
+  integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
+
 ws@^7.5.9:
   version "7.5.9"
   resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz"
   integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
 
+ws@^8.13.0:
+  version "8.17.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea"
+  integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==
+
+xrpl@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/xrpl/-/xrpl-3.0.0.tgz#32b9bd156f96341e82c8f22440a92e694b7c2fe1"
+  integrity sha512-QC+dNx3tvMEn9IrxcXFFa0rWwvBwACkGFNKl+W2miMGYnlgSiIsnjdqwtG2WRs0Pyxs5dd9nBTQHyQ1BPxZ78A==
+  dependencies:
+    "@scure/bip32" "^1.3.1"
+    "@scure/bip39" "^1.2.1"
+    "@xrplf/isomorphic" "^1.0.0"
+    "@xrplf/secret-numbers" "^1.0.0"
+    bignumber.js "^9.0.0"
+    cross-fetch "^4.0.0"
+    eventemitter3 "^5.0.1"
+    ripple-address-codec "^5.0.0"
+    ripple-binary-codec "^2.0.0"
+    ripple-keypairs "^2.0.0"
+
 xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"