diff --git a/typescript/cli/ci-test.sh b/typescript/cli/ci-test.sh index 00e61f55c1..ec80bb2f00 100755 --- a/typescript/cli/ci-test.sh +++ b/typescript/cli/ci-test.sh @@ -73,6 +73,7 @@ echo "Sending test message" yarn workspace @hyperlane-xyz/cli run hyperlane send message \ --origin anvil1 \ --destination anvil2 \ + --messageBody "Howdy!" \ --chains ./examples/anvil-chains.yaml \ --core $CORE_ARTIFACTS_PATH \ --quick \ diff --git a/typescript/cli/src/commands/send.ts b/typescript/cli/src/commands/send.ts index b1ce305981..3de0fd2a0f 100644 --- a/typescript/cli/src/commands/send.ts +++ b/typescript/cli/src/commands/send.ts @@ -1,3 +1,4 @@ +import { ethers } from 'ethers'; import { CommandModule, Options } from 'yargs'; import { TokenType } from '@hyperlane-xyz/sdk'; @@ -57,7 +58,15 @@ const messageOptions: { [k: string]: Options } = { const messageCommand: CommandModule = { command: 'message', describe: 'Send a test message to a remote chain', - builder: (yargs) => yargs.options(messageOptions), + builder: (yargs) => + yargs.options({ + ...messageOptions, + messageBody: { + type: 'string', + description: 'Optional Message body', + default: 'Hello!', + }, + }), handler: async (argv: any) => { const key: string = argv.key || process.env.HYP_KEY; const chainConfigPath: string = argv.chains; @@ -66,12 +75,14 @@ const messageCommand: CommandModule = { const destination: string | undefined = argv.destination; const timeoutSec: number = argv.timeout; const skipWaitForDelivery: boolean = argv.quick; + const messageBody: string = argv.messageBody; await sendTestMessage({ key, chainConfigPath, coreArtifactsPath, origin, destination, + messageBody: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(messageBody)), timeoutSec, skipWaitForDelivery, }); diff --git a/typescript/cli/src/send/message.ts b/typescript/cli/src/send/message.ts index 53051d6c08..563b3de62f 100644 --- a/typescript/cli/src/send/message.ts +++ b/typescript/cli/src/send/message.ts @@ -14,14 +14,13 @@ import { getContext, getMergedContractAddresses } from '../context.js'; import { runPreflightChecks } from '../deploy/utils.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; -const MESSAGE_BODY = '0x48656c6c6f21'; // Hello!' - export async function sendTestMessage({ key, chainConfigPath, coreArtifactsPath, origin, destination, + messageBody, timeoutSec, skipWaitForDelivery, }: { @@ -30,6 +29,7 @@ export async function sendTestMessage({ coreArtifactsPath?: string; origin?: ChainName; destination?: ChainName; + messageBody: string; timeoutSec: number; skipWaitForDelivery: boolean; }) { @@ -67,6 +67,7 @@ export async function sendTestMessage({ executeDelivery({ origin, destination, + messageBody, multiProvider, coreArtifacts, skipWaitForDelivery, @@ -79,12 +80,14 @@ export async function sendTestMessage({ async function executeDelivery({ origin, destination, + messageBody, multiProvider, coreArtifacts, skipWaitForDelivery, }: { origin: ChainName; destination: ChainName; + messageBody: string; multiProvider: MultiProvider; coreArtifacts?: HyperlaneContractsMap; skipWaitForDelivery: boolean; @@ -96,6 +99,14 @@ async function executeDelivery({ ); const mailbox = core.getContracts(origin).mailbox; + let hook = mergedContractAddrs[origin]?.customHook; + if (hook) { + logBlue(`Using custom hook ${hook} for ${origin} -> ${destination}`); + } else { + hook = await mailbox.defaultHook(); + logBlue(`Using default hook ${hook} for ${origin} -> ${destination}`); + } + const destinationDomain = multiProvider.getDomainId(destination); let txReceipt: ethers.ContractReceipt; try { @@ -106,19 +117,29 @@ async function executeDelivery({ const formattedRecipient = addressToBytes32(recipient); log('Getting gas quote'); - const value = await mailbox['quoteDispatch(uint32,bytes32,bytes)']( + const value = await mailbox[ + 'quoteDispatch(uint32,bytes32,bytes,bytes,address)' + ]( destinationDomain, formattedRecipient, - MESSAGE_BODY, + messageBody, + ethers.utils.hexlify([]), + hook, ); log(`Paying for gas with ${value} wei`); log('Dispatching message'); - const messageTx = await mailbox['dispatch(uint32,bytes32,bytes)']( + const messageTx = await mailbox[ + 'dispatch(uint32,bytes32,bytes,bytes,address)' + ]( destinationDomain, formattedRecipient, - MESSAGE_BODY, - { value }, + messageBody, + ethers.utils.hexlify([]), + hook, + { + value, + }, ); txReceipt = await multiProvider.handleTx(origin, messageTx); const message = core.getDispatchedMessages(txReceipt)[0]; diff --git a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts index 6e1226b6fc..cf2c602594 100644 --- a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts +++ b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts @@ -1,6 +1,10 @@ import debug from 'debug'; -import { Mailbox, ValidatorAnnounce } from '@hyperlane-xyz/core'; +import { + IPostDispatchHook, + Mailbox, + ValidatorAnnounce, +} from '@hyperlane-xyz/core'; import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneContracts } from '../contracts/types'; @@ -100,8 +104,8 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< mailbox.initialize( config.owner, defaultIsm, - defaultHook, - requiredHook, + defaultHook.address, + requiredHook.address, this.multiProvider.getTransactionOverrides(chain), ), ); @@ -165,7 +169,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< chain: ChainName, config: HookConfig, coreAddresses: Partial, - ): Promise
{ + ): Promise { const hooks = await this.hookDeployer.deployContracts( chain, config, @@ -176,7 +180,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< this.hookDeployer.deployedContracts[chain], this.hookDeployer.verificationInputs[chain], ); - return hooks[config.type].address; + return hooks[config.type]; } async deployIsm( diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index c47ee7ca67..a9632fa684 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -2,6 +2,8 @@ import { Debugger, debug } from 'debug'; import { Contract, PopulatedTransaction, ethers } from 'ethers'; import { + IPostDispatchHook, + IPostDispatchHook__factory, ITransparentUpgradeableProxy, MailboxClient, Ownable, @@ -240,27 +242,32 @@ export abstract class HyperlaneDeployer< protected async configureHook( chain: ChainName, contract: C, - targetHook: Address, + targetHook: IPostDispatchHook, getHook: (contract: C) => Promise
, setHook: (contract: C, hook: Address) => Promise, ): Promise { const configuredHook = await getHook(contract); - if (!eqAddress(targetHook, configuredHook)) { - await this.runIfOwner(chain, contract, async () => { + if (!eqAddress(targetHook.address, configuredHook)) { + const result = await this.runIfOwner(chain, contract, async () => { this.logger( - `Set hook on ${chain} to ${targetHook}, currently is ${configuredHook}`, + `Set hook on ${chain} to ${targetHook.address}, currently is ${configuredHook}`, ); await this.multiProvider.sendTransaction( chain, - setHook(contract, targetHook), + setHook(contract, targetHook.address), ); const actualHook = await getHook(contract); - if (!eqAddress(targetHook, actualHook)) { + if (!eqAddress(targetHook.address, actualHook)) { throw new Error( - `Set hook failed on ${chain}, wanted ${targetHook}, got ${actualHook}`, + `Set hook failed on ${chain}, wanted ${targetHook.address}, got ${actualHook}`, ); } + return true; }); + // if the signer is not the owner, saving the hook address in the artifacts for later use for sending test messages, etc + if (!result) { + this.addDeployedContracts(chain, { customHook: targetHook }); + } } } @@ -274,7 +281,10 @@ export abstract class HyperlaneDeployer< await this.configureHook( local, client, - config.hook, + IPostDispatchHook__factory.connect( + config.hook, + this.multiProvider.getSignerOrProvider(local), + ), (_client) => _client.hook(), (_client, _hook) => _client.populateTransaction.setHook(_hook), );