diff --git a/analytics/index.js b/analytics/index.js index 9afa6d3..6162604 100644 --- a/analytics/index.js +++ b/analytics/index.js @@ -8,6 +8,7 @@ import Debug from "debug"; import { tick as tickArchethic } from "./src/archethic.js"; import { tick as tickEVM } from "./src/evm.js"; import htlcsController from "./src/controllers/htlcs.js"; +import refundsController from "./src/controllers/refunds.js"; const debug = Debug("server"); const ENDPOINT = config.get("archethic.endpoint"); @@ -33,6 +34,7 @@ app.set("views", "./src/views"); app.set("view engine", "jade"); app.get("/htlcs", htlcsController(db)); +app.get("/refunds", refundsController(db)); app.get("/metrics", (req, res) => { let text = ""; diff --git a/analytics/src/controllers/refunds.js b/analytics/src/controllers/refunds.js new file mode 100644 index 0000000..d7f8133 --- /dev/null +++ b/analytics/src/controllers/refunds.js @@ -0,0 +1,84 @@ +import { getHTLCs as getEVMHTLCs } from "../registry/evm-htlcs.js"; +import { getHTLCs as getArchethicHtlcs } from "../registry/archethic-htlcs.js"; +import { HTLC_STATUS } from "../archethic/get-htlc-statuses.js"; +import config from "config"; + +const ARCHETHIC_ENDPOINT = config.get("archethic.endpoint"); +const EVM_NETWORKS = config.get("evm"); + +export default function (db) { + return async (req, res) => { + const chargeableHTLCs = filterRefund(merge( + await getArchethicHtlcs(db, "chargeable"), + await getEVMHTLCs(db, "chargeable"), + "chargeable", + )); + + const signedHTLCs = filterRefund(merge( + await getArchethicHtlcs(db, "signed"), + await getEVMHTLCs(db, "signed"), + "signed", + )); + + const htlcs = [...chargeableHTLCs, ...signedHTLCs]; + + res.json(htlcs.map((htlc) => { + if (htlc.type == "chargeable") { + return { archethicAddress: htlc.address, claimingAmount: htlc.amount / 100_000_000 } + } + else { + return { evmAddress: htlc.evmHtlc.address, chain: htlc.evmHtlc.chain, claimingAmount: htlc.evmHtlc.amount } + } + })); + }; +} + +function merge(archethicHtlcs, evmHtlcs, type) { + // dump mumbai + archethicHtlcs = archethicHtlcs.filter((htlc) => htlc.evmChainID != 80001); + + let evmHtlcsToDiscard = []; + + // match HTLCs (best effort) + for (const archethicHtlc of archethicHtlcs) { + archethicHtlc.type = type; + if (archethicHtlc.evmContract) { + const match = evmHtlcs.find( + (evmHtlc) => + evmHtlc.address.toLowerCase() == + archethicHtlc.evmContract.toLowerCase(), + ); + if (match != null) { + archethicHtlc.evmHtlc = match; + evmHtlcsToDiscard.push(archethicHtlc.evmHtlc.address); + } + } else { + // Try to match based on the locktime (2s tolerance) + const matches = evmHtlcs.filter( + (evmHtlc) => Math.abs(evmHtlc.lockTime - archethicHtlc.endTime) < 2, + ); + if (matches.length == 1) { + archethicHtlc.evmHtlc = matches[0]; + evmHtlcsToDiscard.push(archethicHtlc.evmHtlc.address); + } + } + } + + // evm HTLCs with no match in archethic + for (const evmHtlc of evmHtlcs) { + if (evmHtlcsToDiscard.includes(evmHtlc.address)) continue; + + archethicHtlcs.push({ evmHtlc: evmHtlc }); + } + return archethicHtlcs; +} + +function filterRefund(htlcs) { + return htlcs.filter((htlc) => { + if (!htlc.evmHtlc || !htlc.creationTime) { + return false + } + + return (htlc.evmHtlc.status == "REFUNDED" && htlc.status != 2) || (htlc.status == 2 && htlc.evmHtlc.status != "REFUNDED") + }) +}