diff --git a/helpers/tx.ts b/helpers/tx.ts index f07727a0..52d03484 100644 --- a/helpers/tx.ts +++ b/helpers/tx.ts @@ -1,9 +1,6 @@ -import { getEndpoints } from "@zetachain/networks"; -import { getHardhatConfigNetworks } from "@zetachain/networks"; +import { getEndpoints, getHardhatConfigNetworks } from "@zetachain/networks"; import axios from "axios"; -import clc from "cli-color"; import { ethers } from "ethers"; -import Spinnies from "spinnies"; const getEndpoint = (key: any): string => { const endpoint = getEndpoints(key, "zeta_testnet")[0]?.url; @@ -26,26 +23,22 @@ const findByChainId = (config: any, targetChainId: Number): Object | null => { const fetchCCTXByInbound = async ( hash: string, - spinnies: any, + emitter: any, + spinners: any, API: string, - cctxList: any, + cctxs: any, json: Boolean ) => { try { const url = `${API}/zeta-chain/crosschain/inTxHashToCctx/${hash}`; const apiResponse = await axios.get(url); const res = apiResponse?.data?.inTxHashToCctx?.cctx_index; - res.forEach((cctxHash: any) => { - if ( - cctxHash && - !cctxList[cctxHash] && - !spinnies.spinners[`spinner-${cctxHash}`] - ) { - cctxList[cctxHash] = []; - if (!json) { - spinnies.add(`spinner-${cctxHash}`, { - text: `${cctxHash}`, - }); + res.forEach((hash: any) => { + if (hash && !cctxs[hash] && !spinners[hash]) { + cctxs[hash] = []; + if (!json && emitter) { + emitter.emit("add", { hash, text: hash }); + spinners[hash] = true; } } }); @@ -53,15 +46,16 @@ const fetchCCTXByInbound = async ( }; const fetchCCTXData = async ( - cctxHash: string, - spinnies: any, + hash: string, + emitter: any, + spinners: any, API: string, - cctxList: any, + cctxs: any, pendingNonces: any, json: Boolean ) => { const networks = getHardhatConfigNetworks(); - const cctx = await getCCTX(cctxHash, API); + const cctx = await getCCTX(hash, API); const receiver_chainId = cctx?.outbound_tx_params[0]?.receiver_chainId; const outbound_tx_hash = cctx?.outbound_tx_params[0]?.outbound_tx_hash; let confirmed_on_destination = false; @@ -80,42 +74,41 @@ const fetchCCTXData = async ( status: cctx?.cctx_status?.status, status_message: cctx?.cctx_status?.status_message, }; - const lastCCTX = cctxList[cctxHash][cctxList[cctxHash].length - 1]; - const isEmpty = cctxList[cctxHash].length === 0; + const lastCCTX = cctxs[hash][cctxs[hash].length - 1]; + const isEmpty = cctxs[hash].length === 0; const statusDefined = tx.status !== undefined && tx.status_message !== undefined; if (isEmpty || (statusDefined && lastCCTX.status !== tx.status)) { - cctxList[cctxHash].push(tx); - const sender = cctxList[cctxHash]?.[0].sender_chain_id; - const receiver = cctxList[cctxHash]?.[0].receiver_chainId; + cctxs[hash].push(tx); + const sender = cctxs[hash]?.[0].sender_chain_id; + const receiver = cctxs[hash]?.[0].receiver_chainId; let queue; if (pendingNonces) { const pending = pendingNonces.find( (n: any) => n.chain_id === tx.receiver_chainId )?.nonce_low; const current = tx.outbound_tx_tss_nonce; - queue = current > pending ? ` (${current - pending} in queue)` : ""; + const diff = current - pending; + queue = diff > 0 ? ` (${diff} in queue)` : ""; } - const path = cctxList[cctxHash] + const path = cctxs[hash] .map( (x: any) => - `${clc.bold.underline(x.status)} ${ - x.status_message && "(" + x.status_message + ")" - }` + `${x.status} ${x.status_message && "(" + x.status_message + ")"}` ) .join(" → "); - const text = { - text: `${cctxHash}: ${sender} → ${receiver}${queue}: ${path}`, - }; - const id = `spinner-${cctxHash}`; - if (!json && spinnies.spinners[id]) { + const text = `${hash}: ${sender} → ${receiver}${queue}: ${path}`; + + if (!json && spinners[hash] && emitter) { const s = tx.status; if (s == "OutboundMined" || s == "Reverted") { - spinnies.succeed(id, text); + emitter.emit("succeed", { hash, text }); + spinners[hash] = false; } else if (s == "Aborted") { - spinnies.fail(id, text); + emitter.emit("fail", { hash, text }); + spinners[hash] = false; } else { - spinnies.update(id, text); + emitter.emit("update", { hash, text }); } } } @@ -148,53 +141,59 @@ const fetchTSS = async (API: string) => { export const trackCCTX = async ( hash: string, - json: Boolean = false + json: Boolean = false, + emitter: any = null ): Promise => { - const spinnies = new Spinnies(); + const spinners: any = {}; const API = getEndpoint("cosmos-http"); const TSS = await fetchTSS(API); return new Promise((resolve, reject) => { - let cctxList: any = {}; + let cctxs: any = {}; let pendingNonces: any = []; - const loopInterval = setInterval(async () => { + setInterval(async () => { pendingNonces = await fetchNonces(API, TSS); - if (Object.keys(cctxList).length === 0) { - if (!json) { - spinnies.add(`search`, { - text: `Looking for cross-chain transactions (CCTXs) on ZetaChain...\n`, - }); + if (Object.keys(cctxs).length === 0) { + if (!json && emitter) { + const text = `Looking for cross-chain transactions (CCTXs) on ZetaChain...\n`; + emitter.emit("search-add", { text }); + spinners["search"] = true; } - await fetchCCTXByInbound(hash, spinnies, API, cctxList, json); + await fetchCCTXByInbound(hash, emitter, spinners, API, cctxs, json); } - if (Object.keys(cctxList).length === 0 && !cctxList[hash]) { - if ((await getCCTX(hash, API)) && !cctxList[hash]) { - cctxList[hash] = []; - if (!spinnies.spinners[`spinner-${hash}`] && !json) { - spinnies.add(`spinner-${hash}`, { - text: `${hash}`, - }); - } + if ( + Object.keys(cctxs).length === 0 && + !cctxs[hash] && + (await getCCTX(hash, API)) && + !cctxs[hash] + ) { + cctxs[hash] = []; + if (!spinners[hash] && !json && emitter) { + spinners[hash] = true; + emitter.emit("add", { hash, text: hash }); + spinners[hash] = true; } } - for (const txHash in cctxList) { - await fetchCCTXByInbound(txHash, spinnies, API, cctxList, json); + for (const txHash in cctxs) { + await fetchCCTXByInbound(txHash, emitter, spinners, API, cctxs, json); } - if (Object.keys(cctxList).length > 0) { - if (spinnies.spinners["search"] && !json) { - spinnies.succeed(`search`, { + if (Object.keys(cctxs).length > 0) { + if (spinners["search"] && !json && emitter) { + emitter.emit("search-end", { text: `CCTXs on ZetaChain found.\n`, }); + spinners["search"] = false; } - for (const cctxHash in cctxList) { + for (const hash in cctxs) { try { fetchCCTXData( - cctxHash, - spinnies, + hash, + emitter, + spinners, API, - cctxList, + cctxs, pendingNonces, json ); @@ -202,18 +201,18 @@ export const trackCCTX = async ( } } if ( - Object.keys(cctxList).length > 0 && - Object.keys(cctxList) + Object.keys(cctxs).length > 0 && + Object.keys(cctxs) .map((c: any) => { - const last = cctxList[c][cctxList[c].length - 1]; + const last = cctxs[c][cctxs[c].length - 1]; return last?.status; }) .filter((s) => !["OutboundMined", "Aborted", "Reverted"].includes(s)) .length === 0 ) { - const allOutboundMined = Object.keys(cctxList) + const allOutboundMined = Object.keys(cctxs) .map((c: any) => { - const last = cctxList[c][cctxList[c].length - 1]; + const last = cctxs[c][cctxs[c].length - 1]; return last?.status; }) .every((s) => s === "OutboundMined"); @@ -221,7 +220,7 @@ export const trackCCTX = async ( if (!allOutboundMined) { reject("CCTX aborted or reverted"); } else { - if (json) console.log(JSON.stringify(cctxList, null, 2)); + if (json) console.log(JSON.stringify(cctxs, null, 2)); resolve(); } } diff --git a/package.json b/package.json index 1da1b53d..15a5e3f8 100644 --- a/package.json +++ b/package.json @@ -81,10 +81,10 @@ "bip39": "^3.1.0", "bitcoinjs-lib": "^6.1.3", "dotenv": "16.0.3", - "cli-color": "^2.0.3", "ecpair": "^2.1.0", "envfile": "^6.18.0", "ethers": "5.4.7", + "eventemitter3": "^5.0.1", "form-data": "^4.0.0", "handlebars": "4.7.7", "hardhat": "^2.15.0", diff --git a/tasks/cctx.ts b/tasks/cctx.ts index c716e2e0..daeb0487 100644 --- a/tasks/cctx.ts +++ b/tasks/cctx.ts @@ -1,12 +1,24 @@ +import EventEmitter from "eventemitter3"; import { task } from "hardhat/config"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; +import Spinnies from "spinnies"; import { trackCCTX } from "../helpers"; -declare const hre: any; +const trackCCTXInteractive = async (hash: string, json: Boolean = false) => { + const s = new Spinnies(); + const emitter = new EventEmitter(); + emitter + .on("search-add", ({ text }) => s.add(`search`, { text })) + .on("search-end", ({ text }) => s.succeed(`search`, { text })) + .on("add", ({ hash, text }) => s.add(hash, { text })) + .on("succeed", ({ hash, text }) => s.succeed(hash, { text })) + .on("fail", ({ hash, text }) => s.fail(hash, { text })) + .on("update", ({ hash, text }) => s.update(hash, { text })); + await trackCCTX(hash, json, emitter); +}; -const main = async (args: any, hre: HardhatRuntimeEnvironment) => { - await trackCCTX(args.tx, args.json); +const main = async (args: any) => { + await trackCCTXInteractive(args.tx, args.json); }; export const cctxTask = task( diff --git a/yarn.lock b/yarn.lock index 774c0d69..dc3dab5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2757,17 +2757,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-color@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.3.tgz#73769ba969080629670f3f2ef69a4bf4e7cc1879" - integrity sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ== - dependencies: - d "^1.0.1" - es5-ext "^0.10.61" - es6-iterator "^2.0.3" - memoizee "^0.4.15" - timers-ext "^0.1.7" - cli-cursor@^3.0.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -3023,14 +3012,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -3398,42 +3379,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -es6-weak-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -3889,13 +3834,10 @@ ethjs-util@0.1.6, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== evp_bytestokey@^1.0.3: version "1.0.3" @@ -3932,13 +3874,6 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -5223,11 +5158,6 @@ is-primitive@^2.0.0: resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" integrity sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q== -is-promise@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -5673,13 +5603,6 @@ lru-cache@^6.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== -lru-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== - dependencies: - es5-ext "~0.10.2" - lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -5726,20 +5649,6 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -memoizee@^0.4.15: - version "0.4.15" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" - integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== - dependencies: - d "^1.0.1" - es5-ext "^0.10.53" - es6-weak-map "^2.0.3" - event-emitter "^0.3.5" - is-promise "^2.2.2" - lru-queue "^0.1.0" - next-tick "^1.1.0" - timers-ext "^0.1.7" - memory-level@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" @@ -6095,11 +6004,6 @@ neo-async@^2.6.0, neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-tick@1, next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - nise@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.4.tgz#491ce7e7307d4ec546f5a659b2efe94a18b4bbc0" @@ -7672,14 +7576,6 @@ then-request@^6.0.0: promise "^8.0.0" qs "^6.4.0" -timers-ext@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" - integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== - dependencies: - es5-ext "~0.10.46" - next-tick "1" - tiny-secp256k1@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-2.2.3.tgz#fe1dde11a64fcee2091157d4b78bcb300feb9b65" @@ -7856,16 +7752,6 @@ type-fest@^0.7.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - typechain@^8.1.0: version "8.3.1" resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.1.tgz#dccbc839b94877997536c356380eff7325395cfb"