diff --git a/.env.juno.example b/.env.juno.example index 3fc97d7..59aa93b 100644 --- a/.env.juno.example +++ b/.env.juno.example @@ -1,26 +1,33 @@ -# The wallet mnemonic to send transactions from -WALLET_MNEMONIC="" +## JUNO ENVIRONMENT SETTINGS EXAMPLE + +## GENERAL SETTINGS +WALLET_MNEMONIC="" # The wallet mnemonic to send transactions from USE_MEMPOOL="1" + GAS_USAGE_PER_HOP="700000" #defines the gas usage per hop, 2 hop arb pays 1400000 gas, 3 hop will pay 2100000 etc PROFIT_THRESHOLD="10000" # LOGGING ENVIRONMENT VARIABLES # SLACK_TOKEN = # SLACK_CHANNEL = +# DISCORD_WEBHOOK_URL = MAX_PATH_HOPS="4" -# # SKIP SPECIFIC ENVIRONMENT VARIABLES +## SKIP SPECIFIC SETTINGS USE_SKIP = "1" SKIP_URL= "http://juno-1-api.skip.money" SKIP_BID_WALLET= "juno10g0l3hd9sau3vnjrayjhergcpxemucxcspgnn4" SKIP_BID_RATE="0.31" #e.g. 20% of the profit is used as a bid to win the auction -# JUNO SETUP -# The asset denom to be used as base asset. This should be the denom of a Native Token only. -BASE_DENOM="ujuno" +## CHAIN SPECIFIC SETTINGS +BASE_DENOM="ujuno" # The asset denom to be used as base asset. This should be the denom of a Native Token only. CHAIN_PREFIX="juno" RPC_URL="https://juno-rpc.reece.sh" GAS_UNIT_PRICE="0.0025" FLASHLOAN_ROUTER_ADDRESS="juno1qa7vdlm6zgq3radal5sltyl4t4qd32feug9qs50kcxda46q230pqzny48s" + +FACTORIES_TO_ROUTERS_MAPPING ='{"factory":"juno14m9rd2trjytvxvu4ldmqvru50ffxsafs8kequmfky7jh97uyqrxqs5xrnx","router":"juno128lewlw6kv223uw4yzdffl8rnh3k9qs8vrf6kef28579w8ygccyq7m90n2"}, +{"factory":"juno16adshp473hd9sruwztdqrtsfckgtd69glqm6sqk0hc4q40c296qsxl3u3s","router":"juno1pctfpv9k03v0ff538pz8kkw5ujlptntzkwjg6c0lrtqv87s9k28qdtl50w"}' FLASHLOAN_FEE="0.3" #in % + POOLS='{"pool": "juno1sg6chmktuhyj4lsrxrrdflem7gsnk4ejv6zkcc4d3vcqulzp55wsf4l4gl","inputfee": 0.301, "outputfee": 0}, {"pool": "juno1ctsmp54v79x7ea970zejlyws50cj9pkrmw49x46085fn80znjmpqz2n642","inputfee": 0.42,"outputfee": 0}, {"pool": "juno1e8n6ch7msks487ecznyeagmzd5ml2pq9tgedqt2u63vra0q0r9mqrjy6ys","inputfee": 0.301,"outputfee": 0}, @@ -80,6 +87,3 @@ POOLS='{"pool": "juno1sg6chmktuhyj4lsrxrrdflem7gsnk4ejv6zkcc4d3vcqulzp55wsf4l4gl {"pool": "juno18zk9xqj9xjm0ry39jjam8qsysj7qh49xwt4qdfp9lgtrk08sd58s2n54ve","inputfee": 0,"outputfee": 0.301}, {"pool": "juno14ke9xn3qfmnjsrh9lh6rfu7zmm90duvj4lpkcrrnzemh0tjpwarqfk97n6","inputfee": 0,"outputfee": 0.301}, {"pool": "juno1x9r54vejw4hnxe7xm4haaf0ymf825frm30xqf9cud6cmnrgkx9lsxpj475","inputfee": 0,"outputfee": 0.301}' - -FACTORIES_TO_ROUTERS_MAPPING ='{"factory":"juno14m9rd2trjytvxvu4ldmqvru50ffxsafs8kequmfky7jh97uyqrxqs5xrnx","router":"juno128lewlw6kv223uw4yzdffl8rnh3k9qs8vrf6kef28579w8ygccyq7m90n2"}, -{"factory":"juno16adshp473hd9sruwztdqrtsfckgtd69glqm6sqk0hc4q40c296qsxl3u3s","router":"juno1pctfpv9k03v0ff538pz8kkw5ujlptntzkwjg6c0lrtqv87s9k28qdtl50w"}' diff --git a/package-lock.json b/package-lock.json index d5f278b..0d31b22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@cosmjs/tendermint-rpc": "^0.29.4", "@skip-mev/skipjs": "^1.1.0", "@slack/web-api": "^6.8.0", + "discord.js": "^14.7.1", "dotenv": "^16.0.1" }, "devDependencies": { @@ -602,6 +603,66 @@ "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.4.tgz", "integrity": "sha512-X1pZWRHDbTPLa6cYW0NHvtig+lSxOdLAX7K/xp67ywBy2knnDOyzz1utGTOowmiM98XuV9quK/BWePKkJOaHpQ==" }, + "node_modules/@discordjs/builders": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", + "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", + "dependencies": { + "@discordjs/util": "^0.1.0", + "@sapphire/shapeshift": "^3.7.1", + "discord-api-types": "^0.37.20", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.2", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/builders/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@discordjs/collection": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", + "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", + "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", + "dependencies": { + "@discordjs/collection": "^1.3.0", + "@discordjs/util": "^0.1.0", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.2.2", + "discord-api-types": "^0.37.23", + "file-type": "^18.0.0", + "tslib": "^2.4.1", + "undici": "^5.13.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@discordjs/util": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", + "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.31.0", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz", @@ -937,6 +998,37 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", + "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@skip-mev/skipjs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@skip-mev/skipjs/-/skipjs-1.1.0.tgz", @@ -1022,6 +1114,11 @@ "node": ">=0.10.0" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@types/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", @@ -1057,6 +1154,14 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.35.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.35.1.tgz", @@ -1494,6 +1599,17 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1788,6 +1904,58 @@ "node": ">=8" } }, + "node_modules/discord-api-types": { + "version": "0.37.33", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.33.tgz", + "integrity": "sha512-ZMH5RU3q1pvYS+2wGUJ5Zvy8jMGTQ4wCpbDlIQDkbIL/k6kJwBPsXnCg81g2GywlOuf0f8ezakxVSe+sZuY6ig==" + }, + "node_modules/discord.js": { + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", + "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", + "dependencies": { + "@discordjs/builders": "^1.4.0", + "@discordjs/collection": "^1.3.0", + "@discordjs/rest": "^1.4.0", + "@discordjs/util": "^0.1.0", + "@sapphire/snowflake": "^3.2.2", + "@types/ws": "^8.5.3", + "discord-api-types": "^0.37.20", + "fast-deep-equal": "^3.1.3", + "lodash.snakecase": "^4.1.1", + "tslib": "^2.4.1", + "undici": "^5.13.0", + "ws": "^8.11.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/discord.js/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/discord.js/node_modules/ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2196,8 +2364,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.2.0", @@ -2288,6 +2455,22 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.0.tgz", + "integrity": "sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==", + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2702,6 +2885,25 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -3033,9 +3235,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -3085,6 +3287,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -3097,6 +3304,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -3757,6 +3969,18 @@ "node": ">=8" } }, + "node_modules/peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3950,6 +4174,34 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4197,6 +4449,41 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4244,6 +4531,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4305,6 +4608,27 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -4372,6 +4696,17 @@ "node": ">=4.2.0" } }, + "node_modules/undici": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz", + "integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==", + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.18" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", @@ -4407,6 +4742,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -5092,6 +5432,58 @@ "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.4.tgz", "integrity": "sha512-X1pZWRHDbTPLa6cYW0NHvtig+lSxOdLAX7K/xp67ywBy2knnDOyzz1utGTOowmiM98XuV9quK/BWePKkJOaHpQ==" }, + "@discordjs/builders": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.4.0.tgz", + "integrity": "sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA==", + "requires": { + "@discordjs/util": "^0.1.0", + "@sapphire/shapeshift": "^3.7.1", + "discord-api-types": "^0.37.20", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.2", + "tslib": "^2.4.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@discordjs/collection": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.3.0.tgz", + "integrity": "sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg==" + }, + "@discordjs/rest": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.5.0.tgz", + "integrity": "sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA==", + "requires": { + "@discordjs/collection": "^1.3.0", + "@discordjs/util": "^0.1.0", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.2.2", + "discord-api-types": "^0.37.23", + "file-type": "^18.0.0", + "tslib": "^2.4.1", + "undici": "^5.13.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@discordjs/util": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", + "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==" + }, "@es-joy/jsdoccomment": { "version": "0.31.0", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz", @@ -5355,6 +5747,25 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "@sapphire/async-queue": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==" + }, + "@sapphire/shapeshift": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", + "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", + "requires": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + } + }, + "@sapphire/snowflake": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==" + }, "@skip-mev/skipjs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@skip-mev/skipjs/-/skipjs-1.1.0.tgz", @@ -5426,6 +5837,11 @@ } } }, + "@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "@types/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", @@ -5461,6 +5877,14 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, + "@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "5.35.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.35.1.tgz", @@ -5737,6 +6161,14 @@ "update-browserslist-db": "^1.0.5" } }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -5950,6 +6382,43 @@ "path-type": "^4.0.0" } }, + "discord-api-types": { + "version": "0.37.33", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.33.tgz", + "integrity": "sha512-ZMH5RU3q1pvYS+2wGUJ5Zvy8jMGTQ4wCpbDlIQDkbIL/k6kJwBPsXnCg81g2GywlOuf0f8ezakxVSe+sZuY6ig==" + }, + "discord.js": { + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.7.1.tgz", + "integrity": "sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA==", + "requires": { + "@discordjs/builders": "^1.4.0", + "@discordjs/collection": "^1.3.0", + "@discordjs/rest": "^1.4.0", + "@discordjs/util": "^0.1.0", + "@sapphire/snowflake": "^3.2.2", + "@types/ws": "^8.5.3", + "discord-api-types": "^0.37.20", + "fast-deep-equal": "^3.1.3", + "lodash.snakecase": "^4.1.1", + "tslib": "^2.4.1", + "undici": "^5.13.0", + "ws": "^8.11.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "ws": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "requires": {} + } + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -6244,8 +6713,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-diff": { "version": "1.2.0", @@ -6316,6 +6784,16 @@ "flat-cache": "^3.0.4" } }, + "file-type": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.0.tgz", + "integrity": "sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==", + "requires": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -6601,6 +7079,11 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -6848,9 +7331,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "levn": { @@ -6885,6 +7368,11 @@ "p-locate": "^5.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -6897,6 +7385,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7390,6 +7883,11 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==" + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7521,6 +8019,24 @@ "safe-buffer": "^5.1.0" } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "requires": { + "readable-stream": "^3.6.0" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7702,6 +8218,26 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -7734,6 +8270,15 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "requires": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7780,6 +8325,20 @@ "is-number": "^7.0.0" } }, + "token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "requires": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + } + }, + "ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -7825,6 +8384,14 @@ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, + "undici": { + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz", + "integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==", + "requires": { + "busboy": "^1.6.0" + } + }, "update-browserslist-db": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", @@ -7844,6 +8411,11 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/package.json b/package.json index 028eb5b..6e1c237 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@cosmjs/tendermint-rpc": "^0.29.4", "@skip-mev/skipjs": "^1.1.0", "@slack/web-api": "^6.8.0", + "discord.js": "^14.7.1", "dotenv": "^16.0.1" } } diff --git a/src/core/logging/discordLogger.ts b/src/core/logging/discordLogger.ts new file mode 100644 index 0000000..43adf61 --- /dev/null +++ b/src/core/logging/discordLogger.ts @@ -0,0 +1,29 @@ +import { WebhookClient } from "discord.js"; + +/** + * + */ +export class DiscordLogger { + public client: WebhookClient; + + /** + * + */ + constructor(url: string) { + this.client = new WebhookClient({ url: url }); + } + + /** + * Sends the `message` to the discord webhook if client is avaialble. + * @param message The message to send. + */ + async sendMessage(message: string) { + if (this.client) { + await this.client.send({ + content: message, + username: "white-whale-bot", + avatarURL: "https://whitewhale.money/favicon.png", + }); + } + } +} diff --git a/src/core/logging/index.ts b/src/core/logging/index.ts new file mode 100644 index 0000000..6adbf76 --- /dev/null +++ b/src/core/logging/index.ts @@ -0,0 +1,3 @@ +export { Logger } from "./logger"; +export { SlackLogger } from "./slackLogger"; +export { DiscordLogger } from "./discordLogger"; \ No newline at end of file diff --git a/src/core/logging/logger.ts b/src/core/logging/logger.ts new file mode 100644 index 0000000..c241d3f --- /dev/null +++ b/src/core/logging/logger.ts @@ -0,0 +1,61 @@ +import { BotConfig } from "../types/base/botConfig"; +import { LogType } from "../types/base/logging"; +import { DiscordLogger } from "./discordLogger"; +import { SlackLogger } from "./slackLogger"; + +/** + * + */ +export class Logger { + private botConfig: BotConfig; + public discordLogger?: DiscordLogger; + public slackLogger?: SlackLogger; + + // Codes that are not sent to external sources (discord, slack) + private externalExemptCodes: Array = [4, 5, 6, 8]; + + /** + * + */ + constructor(config: BotConfig) { + this.botConfig = config; + + if (this.botConfig.discordWebhookUrl) { + this.discordLogger = new DiscordLogger(this.botConfig.discordWebhookUrl); + } + + if (this.botConfig.slackToken && this.botConfig.slackChannel) { + this.slackLogger = new SlackLogger(this.botConfig.slackToken, this.botConfig.slackChannel); + } + } + + /** + * Sends the `message` to the console and other external messaging systems if defined. + * @param message The message to log. + * @param type The type of message, values of type LogType. + * @param code The code number of the message, -1 if not given. + */ + public async sendMessage(message: string, type: LogType = LogType.All, code = -1) { + if (message) { + // Don't send common errors to discord/slack + if (type != LogType.Console && !this.externalExemptCodes.includes(code)) { + // Add indicator on success + if (code === 0) message = ":tada: **Success!** :tada:\n" + message; + + if (this.discordLogger && [LogType.All, LogType.Externals, LogType.Discord].includes(type)) { + await this.discordLogger.sendMessage(message); + } + + if (this.slackLogger && [LogType.All, LogType.Externals, LogType.Slack].includes(type)) { + message = message.replaceAll("**", "*"); + await this.slackLogger.sendMessage(message); + } + } + + if ([LogType.All, LogType.Console].includes(type)) { + message = message.replaceAll("**", ""); + console.log(message); + } + } + } +} diff --git a/src/core/logging/slackLogger.ts b/src/core/logging/slackLogger.ts new file mode 100644 index 0000000..db767b4 --- /dev/null +++ b/src/core/logging/slackLogger.ts @@ -0,0 +1,30 @@ +import { WebClient } from "@slack/web-api"; + +/** + * + */ +export class SlackLogger { + public conversationId: string; + public client: WebClient; + + /** + * + */ + constructor(token: string, channel: string) { + this.client = new WebClient(token); + this.conversationId = channel; + } + + /** + * Sends the `message` to slack if client is avaialble. + * @param message The message to send. + */ + async sendMessage(message: string) { + if (this.client && this.conversationId.length > 0 && message) { + await this.client.chat.postMessage({ + text: message, + channel: this.conversationId, + }); + } + } +} diff --git a/src/core/logging/slacklogger.ts b/src/core/logging/slacklogger.ts index 618048a..db767b4 100644 --- a/src/core/logging/slacklogger.ts +++ b/src/core/logging/slacklogger.ts @@ -1,28 +1,30 @@ import { WebClient } from "@slack/web-api"; /** - * Creates and returns a Slack WebClient using OAuth2 tokens. - * @param token The token for OAuth2 authentication. + * */ -export function getSlackClient(token: string) { - return new WebClient(token); -} -/** - * Sends the `message` to the Slack channel `channel` if `client` is not undefined. If - * `client` is undefined, it will print `message` to stdout. - * @param message The message to send to slack. - * @param client The slack WebClient or undefined. - * @param channel The slack Channel to send the message to or undefined. - */ -export async function sendSlackMessage(message: string, client: WebClient | undefined, channel: string | undefined) { - if (client && channel) { - // send log to slack channel - await client.chat.postMessage({ - text: message, - channel: channel, - }); - } else { - // log to stdout - console.log(message); +export class SlackLogger { + public conversationId: string; + public client: WebClient; + + /** + * + */ + constructor(token: string, channel: string) { + this.client = new WebClient(token); + this.conversationId = channel; + } + + /** + * Sends the `message` to slack if client is avaialble. + * @param message The message to send. + */ + async sendMessage(message: string) { + if (this.client && this.conversationId.length > 0 && message) { + await this.client.chat.postMessage({ + text: message, + channel: this.conversationId, + }); + } } } diff --git a/src/core/logging/slacklogger.ts~ed059be5c2dae5fdda81af669d632c83bc3abb63 b/src/core/logging/slacklogger.ts~ed059be5c2dae5fdda81af669d632c83bc3abb63 new file mode 100644 index 0000000..618048a --- /dev/null +++ b/src/core/logging/slacklogger.ts~ed059be5c2dae5fdda81af669d632c83bc3abb63 @@ -0,0 +1,28 @@ +import { WebClient } from "@slack/web-api"; + +/** + * Creates and returns a Slack WebClient using OAuth2 tokens. + * @param token The token for OAuth2 authentication. + */ +export function getSlackClient(token: string) { + return new WebClient(token); +} +/** + * Sends the `message` to the Slack channel `channel` if `client` is not undefined. If + * `client` is undefined, it will print `message` to stdout. + * @param message The message to send to slack. + * @param client The slack WebClient or undefined. + * @param channel The slack Channel to send the message to or undefined. + */ +export async function sendSlackMessage(message: string, client: WebClient | undefined, channel: string | undefined) { + if (client && channel) { + // send log to slack channel + await client.chat.postMessage({ + text: message, + channel: channel, + }); + } else { + // log to stdout + console.log(message); + } +} diff --git a/src/core/types/arbitrageloops/mempoolLoop.ts b/src/core/types/arbitrageloops/mempoolLoop.ts index 98acfce..5c51b64 100644 --- a/src/core/types/arbitrageloops/mempoolLoop.ts +++ b/src/core/types/arbitrageloops/mempoolLoop.ts @@ -4,8 +4,10 @@ import { createJsonRpcRequest } from "@cosmjs/tendermint-rpc/build/jsonrpc"; import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { OptimalTrade } from "../../arbitrage/arbitrage"; +import { Logger } from "../../logging"; import { BotClients } from "../../node/chainoperator"; import { BotConfig } from "../base/botConfig"; +import { LogType } from "../base/logging"; import { flushTxMemory, Mempool, MempoolTrade, processMempool } from "../base/mempool"; import { Path } from "../base/path"; import { applyMempoolTradesOnPools, Pool } from "../base/pool"; @@ -22,6 +24,7 @@ export class MempoolLoop { sequence = 0; chainid = ""; botConfig: BotConfig; + logger: Logger | undefined; // CACHE VALUES totalBytes = 0; mempool!: Mempool; @@ -37,6 +40,7 @@ export class MempoolLoop { walletAddress: string, flashloancontract: string, ) => [Array, number]; + /** * */ @@ -53,6 +57,7 @@ export class MempoolLoop { botClients: BotClients, account: AccountData, botConfig: BotConfig, + logger: Logger | undefined, ) { this.pools = pools; this.paths = paths; @@ -62,7 +67,9 @@ export class MempoolLoop { this.botClients = botClients; this.account = account; this.botConfig = botConfig; + this.logger = logger; } + /** * */ @@ -124,6 +131,7 @@ export class MempoolLoop { this.totalBytes = 0; flushTxMemory(); } + /** * */ @@ -133,7 +141,9 @@ export class MempoolLoop { this.account.address, this.botConfig.flashloanRouterAddress, ); - console.log(msgs); + + await this.logger?.sendMessage(JSON.stringify(msgs), LogType.Console); + const signerData = { accountNumber: this.accountNumber, sequence: this.sequence, @@ -142,9 +152,7 @@ export class MempoolLoop { const TX_FEE = this.botConfig.txFees.get(arbTrade.path.pools.length) ?? - - Array.from(this.botConfig.txFees.values())[this.botConfig.gasFees.size - 1]; - + Array.from(this.botConfig.txFees.values())[this.botConfig.txFees.size - 1]; // sign, encode and broadcast the transaction const txRaw = await this.botClients.SigningCWClient.sign( @@ -156,7 +164,9 @@ export class MempoolLoop { ); const txBytes = TxRaw.encode(txRaw).finish(); const sendResult = await this.botClients.TMClient.broadcastTxSync({ tx: txBytes }); - console.log(sendResult); + + await this.logger?.sendMessage(JSON.stringify(sendResult), LogType.Console); + this.sequence += 1; await delay(5000); await this.fetchRequiredChainData(); diff --git a/src/core/types/arbitrageloops/skipLoop.ts b/src/core/types/arbitrageloops/skipLoop.ts index 0907334..6315f7a 100644 --- a/src/core/types/arbitrageloops/skipLoop.ts +++ b/src/core/types/arbitrageloops/skipLoop.ts @@ -9,10 +9,11 @@ import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx"; import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { OptimalTrade } from "../../arbitrage/arbitrage"; -import { sendSlackMessage } from "../../logging/slacklogger"; +import { Logger } from "../../logging"; import { BotClients } from "../../node/chainoperator"; import { SkipResult } from "../../node/skipclients"; import { BotConfig } from "../base/botConfig"; +import { LogType } from "../base/logging"; import { MempoolTrade, processMempool } from "../base/mempool"; import { Path } from "../base/path"; import { applyMempoolTradesOnPools, Pool } from "../base/pool"; @@ -25,6 +26,8 @@ export class SkipLoop extends MempoolLoop { skipClient: SkipBundleClient; skipSigner: DirectSecp256k1HdWallet; slackLogger: WebClient | undefined; + logger: Logger | undefined; + /** * */ @@ -43,10 +46,10 @@ export class SkipLoop extends MempoolLoop { botConfig: BotConfig, skipClient: SkipBundleClient, skipSigner: DirectSecp256k1HdWallet, - slackLogger: WebClient | undefined, + logger: Logger | undefined, ) { - super(pools, paths, arbitrage, updateState, messageFunction, botClients, account, botConfig); - (this.skipClient = skipClient), (this.skipSigner = skipSigner), (this.slackLogger = slackLogger); + super(pools, paths, arbitrage, updateState, messageFunction, botClients, account, botConfig, logger); + (this.skipClient = skipClient), (this.skipSigner = skipSigner), (this.logger = logger); } /** @@ -82,6 +85,7 @@ export class SkipLoop extends MempoolLoop { } } } + /** * */ @@ -92,7 +96,10 @@ export class SkipLoop extends MempoolLoop { this.botConfig.skipConfig?.skipBidRate === undefined || this.botConfig.skipConfig?.skipBidWallet === undefined ) { - console.error("please setup skip variables in the config environment file", 1); + await this.logger?.sendMessage( + "Please setup skip variables in the config environment file", + LogType.Console, + ); return; } const bidMsg: MsgSend = MsgSend.fromJSON({ @@ -125,7 +132,7 @@ export class SkipLoop extends MempoolLoop { //if gas fee cannot be found in the botconfig based on pathlengths, pick highest available const TX_FEE = this.botConfig.txFees.get(arbTrade.path.pools.length) ?? - Array.from(this.botConfig.txFees.values())[this.botConfig.gasFees.size - 1]; + Array.from(this.botConfig.txFees.values())[this.botConfig.txFees.size - 1]; const txRaw: TxRaw = await this.botClients.SigningCWClient.sign( this.account.address, @@ -142,47 +149,40 @@ export class SkipLoop extends MempoolLoop { const res = await this.skipClient.sendBundle(signed, 0, true); - let slackMessage = - "<*wallet:* " + - this.account.address + - "\n" + - " *block:* " + - res.result.desired_height + - "\t" + - "*profit:* " + - arbTrade.profit + - "\t" + - "*errorcode* " + - res.result.code + - ":\t" + - res.result.error + - "\n"; - - console.log(res); + let logItem = ""; + let logMessage = `**wallet:** ${this.account.address}\t **block:** ${res.result.desired_height}\t **profit:** ${arbTrade.profit}`; + + if (res.result.code !== 0) { + logMessage += `\t **error code:** ${res.result.code}\n**error:** ${res.result.error}\n`; + } + if (res.result.result_check_txs != undefined) { res.result.result_check_txs.map(async (item, idx) => { if (item["code"] != "0") { - console.log("CheckTx Error on index: ", idx); - console.log(item); + logItem = JSON.stringify(item); - const slackMessageCheckTx = ">*CheckTx Error* on index: " + idx + ":\t" + String(item.log) + "\n"; - slackMessage = slackMessage.concat(slackMessageCheckTx); + const logMessageCheckTx = `**CheckTx Error:** index: ${idx}\t ${String(item.log)}\n`; + logMessage = logMessage.concat(logMessageCheckTx); } }); } if (res.result.result_deliver_txs != undefined) { res.result.result_deliver_txs.map(async (item, idx) => { if (item["code"] != "0") { - console.log("deliver tx result of index: ", idx); - console.log(item); - const slackMessageDeliverTx = - ">*DeliverTx Error* on index: " + idx + "\t" + String(item.log) + "\n"; - slackMessage = slackMessage.concat(slackMessageDeliverTx); + logItem = JSON.stringify(item); + + const logMessageDeliverTx = `**DeliverTx Error:** index: ${idx}\t ${String(item.log)}\n`; + logMessage = logMessage.concat(logMessageDeliverTx); } }); } - console.log(slackMessage); - await sendSlackMessage(slackMessage, this.slackLogger, this.botConfig.slackChannel); + + await this.logger?.sendMessage(logMessage, LogType.All, res.result.code); + + if (logItem.length > 0) { + await this.logger?.sendMessage(logItem, LogType.Console); + } + if (res.result.code === 0) { this.sequence += 1; } else { @@ -191,6 +191,7 @@ export class SkipLoop extends MempoolLoop { await delay(5000); } } + /** * */ diff --git a/src/core/types/base/botConfig.ts b/src/core/types/base/botConfig.ts index f77bea5..bde209c 100644 --- a/src/core/types/base/botConfig.ts +++ b/src/core/types/base/botConfig.ts @@ -30,6 +30,8 @@ export interface BotConfig { slackToken?: string | undefined; // channel the bot logs in to slackChannel?: string | undefined; + // Discord webhook url + discordWebhookUrl?: string | undefined; // Skip specific (optionally) skipConfig: SkipConfig | undefined; @@ -97,6 +99,7 @@ export function setBotConfig(envs: NodeJS.ProcessEnv): BotConfig { txFees: TX_FEES, slackToken: envs.SLACK_TOKEN, slackChannel: envs.SLACK_CHANNEL, + discordWebhookUrl: envs.DISCORD_WEBHOOK_URL, skipConfig: skipConfig, }; return botConfig; diff --git a/src/core/types/base/logging.ts b/src/core/types/base/logging.ts new file mode 100644 index 0000000..0252668 --- /dev/null +++ b/src/core/types/base/logging.ts @@ -0,0 +1,7 @@ +export enum LogType { + All = "ALL", + Console = "CONSOLE", + Externals = "EXTERNALS", + Discord = "DISCORD", + Slack = "SLACK", +} diff --git a/src/index.ts b/src/index.ts index 94dfb81..e86d592 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,75 +4,90 @@ import dotenv from "dotenv"; import * as chains from "./chains"; import { trySomeArb } from "./core/arbitrage/arbitrage"; import { getPaths, newGraph } from "./core/arbitrage/graph"; -import { getSlackClient, sendSlackMessage } from "./core/logging/slacklogger"; +import { Logger } from "./core/logging"; import { getChainOperator } from "./core/node/chainoperator"; import { getSkipClient } from "./core/node/skipclients"; import { MempoolLoop } from "./core/types/arbitrageloops/mempoolLoop"; import { SkipLoop } from "./core/types/arbitrageloops/skipLoop"; import { setBotConfig } from "./core/types/base/botConfig"; +import { LogType } from "./core/types/base/logging"; import { removedUnusedPools } from "./core/types/base/pool"; // load env files dotenv.config(); const botConfig = setBotConfig(process.env); -console.log("---".repeat(30)); -console.log("Environmental variables for setup:"); -console.log("RPC ENPDOINT: ", botConfig.rpcUrl); -console.log("OFFER DENOM: ", botConfig.offerAssetInfo); -// console.log("POOLS: ", botConfig.poolEnvs); -console.log("FACTORIES_TO_ROUTERS_MAPPING", botConfig.mappingFactoryRouter); -console.log("USE MEMPOOL: ", botConfig.useMempool); - +let startupMessage = "===".repeat(30); +startupMessage += "\n**White Whale Bot**\n"; +startupMessage += "===".repeat(30); +startupMessage += `\nEnvironment Variables:\n +**RPC ENPDOINT:** \t${botConfig.rpcUrl} +**OFFER DENOM:** \t${JSON.stringify(botConfig.offerAssetInfo)} +**FACTORIES_TO_ROUTERS_MAPPING:** \t${JSON.stringify(botConfig.mappingFactoryRouter)} +**USE MEMPOOL:** \t${botConfig.useMempool} +**USE SKIP:** \t${botConfig.skipConfig?.useSkip} +`; if (botConfig.skipConfig) { - console.log("USE SKIP: ", botConfig.skipConfig.useSkip); - console.log("SKIP URL: ", botConfig.skipConfig.skipRpcUrl); + startupMessage += `**SKIP URL:** \t${botConfig.skipConfig.skipRpcUrl}\n`; + startupMessage += `**SKIP BID RATE:** \t${botConfig.skipConfig.skipBidRate}\n`; } -console.log("---".repeat(30)); +startupMessage += "---".repeat(30); /** * Runs the main program. */ async function main() { + const logger = new Logger(botConfig); let getFlashArbMessages = chains.defaults.getFlashArbMessages; let getPoolStates = chains.defaults.getPoolStates; let initPools = chains.defaults.initPools; - await import("./chains/" + botConfig.chainPrefix).then((chainSetups) => { + + await import("./chains/" + botConfig.chainPrefix).then(async (chainSetups) => { if (chainSetups === undefined) { - console.log("Unable to resolve specific chain imports, using defaults"); + await logger.sendMessage("Unable to resolve specific chain imports, using defaults", LogType.Console); } getFlashArbMessages = chainSetups.getFlashArbMessages; getPoolStates = chainSetups.getPoolStates; initPools = chainSetups.initPools; return; }); - console.log("Setting up connections and paths"); + const [account, botClients] = await getChainOperator(botConfig); - let slackClient; - if (botConfig.slackToken) { - slackClient = getSlackClient(botConfig.slackToken); - } const { accountNumber, sequence } = await botClients.SigningCWClient.getSequence(account.address); const chainId = await ( await botClients.HttpClient.execute(createJsonRpcRequest("block")) ).result.block.header.chain_id; - console.log("accountnumber: ", accountNumber, " sequence: ", sequence, "chainid: ", chainId); - console.log("Done, Clients established"); - console.log("---".repeat(30)); - console.log("Deriving paths for arbitrage"); + + let setupMessage = ` +Connections Details:\n +**Account Number:** ${accountNumber} +**Sequence:** \t${sequence} +**Chain Id:** \t${chainId}\n`; + setupMessage += "---".repeat(30); + const allPools = await initPools(botClients, botConfig.poolEnvs, botConfig.mappingFactoryRouter); const graph = newGraph(allPools); const paths = getPaths(graph, botConfig.offerAssetInfo, botConfig.maxPathPools) ?? []; - console.log("total paths: ", paths.length); - for (let i = 2; i <= botConfig.maxPathPools; i++) { - const nrOfPaths = paths.filter((path) => path.pools.length === i).length; - console.log(`${i}hop paths: `, nrOfPaths); - } - console.log("---".repeat(30)); + const filteredPools = removedUnusedPools(allPools, paths); - console.log("Removed ", allPools.length - filteredPools.length, " unused pools"); + + setupMessage += `**Total Paths:** \t${paths.length}\n`; + for (let pathlength = 2; 2 <= botConfig.maxPathPools; pathlength++) { + const nrOfPaths = paths.filter((path) => path.pools.length === pathlength).length; + setupMessage += ` + Derived Paths for Arbitrage:\n + **${pathlength} HOP Paths:** \t${nrOfPaths}`; + } + + setupMessage += `(Removed ${allPools.length - filteredPools.length} unused pools)\n`; + setupMessage += "---".repeat(30); + + startupMessage += setupMessage; + + await logger.sendMessage(startupMessage, LogType.Console); + let loop; if (botConfig.skipConfig) { - console.log("Initializing skip loop"); + await logger.sendMessage("Initializing skip loop...", LogType.Console); const [skipClient, skipSigner] = await getSkipClient( botConfig.skipConfig.skipRpcUrl, botConfig.mnemonic, @@ -89,10 +104,11 @@ async function main() { botConfig, skipClient, skipSigner, - slackClient, + logger, ); } else if (botConfig.useMempool === true) { - console.log("Initializing mempool loop"); + await logger.sendMessage("Initializing mempool loop...", LogType.Console); + loop = new MempoolLoop( filteredPools, paths, @@ -102,30 +118,23 @@ async function main() { botClients, account, botConfig, + logger, ); } else { - await sendSlackMessage("loop without mempool or skip not implemented yet", slackClient, botConfig.slackChannel); + await logger.sendMessage("**Info:** loop without mempool or skip not implemented yet"); return; } // main loop of the bot await loop.fetchRequiredChainData(); - console.log("starting loop"); + await logger.sendMessage("Starting loop...", LogType.All); + while (true) { await loop.step(); loop.reset(); if (loop.iterations % 150 === 0) { - await sendSlackMessage( - ">*chain: * " + - loop.chainid + - " *wallet: * " + - account.address + - " sign of life, bot is running for " + - loop.iterations + - " blocks", - slackClient, - botConfig.slackChannel, - ); + const message = `**chain:** ${loop.chainid} **wallet:** ${account.address} **status:** running for ${loop.iterations} blocks`; + await logger.sendMessage(message); } } }