Skip to content

Commit

Permalink
feat(cctx): accept both inbound and cctx hash (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Sep 12, 2023
1 parent f456418 commit d7b6c52
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 34 deletions.
153 changes: 121 additions & 32 deletions helpers/tx.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getEndpoints } from "@zetachain/networks";
import axios from "axios";
import clc from "cli-color";

Check failure on line 3 in helpers/tx.ts

View workflow job for this annotation

GitHub Actions / build

Could not find a declaration file for module 'cli-color'. '/home/runner/work/toolkit/toolkit/node_modules/cli-color/index.js' implicitly has an 'any' type.
import { HardhatRuntimeEnvironment } from "hardhat/types";
import Spinnies from "spinnies";

Check failure on line 5 in helpers/tx.ts

View workflow job for this annotation

GitHub Actions / build

Could not find a declaration file for module 'spinnies'. '/home/runner/work/toolkit/toolkit/node_modules/spinnies/index.js' implicitly has an 'any' type.
import WebSocket from "ws";

Expand All @@ -12,24 +13,40 @@ const getEndpoint = (key: any): string => {
return endpoint;
};

const fetchCCTX = async (
const findByChainId = (config: any, targetChainId: Number): Object | null => {
for (const key in config) {
if (config.hasOwnProperty(key) && config[key].hasOwnProperty("chainId")) {
if (config[key].chainId === targetChainId) {
return config[key];
}
}
}
return null;
};

const fetchCCTXByInbound = async (
hash: string,
spinnies: any,
API: string,
cctxList: any
cctxList: 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]) {
if (
cctxHash &&
!cctxList[cctxHash] &&
!spinnies.spinners[`spinner-${cctxHash}`]
) {
cctxList[cctxHash] = [];
}
if (!spinnies.spinners[`spinner-${cctxHash}`]) {
spinnies.add(`spinner-${cctxHash}`, {
text: `${cctxHash}`,
});
if (!json) {
spinnies.add(`spinner-${cctxHash}`, {
text: `${cctxHash}`,
});
}
}
});
} catch (error) {}
Expand All @@ -46,13 +63,27 @@ const fetchCCTXData = async (
cctxHash: string,
spinnies: any,
API: string,
cctxList: any
cctxList: any,
pendingNonces: any,
json: Boolean,
hre: HardhatRuntimeEnvironment
) => {
const url = `${API}/zeta-chain/crosschain/cctx/${cctxHash}`;
const apiResponse = await axios.get(url);
const cctx = apiResponse?.data?.CrossChainTx;
const { networks } = hre.config;
const cctx = await getCCTX(cctxHash, 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;
if (outbound_tx_hash) {
const rpc = findByChainId(networks, parseInt(receiver_chainId))?.url;

Check failure on line 77 in helpers/tx.ts

View workflow job for this annotation

GitHub Actions / build

Property 'url' does not exist on type 'Object'.
const provider = new hre.ethers.providers.JsonRpcProvider(rpc);

Check failure on line 78 in helpers/tx.ts

View workflow job for this annotation

GitHub Actions / build

Property 'ethers' does not exist on type 'HardhatRuntimeEnvironment'.
const confirmed = await provider.getTransaction(outbound_tx_hash);
confirmed_on_destination = confirmed !== null;
}
const tx = {
receiver_chainId: cctx.outbound_tx_params[0].receiver_chainId,
confirmed_on_destination,
outbound_tx_hash,
outbound_tx_tss_nonce: cctx.outbound_tx_params[0].outbound_tx_tss_nonce,
receiver_chainId,
sender_chain_id: cctx.inbound_tx_params.sender_chain_id,
status: cctx.cctx_status.status,
status_message: cctx.cctx_status.status_message,
Expand All @@ -63,6 +94,12 @@ const fetchCCTXData = async (
cctxList[cctxHash].push(tx);
const sender = cctxList[cctxHash]?.[0].sender_chain_id;
const receiver = cctxList[cctxHash]?.[0].receiver_chainId;
const pendingNonce = pendingNonces.find(
(n: any) => n.chain_id === tx.receiver_chainId
)?.nonce_low;
const txNonce = tx.outbound_tx_tss_nonce;
const queue =
txNonce > pendingNonce ? ` (${txNonce - pendingNonce} in queue)` : "";
const path = cctxList[cctxHash]
.map(
(x: any) =>
Expand All @@ -72,53 +109,104 @@ const fetchCCTXData = async (
)
.join(" → ");
const text = {
text: `${cctxHash}: ${sender}${receiver}: ${path}`,
text: `${cctxHash}: ${sender}${receiver}${queue}: ${path}`,
};
const id = `spinner-${cctxHash}`;
switch (tx.status) {
case "OutboundMined":
case "Reverted":
if (!json && spinnies.spinners[id]) {
const s = tx.status;
if (s == "OutboundMined" || s == "Reverted") {
spinnies.succeed(id, text);
break;
case "Aborted":
} else if (s == "Aborted") {
spinnies.fail(id, text);
break;
default:
} else {
spinnies.update(id, text);
break;
}
}
}
};

export const trackCCTX = async (inboundTxHash: string): Promise<void> => {
const getCCTX = async (hash: string, API: string) => {
try {
const url = `${API}/zeta-chain/crosschain/cctx/${hash}`;
const apiResponse = await axios.get(url);
return apiResponse?.data?.CrossChainTx;
} catch (e) {}
};

const fetchNonces = async (API: string, TSS: string) => {
try {
const url = `${API}/zeta-chain/crosschain/pendingNonces`;
const apiResponse = await axios.get(url);
const nonces = apiResponse?.data?.pending_nonces;
return nonces.filter((n: any) => n.tss === TSS);
} catch (e) {}
};

const fetchTSS = async (API: string) => {
try {
const url = `${API}/zeta-chain/crosschain/TSS`;
const apiResponse = await axios.get(url);
return apiResponse?.data?.TSS.tss_pubkey;
} catch (e) {}
};

export const trackCCTX = async (
hash: string,
hre: HardhatRuntimeEnvironment,
json: Boolean
): Promise<void> => {
const spinnies = new Spinnies();

const API = getEndpoint("cosmos-http");
const WSS = getEndpoint("tendermint-ws");
const WSS = "wss://rpc-archive.athens.zetachain.com:26657/websocket";
const TSS = await fetchTSS(API);

return new Promise((resolve, reject) => {
let cctxList: any = {};
let pendingNonces: any = [];

const socket = new WebSocket(WSS);
socket.on("open", () => socket.send(JSON.stringify(SUBSCRIBE_MESSAGE)));
socket.on("message", async () => {
pendingNonces = await fetchNonces(API, TSS);
if (Object.keys(cctxList).length === 0) {
spinnies.add(`search`, {
text: `Looking for cross-chain transactions (CCTXs) on ZetaChain...\n`,
});
await fetchCCTX(inboundTxHash, spinnies, API, cctxList);
if (!json) {
spinnies.add(`search`, {
text: `Looking for cross-chain transactions (CCTXs) on ZetaChain...\n`,
});
}
await fetchCCTXByInbound(hash, spinnies, API, cctxList, 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}`,
});
}
}
}
for (const txHash in cctxList) {
await fetchCCTX(txHash, spinnies, API, cctxList);
await fetchCCTXByInbound(txHash, spinnies, API, cctxList, json);
}
if (Object.keys(cctxList).length > 0) {
if (spinnies.spinners["search"]) {
if (spinnies.spinners["search"] && !json) {
spinnies.succeed(`search`, {
text: `CCTXs on ZetaChain found.\n`,
});
}
for (const cctxHash in cctxList) {
try {
fetchCCTXData(cctxHash, spinnies, API, cctxList);
fetchCCTXData(
cctxHash,
spinnies,
API,
cctxList,
pendingNonces,
json,
hre
);
} catch (error) {}
}
}
Expand All @@ -132,13 +220,14 @@ export const trackCCTX = async (inboundTxHash: string): Promise<void> => {
.filter((s) => !["OutboundMined", "Aborted", "Reverted"].includes(s))
.length === 0
) {
if (json) console.log(JSON.stringify(cctxList, null, 2));
socket.close();
}
});
socket.on("error", (error: any) => {
reject(error);
});
socket.on("close", () => {
socket.on("close", (code) => {
resolve();
});
});
Expand Down
6 changes: 4 additions & 2 deletions tasks/cctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { trackCCTX } from "../helpers";
declare const hre: any;

const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
await trackCCTX(args.tx);
await trackCCTX(args.tx, hre, args.json);
};

export const cctxTask = task(
"cctx",
"Track cross-chain transaction status",
main
).addParam("tx", "Inbound transaction hash");
)
.addPositionalParam("tx", "TX hash of an inbound transaction or a CCTX")
.addFlag("json", "Output as JSON");

0 comments on commit d7b6c52

Please sign in to comment.