Skip to content

Commit

Permalink
feat: smart M bridging
Browse files Browse the repository at this point in the history
  • Loading branch information
0xIryna committed Dec 2, 2024
1 parent ddf583b commit 03ef8d1
Show file tree
Hide file tree
Showing 24 changed files with 560 additions and 93 deletions.
2 changes: 1 addition & 1 deletion lib/example-native-token-transfers
15 changes: 13 additions & 2 deletions script/deploy/DeployBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ contract DeployBase is Script, Utils {

struct HubConfiguration {
address mToken;
address smartMToken;
address registrar;
WormholeConfiguration wormhole;
}
Expand Down Expand Up @@ -132,7 +133,12 @@ contract DeployBase is Script, Utils {
}

function _deployHubPortal(address deployer_, HubConfiguration memory config_) internal returns (address) {
HubPortal implementation_ = new HubPortal(config_.mToken, config_.registrar, config_.wormhole.chainId);
HubPortal implementation_ = new HubPortal(
config_.mToken,
config_.smartMToken,
config_.registrar,
config_.wormhole.chainId
);
HubPortal hubPortalProxy_ = HubPortal(
_deployCreate3Proxy(address(implementation_), _computeSalt(deployer_, "Portal"))
);
Expand All @@ -150,7 +156,10 @@ contract DeployBase is Script, Utils {
address registrar_,
uint16 wormholeChainId_
) internal returns (address) {
SpokePortal implementation_ = new SpokePortal(mToken_, registrar_, wormholeChainId_);
// Pre-compute the expected SpokeSmartMToken proxy address.
address expectedSmartMTokenProxy_ = ContractHelper.getContractFrom(deployer_, _SPOKE_SMART_M_TOKEN_PROXY_NONCE);

SpokePortal implementation_ = new SpokePortal(mToken_, expectedSmartMTokenProxy_, registrar_, wormholeChainId_);
SpokePortal spokePortalProxy_ = SpokePortal(
_deployCreate3Proxy(address(implementation_), _computeSalt(deployer_, "Portal"))
);
Expand Down Expand Up @@ -340,9 +349,11 @@ contract DeployBase is Script, Utils {
console.log("Hub configuration for chain ID %s loaded:", chainId_);

hubConfig_.mToken = file_.readAddress(_readKey(hub_, "m_token"));
hubConfig_.smartMToken = file_.readAddress(_readKey(hub_, "smart_m_token"));
hubConfig_.registrar = file_.readAddress(_readKey(hub_, "registrar"));

console.log("M Token:", hubConfig_.mToken);
console.log("Smart M Token:", hubConfig_.smartMToken);
console.log("Registrar:", hubConfig_.registrar);

hubConfig_.wormhole = _loadWormholeConfig(file_, hub_);
Expand Down
1 change: 1 addition & 0 deletions script/helpers/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract Utils {

address internal constant _MAINNET_REGISTRAR = 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c;
address internal constant _MAINNET_M_TOKEN = 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b;
address internal constant _MAINNET_SMART_M_TOKEN = 0x437cc33344a0B27A429f795ff6B469C72698B291;
address internal constant _MAINNET_VAULT = 0xd7298f620B0F752Cf41BD818a16C756d9dCAA34f;

address internal constant _SEPOLIA_REGISTRAR = 0x975Bf5f212367D09CB7f69D3dc4BA8C9B440aD3A;
Expand Down
17 changes: 15 additions & 2 deletions script/upgrade/UpgradeBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ contract UpgradeBase is Script, Utils {

struct PortalConfiguration {
address mToken;
address smartMToken;
address registrar;
address portal;
uint16 wormholeChainId;
Expand Down Expand Up @@ -57,15 +58,25 @@ contract UpgradeBase is Script, Utils {
}

function _upgradeHubPortal(PortalConfiguration memory config_) internal {
HubPortal implementation_ = new HubPortal(config_.mToken, config_.registrar, config_.wormholeChainId);
HubPortal implementation_ = new HubPortal(
config_.mToken,
config_.smartMToken,
config_.registrar,
config_.wormholeChainId
);

console.log("HubPortal implementation deployed at: ", address(implementation_));

IManagerBase(config_.portal).upgrade(address(implementation_));
}

function _upgradeSpokePortal(PortalConfiguration memory config_) internal {
SpokePortal implementation_ = new SpokePortal(config_.mToken, config_.registrar, config_.wormholeChainId);
SpokePortal implementation_ = new SpokePortal(
config_.mToken,
config_.smartMToken,
config_.registrar,
config_.wormholeChainId
);

console.log("SpokePortal implementation deployed at: ", address(implementation_));

Expand All @@ -84,11 +95,13 @@ contract UpgradeBase is Script, Utils {
console.log("Portal configuration for chain ID %s loaded:", chainId_);

portalConfig_.mToken = file_.readAddress(_readKey(config_, "m_token"));
portalConfig_.smartMToken = file_.readAddress(_readKey(config_, "smart_m_token"));
portalConfig_.registrar = file_.readAddress(_readKey(config_, "registrar"));
portalConfig_.portal = file_.readAddress(_readKey(config_, "portal"));
portalConfig_.wormholeChainId = uint16(file_.readUint(_readKey(config_, "wormhole.chain_id")));

console.log("M Token:", portalConfig_.mToken);
console.log("Smart M Token:", portalConfig_.smartMToken);
console.log("Registrar:", portalConfig_.registrar);
console.log("Portal:", portalConfig_.portal);
console.log("Wormhole chain ID:", portalConfig_.wormholeChainId);
Expand Down
48 changes: 16 additions & 32 deletions src/HubPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ import { TypeConverter } from "./libs/TypeConverter.sol";
contract HubPortal is IHubPortal, Portal {
using TypeConverter for address;

/// @dev Use only standard WormholeTransceiver with relaying enabled
bytes public constant DEFAULT_TRANSCEIVER_INSTRUCTIONS = new bytes(1);

/* ============ Variables ============ */

/// @inheritdoc IHubPortal
Expand All @@ -35,15 +32,17 @@ contract HubPortal is IHubPortal, Portal {

/**
* @notice Constructs the contract.
* @param mToken_ The address of the M token to bridge.
* @param registrar_ The address of the Registrar.
* @param chainId_ Wormhole chain id.
* @param mToken_ The address of the M token to bridge.
* @param smartMToken_ The address of the Smart M token to bridge.
* @param registrar_ The address of the Registrar.
* @param chainId_ Wormhole chain id.
*/
constructor(
address mToken_,
address smartMToken_,
address registrar_,
uint16 chainId_
) Portal(mToken_, registrar_, Mode.LOCKING, chainId_) {}
) Portal(mToken_, smartMToken_, registrar_, Mode.LOCKING, chainId_) {}

/* ============ Interactive Functions ============ */

Expand All @@ -54,7 +53,7 @@ contract HubPortal is IHubPortal, Portal {
) external payable returns (bytes32 messageId_) {
uint128 index_ = _currentIndex();
bytes memory payload_ = PayloadEncoder.encodeIndex(index_, destinationChainId_);
messageId_ = _sendMessage(destinationChainId_, refundAddress_, payload_);
messageId_ = _sendCustomMessage(destinationChainId_, refundAddress_, payload_);

emit MTokenIndexSent(destinationChainId_, messageId_, index_);
}
Expand All @@ -67,7 +66,7 @@ contract HubPortal is IHubPortal, Portal {
) external payable returns (bytes32 messageId_) {
bytes32 value_ = IRegistrarLike(registrar).get(key_);
bytes memory payload_ = PayloadEncoder.encodeKey(key_, value_, destinationChainId_);
messageId_ = _sendMessage(destinationChainId_, refundAddress_, payload_);
messageId_ = _sendCustomMessage(destinationChainId_, refundAddress_, payload_);

emit RegistrarKeySent(destinationChainId_, messageId_, key_, value_);
}
Expand All @@ -81,7 +80,7 @@ contract HubPortal is IHubPortal, Portal {
) external payable returns (bytes32 messageId_) {
bool status_ = IRegistrarLike(registrar).listContains(listName_, account_);
bytes memory payload_ = PayloadEncoder.encodeListUpdate(listName_, account_, status_, destinationChainId_);
messageId_ = _sendMessage(destinationChainId_, refundAddress_, payload_);
messageId_ = _sendCustomMessage(destinationChainId_, refundAddress_, payload_);

emit RegistrarListStatusSent(destinationChainId_, messageId_, listName_, account_, status_);
}
Expand Down Expand Up @@ -118,43 +117,28 @@ contract HubPortal is IHubPortal, Portal {
* @param amount_ The amount of M Token to unlock to the recipient.
*/
function _mintOrUnlock(address recipient_, uint256 amount_, uint128) internal override {
IERC20(mToken()).transfer(recipient_, amount_);
if (recipient_ != address(this)) {
IERC20(mToken()).transfer(recipient_, amount_);
}
}

/// @notice Sends a generic message to the destination chain.
/// @dev The implementation is adapted from `NttManager` `_transfer` function.
function _sendMessage(
/// @dev Sends a custom (not a transfer) message to the destination chain.
function _sendCustomMessage(
uint16 destinationChainId_,
bytes32 refundAddress_,
bytes memory payload_
) private returns (bytes32 messageId_) {
if (refundAddress_ == bytes32(0)) revert InvalidRefundAddress();

(
address[] memory enabledTransceivers_,
TransceiverStructs.TransceiverInstruction[] memory instructions_,
uint256[] memory priceQuotes_,

) = _prepareForTransfer(destinationChainId_, DEFAULT_TRANSCEIVER_INSTRUCTIONS);

TransceiverStructs.NttManagerMessage memory message_ = TransceiverStructs.NttManagerMessage(
bytes32(uint256(_useMessageSequence())),
msg.sender.toBytes32(),
payload_
);

// send the message
_sendMessageToTransceivers(
destinationChainId_,
refundAddress_,
_getPeersStorage()[destinationChainId_].peerAddress,
priceQuotes_,
instructions_,
enabledTransceivers_,
TransceiverStructs.encodeNttManagerMessage(message_)
);
_sendMessage(destinationChainId_, refundAddress_, message_);

return TransceiverStructs.nttManagerMessageDigest(chainId, message_);
messageId_ = TransceiverStructs.nttManagerMessageDigest(chainId, message_);
}

/* ============ Internal View/Pure Functions ============ */
Expand Down
Loading

0 comments on commit 03ef8d1

Please sign in to comment.