diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 471da63..2cb2556 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -38,6 +38,7 @@ import { totalPollingOnChainChecks, totalPollingRuns, totalPollingUnexpectedErrors, + totalPollingOnChainEthersErrors, } from "../utils/metrics"; const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"; @@ -529,6 +530,7 @@ async function _pollLegacy( const { contract, multicall, chainId } = context; const logPrefix = `checkForAndPlaceOrder:_pollLegacy:${orderRef}`; const log = getLogger(logPrefix); + const { handler } = conditionalOrder.params; // as we going to use multicall, with `aggregate3Value`, there is no need to do any simulation as the // calls are guaranteed to pass, and will return the results, or the reversion within the ABI-encoded data. // By not using `populateTransaction`, we avoid an `eth_estimateGas` RPC call. @@ -537,6 +539,8 @@ async function _pollLegacy( "getTradeableOrderWithSignature", [owner, conditionalOrder.params, offchainInput, proof] ); + const id = ConditionalOrderSDK.leafToId(conditionalOrder.params); + const metricLabels = [chainId.toString(), owner, handler, id]; try { const lowLevelCall = await multicall.callStatic.aggregate3Value([ @@ -573,12 +577,13 @@ async function _pollLegacy( target, callData, revertData: returnData, + metricLabels, }); } catch (error: any) { // We can only get here from some provider / ethers failure. As the contract hasn't had it's say // we will defer to try again. - // TODO: Add metrics to track this log.error(`${logPrefix} ethers/call Unexpected error`, error); + totalPollingOnChainEthersErrors.labels(...metricLabels).inc(); return { result: PollResultCode.TRY_NEXT_BLOCK, reason: diff --git a/src/utils/contracts.ts b/src/utils/contracts.ts index 2d01bad..4859ebc 100644 --- a/src/utils/contracts.ts +++ b/src/utils/contracts.ts @@ -8,6 +8,7 @@ import { SupportedChainId, } from "@cowprotocol/cow-sdk"; import { getLogger } from "./logging"; +import { totalPollingOnChainInvalidInterfaces } from "./metrics"; // Selectors that are required to be part of the contract's bytecode in order to be considered compatible const REQUIRED_SELECTORS = [ @@ -196,8 +197,17 @@ export function handleOnChainCustomError(params: { target: string; callData: string; revertData: string; + metricLabels: string[]; }): PollResultErrors { - const { owner, orderRef, chainId, target, callData, revertData } = params; + const { + owner, + orderRef, + chainId, + target, + callData, + revertData, + metricLabels, + } = params; const logPrefix = `contracts:handleOnChainCustomError:${orderRef}`; try { @@ -287,6 +297,7 @@ export function handleOnChainCustomError(params: { log.debug( `Contract returned a non-compliant interface revert via getTradeableOrderWithSignature. Simulate: https://dashboard.tenderly.co/gp-v2/watch-tower-prod/simulator/new?network=${chainId}&contractAddress=${target}&rawFunctionInput=${callData}` ); + totalPollingOnChainInvalidInterfaces.labels(...metricLabels).inc(); return { result: PollResultCode.DONT_TRY_AGAIN, reason: "Order returned a non-compliant (invalid/erroneous) revert hint", diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index c753b6e..c3b7ded 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -108,6 +108,18 @@ const pollingOnChainDurationSeconds = new client.Histogram({ labelNames: ["chain_id", "handler", "owner", "id"], }); +const totalPollingOnChainInvalidInterfaces = new client.Counter({ + name: "watch_tower_polling_onchain_invalid_interface_total", + help: "Total number of invalid on-chain hint interface", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +const totalPollingOnChainEthersErrors = new client.Counter({ + name: "watch_tower_polling_onchain_ethers_errors_total", + help: "Total number of ethers on-chain hint errors", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + const totalPollingUnexpectedErrors = new client.Counter({ name: "watch_tower_polling_unexpected_errors_total", help: "Total number of unexpected polling errors", @@ -132,6 +144,8 @@ export { totalOrderBookErrors, totalPollingRuns, totalPollingOnChainChecks, + totalPollingOnChainInvalidInterfaces, + totalPollingOnChainEthersErrors, pollingOnChainDurationSeconds, totalPollingUnexpectedErrors, }; diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts index 2e90c03..a0048c6 100644 --- a/src/utils/utils.spec.ts +++ b/src/utils/utils.spec.ts @@ -100,6 +100,7 @@ describe("handle on-chain custom errors", () => { revertData: abiToSelector( CUSTOM_ERROR_ABI_MAP[CustomErrorSelectors.SINGLE_ORDER_NOT_AUTHED] ), + metricLabels: [], }; it("should pass a known selector correctly", () => {