Skip to content

Commit 50a5cb3

Browse files
committed
add mip b53
1 parent 4c9b186 commit 50a5cb3

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed

proposals/mips/mip-b53/b53.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
## MIP-B53: WETH Market Ownership Wrapper
2+
3+
### **Summary**
4+
5+
This proposal deploys and migrates the WETH market admin to a new owner wrapper
6+
contract that can reliably receive native ETH. This solves a critical issue
7+
preventing the protocol from reducing WETH market reserves due to execution
8+
context limitations when TEMPORAL_GOVERNOR receives ETH during proposal
9+
execution.
10+
11+
### **Background**
12+
13+
The WETH market on Base uses an unwrapper contract that converts WETH to native
14+
ETH when reducing reserves. While TEMPORAL_GOVERNOR has a receive() function,
15+
there have been persistent issues when receiving ETH during proposal execution,
16+
causing reserve reduction operations to fail.
17+
18+
This issue was previously identified in MIP-X37, which attempted to reduce 347
19+
WETH in reserves but had to be reverted due to these ETH receiving problems. The
20+
WETH reserves were also removed from MIP-B52 for the same reason.
21+
22+
### **Proposal**
23+
24+
This proposal implements a solution by deploying an MWethOwnerWrapper contract
25+
that:
26+
27+
1. **Acts as the new admin for the WETH market** - The wrapper becomes the
28+
admin, replacing TEMPORAL_GOVERNOR as the direct admin.
29+
30+
2. **Automatically wraps received ETH to WETH** - When the wrapper receives
31+
native ETH (from reserve reductions), it automatically wraps it to WETH,
32+
avoiding any receive() execution context issues.
33+
34+
3. **Delegates all admin functions** - The wrapper forwards all admin function
35+
calls to the underlying WETH market, including:
36+
37+
- `_reduceReserves(uint256)` - Reduce reserves
38+
- `_setPendingAdmin(address)` - Admin transfers
39+
- `_setReserveFactor(uint256)` - Economic parameters
40+
- `_setInterestRateModel(address)` - Rate model updates
41+
- `_setProtocolSeizeShare(uint256)` - Protocol parameters
42+
- `_addReserves(uint256)` - Reserve additions
43+
44+
4. **Remains controlled by TEMPORAL_GOVERNOR** - The wrapper itself is owned by
45+
TEMPORAL_GOVERNOR, maintaining governance control.
46+
47+
5. **Enables token extraction** - The wrapper includes a `withdrawToken()`
48+
function to extract WETH after reserve reductions, allowing the protocol to
49+
use the funds.
50+
51+
### **Technical Implementation**
52+
53+
The proposal executes the following actions:
54+
55+
1. Deploy MWethOwnerWrapper implementation contract
56+
2. Deploy TransparentUpgradeableProxy for the wrapper
57+
3. Initialize the wrapper with:
58+
- MOONWELL_WETH as the target mToken
59+
- WETH token address
60+
- TEMPORAL_GOVERNOR as the owner
61+
4. Call `MOONWELL_WETH._setPendingAdmin(wrapperProxy)` to set the wrapper as
62+
pending admin
63+
5. Call `wrapperProxy._acceptAdmin()` to complete the admin transfer
64+
65+
### **Rationale**
66+
67+
This approach provides several benefits:
68+
69+
1. **Reliability** - The automatic ETH-to-WETH wrapping in the receive()
70+
function ensures no execution context issues when receiving funds from the
71+
unwrapper.
72+
73+
2. **Governance Control** - TEMPORAL_GOVERNOR remains in control by owning the
74+
wrapper, maintaining the existing governance security model.
75+
76+
3. **Flexibility** - The wrapper delegates all admin functions, so future
77+
parameter updates and reserve management can be performed normally.
78+
79+
4. **Upgradeability** - The wrapper uses the TransparentUpgradeableProxy
80+
pattern, allowing future upgrades if needed (controlled by MRD_PROXY_ADMIN).
81+
82+
5. **Enables Future Reserve Reductions** - Once deployed, the protocol can
83+
successfully reduce WETH reserves and use them for bad debt remediation or
84+
other purposes.
85+
86+
### **Next Steps**
87+
88+
After this proposal passes, a follow-up proposal can be created to:
89+
90+
- Reduce WETH reserves (previously attempted in MIP-X37)
91+
- Call `wrapperProxy.withdrawToken()` to extract the WETH
92+
- Use the WETH for bad debt repayment or other protocol needs
93+
94+
### **Voting Options**
95+
96+
- **Yes** — Deploy the MWethOwnerWrapper and migrate WETH market admin to enable
97+
reserve reductions.
98+
- **No** — Reject the wrapper deployment; WETH reserves remain inaccessible.
99+
- **Abstain** — No preference.

proposals/mips/mip-b53/mip-b53.sol

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
pragma solidity 0.8.19;
3+
4+
import "@forge-std/Test.sol";
5+
6+
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
7+
8+
import {MTokenInterface} from "@protocol/MTokenInterfaces.sol";
9+
import {MWethOwnerWrapper} from "@protocol/MWethOwnerWrapper.sol";
10+
11+
import {HybridProposal, ActionType} from "@proposals/proposalTypes/HybridProposal.sol";
12+
import {AllChainAddresses as Addresses} from "@proposals/Addresses.sol";
13+
import {BASE_FORK_ID} from "@utils/ChainIds.sol";
14+
import {ProposalActions} from "@proposals/utils/ProposalActions.sol";
15+
16+
import {DeployMWethOwnerWrapper} from "@script/DeployMWethOwnerWrapper.s.sol";
17+
18+
/// @title MIP-B53: WETH Market Ownership Wrapper
19+
/// @notice Proposal to deploy and migrate WETH market admin to a wrapper contract
20+
/// that can reliably receive native ETH, enabling reserve reductions.
21+
/// @dev This proposal:
22+
/// 1. Deploys MWethOwnerWrapper implementation and proxy
23+
/// 2. Transfers WETH market admin from TEMPORAL_GOVERNOR to the wrapper
24+
/// 3. Wrapper is owned by TEMPORAL_GOVERNOR, maintaining governance control
25+
/// 4. Enables future WETH reserve reductions via the wrapper
26+
contract mipb53 is HybridProposal {
27+
using ProposalActions for *;
28+
29+
string public constant override name = "MIP-B53";
30+
31+
constructor() {
32+
bytes memory proposalDescription = abi.encodePacked(
33+
vm.readFile("./proposals/mips/mip-b53/b53.md")
34+
);
35+
_setProposalDescription(proposalDescription);
36+
}
37+
38+
function primaryForkId() public pure override returns (uint256) {
39+
return BASE_FORK_ID;
40+
}
41+
42+
function deploy(Addresses addresses, address) public override {
43+
vm.selectFork(BASE_FORK_ID);
44+
45+
// Deploy the MWethOwnerWrapper implementation and proxy
46+
DeployMWethOwnerWrapper deployer = new DeployMWethOwnerWrapper();
47+
(TransparentUpgradeableProxy proxy, ) = deployer.deploy(addresses);
48+
49+
console.log("MWethOwnerWrapper deployed at:", address(proxy));
50+
}
51+
52+
function build(Addresses addresses) public override {
53+
vm.selectFork(BASE_FORK_ID);
54+
55+
address wrapperProxy = addresses.getAddress("MWETH_OWNER_WRAPPER");
56+
address moonwellWeth = addresses.getAddress("MOONWELL_WETH");
57+
58+
// Step 1: Set the wrapper as pending admin of the WETH market
59+
_pushAction(
60+
moonwellWeth,
61+
abi.encodeWithSignature(
62+
"_setPendingAdmin(address)",
63+
payable(wrapperProxy)
64+
),
65+
"Set MWethOwnerWrapper as pending admin of MOONWELL_WETH",
66+
ActionType.Base
67+
);
68+
69+
// Step 2: Accept admin role from the wrapper
70+
_pushAction(
71+
wrapperProxy,
72+
abi.encodeWithSignature("_acceptAdmin()"),
73+
"MWethOwnerWrapper accepts admin role for MOONWELL_WETH",
74+
ActionType.Base
75+
);
76+
}
77+
78+
function validate(Addresses addresses, address) public override {
79+
vm.selectFork(BASE_FORK_ID);
80+
81+
address wrapperProxy = addresses.getAddress("MWETH_OWNER_WRAPPER");
82+
address moonwellWeth = addresses.getAddress("MOONWELL_WETH");
83+
address temporalGovernor = addresses.getAddress("TEMPORAL_GOVERNOR");
84+
address weth = addresses.getAddress("WETH");
85+
86+
// Validate wrapper configuration
87+
MWethOwnerWrapper wrapper = MWethOwnerWrapper(payable(wrapperProxy));
88+
89+
assertEq(
90+
wrapper.owner(),
91+
temporalGovernor,
92+
"Wrapper owner should be TEMPORAL_GOVERNOR"
93+
);
94+
95+
assertEq(
96+
address(wrapper.mToken()),
97+
moonwellWeth,
98+
"Wrapper mToken should be MOONWELL_WETH"
99+
);
100+
101+
assertEq(
102+
address(wrapper.weth()),
103+
weth,
104+
"Wrapper WETH address should be correct"
105+
);
106+
107+
// Validate admin transfer
108+
MTokenInterface mToken = MTokenInterface(moonwellWeth);
109+
110+
assertEq(
111+
mToken.admin(),
112+
wrapperProxy,
113+
"MOONWELL_WETH admin should be the wrapper"
114+
);
115+
116+
assertEq(
117+
mToken.pendingAdmin(),
118+
address(0),
119+
"MOONWELL_WETH pendingAdmin should be zero after accepting"
120+
);
121+
122+
console.log("✓ MWethOwnerWrapper successfully deployed and configured");
123+
console.log("✓ MOONWELL_WETH admin transferred to wrapper");
124+
console.log("✓ Wrapper owned by TEMPORAL_GOVERNOR");
125+
console.log("✓ Future WETH reserve reductions now enabled");
126+
}
127+
}

proposals/mips/mips.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
[
2+
{
3+
"envpath": "",
4+
"governor": "MultichainGovernor",
5+
"id": 0,
6+
"path": "mip-b53.sol/mipb53.json",
7+
"proposalType": "HybridProposal"
8+
},
29
{
310
"envpath": "",
411
"governor": "MultichainGovernor",

0 commit comments

Comments
 (0)