@@ -71,9 +71,6 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
7171 /// @inheritdoc IWrappedMToken
7272 address public immutable earnerManager;
7373
74- /// @inheritdoc IWrappedMToken
75- address public immutable migrationAdmin;
76-
7774 /// @inheritdoc IWrappedMToken
7875 address public immutable mToken;
7976
@@ -83,6 +80,9 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
8380 /// @inheritdoc IWrappedMToken
8481 address public immutable excessDestination;
8582
83+ /// @dev Used to check if the contract is the implementation or a proxy.
84+ address internal immutable _self = address (this );
85+
8686 /// @inheritdoc IWrappedMToken
8787 uint112 public totalEarningPrincipal;
8888
@@ -100,29 +100,49 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
100100
101101 mapping (address account = > address claimRecipient ) internal _claimRecipients;
102102
103- /* ============ Constructor ============ */
103+ /// @inheritdoc IWrappedMToken
104+ address public migrationAdmin;
105+
106+ /// @inheritdoc IWrappedMToken
107+ address public pendingMigrationAdmin;
108+
109+ /* ============ Modifiers ============ */
110+
111+ modifier onlyMigrationAdmin () {
112+ _revertIfNotMigrationAdmin ();
113+ _;
114+ }
115+
116+ /* ============ Constructor and Initializer ============ */
104117
105118 /**
106119 * @dev Constructs the contract given an M Token address and migration admin.
107- * Note that a proxy will not need to initialize since there are no mutable storage values affected.
108120 * @param mToken_ The address of an M Token.
109121 * @param registrar_ The address of a Registrar.
110122 * @param earnerManager_ The address of an Earner Manager.
111123 * @param excessDestination_ The address of an excess destination.
112- * @param migrationAdmin_ The address of a migration admin.
113124 */
114125 constructor (
115126 address mToken_ ,
116127 address registrar_ ,
117128 address earnerManager_ ,
118- address excessDestination_ ,
119- address migrationAdmin_
129+ address excessDestination_
120130 ) ERC20Extended ("M (Wrapped) by M^0 " , "wM " , 6 ) {
121131 if ((mToken = mToken_) == address (0 )) revert ZeroMToken ();
122132 if ((registrar = registrar_) == address (0 )) revert ZeroRegistrar ();
123133 if ((earnerManager = earnerManager_) == address (0 )) revert ZeroEarnerManager ();
124134 if ((excessDestination = excessDestination_) == address (0 )) revert ZeroExcessDestination ();
125- if ((migrationAdmin = migrationAdmin_) == address (0 )) revert ZeroMigrationAdmin ();
135+ }
136+
137+ /**
138+ * @dev Used by a proxy of this implementation to initialize its state.
139+ * @param migrationAdmin_ The address of a migration admin.
140+ */
141+ function initialize (address migrationAdmin_ ) external {
142+ if (address (this ) == _self) revert NotProxy ();
143+ if (migrationAdmin != address (0 )) revert AlreadyInitialized ();
144+
145+ _setMigrationAdmin (migrationAdmin_);
126146 }
127147
128148 /* ============ Interactive Functions ============ */
@@ -266,12 +286,24 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
266286 /* ============ Temporary Admin Migration ============ */
267287
268288 /// @inheritdoc IWrappedMToken
269- function migrate (address migrator_ ) external {
270- if (msg .sender != migrationAdmin) revert UnauthorizedMigration ();
271-
289+ function migrate (address migrator_ ) external onlyMigrationAdmin {
272290 _migrate (migrator_);
273291 }
274292
293+ /// @inheritdoc IWrappedMToken
294+ function setPendingMigrationAdmin (address migrationAdmin_ ) external onlyMigrationAdmin {
295+ emit PendingMigrationAdminSet (pendingMigrationAdmin = migrationAdmin_);
296+ }
297+
298+ /// @inheritdoc IWrappedMToken
299+ function acceptMigrationAdmin () external {
300+ if (msg .sender != pendingMigrationAdmin) revert NotPendingMigrationAdmin ();
301+
302+ _setMigrationAdmin (msg .sender );
303+
304+ delete pendingMigrationAdmin;
305+ }
306+
275307 /* ============ View/Pure Functions ============ */
276308
277309 /// @inheritdoc IWrappedMToken
@@ -781,6 +813,16 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
781813 emit StoppedEarning (account_);
782814 }
783815
816+ /**
817+ * @dev Sets the migration admin to `migrationAdmin_`.
818+ * @param migrationAdmin_ The address of the account to set as the migration admin.
819+ */
820+ function _setMigrationAdmin (address migrationAdmin_ ) internal {
821+ if ((migrationAdmin = migrationAdmin_) == address (0 )) revert ZeroMigrationAdmin ();
822+
823+ emit MigrationAdminSet (migrationAdmin_);
824+ }
825+
784826 /* ============ Internal View/Pure Functions ============ */
785827
786828 /// @dev Returns the current index of the M Token.
@@ -878,6 +920,11 @@ contract WrappedMToken is IWrappedMToken, Migratable, ERC20Extended {
878920 if (account_ == address (0 )) revert InvalidRecipient (account_);
879921 }
880922
923+ /// @dev Reverts if the caller is not the migration admin.
924+ function _revertIfNotMigrationAdmin () internal view {
925+ if (msg .sender != migrationAdmin) revert NotMigrationAdmin ();
926+ }
927+
881928 /**
882929 * @dev Reads the uint128 value at some index of an array of uint128 values whose storage pointer is given,
883930 * assuming the index is valid, without wasting gas checking for out-of-bounds errors.
0 commit comments