@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
247 lines • 9.93 kB
JavaScript
import { z } from 'zod';
import { isNullish, rootLogger, } from '@hyperlane-xyz/utils';
import { ProtocolAgnositicGasOracleConfigWithTypicalCostSchema } from '../gas/oracle/types.js';
import { ZHash } from '../metadata/customZodTypes.js';
import { OwnableSchema, PausableSchema, } from '../types.js';
// As found in IPostDispatchHook.sol
export var OnchainHookType;
(function (OnchainHookType) {
OnchainHookType[OnchainHookType["UNUSED"] = 0] = "UNUSED";
OnchainHookType[OnchainHookType["ROUTING"] = 1] = "ROUTING";
OnchainHookType[OnchainHookType["AGGREGATION"] = 2] = "AGGREGATION";
OnchainHookType[OnchainHookType["MERKLE_TREE"] = 3] = "MERKLE_TREE";
OnchainHookType[OnchainHookType["INTERCHAIN_GAS_PAYMASTER"] = 4] = "INTERCHAIN_GAS_PAYMASTER";
OnchainHookType[OnchainHookType["FALLBACK_ROUTING"] = 5] = "FALLBACK_ROUTING";
OnchainHookType[OnchainHookType["ID_AUTH_ISM"] = 6] = "ID_AUTH_ISM";
OnchainHookType[OnchainHookType["PAUSABLE"] = 7] = "PAUSABLE";
OnchainHookType[OnchainHookType["PROTOCOL_FEE"] = 8] = "PROTOCOL_FEE";
OnchainHookType[OnchainHookType["DEPRECATED"] = 9] = "DEPRECATED";
OnchainHookType[OnchainHookType["RATE_LIMITED"] = 10] = "RATE_LIMITED";
OnchainHookType[OnchainHookType["ARB_L2_TO_L1"] = 11] = "ARB_L2_TO_L1";
OnchainHookType[OnchainHookType["OP_L2_TO_L1"] = 12] = "OP_L2_TO_L1";
OnchainHookType[OnchainHookType["MAILBOX_DEFAULT_HOOK"] = 13] = "MAILBOX_DEFAULT_HOOK";
OnchainHookType[OnchainHookType["AMOUNT_ROUTING"] = 14] = "AMOUNT_ROUTING";
OnchainHookType[OnchainHookType["CCTP"] = 15] = "CCTP";
OnchainHookType[OnchainHookType["TIMELOCK_ROUTING"] = 16] = "TIMELOCK_ROUTING";
OnchainHookType[OnchainHookType["PREDICATE_ROUTER_WRAPPER"] = 17] = "PREDICATE_ROUTER_WRAPPER";
})(OnchainHookType || (OnchainHookType = {}));
export const HookType = {
/**
* Retained for backwards compatibility with pre-deployed hooks that don't fit
* a named type. Excluded from `DeployableHookType` — cannot be deployed via
* `HyperlaneHookDeployer`. New code should use a specific named hook type.
*/
CUSTOM: 'custom',
MERKLE_TREE: 'merkleTreeHook',
INTERCHAIN_GAS_PAYMASTER: 'interchainGasPaymaster',
AGGREGATION: 'aggregationHook',
PROTOCOL_FEE: 'protocolFee',
OP_STACK: 'opStackHook',
ROUTING: 'domainRoutingHook',
FALLBACK_ROUTING: 'fallbackRoutingHook',
AMOUNT_ROUTING: 'amountRoutingHook',
PAUSABLE: 'pausableHook',
ARB_L2_TO_L1: 'arbL2ToL1Hook',
MAILBOX_DEFAULT: 'defaultHook',
CCIP: 'ccipHook',
/**
* References a pre-deployed CCTP hook by address. Excluded from
* `DeployableHookType` — not deployed via `HyperlaneHookDeployer`; the
* `EvmHookModule.deploy` path just connects to `config.address`.
*/
CCTP: 'cctpHook',
/**
* Rate-limits outbound token volume on the origin chain at dispatch time.
* Warp-route only. Not valid for core required/default hooks.
*/
RATE_LIMITED: 'rateLimitedHook',
UNKNOWN: 'unknownHook',
PREDICATE: 'predicateHook',
};
export const HookTypeToContractNameMap = {
[HookType.MERKLE_TREE]: 'merkleTreeHook',
[HookType.INTERCHAIN_GAS_PAYMASTER]: 'interchainGasPaymaster',
[HookType.AGGREGATION]: 'staticAggregationHook',
[HookType.PROTOCOL_FEE]: 'protocolFee',
[HookType.OP_STACK]: 'opStackHook',
[HookType.ROUTING]: 'domainRoutingHook',
[HookType.FALLBACK_ROUTING]: 'fallbackDomainRoutingHook',
[HookType.AMOUNT_ROUTING]: 'amountRoutingHook',
[HookType.PAUSABLE]: 'pausableHook',
[HookType.ARB_L2_TO_L1]: 'arbL2ToL1Hook',
[HookType.MAILBOX_DEFAULT]: 'defaultHook',
[HookType.CCIP]: 'ccipHook',
[HookType.RATE_LIMITED]: 'rateLimitedHook',
};
// Hook types that can be updated in-place
export const MUTABLE_HOOK_TYPE = [
HookType.INTERCHAIN_GAS_PAYMASTER,
HookType.PROTOCOL_FEE,
HookType.ROUTING,
HookType.FALLBACK_ROUTING,
HookType.PAUSABLE,
HookType.RATE_LIMITED,
];
export const ProtocolFeeSchema = OwnableSchema.extend({
type: z.literal(HookType.PROTOCOL_FEE),
beneficiary: z.string(),
maxProtocolFee: z.string(),
protocolFee: z.string(),
});
export const MerkleTreeSchema = z.object({
type: z.literal(HookType.MERKLE_TREE),
});
export const PredicateHookSchema = z.object({
type: z.literal(HookType.PREDICATE),
address: z.string(),
});
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(),
destinationChain: z.string(),
});
export const ArbL2ToL1HookSchema = z.object({
type: z.literal(HookType.ARB_L2_TO_L1),
arbSys: z
.string()
.describe('precompile for sending messages to L1, interface here: https://github.com/OffchainLabs/nitro-contracts/blob/90037b996509312ef1addb3f9352457b8a99d6a6/src/precompiles/ArbSys.sol#L12'),
bridge: z
.string()
.optional()
.describe('address of the bridge contract on L1, optional only needed for non @arbitrum/sdk chains'),
destinationChain: z.string(),
childHook: z.lazy(() => HookConfigSchema),
});
export const IgpSchema = OwnableSchema.extend({
type: z.literal(HookType.INTERCHAIN_GAS_PAYMASTER),
beneficiary: z.string(),
oracleKey: z.string(),
overhead: z.record(z.number()),
oracleConfig: z.record(ProtocolAgnositicGasOracleConfigWithTypicalCostSchema),
quoteSigners: z.array(z.string()).optional(),
contractVersion: z.string().optional(),
});
export const DomainRoutingHookConfigSchema = z.lazy(() => OwnableSchema.extend({
type: z.literal(HookType.ROUTING),
domains: z.record(HookConfigSchema),
}));
export const FallbackRoutingHookConfigSchema = z.lazy(() => OwnableSchema.extend({
type: z.literal(HookType.FALLBACK_ROUTING),
domains: z.record(HookConfigSchema),
fallback: HookConfigSchema,
}));
export const AmountRoutingHookConfigSchema = z.lazy(() => z.object({
type: z.literal(HookType.AMOUNT_ROUTING),
threshold: z.number(),
lowerHook: HookConfigSchema,
upperHook: HookConfigSchema,
}));
export const AggregationHookConfigSchema = z.lazy(() => z.object({
type: z.literal(HookType.AGGREGATION),
hooks: z.array(HookConfigSchema),
}));
export const CCIPHookSchema = z.object({
type: z.literal(HookType.CCIP),
destinationChain: z.string(),
});
export const CctpHookSchema = z.object({
type: z.literal(HookType.CCTP),
address: ZHash,
});
export const UnknownHookSchema = z
.object({
type: z.literal(HookType.UNKNOWN),
})
.passthrough();
export const RateLimitedHookSchema = OwnableSchema.extend({
type: z.literal(HookType.RATE_LIMITED),
maxCapacity: z
.string()
.regex(/^\d+$/, 'maxCapacity must be a base-10 integer string'),
})
.refine((val) => BigInt(val.maxCapacity) >= 86400n, {
message: 'maxCapacity must be at least 86400',
path: ['maxCapacity'],
})
.transform((val) => {
const capacity = BigInt(val.maxCapacity);
if (capacity % 86400n !== 0n) {
const rounded = ((capacity / 86400n) * 86400n).toString();
rootLogger.warn(`RateLimitedHook maxCapacity ${val.maxCapacity} is not divisible by 86400; rounding down to ${rounded}`);
return { ...val, maxCapacity: rounded };
}
return val;
});
const KnownHookTypes = Object.values(HookType).filter((t) => t !== HookType.UNKNOWN);
/**
* Recursively normalizes unknown hook type values to HookType.UNKNOWN.
* Use this before parsing with HookConfigSchema when configs may contain
* hook types not yet known to this SDK version.
*
* Note: String address configs (e.g., "0x...") are passed through unchanged
* since they represent deployed hook addresses, not hook type configs.
*/
export function normalizeUnknownHookTypes(config) {
// Handle nullish values and primitives (including string addresses)
if (isNullish(config) || typeof config !== 'object') {
return config;
}
if (Array.isArray(config)) {
return config.map(normalizeUnknownHookTypes);
}
// At this point, config must be a non-null object (not array, not primitive)
const obj = config;
const normalized = {};
for (const [key, value] of Object.entries(obj)) {
if (key === 'type' && typeof value === 'string') {
normalized[key] = KnownHookTypes.includes(value)
? value
: HookType.UNKNOWN;
}
else if (typeof value === 'object' && !isNullish(value)) {
normalized[key] = normalizeUnknownHookTypes(value);
}
else {
normalized[key] = value;
}
}
return normalized;
}
export const HookConfigSchema = z.union([
ZHash,
ProtocolFeeSchema,
PausableHookSchema,
OpStackHookSchema,
MerkleTreeSchema,
IgpSchema,
DomainRoutingHookConfigSchema,
FallbackRoutingHookConfigSchema,
AmountRoutingHookConfigSchema,
AggregationHookConfigSchema,
ArbL2ToL1HookSchema,
MailboxDefaultHookSchema,
CCIPHookSchema,
CctpHookSchema,
RateLimitedHookSchema,
UnknownHookSchema,
PredicateHookSchema,
]);
/**
* Forward-compatible hook config schema that normalizes unknown hook types.
* Use this instead of HookConfigSchema when parsing configs that may contain
* hook types added in newer registry versions.
*/
export const SafeParseHookConfigSchema = z.preprocess(normalizeUnknownHookTypes, HookConfigSchema);
// TODO: deprecate in favor of CoreConfigSchema
export const HooksConfigSchema = z.object({
default: HookConfigSchema,
required: HookConfigSchema,
});
export const HooksConfigMapSchema = z.record(HooksConfigSchema);
//# sourceMappingURL=types.js.map