Skip to content

Commit

Permalink
feat(AssetMigrationUpgradeable): add extension for admin migrating as…
Browse files Browse the repository at this point in the history
…set into another pools
  • Loading branch information
TuDo1403 committed Dec 5, 2024
1 parent 0791a29 commit 1be7cd9
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 1 deletion.
197 changes: 197 additions & 0 deletions src/extensions/AssetMigrationUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { IWETH } from "src/interfaces/IWETH.sol";
import { ErrLengthMismatch, ErrEmptyArray } from "src/utils/CommonErrors.sol";

abstract contract AssetMigrationUpgradeable is Initializable {
using SafeERC20 for IERC20;

/// @dev Error when the storage location is null
error ErrInvalidStorageLocation();
/// @dev Error when the caller is not authorized
error ErrUnauthorizedCaller(address expected, address caller);

/// @dev The native token indicator address
address internal constant _NATIVE_TOKEN_INDICATOR = address(0);

/// @custom:storage-location erc7201:ronin.bridge.AssetMigration
struct AssetMigrationStorage {
IWETH _wnt;
// Migrator address
address _addr;
// Pending migrator address
address _pendingAddr;
}

/// @dev Emitted when a new migrator is set.
event NewMigrator(address indexed by, address indexed migrator);
/// @dev Emitted when the wrapped native token is set.
event WrappedNativeTokenSet(address indexed by, address indexed wnt);
/// @dev Emitted when the migrator transfer process is started.
event MigratorTransferStarted(address indexed migrator, address indexed newMigrator);

/**
* @dev Modifier to check if the caller is the migrator.
*/
modifier onlyMigrator() {
_requireMigrator();
_;
}

/**
* @dev Returns the wrapped native token.
* - For `RoninGatewayV3`, it MUST return the WRON token.
* - For `MainchainGatewayV3`, it MUST return the WETH token.
*/
function _getWrappedNativeToken() internal view returns (IWETH) {
return _getAssetMigration()._wnt;
}

/**
* @dev Initializes the neccessary data.
*/
function __AssetMigration_init(address wnt, address migrator) internal onlyInitializing {
__AssetMigration_init_unchained(wnt, migrator);
}

/**
* @dev Initializes the neccessary data.
*/
function __AssetMigration_init_unchained(address wnt, address migrator) internal onlyInitializing {
_setMigrator(migrator);
_setWrappedNativeToken(wnt);
}

/**
* @dev Migrates the given tokens to the specified addresses.
*
* When the token is the native (i.e RON or ETH), it will be wrapped (i.e WRON, WETH).
*
* Requirements:
* - The caller must be the migrator.
* - The length of the arrays must be the same.
* - The length of the arrays must not be zero.
*/
function adminMigrate(address[] calldata tos, IERC20[] calldata tokens, uint256[] calldata amounts) external onlyMigrator {
uint256 length = tos.length;

require(length != 0, ErrEmptyArray());
require(length == tokens.length && tokens.length == amounts.length, ErrLengthMismatch(msg.sig));

IERC20 token;

for (uint256 i; i < length; ++i) {
token = tokens[i];

if (address(token) == address(_NATIVE_TOKEN_INDICATOR)) {
token = _wrap(amounts[i]);
}

token.safeTransfer(tos[i], amounts[i]);
}
}

/**
* @dev Renounces the migrator role.
*/
function renounceMigrator() external onlyMigrator {
_setMigrator(address(0));
}

/**
* @dev Starts the process to change the migrator.
* Replaces the pending migrator address if there is one.
*
* Setting `newMigrator` to the zero address is allowed. This will cancel the pending migrator.
*/
function changeMigrator(
address newMigrator
) external onlyMigrator {
_getAssetMigration()._pendingAddr = newMigrator;

emit MigratorTransferStarted(msg.sender, newMigrator);
}

/**
* @dev Accepts the migrator role.
*/
function acceptMigrator() external {
require(_getAssetMigration()._pendingAddr == msg.sender, ErrUnauthorizedCaller(_getAssetMigration()._pendingAddr, msg.sender));

_setMigrator(msg.sender);
}

/**
* @dev Returns the pending migrator address.
*/
function getPendingMigrator() external view returns (address) {
return _getAssetMigration()._pendingAddr;
}

/**
* @dev Returns the pointer of the AssetMigrationStorage struct.
*/
function _getAssetMigration() internal pure returns (AssetMigrationStorage storage $) {
bytes32 loc = _$$AssetMigrationLocation();
require(loc != 0, ErrInvalidStorageLocation());

assembly ("memory-safe") {
$.slot := loc
}
}

/**
* @dev Returns the custom storage location of the AssetMigrationStorage struct.
*/
function _$$AssetMigrationLocation() internal pure virtual returns (bytes32 storageLoc) {
// value is equal to keccak256(abi.encode(uint256(keccak256("ronin.bridge.AssetMigration")) - 1)) &
// ~bytes32(uint256(0xff))
storageLoc = 0xbfb7c7cbd9f93146fbbf37acaa2236698ffc013cbf87c0b4f57505d22ae7c200;
}

/**
* @dev Converts the native token to its wrapped version.
*/
function _wrap(
uint256 amount
) internal returns (IERC20) {
IWETH wrappedToken = _getWrappedNativeToken();
wrappedToken.deposit{ value: amount }();

return IERC20(address(wrappedToken));
}

/**
* @dev Sets the migrator address.
*/
function _setMigrator(
address addr
) internal {
_getAssetMigration()._addr = addr;

emit NewMigrator(msg.sender, addr);
}

/**
* @dev Sets the wrapped native token.
*/
function _setWrappedNativeToken(
address wnt
) internal {
_getAssetMigration()._wnt = IWETH(wnt);

emit WrappedNativeTokenSet(msg.sender, wnt);
}

/**
* @dev Throws if the caller is not the migrator.
*/
function _requireMigrator() internal view {
require(_getAssetMigration()._addr != msg.sender, ErrUnauthorizedCaller(_getAssetMigration()._addr, msg.sender));
}
}
6 changes: 6 additions & 0 deletions src/mainchain/MainchainGatewayV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IBridgeManagerCallback } from "../interfaces/bridge/IBridgeManagerCallb
import { HasContracts, ContractType } from "../extensions/collections/HasContracts.sol";
import "../extensions/WethUnwrapper.sol";
import "../extensions/WithdrawalLimitation.sol";
import "../extensions/AssetMigrationUpgradeable.sol";
import "../libraries/Transfer.sol";
import "../interfaces/IMainchainGatewayV3.sol";

Expand All @@ -20,6 +21,7 @@ contract MainchainGatewayV3 is
ERC1155Holder,
IMainchainGatewayV3,
HasContracts,
AssetMigrationUpgradeable,
IBridgeManagerCallback
{
using LibTokenInfo for TokenInfo;
Expand Down Expand Up @@ -135,6 +137,10 @@ contract MainchainGatewayV3 is
*/
}

function initializeV5(address migrator) external reinitializer(5) {
__AssetMigration_init_unchained(address(wrappedNativeToken), migrator);
}

/**
* @dev Receives ether without doing anything. Use this function to topup native token.
*/
Expand Down
8 changes: 7 additions & 1 deletion src/ronin/gateway/RoninGatewayV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "../../extensions/GatewayV3.sol";
import "../../extensions/collections/HasContracts.sol";
import "../../extensions/MinimumWithdrawal.sol";
import "../../extensions/AssetMigrationUpgradeable.sol";
import "../../interfaces/IERC20Mintable.sol";
import "../../interfaces/IERC721Mintable.sol";
import "../../interfaces/bridge/IBridgeTracking.sol";
Expand All @@ -24,7 +25,8 @@ contract RoninGatewayV3 is
ERC1155Holder,
VoteStatusConsumer,
IRoninGatewayV3,
HasContracts
HasContracts,
AssetMigrationUpgradeable
{
using LibTokenInfo for TokenInfo;
using Transfer for Transfer.Request;
Expand Down Expand Up @@ -126,6 +128,10 @@ contract RoninGatewayV3 is
_setContract(ContractType.BRIDGE_MANAGER, bridgeAdmin);
}

function initializeV4(address wnt, address migrator) external reinitializer(4) {
__AssetMigration_init(wnt, migrator);
}

/**
* @inheritdoc IRoninGatewayV3
*/
Expand Down

0 comments on commit 1be7cd9

Please sign in to comment.