From 3a04c6eec4583e95a6d28cd3725c0f7c113c709c Mon Sep 17 00:00:00 2001 From: Xaroz Date: Fri, 13 Dec 2024 18:56:50 -0600 Subject: [PATCH 1/4] feat: check proxy contract and implementation contract --- src/features/debugger/debugMessage.ts | 80 ++++++++++++++++++--------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/src/features/debugger/debugMessage.ts b/src/features/debugger/debugMessage.ts index a97fbb9..8405335 100644 --- a/src/features/debugger/debugMessage.ts +++ b/src/features/debugger/debugMessage.ts @@ -10,12 +10,20 @@ import { IMultisigIsm__factory as MultisigIsmFactory, } from '@hyperlane-xyz/core'; import { IRegistry } from '@hyperlane-xyz/registry'; -import { ChainMap, ChainMetadata, MAILBOX_VERSION, MultiProvider } from '@hyperlane-xyz/sdk'; +import { + ChainMap, + ChainMetadata, + MAILBOX_VERSION, + MultiProvider, + isProxy, + proxyImplementation, +} from '@hyperlane-xyz/sdk'; import { addressToBytes32, errorToString, formatMessage, isValidAddress, + strip0x, trimToLength, } from '@hyperlane-xyz/utils'; @@ -29,7 +37,7 @@ import { GasPayment, IsmModuleTypes, MessageDebugResult, MessageDebugStatus } fr type Provider = providers.Provider; -// const HANDLE_FUNCTION_SIG = 'handle(uint32,bytes32,bytes)'; +const HANDLE_FUNCTION_SIG = 'handle(uint32,bytes32,bytes)'; const IGP_PAYMENT_CHECK_DELAY = 30_000; // 30 seconds export async function debugMessage( @@ -174,16 +182,6 @@ async function debugMessageDelivery( const errorReason = extractReasonString(err); logger.debug(errorReason); - // const bytecodeHasHandle = await tryCheckBytecodeHandle(destProvider, recipient); - // if (!bytecodeHasHandle) { - // logger.info('Bytecode does not have function matching handle sig'); - // return { - // status: MessageDebugStatus.RecipientNotHandler, - // description: `Recipient contract should have handle function of signature: ${HANDLE_FUNCTION_SIG}. Check that recipient is not a proxy. Error: ${errorReason}`, - // calldataDetails, - // }; - // } - if (debugIgnoredChains.includes(destName)) { return { status: null, @@ -192,6 +190,26 @@ async function debugMessageDelivery( }; } + const proxyImplementationContract = await tryGetProxyImplementationContract( + destProvider, + recipient, + ); + if (proxyImplementationContract) { + const bytecodeHasHandle = await tryCheckBytecodeHandle( + destProvider, + proxyImplementationContract, + ); + + if (!bytecodeHasHandle) { + logger.info('Bytecode does not have function matching handle sig'); + return { + status: MessageDebugStatus.RecipientNotHandler, + description: `Recipient contract should have handle function of signature: ${HANDLE_FUNCTION_SIG}. Check that recipient is not a proxy. Error: ${errorReason}`, + calldataDetails, + }; + } + } + const icaCallErr = await tryDebugIcaMsg(sender, recipient, body, originDomain, destProvider); if (icaCallErr) { return { @@ -338,19 +356,31 @@ async function fetchGasPaymentEvents(provider: Provider, messageId: string) { return { contractToPayments, contractToTotalGas, numPayments, numIGPs }; } -// async function tryCheckBytecodeHandle(provider: Provider, recipientAddress: string) { -// try { -// // scan bytecode for handle function selector -// const bytecode = await provider.getCode(recipientAddress); -// const msgRecipientInterface = MessageRecipientFactory.createInterface(); -// const handleFunction = msgRecipientInterface.functions[HANDLE_FUNCTION_SIG]; -// const handleSignature = msgRecipientInterface.getSighash(handleFunction); -// return bytecode.includes(strip0x(handleSignature)); -// } catch (error) { -// logger.error('Error checking bytecode for handle fn', error); -// return true; -// } -// } +async function tryGetProxyImplementationContract(provider: Provider, recipientAddress: string) { + try { + const isProxyContract = await isProxy(provider, recipientAddress); + if (!isProxyContract) return undefined; + + return await proxyImplementation(provider, recipientAddress); + } catch (error) { + logger.error('Error trying to check proxy contract', error); + return undefined; + } +} + +async function tryCheckBytecodeHandle(provider: Provider, recipientAddress: string) { + try { + // scan bytecode for handle function selector + const bytecode = await provider.getCode(recipientAddress); + const msgRecipientInterface = MessageRecipientFactory.createInterface(); + const handleFunction = msgRecipientInterface.functions[HANDLE_FUNCTION_SIG]; + const handleSignature = msgRecipientInterface.getSighash(handleFunction); + return bytecode.includes(strip0x(handleSignature)); + } catch (error) { + logger.error('Error checking bytecode for handle fn', error); + return true; + } +} async function tryDebugIcaMsg( sender: Address, From d113427b37ec57600d3243f51310976c9e5c8d94 Mon Sep 17 00:00:00 2001 From: Xaroz Date: Fri, 13 Dec 2024 19:25:29 -0600 Subject: [PATCH 2/4] chore: run bytecode check when contract is not proxy --- src/features/debugger/debugMessage.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/features/debugger/debugMessage.ts b/src/features/debugger/debugMessage.ts index 8405335..e1bfb31 100644 --- a/src/features/debugger/debugMessage.ts +++ b/src/features/debugger/debugMessage.ts @@ -194,12 +194,10 @@ async function debugMessageDelivery( destProvider, recipient, ); - if (proxyImplementationContract) { const bytecodeHasHandle = await tryCheckBytecodeHandle( destProvider, - proxyImplementationContract, - ); - + proxyImplementationContract || recipient, + ) if (!bytecodeHasHandle) { logger.info('Bytecode does not have function matching handle sig'); return { From d08595518137147708e3f0dffc268b850a0f3b80 Mon Sep 17 00:00:00 2001 From: Xaroz Date: Fri, 13 Dec 2024 19:28:22 -0600 Subject: [PATCH 3/4] fix: extra parenthesis --- src/features/debugger/debugMessage.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/features/debugger/debugMessage.ts b/src/features/debugger/debugMessage.ts index e1bfb31..4c1ed70 100644 --- a/src/features/debugger/debugMessage.ts +++ b/src/features/debugger/debugMessage.ts @@ -194,18 +194,17 @@ async function debugMessageDelivery( destProvider, recipient, ); - const bytecodeHasHandle = await tryCheckBytecodeHandle( - destProvider, - proxyImplementationContract || recipient, - ) - if (!bytecodeHasHandle) { - logger.info('Bytecode does not have function matching handle sig'); - return { - status: MessageDebugStatus.RecipientNotHandler, - description: `Recipient contract should have handle function of signature: ${HANDLE_FUNCTION_SIG}. Check that recipient is not a proxy. Error: ${errorReason}`, - calldataDetails, - }; - } + const bytecodeHasHandle = await tryCheckBytecodeHandle( + destProvider, + proxyImplementationContract || recipient, + ); + if (!bytecodeHasHandle) { + logger.info('Bytecode does not have function matching handle sig'); + return { + status: MessageDebugStatus.RecipientNotHandler, + description: `Recipient contract should have handle function of signature: ${HANDLE_FUNCTION_SIG}. Check that recipient is not a proxy. Error: ${errorReason}`, + calldataDetails, + }; } const icaCallErr = await tryDebugIcaMsg(sender, recipient, body, originDomain, destProvider); From db31d29738eb0c81d0a643227d1d2df7c56d5a89 Mon Sep 17 00:00:00 2001 From: Xaroz Date: Mon, 16 Dec 2024 09:46:06 -0600 Subject: [PATCH 4/4] chore: remove unnecessary description --- src/features/debugger/debugMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/debugger/debugMessage.ts b/src/features/debugger/debugMessage.ts index 4c1ed70..92cc951 100644 --- a/src/features/debugger/debugMessage.ts +++ b/src/features/debugger/debugMessage.ts @@ -202,7 +202,7 @@ async function debugMessageDelivery( logger.info('Bytecode does not have function matching handle sig'); return { status: MessageDebugStatus.RecipientNotHandler, - description: `Recipient contract should have handle function of signature: ${HANDLE_FUNCTION_SIG}. Check that recipient is not a proxy. Error: ${errorReason}`, + description: `Recipient contract should have handle function of signature: ${HANDLE_FUNCTION_SIG}. Error: ${errorReason}`, calldataDetails, }; }