Skip to content

Commit

Permalink
feat: support Mailbox Default Hook in the SDK (#5435)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalaji authored Feb 11, 2025
1 parent 9a010df commit cb7c157
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/popular-panthers-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': minor
---

Support DefaultHook in the SDK.
1 change: 1 addition & 0 deletions typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function randomHookConfig(

switch (hookType) {
case HookType.MERKLE_TREE:
case HookType.MAILBOX_DEFAULT:
return { type: hookType };

case HookType.AGGREGATION:
Expand Down
3 changes: 2 additions & 1 deletion typescript/sdk/src/hook/EvmHookModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,8 @@ export class EvmHookModule extends HyperlaneModule<

switch (config.type) {
case HookType.MERKLE_TREE:
return this.deployer.deployContract(this.chain, HookType.MERKLE_TREE, [
case HookType.MAILBOX_DEFAULT:
return this.deployer.deployContract(this.chain, config.type, [
this.args.addresses.mailbox,
]);
case HookType.INTERCHAIN_GAS_PAYMASTER:
Expand Down
35 changes: 35 additions & 0 deletions typescript/sdk/src/hook/EvmHookReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { randomBytes } from 'ethers/lib/utils.js';
import sinon from 'sinon';

import {
DefaultHook,
DefaultHook__factory,
IPostDispatchHook,
IPostDispatchHook__factory,
MerkleTreeHook,
Expand All @@ -24,6 +26,7 @@ import { randomAddress } from '../test/testUtils.js';
import { EvmHookReader } from './EvmHookReader.js';
import {
HookType,
MailboxDefaultHookConfig,
MerkleTreeHookConfig,
OnchainHookType,
OpStackHookConfig,
Expand Down Expand Up @@ -148,6 +151,38 @@ describe('EvmHookReader', () => {
expect(config).to.deep.equal(hookConfig);
});

it('should derive mailbox default hook config correctly', async () => {
const mockAddress = randomAddress();
const mockMailbox = randomAddress();

// Mocking the connect method + returned what we need from contract object
const mockContract = {
hookType: sandbox.stub().resolves(OnchainHookType.MAILBOX_DEFAULT_HOOK),
mailbox: sandbox.stub().resolves(mockMailbox),
};
sandbox
.stub(DefaultHook__factory, 'connect')
.returns(mockContract as unknown as DefaultHook);
sandbox
.stub(IPostDispatchHook__factory, 'connect')
.returns(mockContract as unknown as IPostDispatchHook);

const expectedConfig: WithAddress<MailboxDefaultHookConfig> = {
address: mockAddress,
type: HookType.MAILBOX_DEFAULT,
};

// top-level method infers hook type
const hookConfig = await evmHookReader.deriveHookConfig(mockAddress);
expect(hookConfig).to.deep.equal(expectedConfig);

// should get same result if we call the specific method for the hook type
const config = await evmHookReader.deriveMailboxDefaultHookConfig(
mockAddress,
);
expect(config).to.deep.equal(hookConfig);
});

it('should derive op stack config correctly', async () => {
const mockAddress = randomAddress();
const mockOwner = randomAddress();
Expand Down
26 changes: 26 additions & 0 deletions typescript/sdk/src/hook/EvmHookReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ethers } from 'ethers';

import {
ArbL2ToL1Hook__factory,
DefaultHook__factory,
DomainRoutingHook,
DomainRoutingHook__factory,
FallbackDomainRoutingHook,
Expand Down Expand Up @@ -39,6 +40,7 @@ import {
HookConfig,
HookType,
IgpHookConfig,
MailboxDefaultHookConfig,
MerkleTreeHookConfig,
OnchainHookType,
OpStackHookConfig,
Expand Down Expand Up @@ -159,6 +161,11 @@ export class EvmHookReader extends HyperlaneReader implements HookReader {
case OnchainHookType.ARB_L2_TO_L1:
derivedHookConfig = await this.deriveArbL2ToL1Config(address);
break;
case OnchainHookType.MAILBOX_DEFAULT_HOOK:
derivedHookConfig = await this.deriveMailboxDefaultHookConfig(
address,
);
break;
default:
throw new Error(
`Unsupported HookType: ${OnchainHookType[onchainHookType]}`,
Expand All @@ -185,6 +192,25 @@ export class EvmHookReader extends HyperlaneReader implements HookReader {
return derivedHookConfig;
}

async deriveMailboxDefaultHookConfig(
address: Address,
): Promise<WithAddress<MailboxDefaultHookConfig>> {
const hook = DefaultHook__factory.connect(address, this.provider);
this.assertHookType(
await hook.hookType(),
OnchainHookType.MAILBOX_DEFAULT_HOOK,
);

const config: WithAddress<MailboxDefaultHookConfig> = {
address,
type: HookType.MAILBOX_DEFAULT,
};

this._cache.set(address, config);

return config;
}

async deriveMerkleTreeConfig(
address: Address,
): Promise<WithAddress<MerkleTreeHookConfig>> {
Expand Down
5 changes: 4 additions & 1 deletion typescript/sdk/src/hook/HyperlaneHookDeployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
}

let hook: DeployedHook;
if (config.type === HookType.MERKLE_TREE) {
if (
config.type === HookType.MERKLE_TREE ||
config.type === HookType.MAILBOX_DEFAULT
) {
const mailbox = coreAddresses.mailbox;
if (!mailbox) {
throw new Error(`Mailbox address is required for ${config.type}`);
Expand Down
2 changes: 2 additions & 0 deletions typescript/sdk/src/hook/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ArbL2ToL1Hook__factory,
DefaultHook__factory,
DomainRoutingHook__factory,
FallbackDomainRoutingHook__factory,
InterchainGasPaymaster__factory,
Expand All @@ -23,6 +24,7 @@ export const hookFactories = {
[HookType.FALLBACK_ROUTING]: new FallbackDomainRoutingHook__factory(),
[HookType.PAUSABLE]: new PausableHook__factory(),
[HookType.ARB_L2_TO_L1]: new ArbL2ToL1Hook__factory(),
[HookType.MAILBOX_DEFAULT]: new DefaultHook__factory(),
};

export type HookFactories = typeof hookFactories;
Expand Down
9 changes: 9 additions & 0 deletions typescript/sdk/src/hook/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export enum OnchainHookType {
LAYER_ZERO_V1,
RATE_LIMITED,
ARB_L2_TO_L1,
OP_L2_TO_L1,
MAILBOX_DEFAULT_HOOK,
}

export enum HookType {
Expand All @@ -36,6 +38,7 @@ export enum HookType {
FALLBACK_ROUTING = 'fallbackRoutingHook',
PAUSABLE = 'pausableHook',
ARB_L2_TO_L1 = 'arbL2ToL1Hook',
MAILBOX_DEFAULT = 'defaultHook',
}

export type MerkleTreeHookConfig = z.infer<typeof MerkleTreeSchema>;
Expand All @@ -44,6 +47,7 @@ export type ProtocolFeeHookConfig = z.infer<typeof ProtocolFeeSchema>;
export type PausableHookConfig = z.infer<typeof PausableHookSchema>;
export type OpStackHookConfig = z.infer<typeof OpStackHookSchema>;
export type ArbL2ToL1HookConfig = z.infer<typeof ArbL2ToL1HookSchema>;
export type MailboxDefaultHookConfig = z.infer<typeof MailboxDefaultHookSchema>;

// explicitly typed to avoid zod circular dependency
export type AggregationHookConfig = {
Expand Down Expand Up @@ -87,6 +91,10 @@ export const PausableHookSchema = PausableSchema.extend({
type: z.literal(HookType.PAUSABLE),
});

export const MailboxDefaultHookSchema = z.object({
type: z.literal(HookType.MAILBOX_DEFAULT),
});

export const OpStackHookSchema = OwnableSchema.extend({
type: z.literal(HookType.OP_STACK),
nativeBridge: z.string(),
Expand Down Expand Up @@ -154,4 +162,5 @@ export const HookConfigSchema = z.union([
FallbackRoutingHookConfigSchema,
AggregationHookConfigSchema,
ArbL2ToL1HookSchema,
MailboxDefaultHookSchema,
]);

0 comments on commit cb7c157

Please sign in to comment.