@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
120 lines • 6.13 kB
JavaScript
import { BigNumber } from 'ethers';
import { eqAddress } from '@hyperlane-xyz/utils';
import { BytecodeHash } from '../consts/bytecode.js';
import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js';
import { proxyImplementation } from '../deploy/proxy.js';
import { IgpViolationType, } from './types.js';
export class HyperlaneIgpChecker extends HyperlaneAppChecker {
async checkChain(chain) {
await this.checkDomainOwnership(chain);
await this.checkProxiedContracts(chain, this.configMap[chain].owner);
await this.checkBytecodes(chain);
await this.checkOverheadInterchainGasPaymaster(chain);
await this.checkInterchainGasPaymaster(chain);
}
async checkDomainOwnership(chain) {
const config = this.configMap[chain];
await super.checkOwnership(chain, config.owner, config.ownerOverrides);
}
async checkBytecodes(chain) {
const contracts = this.app.getContracts(chain);
const implementation = await proxyImplementation(this.multiProvider.getProvider(chain), contracts.interchainGasPaymaster.address);
await this.checkBytecode(chain, 'InterchainGasPaymaster implementation', implementation, [
BytecodeHash.INTERCHAIN_GAS_PAYMASTER_BYTECODE_HASH,
BytecodeHash.OPT_INTERCHAIN_GAS_PAYMASTER_BYTECODE_HASH,
], (bytecode) => bytecode // We persist the block number in the bytecode now too, so we have to strip it
.replaceAll(/(00000000000000000000000000000000000000000000000000000000[a-f0-9]{0,22})81565/g, (match, _offset) => (match.length % 2 === 0 ? '' : '0')));
await this.checkProxy(chain, 'InterchainGasPaymaster proxy', contracts.interchainGasPaymaster.address);
}
async checkOverheadInterchainGasPaymaster(local) {
const coreContracts = this.app.getContracts(local);
const defaultIsmIgp = coreContracts.interchainGasPaymaster;
// Construct the violation, updating the actual & expected
// objects as violations are found.
// A single violation is used so that only a single `setDestinationGasOverheads`
// call is generated to set multiple gas overheads.
const overheadViolation = {
type: 'InterchainGasPaymaster',
subType: IgpViolationType.Overhead,
contract: defaultIsmIgp,
chain: local,
actual: {},
expected: {},
};
const remotes = await this.app.remoteChains(local);
for (const remote of remotes) {
let expectedOverhead = this.configMap[local].overhead[remote];
if (!expectedOverhead) {
this.app.logger.debug(`No overhead configured for ${local} -> ${remote}, defaulting to 0`);
expectedOverhead = 0;
}
// TODO: add back support for non-EVM remotes.
const remoteId = this.multiProvider.tryGetDomainId(remote);
if (remoteId === null) {
this.app.logger.warn(`Skipping checking IGP ${local} -> ${remote}. Expected if the remote is a non-EVM chain.`);
continue;
}
const existingOverhead = await defaultIsmIgp.destinationGasLimit(remoteId, 0);
if (!existingOverhead.eq(expectedOverhead)) {
const remoteChain = remote;
overheadViolation.actual[remoteChain] = existingOverhead;
overheadViolation.expected[remoteChain] =
BigNumber.from(expectedOverhead);
}
}
if (Object.keys(overheadViolation.actual).length > 0) {
this.addViolation(overheadViolation);
}
}
async checkInterchainGasPaymaster(local) {
const coreContracts = this.app.getContracts(local);
const igp = coreContracts.interchainGasPaymaster;
// Construct the violation, updating the actual & expected
// objects as violations are found.
// A single violation is used so that only a single `setGasOracles`
// call is generated to set multiple gas oracles.
const gasOraclesViolation = {
type: 'InterchainGasPaymaster',
subType: IgpViolationType.GasOracles,
contract: igp,
chain: local,
actual: {},
expected: {},
};
const remotes = new Set(Object.keys(this.configMap[local].oracleConfig ?? {}));
for (const remote of remotes) {
// TODO: add back support for non-EVM remotes.
const remoteId = this.multiProvider.tryGetDomainId(remote);
if (remoteId === null) {
this.app.logger.warn(`Skipping checking IGP ${local} -> ${remote}. Expected if the remote is a non-EVM chain.`);
continue;
}
const destinationGasConfigs = await igp.destinationGasConfigs(remoteId);
const actualGasOracle = destinationGasConfigs.gasOracle;
const expectedGasOracle = coreContracts.storageGasOracle.address;
if (!eqAddress(actualGasOracle, expectedGasOracle)) {
const remoteChain = remote;
gasOraclesViolation.actual[remoteChain] = actualGasOracle;
gasOraclesViolation.expected[remoteChain] = expectedGasOracle;
}
}
// Add the violation only if it's been populated with gas oracle inconsistencies
if (Object.keys(gasOraclesViolation.actual).length > 0) {
this.addViolation(gasOraclesViolation);
}
const actualBeneficiary = await igp.beneficiary();
const expectedBeneficiary = this.configMap[local].beneficiary;
if (!eqAddress(actualBeneficiary, expectedBeneficiary)) {
const violation = {
type: 'InterchainGasPaymaster',
subType: IgpViolationType.Beneficiary,
contract: igp,
chain: local,
actual: actualBeneficiary,
expected: expectedBeneficiary,
};
this.addViolation(violation);
}
}
}
//# sourceMappingURL=HyperlaneIgpChecker.js.map