Upgradable warp routes (#3474)

### Description

Makes the following Warp Routes upgradable through CLI deployment
```
- FastHypERC20Collateral
- FastHypERC20
- HypERC20
- HypERC20Collateral
- HypERC20CollateralVaultDeposit
- HypNative
- HypNativeScaled
- HypERC721Collateral
- HypERC721
- HypERC721URICollateral
- HypERC721URIStorage
```

- Adds `initialize()` to each contract
- It mostly contains `_MailboxClient_initialize`, and any additional
proxy specific constructor logic
- Refactors
	- Update `GasRouterDeployer` to inherit from `ProxiedRouterDeployer`
- Update `ProxiedRouterDeployer.routerContractName` to `abstract
ProxiedRouterDeployer.routerContractName(): RouterKey` .
- This allows child classes to specify their own contract name instead
of being locked-in to a single name upon construction. Similar concept
to the existing `ProxiedRouterDeployer.constructorArgs()` and
`ProxiedRouterDeployer.initializeArgs()`
	- Update `router()` 
		- Add function into `InterchainQueryDeployer`
		- Add function into `InterchainAccountDeployer`
		- Update to abstract function in `ProxiedRouterDeployer`
		- ### Drive-by changes

### Related issues
Fixes #999 

### Backward compatibility
- Should be backward compatible

### Testing
- Updates contract unit tests
- Manual testing through CLI and verified contracts
pull/3522/head
Lee 7 months ago committed by GitHub
parent 5373d54ca5
commit db85f8aaf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      solidity/contracts/token/HypERC20.sol
  2. 8
      solidity/contracts/token/HypERC20Collateral.sol
  3. 8
      solidity/contracts/token/HypERC20CollateralVaultDeposit.sol
  4. 14
      solidity/contracts/token/HypERC721.sol
  5. 14
      solidity/contracts/token/HypERC721Collateral.sol
  6. 14
      solidity/contracts/token/HypNative.sol
  7. 51
      solidity/test/token/HypERC20.t.sol
  8. 19
      solidity/test/token/HypERC20CollateralVaultDeposit.t.sol
  9. 89
      solidity/test/token/HypERC721.t.sol
  10. 39
      solidity/test/token/HypNativeScaled.t.sol
  11. 2
      typescript/cli/src/deploy/warp.ts
  12. 1
      typescript/sdk/src/core/HyperlaneCoreDeployer.ts
  13. 6
      typescript/sdk/src/deploy/HyperlaneDeployer.ts
  14. 1
      typescript/sdk/src/gas/HyperlaneIgpDeployer.ts
  15. 28
      typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts
  16. 27
      typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts
  17. 30
      typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts
  18. 14
      typescript/sdk/src/router/GasRouterDeployer.ts
  19. 36
      typescript/sdk/src/router/ProxiedRouterDeployer.ts
  20. 4
      typescript/sdk/src/token/config.ts
  21. 9
      typescript/sdk/src/token/contracts.ts
  22. 270
      typescript/sdk/src/token/deploy.ts

@ -26,11 +26,15 @@ contract HypERC20 is ERC20Upgradeable, TokenRouter {
function initialize(
uint256 _totalSupply,
string memory _name,
string memory _symbol
string memory _symbol,
address _hook,
address _interchainSecurityModule,
address _owner
) external initializer {
// Initialize ERC20 metadata
__ERC20_init(_name, _symbol);
_mint(msg.sender, _totalSupply);
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
}
function decimals() public view override returns (uint8) {

@ -25,6 +25,14 @@ contract HypERC20Collateral is TokenRouter {
wrappedToken = IERC20(erc20);
}
function initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) public virtual initializer {
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
}
function balanceOf(
address _account
) external view override returns (uint256) {

@ -21,7 +21,15 @@ contract HypERC20CollateralVaultDeposit is HypERC20Collateral {
address _mailbox
) HypERC20Collateral(_vault.asset(), _mailbox) {
vault = _vault;
}
function initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) public override initializer {
wrappedToken.approve(address(vault), type(uint256).max);
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
}
/**

@ -19,18 +19,22 @@ contract HypERC721 is ERC721EnumerableUpgradeable, TokenRouter {
* @param _mintAmount The amount of NFTs to mint to `msg.sender`.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
* @param _hook The post-dispatch hook contract.
@param _interchainSecurityModule The interchain security module contract.
@param _owner The this contract.
*/
function initialize(
uint256 _mintAmount,
string memory _name,
string memory _symbol
string memory _symbol,
address _hook,
address _interchainSecurityModule,
address _owner
) external initializer {
address owner = msg.sender;
_transferOwnership(owner);
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
__ERC721_init(_name, _symbol);
for (uint256 i = 0; i < _mintAmount; i++) {
_safeMint(owner, i);
_safeMint(msg.sender, i);
}
}

@ -21,6 +21,20 @@ contract HypERC721Collateral is TokenRouter {
wrappedToken = IERC721(erc721);
}
/**
* @notice Initializes the Hyperlane router
* @param _hook The post-dispatch hook contract.
@param _interchainSecurityModule The interchain security module contract.
@param _owner The this contract.
*/
function initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) public virtual initializer {
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
}
function ownerOf(uint256 _tokenId) external view returns (address) {
return IERC721(wrappedToken).ownerOf(_tokenId);
}

@ -20,6 +20,20 @@ contract HypNative is TokenRouter {
constructor(address _mailbox) TokenRouter(_mailbox) {}
/**
* @notice Initializes the Hyperlane router
* @param _hook The post-dispatch hook contract.
@param _interchainSecurityModule The interchain security module contract.
@param _owner The this contract.
*/
function initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) public initializer {
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
}
/**
* @inheritdoc TokenRouter
* @dev uses (`msg.value` - `_amount`) as interchain gas payment and `msg.sender` as refund address.

@ -14,6 +14,7 @@ pragma solidity ^0.8.13;
@@@@@@@@@ @@@@@@@@*/
import "forge-std/Test.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
@ -40,6 +41,7 @@ abstract contract HypTokenTest is Test {
string internal constant SYMBOL = "HYP";
address internal constant ALICE = address(0x1);
address internal constant BOB = address(0x2);
address internal constant PROXY_ADMIN = address(0x37);
ERC20Test internal primaryToken;
TokenRouter internal localToken;
@ -73,8 +75,24 @@ abstract contract HypTokenTest is Test {
REQUIRED_VALUE = noopHook.quoteDispatch("", "");
remoteToken = new HypERC20(DECIMALS, address(remoteMailbox));
remoteToken.initialize(TOTAL_SUPPLY, NAME, SYMBOL);
HypERC20 implementation = new HypERC20(
DECIMALS,
address(remoteMailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC20.initialize.selector,
TOTAL_SUPPLY,
NAME,
SYMBOL,
address(noopHook),
address(igp),
address(this)
)
);
remoteToken = HypERC20(address(proxy));
remoteToken.enrollRemoteRouter(
ORIGIN,
address(localToken).addressToBytes32()
@ -188,10 +206,22 @@ contract HypERC20Test is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypERC20(DECIMALS, address(localMailbox));
erc20Token = HypERC20(address(localToken));
erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL);
HypERC20 implementation = new HypERC20(DECIMALS, address(localMailbox));
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC20.initialize.selector,
TOTAL_SUPPLY,
NAME,
SYMBOL,
address(address(noopHook)),
address(igp),
address(this)
)
);
localToken = HypERC20(address(proxy));
erc20Token = HypERC20(address(proxy));
erc20Token.enrollRemoteRouter(
DESTINATION,
@ -204,7 +234,14 @@ contract HypERC20Test is HypTokenTest {
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL);
erc20Token.initialize(
TOTAL_SUPPLY,
NAME,
SYMBOL,
address(address(noopHook)),
address(igp),
BOB
);
}
function testTotalSupply() public {

@ -14,6 +14,8 @@ pragma solidity ^0.8.13;
@@@@@@@@@ @@@@@@@@*/
import "forge-std/Test.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {ERC4626Test} from "../../contracts/test/ERC4626/ERC4626Test.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {HypTokenTest} from "./HypERC20.t.sol";
@ -31,10 +33,21 @@ contract HypERC20CollateralVaultDepositTest is HypTokenTest {
super.setUp();
vault = new ERC4626Test(address(primaryToken), "Regular Vault", "RV");
localToken = new HypERC20CollateralVaultDeposit(
vault,
address(localMailbox)
HypERC20CollateralVaultDeposit implementation = new HypERC20CollateralVaultDeposit(
vault,
address(localMailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC20CollateralVaultDeposit.initialize.selector,
address(address(noopHook)),
address(igp),
address(this)
)
);
localToken = HypERC20CollateralVaultDeposit(address(proxy));
erc20CollateralVaultDeposit = HypERC20CollateralVaultDeposit(
address(localToken)
);

@ -14,6 +14,9 @@ pragma solidity ^0.8.13;
@@@@@@@@@ @@@@@@@@*/
import "forge-std/Test.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {ERC721URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol";
@ -26,9 +29,6 @@ import {HypERC721Collateral} from "../../contracts/token/HypERC721Collateral.sol
import {HypERC721URIStorage} from "../../contracts/token/extensions/HypERC721URIStorage.sol";
import {HypERC721URICollateral} from "../../contracts/token/extensions/HypERC721URICollateral.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {ERC721URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
abstract contract HypTokenTest is Test, IERC721Receiver {
using TypeCasts for address;
@ -38,6 +38,7 @@ abstract contract HypTokenTest is Test, IERC721Receiver {
address internal constant ALICE = address(0x1);
address internal constant BOB = address(0x2);
address internal constant PROXY_ADMIN = address(0x37);
uint32 internal constant ORIGIN = 11;
uint32 internal constant DESTINATION = 22;
uint256 internal constant TRANSFER_ID = 0;
@ -77,19 +78,42 @@ abstract contract HypTokenTest is Test, IERC721Receiver {
function _deployRemoteToken(bool isCollateral) internal {
if (isCollateral) {
remoteToken = new HypERC721Collateral(
HypERC721Collateral implementation = new HypERC721Collateral(
address(remotePrimaryToken),
address(remoteMailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC721Collateral.initialize.selector,
address(0),
address(0),
address(this)
)
);
remoteToken = HypERC721Collateral(address(proxy));
remotePrimaryToken.transferFrom(
address(this),
address(remoteToken),
0
); // need for processing messages
} else {
HypERC721 erc721 = new HypERC721(address(remoteMailbox));
erc721.initialize(0, NAME, SYMBOL);
remoteToken = TokenRouter(address(erc721));
HypERC721 implementation = new HypERC721(address(remoteMailbox));
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC721.initialize.selector,
0,
NAME,
SYMBOL,
address(0),
address(0),
address(this)
)
);
remoteToken = TokenRouter(address(proxy));
}
remoteToken.enrollRemoteRouter(
ORIGIN,
@ -151,10 +175,22 @@ contract HypERC721Test is HypTokenTest {
function setUp() public virtual override {
super.setUp();
localToken = new HypERC721(address(localMailbox));
hyp721 = HypERC721(address(localToken));
hyp721.initialize(INITIAL_SUPPLY, NAME, SYMBOL);
HypERC721 implementation = new HypERC721(address(localMailbox));
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC721.initialize.selector,
INITIAL_SUPPLY,
NAME,
SYMBOL,
address(0),
address(0),
address(this)
)
);
localToken = HypERC721(address(proxy));
hyp721 = HypERC721(address(proxy));
hyp721.enrollRemoteRouter(
DESTINATION,
@ -164,7 +200,14 @@ contract HypERC721Test is HypTokenTest {
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
hyp721.initialize(INITIAL_SUPPLY, NAME, SYMBOL);
hyp721.initialize(
INITIAL_SUPPLY,
NAME,
SYMBOL,
address(0),
address(0),
address(this)
);
}
function testTotalSupply() public {
@ -228,7 +271,14 @@ contract HypERC721URIStorageTest is HypTokenTest {
localToken = new MockHypERC721URIStorage(address(localMailbox));
hyp721Storage = MockHypERC721URIStorage(address(localToken));
hyp721Storage.initialize(INITIAL_SUPPLY, NAME, SYMBOL);
hyp721Storage.initialize(
INITIAL_SUPPLY,
NAME,
SYMBOL,
address(0),
address(0),
address(this)
);
hyp721Storage.setTokenURI(0, URI);
hyp721Storage.enrollRemoteRouter(
DESTINATION,
@ -254,10 +304,21 @@ contract HypERC721CollateralTest is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypERC721Collateral(
HypERC721Collateral implementation = new HypERC721Collateral(
address(localPrimaryToken),
address(localMailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC721Collateral.initialize.selector,
address(0),
address(0),
address(this)
)
);
localToken = HypERC721Collateral(address(proxy));
hyp721Collateral = HypERC721Collateral(address(localToken));
hyp721Collateral.enrollRemoteRouter(

@ -2,9 +2,11 @@
pragma solidity >=0.8.0;
import "forge-std/Test.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {HypNativeScaled} from "../../contracts/token/extensions/HypNativeScaled.sol";
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {HypNative} from "../../contracts/token/HypNative.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {MockHyperlaneEnvironment} from "../../contracts/mock/MockHyperlaneEnvironment.sol";
@ -37,20 +39,41 @@ contract HypNativeScaledTest is Test {
function setUp() public {
environment = new MockHyperlaneEnvironment(synthDomain, nativeDomain);
synth = new HypERC20(
HypERC20 implementationSynth = new HypERC20(
decimals,
address(environment.mailboxes(synthDomain))
);
synth.initialize(
mintAmount * (10 ** decimals),
"Zebec BSC Token",
"ZBC"
);
native = new HypNativeScaled(
TransparentUpgradeableProxy proxySynth = new TransparentUpgradeableProxy(
address(implementationSynth),
address(9),
abi.encodeWithSelector(
HypERC20.initialize.selector,
mintAmount * (10 ** decimals),
"Zebec BSC Token",
"ZBC",
address(0),
address(0),
address(this)
)
);
synth = HypERC20(address(proxySynth));
HypNativeScaled implementationNative = new HypNativeScaled(
scale,
address(environment.mailboxes(nativeDomain))
);
TransparentUpgradeableProxy proxyNative = new TransparentUpgradeableProxy(
address(implementationNative),
address(9),
abi.encodeWithSelector(
HypNative.initialize.selector,
address(0),
address(0),
address(this)
)
);
native = HypNativeScaled(payable(address(proxyNative)));
native.enrollRemoteRouter(
synthDomain,

@ -296,7 +296,7 @@ async function fetchBaseTokenMetadata(
(base.type === TokenType.collateral && address)
) {
// If it's a collateral type, use a TokenAdapter to query for its metadata
log(`Fetching token metadata for ${address} on ${chainName}}`);
log(`Fetching token metadata for ${address} on ${chainName}`);
const adapter = new EvmTokenAdapter(
chainName,
MultiProtocolProvider.fromMultiProvider(multiProvider),

@ -65,6 +65,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
const mailbox = await this.deployProxiedContract(
chain,
'mailbox',
'mailbox',
proxyAdmin,
[domain],
);

@ -642,14 +642,16 @@ export abstract class HyperlaneDeployer<
*/
async deployProxiedContract<K extends keyof Factories>(
chain: ChainName,
contractName: K,
contractKey: K,
contractName: string,
proxyAdmin: string,
constructorArgs: Parameters<Factories[K]['deploy']>,
initializeArgs?: Parameters<HyperlaneContracts<Factories>[K]['initialize']>,
): Promise<HyperlaneContracts<Factories>[K]> {
// Try to initialize the implementation even though it may not be necessary
const implementation = await this.deployContract(
const implementation = await this.deployContractWithName(
chain,
contractKey,
contractName,
constructorArgs,
initializeArgs,

@ -40,6 +40,7 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
const igp = await this.deployProxiedContract(
chain,
'interchainGasPaymaster',
'interchainGasPaymaster',
proxyAdmin.address,
[],
[await this.multiProvider.getSignerAddress(chain), beneficiary],

@ -1,5 +1,7 @@
import { ethers } from 'ethers';
import { Router } from '@hyperlane-xyz/core';
import { HyperlaneContracts } from '../../contracts/types';
import { ContractVerifier } from '../../deploy/verify/ContractVerifier';
import { MultiProvider } from '../../providers/MultiProvider';
@ -16,11 +18,8 @@ export type InterchainAccountConfig = ProxiedRouterConfig;
export class InterchainAccountDeployer extends ProxiedRouterDeployer<
InterchainAccountConfig,
InterchainAccountFactories,
'interchainAccountRouter'
InterchainAccountFactories
> {
readonly routerContractName = 'interchainAccountRouter';
constructor(
multiProvider: MultiProvider,
contractVerifier?: ContractVerifier,
@ -29,15 +28,26 @@ export class InterchainAccountDeployer extends ProxiedRouterDeployer<
contractVerifier,
});
}
routerContractName(): string {
return 'InterchainAccountRouter';
}
async constructorArgs(_: string, config: RouterConfig): Promise<[string]> {
return [config.mailbox];
routerContractKey<K extends keyof InterchainAccountFactories>(): K {
return 'interchainAccountRouter' as K;
}
async initializeArgs(
chain: string,
router(contracts: HyperlaneContracts<InterchainAccountFactories>): Router {
return contracts.interchainAccountRouter;
}
async constructorArgs<K extends keyof InterchainAccountFactories>(
_: string,
config: RouterConfig,
): Promise<[string, string, string]> {
): Promise<Parameters<InterchainAccountFactories[K]['deploy']>> {
return [config.mailbox] as any;
}
async initializeArgs(chain: string, config: RouterConfig): Promise<any> {
const owner = await this.multiProvider.getSignerAddress(chain);
return [
config.hook ?? ethers.constants.AddressZero,

@ -4,6 +4,7 @@ import {
CircleBridgeAdapter,
LiquidityLayerRouter,
PortalAdapter,
Router,
} from '@hyperlane-xyz/core';
import { Address, eqAddress, objFilter, objMap } from '@hyperlane-xyz/utils';
@ -53,11 +54,8 @@ export type LiquidityLayerConfig = RouterConfig & BridgeAdapterConfig;
export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
LiquidityLayerConfig,
LiquidityLayerFactories,
'liquidityLayerRouter'
LiquidityLayerFactories
> {
readonly routerContractName = 'liquidityLayerRouter';
constructor(
multiProvider: MultiProvider,
contractVerifier?: ContractVerifier,
@ -66,18 +64,31 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
contractVerifier,
});
}
routerContractName(): string {
return 'LiquidityLayerRouter';
}
routerContractKey<K extends keyof LiquidityLayerFactories>(
_: RouterConfig,
): K {
return 'liquidityLayerRouter' as K;
}
router(contracts: HyperlaneContracts<LiquidityLayerFactories>): Router {
return contracts.liquidityLayerRouter;
}
async constructorArgs(
async constructorArgs<K extends keyof LiquidityLayerFactories>(
_: string,
config: LiquidityLayerConfig,
): Promise<[string]> {
return [config.mailbox];
): Promise<Parameters<LiquidityLayerFactories[K]['deploy']>> {
return [config.mailbox] as any;
}
async initializeArgs(
chain: string,
config: LiquidityLayerConfig,
): Promise<[string, string, string]> {
): Promise<any> {
const owner = await this.multiProvider.getSignerAddress(chain);
if (typeof config.interchainSecurityModule === 'object') {
throw new Error('ISM as object unimplemented');

@ -1,5 +1,8 @@
import { ethers } from 'ethers';
import { Router } from '@hyperlane-xyz/core';
import { HyperlaneContracts } from '../../contracts/types';
import { ContractVerifier } from '../../deploy/verify/ContractVerifier';
import { MultiProvider } from '../../providers/MultiProvider';
import { ProxiedRouterDeployer } from '../../router/ProxiedRouterDeployer';
@ -14,11 +17,8 @@ export type InterchainQueryConfig = RouterConfig;
export class InterchainQueryDeployer extends ProxiedRouterDeployer<
InterchainQueryConfig,
InterchainQueryFactories,
'interchainQueryRouter'
InterchainQueryFactories
> {
readonly routerContractName = 'interchainQueryRouter';
constructor(
multiProvider: MultiProvider,
contractVerifier?: ContractVerifier,
@ -28,14 +28,26 @@ export class InterchainQueryDeployer extends ProxiedRouterDeployer<
});
}
async constructorArgs(_: string, config: RouterConfig): Promise<[string]> {
return [config.mailbox];
routerContractName(): string {
return 'InterchainQueryRouter';
}
routerContractKey<K extends keyof InterchainQueryFactories>(): K {
return 'interchainQueryRouter' as K;
}
async initializeArgs(
chain: string,
router(contracts: HyperlaneContracts<InterchainQueryFactories>): Router {
return contracts.interchainQueryRouter;
}
async constructorArgs<K extends keyof InterchainQueryFactories>(
_: string,
config: RouterConfig,
): Promise<[string, string, string]> {
): Promise<Parameters<InterchainQueryFactories[K]['deploy']>> {
return [config.mailbox] as any;
}
async initializeArgs(chain: string, config: RouterConfig): Promise<any> {
const owner = await this.multiProvider.getSignerAddress(chain);
if (typeof config.interchainSecurityModule === 'object') {
throw new Error('ISM as object unimplemented');

@ -1,20 +1,16 @@
import { GasRouter } from '@hyperlane-xyz/core';
import { Address } from '@hyperlane-xyz/utils';
import {
HyperlaneContracts,
HyperlaneContractsMap,
HyperlaneFactories,
} from '../contracts/types';
import { HyperlaneContracts, HyperlaneContractsMap } from '../contracts/types';
import { ChainMap } from '../types';
import { HyperlaneRouterDeployer } from './HyperlaneRouterDeployer';
import { GasRouterConfig } from './types';
import { ProxiedRouterDeployer } from './ProxiedRouterDeployer';
import { GasRouterConfig, ProxiedFactories } from './types';
export abstract class GasRouterDeployer<
Config extends GasRouterConfig,
Factories extends HyperlaneFactories,
> extends HyperlaneRouterDeployer<Config, Factories> {
Factories extends ProxiedFactories,
> extends ProxiedRouterDeployer<Config, Factories> {
abstract router(contracts: HyperlaneContracts<Factories>): GasRouter;
async enrollRemoteRouters(

@ -16,20 +16,37 @@ import { ProxiedFactories, ProxiedRouterConfig } from './types';
export abstract class ProxiedRouterDeployer<
Config extends ProxiedRouterConfig,
Factories extends ProxiedFactories,
RouterKey extends keyof Factories,
> extends HyperlaneRouterDeployer<Config, Factories> {
abstract routerContractName: RouterKey;
abstract router(contracts: HyperlaneContracts<Factories>): Router;
router(contracts: HyperlaneContracts<Factories>): Router {
return contracts[this.routerContractName] as Router;
}
/**
* Returns the contract name
* @param config Router config
*/
abstract routerContractName(config: Config): string;
/**
* Returns the contract key
* @param config Router config
*/
abstract routerContractKey(config: Config): keyof Factories;
abstract constructorArgs(
/**
* Returns the constructor arguments for the proxy
* @param chain Name of chain
* @param config Router config
*/
abstract constructorArgs<RouterKey extends keyof Factories>(
chain: ChainName,
config: Config,
): Promise<Parameters<Factories[RouterKey]['deploy']>>;
abstract initializeArgs(
/**
* Returns the initialize arguments for the proxy
* @param chain Name of chain
* @param config Router config
*/
abstract initializeArgs<RouterKey extends keyof Factories>(
chain: ChainName,
config: Config,
): Promise<
@ -79,14 +96,15 @@ export abstract class ProxiedRouterDeployer<
const proxiedRouter = await this.deployProxiedContract(
chain,
this.routerContractName,
this.routerContractKey(config),
this.routerContractName(config),
proxyAdmin.address,
await this.constructorArgs(chain, config),
await this.initializeArgs(chain, config),
);
return {
[this.routerContractName]: proxiedRouter,
[this.routerContractKey(config)]: proxiedRouter,
proxyAdmin,
timelockController,
} as HyperlaneContracts<Factories>;

@ -60,6 +60,10 @@ export const isCollateralConfig = (
config.type === TokenType.fastCollateral ||
config.type == TokenType.collateralVault;
export const isCollateralVaultConfig = (
config: TokenConfig,
): config is CollateralConfig => config.type === TokenType.collateralVault;
export const isSyntheticConfig = (
config: TokenConfig,
): config is SyntheticConfig =>

@ -12,6 +12,8 @@ import {
HypNative__factory,
} from '@hyperlane-xyz/core';
import { proxiedFactories } from '../router/types';
import { TokenType } from './config';
export const hypERC20contracts = {
@ -23,6 +25,8 @@ export const hypERC20contracts = {
[TokenType.native]: 'HypNative',
[TokenType.nativeScaled]: 'HypNativeScaled',
};
export type HypERC20contracts = typeof hypERC20contracts;
export const hypERC20factories = {
[TokenType.fastCollateral]: new FastHypERC20Collateral__factory(),
[TokenType.fastSynthetic]: new FastHypERC20__factory(),
@ -31,6 +35,7 @@ export const hypERC20factories = {
[TokenType.collateralVault]: new HypERC20CollateralVaultDeposit__factory(),
[TokenType.native]: new HypNative__factory(),
[TokenType.nativeScaled]: new HypNativeScaled__factory(),
...proxiedFactories,
};
export type HypERC20Factories = typeof hypERC20factories;
@ -40,11 +45,15 @@ export const hypERC721contracts = {
[TokenType.syntheticUri]: 'HypERC721URIStorage',
[TokenType.synthetic]: 'HypERC721',
};
export type HypERC721contracts = typeof hypERC721contracts;
export const hypERC721factories = {
[TokenType.collateralUri]: new HypERC721URICollateral__factory(),
[TokenType.collateral]: new HypERC721Collateral__factory(),
[TokenType.syntheticUri]: new HypERC721URIStorage__factory(),
[TokenType.synthetic]: new HypERC721__factory(),
...proxiedFactories,
};
export type HypERC721Factories = typeof hypERC721factories;

@ -1,14 +1,11 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { providers } from 'ethers';
import { constants, providers } from 'ethers';
import {
ERC20__factory,
ERC721EnumerableUpgradeable__factory,
HypERC20,
HypERC20Collateral,
HypERC721,
HypERC721Collateral,
HypNative,
GasRouter,
MailboxClient,
} from '@hyperlane-xyz/core';
import { objKeys, objMap, rootLogger } from '@hyperlane-xyz/utils';
@ -25,15 +22,13 @@ import {
ERC20Metadata,
ERC20RouterConfig,
ERC721RouterConfig,
HypERC20CollateralConfig,
HypERC20Config,
HypERC721CollateralConfig,
HypERC721Config,
HypNativeConfig,
TokenConfig,
TokenMetadata,
TokenType,
isCollateralConfig,
isCollateralVaultConfig,
isErc20Metadata,
isFastConfig,
isNativeConfig,
@ -44,6 +39,7 @@ import {
import {
HypERC20Factories,
HypERC721Factories,
HypERC721contracts,
hypERC20contracts,
hypERC20factories,
hypERC721contracts,
@ -66,6 +62,73 @@ export class HypERC20Deployer extends GasRouterDeployer<
}); // factories not used in deploy
}
routerContractName(config: ERC20RouterConfig): string {
return hypERC20contracts[this.routerContractKey(config)];
}
routerContractKey(config: ERC20RouterConfig) {
if (isCollateralConfig(config)) {
if (isFastConfig(config)) {
return TokenType.fastCollateral;
} else if (isCollateralVaultConfig(config)) {
return TokenType.collateralVault;
} else {
return TokenType.collateral;
}
} else if (isNativeConfig(config)) {
return config.scale ? TokenType.nativeScaled : TokenType.native;
} else if (isSyntheticConfig(config)) {
return isFastConfig(config)
? TokenType.fastSynthetic
: TokenType.synthetic;
} else {
throw new Error('Unknown collateral type when constructing router name');
}
}
async constructorArgs<K extends keyof HypERC20Factories>(
_: ChainName,
config: ERC20RouterConfig,
): Promise<Parameters<HypERC20Factories[K]['deploy']>> {
if (isCollateralConfig(config)) {
return [config.token, config.mailbox] as any;
} else if (isNativeConfig(config)) {
return config.scale
? [config.scale, config.mailbox]
: ([config.mailbox] as any);
} else if (isSyntheticConfig(config)) {
return [config.decimals, config.mailbox] as any;
} else {
throw new Error('Unknown collateral type when constructing arguments');
}
}
async initializeArgs(_: ChainName, config: HypERC20Config): Promise<any> {
// ISM config can be an object, but is not supported right now
if (typeof config.interchainSecurityModule === 'object') {
throw new Error('Token deployer does not support ISM objects currently');
}
const defaultArgs = [
config.hook ?? constants.AddressZero,
config.interchainSecurityModule ?? constants.AddressZero,
config.owner,
];
if (isCollateralConfig(config)) {
return defaultArgs as any;
} else if (isNativeConfig(config)) {
return defaultArgs as any;
} else if (isSyntheticConfig(config)) {
return [
config.totalSupply,
config.name,
config.symbol,
...defaultArgs,
] as any;
} else {
throw new Error('Unknown collateral type when initializing arguments');
}
}
static async fetchMetadata(
provider: providers.Provider,
config: CollateralConfig,
@ -132,104 +195,20 @@ export class HypERC20Deployer extends GasRouterDeployer<
} as ERC20Metadata;
}
protected async deployCollateral(
chain: ChainName,
config: HypERC20CollateralConfig,
): Promise<HypERC20Collateral> {
let tokenType:
| TokenType.fastCollateral
| TokenType.collateral
| TokenType.collateralVault;
switch (config.type) {
case TokenType.fastSynthetic || TokenType.fastCollateral:
tokenType = TokenType.fastCollateral;
break;
case TokenType.collateral:
tokenType = TokenType.collateral;
break;
case TokenType.collateralVault:
tokenType = TokenType.collateralVault;
break;
default:
throw new Error(`Unknown collateral type ${config.type}`);
}
return this.deployContractWithName(
chain,
tokenType,
hypERC20contracts[tokenType],
[config.token, config.mailbox],
);
}
protected async deployNative(
chain: ChainName,
config: HypNativeConfig,
): Promise<HypNative> {
if (config.scale) {
return this.deployContractWithName(
chain,
TokenType.nativeScaled,
hypERC20contracts[TokenType.nativeScaled],
[config.scale, config.mailbox],
);
} else {
return this.deployContractWithName(
chain,
TokenType.native,
hypERC20contracts[TokenType.native],
[config.mailbox],
);
}
}
protected async deploySynthetic(
chain: ChainName,
config: HypERC20Config,
): Promise<HypERC20> {
const tokenType = isFastConfig(config)
? TokenType.fastSynthetic
: TokenType.synthetic;
const router: HypERC20 = await this.deployContractWithName(
chain,
tokenType,
hypERC20contracts[tokenType],
[config.decimals, config.mailbox],
);
try {
await this.multiProvider.handleTx(
chain,
router.initialize(config.totalSupply, config.name, config.symbol),
);
} catch (e: any) {
if (!e.message.includes('already initialized')) {
throw e;
}
this.logger.debug(`${config.type} already initialized`);
}
return router;
}
router(contracts: HyperlaneContracts<HypERC20Factories>) {
for (const key of objKeys(hypERC20factories)) {
if (contracts[key]) {
return contracts[key];
return contracts[key] as GasRouter;
}
}
throw new Error('No matching contract found');
}
async deployContracts(chain: ChainName, config: HypERC20Config) {
let router: HypERC20 | HypERC20Collateral | HypNative;
if (isCollateralConfig(config)) {
router = await this.deployCollateral(chain, config);
} else if (isNativeConfig(config)) {
router = await this.deployNative(chain, config);
} else if (isSyntheticConfig(config)) {
router = await this.deploySynthetic(chain, config);
} else {
throw new Error('Invalid ERC20 token router config');
}
await this.configureClient(chain, router, config);
const { [this.routerContractKey(config)]: router } =
await super.deployContracts(chain, config);
await this.configureClient(chain, router as MailboxClient, config);
return { [config.type]: router } as any;
}
@ -306,6 +285,52 @@ export class HypERC721Deployer extends GasRouterDeployer<
contractVerifier,
});
}
routerContractName(config: ERC721RouterConfig): string {
return hypERC721contracts[this.routerContractKey(config)];
}
routerContractKey<K extends keyof HypERC721contracts>(
config: ERC721RouterConfig,
): K {
if (isCollateralConfig(config)) {
return (
isUriConfig(config) ? TokenType.collateralUri : TokenType.collateral
) as K;
} else {
// if isSyntheticConfig
return (
isUriConfig(config) ? TokenType.syntheticUri : TokenType.synthetic
) as K;
}
}
async constructorArgs(
_: ChainName,
config: ERC721RouterConfig,
): Promise<any> {
if (isCollateralConfig(config)) {
return [config.token, config.mailbox];
} else if (isSyntheticConfig(config)) {
return [config.mailbox];
} else {
throw new Error('Unknown collateral type when constructing arguments');
}
}
async initializeArgs(_: ChainName, config: ERC721RouterConfig): Promise<any> {
const defaultArgs = [
config.hook ?? constants.AddressZero,
config.interchainSecurityModule ?? constants.AddressZero,
config.owner,
];
if (isCollateralConfig(config)) {
return defaultArgs;
} else if (isSyntheticConfig(config)) {
return [config.totalSupply, config.name, config.symbol, ...defaultArgs];
} else {
throw new Error('Unknown collateral type when initializing arguments');
}
}
static async fetchMetadata(
provider: providers.Provider,
@ -337,59 +362,20 @@ export class HypERC721Deployer extends GasRouterDeployer<
}
}
protected async deployCollateral(
chain: ChainName,
config: HypERC721CollateralConfig,
): Promise<HypERC721Collateral> {
const tokenType = isUriConfig(config)
? TokenType.collateralUri
: TokenType.collateral;
return this.deployContractWithName(
chain,
tokenType,
hypERC721contracts[tokenType],
[config.token, config.mailbox],
);
}
protected async deploySynthetic(
chain: ChainName,
config: HypERC721Config,
): Promise<HypERC721> {
const tokenType = isUriConfig(config)
? TokenType.syntheticUri
: TokenType.synthetic;
const router = await this.deployContractWithName(
chain,
tokenType,
hypERC721contracts[tokenType],
[config.mailbox],
);
await this.multiProvider.handleTx(
chain,
router.initialize(config.totalSupply, config.name, config.symbol),
);
return router;
}
router(contracts: HyperlaneContracts<HypERC721Factories>) {
for (const key of objKeys(hypERC721factories)) {
if (contracts[key]) {
return contracts[key];
return contracts[key] as GasRouter;
}
}
throw new Error('No matching contract found');
}
async deployContracts(chain: ChainName, config: HypERC721Config) {
let router: HypERC721 | HypERC721Collateral;
if (isCollateralConfig(config)) {
router = await this.deployCollateral(chain, config);
} else if (isSyntheticConfig(config)) {
router = await this.deploySynthetic(chain, config);
} else {
throw new Error('Invalid ERC721 token router config');
}
const { [this.routerContractKey(config)]: router } =
await super.deployContracts(chain, config);
await this.configureClient(chain, router as MailboxClient, config);
return { [config.type]: router } as any;
}

Loading…
Cancel
Save