Skip to content

Commit

Permalink
Upgradable warp routes (#3474)
Browse files Browse the repository at this point in the history
### 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
  • Loading branch information
ltyu authored and yorhodes committed Apr 14, 2024
1 parent 19a4e99 commit d1e4112
Show file tree
Hide file tree
Showing 22 changed files with 463 additions and 227 deletions.
6 changes: 5 additions & 1 deletion solidity/contracts/token/HypERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions solidity/contracts/token/HypERC20Collateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions solidity/contracts/token/HypERC20CollateralVaultDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
14 changes: 9 additions & 5 deletions solidity/contracts/token/HypERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
14 changes: 14 additions & 0 deletions solidity/contracts/token/HypERC721Collateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
14 changes: 14 additions & 0 deletions solidity/contracts/token/HypNative.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
51 changes: 44 additions & 7 deletions solidity/test/token/HypERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
19 changes: 16 additions & 3 deletions solidity/test/token/HypERC20CollateralVaultDeposit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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)
);
Expand Down
89 changes: 75 additions & 14 deletions solidity/test/token/HypERC721.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand Down
Loading

0 comments on commit d1e4112

Please sign in to comment.