@ -1,22 +1,26 @@
import debug from 'debug' ;
import { ethers } from 'ethers' ;
import {
InterchainGasPaymaster ,
ProxyAdmin ,
StorageGasOracle ,
StorageGasOracle__factory ,
} from '@hyperlane-xyz/core' ;
import { eqAddress } from '@hyperlane-xyz/utils' ;
import { Address , eqAddress , warn } from '@hyperlane-xyz/utils' ;
import { HyperlaneContracts } from '../contracts/types' ;
import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer' ;
import { MultiProvider } from '../providers/MultiProvider' ;
import { ChainName } from '../types' ;
import { ChainMap , Chain Name } from '../types' ;
import { IgpFactories , igpFactories } from './contracts' ;
import { prettyRemoteGasData } from './oracle/logging' ;
import { OracleConfig , StorageGasOracleConfig } from './oracle/types' ;
import { IgpConfig } from './types' ;
export class HyperlaneIgpDeployer extends HyperlaneDeployer <
IgpConfig ,
IgpConfig & Partial < OracleConfig > ,
IgpFactories
> {
constructor ( multiProvider : MultiProvider ) {
@ -31,14 +35,13 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
storageGasOracle : StorageGasOracle ,
config : IgpConfig ,
) : Promise < InterchainGasPaymaster > {
const owner = config . owner ;
const beneficiary = config . beneficiary ;
const igp = await this . deployProxiedContract (
chain ,
'interchainGasPaymaster' ,
proxyAdmin . address ,
[ ] ,
[ owner , beneficiary ] ,
[ await this . multiProvider . getSignerAddress ( chain ) , beneficiary ] ,
) ;
const gasParamsToSet : InterchainGasPaymaster.GasParamStruct [ ] = [ ] ;
@ -52,7 +55,9 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
! eqAddress ( currentGasConfig . gasOracle , storageGasOracle . address ) ||
! currentGasConfig . gasOverhead . eq ( newGasOverhead )
) {
this . logger ( ` Setting gas params for ${ remote } to ${ newGasOverhead } ` ) ;
this . logger (
` Setting gas params for ${ chain } -> ${ remote } : gasOverhead = ${ newGasOverhead } gasOracle = ${ storageGasOracle . address } ` ,
) ;
gasParamsToSet . push ( {
remoteDomain : remoteId ,
config : {
@ -74,6 +79,7 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
) ,
) ;
}
return igp ;
}
@ -81,9 +87,87 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
return this . deployContract ( chain , 'storageGasOracle' , [ ] ) ;
}
async configureStorageGasOracle (
chain : ChainName ,
igp : InterchainGasPaymaster ,
gasOracleConfig : ChainMap < StorageGasOracleConfig > ,
) : Promise < void > {
this . logger ( ` Configuring gas oracles for ${ chain } ... ` ) ;
const remotes = Object . keys ( gasOracleConfig ) ;
const configsToSet : Record <
Address ,
StorageGasOracle . RemoteGasDataConfigStruct [ ]
> = { } ;
// For each remote, check if the gas oracle has the correct data
for ( const remote of remotes ) {
const desiredGasData = gasOracleConfig [ remote ] ;
const remoteId = this . multiProvider . getDomainId ( remote ) ;
// each destination can have a different gas oracle
const gasOracleAddress = ( await igp . destinationGasConfigs ( remoteId ) )
. gasOracle ;
if ( eqAddress ( gasOracleAddress , ethers . constants . AddressZero ) ) {
warn ( ` No gas oracle set for ${ chain } -> ${ remote } , cannot configure ` ) ;
continue ;
}
const gasOracle = StorageGasOracle__factory . connect (
gasOracleAddress ,
this . multiProvider . getSigner ( chain ) ,
) ;
configsToSet [ gasOracleAddress ] || = [ ] ;
this . logger ( ` Checking gas oracle ${ gasOracleAddress } for ${ remote } ... ` ) ;
const remoteGasDataConfig = await gasOracle . remoteGasData ( remoteId ) ;
if (
! remoteGasDataConfig . gasPrice . eq ( desiredGasData . gasPrice ) ||
! remoteGasDataConfig . tokenExchangeRate . eq (
desiredGasData . tokenExchangeRate ,
)
) {
this . logger (
` ${ chain } -> ${ remote } existing gas data: \ n ` ,
prettyRemoteGasData ( remoteGasDataConfig ) ,
) ;
this . logger (
` ${ chain } -> ${ remote } desired gas data: \ n ` ,
prettyRemoteGasData ( desiredGasData ) ,
) ;
configsToSet [ gasOracleAddress ] . push ( {
remoteDomain : this.multiProvider.getDomainId ( remote ) ,
. . . desiredGasData ,
} ) ;
}
}
// loop through each gas oracle and batch set the remote gas data
for ( const gasOracle of Object . keys ( configsToSet ) ) {
const gasOracleContract = StorageGasOracle__factory . connect (
gasOracle ,
this . multiProvider . getSigner ( chain ) ,
) ;
if ( configsToSet [ gasOracle ] . length > 0 ) {
await this . runIfOwner ( chain , gasOracleContract , async ( ) = > {
this . logger (
` Setting gas oracle on ${ gasOracle } for ${ configsToSet [
gasOracle
] . map ( ( config ) = > config . remoteDomain ) } ` ,
) ;
return this . multiProvider . handleTx (
chain ,
gasOracleContract . setRemoteGasDataConfigs (
configsToSet [ gasOracle ] ,
this . multiProvider . getTransactionOverrides ( chain ) ,
) ,
) ;
} ) ;
}
}
}
async deployContracts (
chain : ChainName ,
config : IgpConfig ,
config : IgpConfig & Partial < OracleConfig > ,
) : Promise < HyperlaneContracts < IgpFactories > > {
// NB: To share ProxyAdmins with HyperlaneCore, ensure the ProxyAdmin
// is loaded into the contract cache.
@ -96,6 +180,16 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
storageGasOracle ,
config ,
) ;
// Configure storage gas oracle with remote gas data if provided
if ( config . oracleConfig ) {
await this . configureStorageGasOracle (
chain ,
interchainGasPaymaster ,
config . oracleConfig ,
) ;
}
await this . transferOwnershipOfContracts ( chain , config . owner , {
interchainGasPaymaster ,
} ) ;