Support DefaultIsmInterchainGasPaymaster in checker & govern (#1926)

### Description

* Adds DefaultIsmInterchainGasPaymaster to the checker and govern logic
* I'm aware this conflicts with
https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/1907 -- I wrote
this mostly so I could propose new multisig txs to update the overhead
gas amounts on mainnet. Happy to either merge this before or after
https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/1907, whatever
works easiest

### Drive-by changes

* Some fixes in the wake of the sdk refactor, e.g.:
* if some tx params like `maxPriorityFeePerGas` or `maxFeePerGas` were
specified when we estimate gas, it can fail if the balance of the sender
is insufficient. For cases where we want to estimate gas from the owner
multisig which may not have native tokens, this caused issues.
* We should've been passing the `from` address into the
MultiProvider.estimateGas fn as a separate param

### Related issues

n/a

### Backward compatibility

_Are these changes backward compatible?_

Yes

_Are there any infrastructure implications, e.g. changes that would
prohibit deploying older commits using this infra tooling?_

None


### Testing

_What kind of testing have these changes undergone?_

Ran & proposed some txs on mainnet to update the destination gas
overheads
mattie/serejke-fix-1924
Trevor Porter 2 years ago committed by GitHub
parent ce16fa7421
commit 20e6857ac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      typescript/infra/src/core/govern.ts
  2. 64
      typescript/sdk/src/core/HyperlaneCoreChecker.ts
  3. 5
      typescript/sdk/src/core/HyperlaneCoreDeployer.ts
  4. 21
      typescript/sdk/src/core/types.ts
  5. 2
      typescript/sdk/src/index.ts
  6. 9
      typescript/sdk/src/providers/MultiProvider.ts

@ -1,11 +1,13 @@
import { prompts } from 'prompts'; import { prompts } from 'prompts';
import { InterchainGasPaymaster } from '@hyperlane-xyz/core'; import { InterchainGasPaymaster, OverheadIgp } from '@hyperlane-xyz/core';
import { import {
ChainMap, ChainMap,
ChainName, ChainName,
CoreContracts, CoreContracts,
CoreViolationType, CoreViolationType,
DefaultIsmIgpViolation,
DefaultIsmIgpViolationType,
EnrolledValidatorsViolation, EnrolledValidatorsViolation,
HyperlaneCoreChecker, HyperlaneCoreChecker,
IgpBeneficiaryViolation, IgpBeneficiaryViolation,
@ -149,6 +151,12 @@ export class HyperlaneCoreGovernor {
this.handleIgpViolation(violation as IgpViolation); this.handleIgpViolation(violation as IgpViolation);
break; break;
} }
case CoreViolationType.DefaultIsmInterchainGasPaymaster: {
this.handleDefaultIsmIgpViolation(
violation as DefaultIsmIgpViolation,
);
break;
}
default: default:
throw new Error(`Unsupported violation type ${violation.type}`); throw new Error(`Unsupported violation type ${violation.type}`);
} }
@ -191,10 +199,7 @@ export class HyperlaneCoreGovernor {
submitterAddress: types.Address, submitterAddress: types.Address,
): Promise<boolean> => { ): Promise<boolean> => {
try { try {
await multiProvider.estimateGas(chain, { await multiProvider.estimateGas(chain, call, submitterAddress);
...call,
from: submitterAddress,
});
return true; return true;
} catch (e) {} // eslint-disable-line no-empty } catch (e) {} // eslint-disable-line no-empty
return false; return false;
@ -362,4 +367,41 @@ export class HyperlaneCoreGovernor {
throw new Error(`Unsupported IgpViolationType: ${violation.subType}`); throw new Error(`Unsupported IgpViolationType: ${violation.subType}`);
} }
} }
handleDefaultIsmIgpViolation(violation: DefaultIsmIgpViolation) {
switch (violation.subType) {
case DefaultIsmIgpViolationType.DestinationGasOverheads: {
const configs: OverheadIgp.DomainConfigStruct[] = Object.entries(
violation.expected,
).map(
([remote, gasOverhead]) =>
({
domain: this.checker.multiProvider.getDomainId(remote),
gasOverhead: gasOverhead,
} as OverheadIgp.DomainConfigStruct),
);
this.pushCall(violation.chain, {
to: violation.contract.address,
data: violation.contract.interface.encodeFunctionData(
'setDestinationGasOverheads',
[configs],
),
description: `Setting ${Object.keys(violation.expected)
.map((remoteStr) => {
const remote = remoteStr as ChainName;
const remoteId = this.checker.multiProvider.getDomainId(remote);
const expected = violation.expected[remote];
return `destination gas overhead for ${remote} (domain ID ${remoteId}) to ${expected}`;
})
.join(', ')}`,
});
break;
}
default:
throw new Error(
`Unsupported DefaultIsmIgpViolationType: ${violation.subType}`,
);
}
}
} }

@ -1,7 +1,8 @@
import { utils as ethersUtils } from 'ethers'; import { BigNumber, utils as ethersUtils } from 'ethers';
import { types, utils } from '@hyperlane-xyz/utils'; import { types, utils } from '@hyperlane-xyz/utils';
import multisigIsmVerifyCosts from '../consts/multisigIsmVerifyCosts.json';
import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker';
import { ChainName } from '../types'; import { ChainName } from '../types';
@ -9,6 +10,8 @@ import { HyperlaneCore } from './HyperlaneCore';
import { import {
CoreConfig, CoreConfig,
CoreViolationType, CoreViolationType,
DefaultIsmIgpDestinationGasOverheadsViolation,
DefaultIsmIgpViolationType,
EnrolledValidatorsViolation, EnrolledValidatorsViolation,
GasOracleContractType, GasOracleContractType,
IgpBeneficiaryViolation, IgpBeneficiaryViolation,
@ -51,6 +54,7 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
await this.checkMultisigIsm(chain); await this.checkMultisigIsm(chain);
await this.checkBytecodes(chain); await this.checkBytecodes(chain);
await this.checkValidatorAnnounce(chain); await this.checkValidatorAnnounce(chain);
await this.checkDefaultIsmInterchainGasPaymaster(chain);
await this.checkInterchainGasPaymaster(chain); await this.checkInterchainGasPaymaster(chain);
} }
@ -262,6 +266,48 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
} }
} }
async checkDefaultIsmInterchainGasPaymaster(local: ChainName): Promise<void> {
const coreContracts = this.app.getContracts(local);
const defaultIsmIgp = coreContracts.defaultIsmInterchainGasPaymaster;
// 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 gasOverheadViolation: DefaultIsmIgpDestinationGasOverheadsViolation =
{
type: CoreViolationType.DefaultIsmInterchainGasPaymaster,
subType: DefaultIsmIgpViolationType.DestinationGasOverheads,
contract: defaultIsmIgp,
chain: local,
actual: {},
expected: {},
};
const remotes = this.app.remoteChains(local);
for (const remote of remotes) {
const { validators, threshold } = this.configMap[remote].multisigIsm;
const expectedOverhead = this.getExpectedOverheadGas(
threshold,
validators.length,
);
const remoteId = this.multiProvider.getDomainId(remote);
const existingOverhead = await defaultIsmIgp.destinationGasOverhead(
remoteId,
);
if (!expectedOverhead.eq(existingOverhead)) {
const remoteChain = remote as ChainName;
gasOverheadViolation.actual[remoteChain] = existingOverhead;
gasOverheadViolation.expected[remoteChain] = expectedOverhead;
}
}
if (Object.keys(gasOverheadViolation.actual).length > 0) {
this.addViolation(gasOverheadViolation);
}
}
async checkInterchainGasPaymaster(local: ChainName): Promise<void> { async checkInterchainGasPaymaster(local: ChainName): Promise<void> {
const coreContracts = this.app.getContracts(local); const coreContracts = this.app.getContracts(local);
const igp = coreContracts.interchainGasPaymaster.contract; const igp = coreContracts.interchainGasPaymaster.contract;
@ -279,7 +325,7 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
expected: {}, expected: {},
}; };
const remotes = this.multiProvider.getRemoteChains(local); const remotes = this.app.remoteChains(local);
for (const remote of remotes) { for (const remote of remotes) {
const remoteId = this.multiProvider.getDomainId(remote); const remoteId = this.multiProvider.getDomainId(remote);
const actualGasOracle = await igp.gasOracles(remoteId); const actualGasOracle = await igp.gasOracles(remoteId);
@ -327,4 +373,18 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker<
throw Error(`Unsupported gas oracle type ${gasOracleType}`); throw Error(`Unsupported gas oracle type ${gasOracleType}`);
} }
} }
private getExpectedOverheadGas(
threshold: number,
validatorSetCount: number,
): BigNumber {
const expectedOverhead: number | undefined =
// @ts-ignore
multisigIsmVerifyCosts[`${validatorSetCount}`][`${threshold}`];
if (!expectedOverhead)
throw new Error(
`Unknown verification cost for ${threshold} of ${validatorSetCount}`,
);
return BigNumber.from(expectedOverhead);
}
} }

@ -78,7 +78,10 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
// Set the gas oracles // Set the gas oracles
const remotes = this.multiProvider.getRemoteChains(chain); const configChains = Object.keys(this.configMap);
const remotes = this.multiProvider
.intersect(configChains, false)
.multiProvider.getRemoteChains(chain);
const gasOracleConfigsToSet: InterchainGasPaymaster.GasOracleConfigStruct[] = const gasOracleConfigsToSet: InterchainGasPaymaster.GasOracleConfigStruct[] =
[]; [];

@ -1,7 +1,10 @@
import { BigNumber } from 'ethers';
import { import {
InterchainGasPaymaster, InterchainGasPaymaster,
Mailbox, Mailbox,
MultisigIsm, MultisigIsm,
OverheadIgp,
} from '@hyperlane-xyz/core'; } from '@hyperlane-xyz/core';
import type { types } from '@hyperlane-xyz/utils'; import type { types } from '@hyperlane-xyz/utils';
@ -35,6 +38,7 @@ export enum CoreViolationType {
ConnectionManager = 'ConnectionManager', ConnectionManager = 'ConnectionManager',
ValidatorAnnounce = 'ValidatorAnnounce', ValidatorAnnounce = 'ValidatorAnnounce',
InterchainGasPaymaster = 'InterchainGasPaymaster', InterchainGasPaymaster = 'InterchainGasPaymaster',
DefaultIsmInterchainGasPaymaster = 'DefaultIsmInterchainGasPaymaster',
} }
export enum MultisigIsmViolationType { export enum MultisigIsmViolationType {
@ -46,6 +50,10 @@ export enum MailboxViolationType {
DefaultIsm = 'DefaultIsm', DefaultIsm = 'DefaultIsm',
} }
export enum DefaultIsmIgpViolationType {
DestinationGasOverheads = 'DestinationGasOverheads',
}
export enum IgpViolationType { export enum IgpViolationType {
Beneficiary = 'Beneficiary', Beneficiary = 'Beneficiary',
GasOracles = 'GasOracles', GasOracles = 'GasOracles',
@ -106,3 +114,16 @@ export interface IgpGasOraclesViolation extends IgpViolation {
actual: ChainMap<types.Address>; actual: ChainMap<types.Address>;
expected: ChainMap<types.Address>; expected: ChainMap<types.Address>;
} }
export interface DefaultIsmIgpViolation extends CheckerViolation {
type: CoreViolationType.DefaultIsmInterchainGasPaymaster;
contract: OverheadIgp;
subType: DefaultIsmIgpViolationType;
}
export interface DefaultIsmIgpDestinationGasOverheadsViolation
extends DefaultIsmIgpViolation {
subType: DefaultIsmIgpViolationType.DestinationGasOverheads;
actual: ChainMap<BigNumber>;
expected: ChainMap<BigNumber>;
}

@ -55,6 +55,8 @@ export { HyperlaneCoreDeployer } from './core/HyperlaneCoreDeployer';
export { export {
CoreConfig, CoreConfig,
CoreViolationType, CoreViolationType,
DefaultIsmIgpViolation,
DefaultIsmIgpViolationType,
EnrolledValidatorsViolation, EnrolledValidatorsViolation,
GasOracleContractType, GasOracleContractType,
IgpBeneficiaryViolation, IgpBeneficiaryViolation,

@ -550,7 +550,14 @@ export class MultiProvider {
tx: PopulatedTransaction, tx: PopulatedTransaction,
from?: string, from?: string,
): Promise<BigNumber> { ): Promise<BigNumber> {
const txReq = await this.prepareTx(chainNameOrId, tx, from); const txReq = {
...(await this.prepareTx(chainNameOrId, tx, from)),
// Reset any tx request params that may have an unintended effect on gas estimation
gasLimit: undefined,
gasPrice: undefined,
maxPriorityFeePerGas: undefined,
maxFeePerGas: undefined,
};
const provider = this.getProvider(chainNameOrId); const provider = this.getProvider(chainNameOrId);
return provider.estimateGas(txReq); return provider.estimateGas(txReq);
} }

Loading…
Cancel
Save