Skip to content

Commit

Permalink
feat(script): add SmartM token deploy script
Browse files Browse the repository at this point in the history
  • Loading branch information
PierrickGT committed Oct 18, 2024
1 parent ea158dc commit 8b6dd2c
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@
path = lib/example-native-token-transfers
url = [email protected]:wormhole-foundation/example-native-token-transfers.git
branch = main
[submodule "lib/wrapped-m-token"]
path = lib/wrapped-m-token
url = https://github.com/m0-foundation/wrapped-m-token
1 change: 1 addition & 0 deletions lib/wrapped-m-token
Submodule wrapped-m-token added at 5c1e17
75 changes: 65 additions & 10 deletions script/deploy/DeployBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ pragma solidity 0.8.26;

import { Script, console2 } from "../../lib/forge-std/src/Script.sol";

import { ContractHelper } from "../../lib/common/src/libs/ContractHelper.sol";

import { MToken as SpokeMToken } from "../../lib/protocol/src/MToken.sol";
import { Registrar as SpokeRegistrar } from "../../lib/ttg/src/Registrar.sol";
import { WrappedMToken as SpokeSmartMToken } from "../../lib/wrapped-m-token/src/WrappedMToken.sol";
import { Proxy as SpokeSmartMTokenProxy } from "../../lib/wrapped-m-token/src/Proxy.sol";

import {
ERC1967Proxy
Expand Down Expand Up @@ -83,16 +87,16 @@ contract DeployBase is Script, Utils {

/**
* @dev Deploys Spoke components.
* @param deployer_ The address of the deployer.
* @param wormholeChainId_ The Wormhole chain ID on which the contracts will be deployed.
* @param wormholeCoreBridge_ The address of the Wormhole Core Bridge.
* @param wormholeRelayerAddr_ The address of the Wormhole Standard Relayer.
* @param specialRelayerAddr_ The address of the Specialized Relayer.
* @param burnNonces_ The function to burn nonces.
* @return spokePortal_ The address of the deployed Spoke Portal.
* @return spokeTransceiver_ The address of the deployed Spoke WormholeTransceiver.
* @return spokeRegistrar_ The address of the deployed Spoke Registrar.
* @return spokeMToken_ The address of the deployed Spoke MToken.
* @param deployer_ The address of the deployer.
* @param wormholeChainId_ The Wormhole chain ID on which the contracts will be deployed.
* @param wormholeCoreBridge_ The address of the Wormhole Core Bridge.
* @param wormholeRelayerAddr_ The address of the Wormhole Standard Relayer.
* @param specialRelayerAddr_ The address of the Specialized Relayer.
* @param burnNonces_ The function to burn nonces.
* @return spokePortal_ The address of the deployed Spoke Portal.
* @return spokeTransceiver_ The address of the deployed Spoke WormholeTransceiver.
* @return spokeRegistrar_ The address of the deployed Spoke Registrar.
* @return spokeMToken_ The address of the deployed Spoke MToken.
*/
function _deploySpokeComponents(
address deployer_,
Expand Down Expand Up @@ -235,6 +239,57 @@ contract DeployBase is Script, Utils {
return address(spokeMToken_);
}

function _deploySpokeSmartMToken(
address deployer_,
address spokeMToken_,
address registrar_,
address migrationAdmin_,
function(address, uint64, uint64) internal burnNonces_
) internal returns (address spokeSmartMTokenImplementation_, address spokeSmartMTokenProxy_) {
uint64 deployerNonce_ = vm.getNonce(deployer_);

if (deployerNonce_ > _SPOKE_SMART_TOKEN_NONCE) {
revert DeployerNonceTooHigh(_SPOKE_SMART_TOKEN_NONCE, deployerNonce_);
}

burnNonces_(deployer_, deployerNonce_, _SPOKE_SMART_TOKEN_NONCE);

deployerNonce_ = vm.getNonce(deployer_);
if (deployerNonce_ != _SPOKE_SMART_TOKEN_NONCE) {
revert DeployerNonceTooHigh(_SPOKE_SMART_TOKEN_NONCE, deployerNonce_);
}

// Pre-compute the expected SpokeSmartMToken implementation address.
address expectedSmartMTokenImplementation_ = ContractHelper.getContractFrom(
deployer_,
_SPOKE_SMART_TOKEN_NONCE
);

spokeSmartMTokenImplementation_ = address(new SpokeSmartMToken(spokeMToken_, registrar_, migrationAdmin_));

if (expectedSmartMTokenImplementation_ != spokeSmartMTokenImplementation_) {
revert ExpectedAddressMismatch(expectedSmartMTokenImplementation_, spokeSmartMTokenImplementation_);
}

console2.log("SpokeSmartMTokenImplementation:", spokeSmartMTokenImplementation_);

deployerNonce_ = vm.getNonce(deployer_);
if (deployerNonce_ != _SPOKE_SMART_TOKEN_PROXY_NONCE) {
revert DeployerNonceTooHigh(_SPOKE_SMART_TOKEN_PROXY_NONCE, deployerNonce_);
}

// Pre-compute the expected SpokeSmartMToken proxy address.
address expectedSmartMTokenProxy_ = ContractHelper.getContractFrom(deployer_, _SPOKE_SMART_TOKEN_PROXY_NONCE);

spokeSmartMTokenProxy_ = address(new SpokeSmartMTokenProxy(spokeSmartMTokenImplementation_));

if (expectedSmartMTokenProxy_ != spokeSmartMTokenProxy_) {
revert ExpectedAddressMismatch(expectedSmartMTokenProxy_, spokeSmartMTokenProxy_);
}

console2.log("SpokeSmartMTokenProxy:", spokeSmartMTokenProxy_);
}

function _configurePortal(address portal_, address transceiver_) internal {
IManagerBase(portal_).setTransceiver(transceiver_);
console2.log("Transceiver address set: ", transceiver_);
Expand Down
32 changes: 32 additions & 0 deletions script/deploy/dev/DeployDevSpoke.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,28 @@ contract DeployDevSpoke is DeployBase {
_burnNonces
);

(
address spokeBaseSepoliaSmartMTokenImplementation_,
address spokeBaseSepoliaSmartMTokenProxy_
) = _deploySpokeSmartMToken(
deployer_,
spokeBaseSepoliaMToken_,
spokeBaseSepoliaRegistrar_,
deployer_,
_burnNonces
);

vm.stopBroadcast();

console2.log("Base Sepolia Spoke NTT Manager address:", spokeBaseSepoliaNTTManager_);
console2.log("Base Sepolia Spoke Wormhole Transceiver address:", spokeBaseSepoliaWormholeTransceiver_);
console2.log("Base Sepolia Spoke Registrar address:", spokeBaseSepoliaRegistrar_);
console2.log("Base Sepolia Spoke MToken address:", spokeBaseSepoliaMToken_);
console2.log(
"Base Sepolia SmartMToken implementation address:",
spokeBaseSepoliaSmartMTokenImplementation_
);
console2.log("Base Sepolia Spoke MToken proxy address:", spokeBaseSepoliaSmartMTokenProxy_);
} else if (block.chainid == _OPTIMISM_SEPOLIA_CHAIN_ID) {
vm.startBroadcast(deployer_);

Expand All @@ -50,13 +66,29 @@ contract DeployDevSpoke is DeployBase {
_burnNonces
);

(
address spokeOptimismSepoliaSmartMTokenImplementation_,
address spokeOptimismSepoliaSmartMTokenProxy_
) = _deploySpokeSmartMToken(
deployer_,
spokeOptimismSepoliaMToken_,
spokeOptimismSepoliaRegistrar_,
deployer_,
_burnNonces
);

console2.log("Optimism Sepolia Spoke NTT Manager address:", spokeOptimismSepoliaNTTManager_);
console2.log(
"Optimism Sepolia Spoke Wormhole Transceiver address:",
spokeOptimismSepoliaWormholeTransceiver_
);
console2.log("Optimism Sepolia Spoke Registrar address:", spokeOptimismSepoliaRegistrar_);
console2.log("Optimism Sepolia Spoke MToken address:", spokeOptimismSepoliaMToken_);
console2.log(
"Optimism Sepolia SmartMToken implementation address:",
spokeOptimismSepoliaSmartMTokenImplementation_
);
console2.log("Optimism Sepolia Spoke MToken proxy address:", spokeOptimismSepoliaSmartMTokenProxy_);

vm.stopBroadcast();
} else {
Expand Down
2 changes: 2 additions & 0 deletions script/helpers/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ICreateXLike } from "../deploy/interfaces/ICreateXLike.sol";
contract Utils {
uint64 internal constant _SPOKE_REGISTRAR_NONCE = 7;
uint64 internal constant _SPOKE_M_TOKEN_NONCE = 8;
uint64 internal constant _SPOKE_SMART_TOKEN_NONCE = 39;
uint64 internal constant _SPOKE_SMART_TOKEN_PROXY_NONCE = 40;

address internal constant _MAINNET_REGISTRAR = 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c;
address internal constant _MAINNET_M_TOKEN = 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b;
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IMTokenLike.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ interface IMTokenLike {

/// @notice Stops earning for caller.
function stopEarning() external;
}
}
2 changes: 1 addition & 1 deletion src/interfaces/IRegistrarLike.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ interface IRegistrarLike {

/// @notice Returns the address of the Portal contract.
function portal() external view returns (address);
}
}
1 change: 0 additions & 1 deletion src/interfaces/ISpokeMTokenLike.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,3 @@ interface ISpokeMTokenLike is IMTokenLike {
*/
function updateIndex(uint128 index) external;
}

15 changes: 15 additions & 0 deletions test/fork/Deploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity 0.8.26;

import { Test } from "../../lib/forge-std/src/Test.sol";

import { ContractHelper } from "../../lib/common/src/libs/ContractHelper.sol";

import { MToken as SpokeMToken } from "../../lib/protocol/src/MToken.sol";
import { Registrar as SpokeRegistrar } from "../../lib/ttg/src/Registrar.sol";

Expand Down Expand Up @@ -63,6 +65,14 @@ contract Deploy is DeployBase, Test {
_burnNonces
);

// TODO: remove once Wrapped M Token has been updated to pass vault address to constructor.
vm.mockCall(baseSpokeRegistrar_, abi.encodeWithSelector(bytes4(keccak256("vault()"))), abi.encode(address(0)));

(
address spokeBaseSepoliaSmartMTokenImplementation_,
address spokeBaseSepoliaSmartMTokenProxy_
) = _deploySpokeSmartMToken(_DEPLOYER, baseSpokeMToken_, baseSpokeRegistrar_, _DEPLOYER, _burnNonces);

vm.stopPrank();

// Contracts addresses should be the same across all networks.
Expand All @@ -72,10 +82,15 @@ contract Deploy is DeployBase, Test {
_computeSalt(_DEPLOYER, "WormholeTransceiver")
);

address expectedSmartMTokenImplementation_ = ContractHelper.getContractFrom(_DEPLOYER, 39);
address expectedSmartMTokenProxy_ = ContractHelper.getContractFrom(_DEPLOYER, 40);

assertEq(baseSpokePortal_, expectedSpokePortal_);
assertEq(baseSpokeWormholeTransceiver_, expectedSpokeWormholeTransceiver_);
assertEq(baseSpokeRegistrar_, _MAINNET_REGISTRAR);
assertEq(baseSpokeMToken_, _MAINNET_M_TOKEN);
assertEq(spokeBaseSepoliaSmartMTokenImplementation_, expectedSmartMTokenImplementation_);
assertEq(spokeBaseSepoliaSmartMTokenProxy_, expectedSmartMTokenProxy_);

assertEq(SpokeMToken(baseSpokeMToken_).portal(), baseSpokePortal_);
assertEq(SpokeMToken(baseSpokeMToken_).registrar(), baseSpokeRegistrar_);
Expand Down

0 comments on commit 8b6dd2c

Please sign in to comment.