Skip to content

Commit f701fe5

Browse files
committed
refactor: use the ERC20 interface and pass arguments
1 parent fc85f3b commit f701fe5

File tree

1 file changed

+89
-38
lines changed

1 file changed

+89
-38
lines changed

src/apps/Morpho.sol

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,41 @@ import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
66
import {SignetStd} from "../SignetStd.sol";
77
import {RollupOrders} from "zenith/src/orders/RollupOrders.sol";
88

9-
// Rollup contract that uses a host chain Morpho shortcut to supply collateral
10-
// and repay loans on behalf of the rollup user.
9+
// How this works:
10+
// - The Signet Orders system calls `transferFrom` and check for the presence
11+
// of a `Fill` event.
12+
// - The MorphoShortcut contract implements `transferFrom` to run
13+
// supply/repay logic on a specific Morpho Market.
14+
// - The rollup contract uses Orders to send tokens to the shortcut
15+
// and then call the shortcut to perform the action.
16+
// - This all occurs in the same block.
17+
18+
// The rollup order is structured as follows:
19+
// - Input: X token on rollup chain.
20+
// - Output: transfer X token on host chain to shortcut.
21+
// - Output: invoke host chain shortcut contract.
22+
//
23+
// The Signet STF logic ensures that the Input is not delivered unless the
24+
// outputs are succesful.
25+
26+
// Note:
27+
// We've provided a rollup contract for the example, but this could also be
28+
// initiated via a signed order from an EOA. No contract is needed. Both
29+
// contracts and EOAs can use the Morpho shortcut to interact with Morpho from
30+
// Signet.
31+
32+
// Note:
33+
// The example order has no spread. Real orders can have a spread, and can also
34+
// have their inputs in ANY asset. I.e. the user can use rollup ETH to pay for
35+
// USDC collateral or repay a WBTC loan on the host Morpho market. This is
36+
// pretty neat.
37+
38+
// The Rollup contract creates orders to interact with Morpho on host net via
39+
// the shortcut.
1140
contract UseMorpho is SignetStd {
12-
address immutable shortcut;
41+
/// @dev Address of the shortcut on the host chain.
42+
address immutable repayShortcut;
43+
address immutable supplyShortcut;
1344

1445
address immutable hostLoanToken;
1546
address immutable hostCollateralToken;
@@ -18,10 +49,12 @@ contract UseMorpho is SignetStd {
1849
IERC20 immutable collateralToken;
1950

2051
constructor(address _shortcut, address _hostLoan, address _hostCollateral) SignetStd() {
52+
// The address of the shortcut contract should be well-known.
2153
shortcut = _shortcut;
54+
55+
// Autodetect rollup tokens based on token addresses on host network.
2256
hostLoanToken = _hostLoan;
2357
hostCollateralToken = _hostCollateral;
24-
2558
if (_hostLoan == HOST_WETH) {
2659
loanToken = WETH;
2760
} else if (_hostLoan == HOST_WBTC) {
@@ -32,11 +65,12 @@ contract UseMorpho is SignetStd {
3265
revert("Unsupported loan token");
3366
}
3467

68+
// Pre-emptively approve the Orders contract to spend our tokens.
3569
loanToken.approve(address(ORDERS), type(uint256).max);
3670
collateralToken.approve(address(ORDERS), type(uint256).max);
3771
}
3872

39-
function supply(uint256 amount) external {
73+
function supply(address onBehalf, uint256 amount) external {
4074
if (amount > 0) {
4175
collateralToken.transferFrom(msg.sender, address(this), amount);
4276
}
@@ -47,12 +81,11 @@ contract UseMorpho is SignetStd {
4781
RollupOrders.Input[] memory inputs = new RollupOrders.Input[](1);
4882
inputs[0] = makeInput(address(collateralToken), amount);
4983

50-
// The output pays the collateral token to the shortcut,
51-
// Then calls the shortcut to supply the collateral.
84+
// The first output pays the collateral token to the shortcut.
85+
// The second output calls the shortcut to supply the collateral.
5286
RollupOrders.Output[] memory outputs = new RollupOrders.Output[](2);
53-
outputs[0] = makeHostOutput(address(hostCollateralToken), amount, shortcut);
54-
// call shortcut
55-
outputs[1] = makeHostOutput(shortcut, 0, address(this));
87+
outputs[0] = makeHostOutput(address(hostCollateralToken), amount, supplyShortcut);
88+
outputs[1] = makeHostOutput(supplyShortcut, amount, onBehalf);
5689

5790
ORDERS.initiate(
5891
block.timestamp, // no deadline
@@ -61,22 +94,22 @@ contract UseMorpho is SignetStd {
6194
);
6295
}
6396

64-
function repay(uint256 amount) external {
97+
function repay(address onBehalf, uint256 amount) external {
6598
if (amount > 0) {
6699
loanToken.transferFrom(msg.sender, address(this), amount);
67100
}
68101

69-
// the amount is whatever our current balance is
102+
// Send all tokens.
70103
amount = loanToken.balanceOf(address(this));
71104

72105
RollupOrders.Input[] memory inputs = new RollupOrders.Input[](1);
73106
inputs[0] = makeInput(address(loanToken), amount);
74107

75-
// The output pays the loan token to the shortcut,
76-
// Then calls the shortcut to repay the loan.
108+
// The first output pays the loan token to the shortcut.
109+
// The second output calls the shortcut to repay the loan.
77110
RollupOrders.Output[] memory outputs = new RollupOrders.Output[](2);
78-
outputs[0] = makeHostOutput(address(hostLoanToken), amount, shortcut);
79-
outputs[1] = makeHostOutput(shortcut, 0, address(this)); // call shortcut
111+
outputs[0] = makeHostOutput(address(hostLoanToken), amount, repayShortcut);
112+
outputs[1] = makeHostOutput(repayShortcut, amount, onBehalf);
80113

81114
ORDERS.initiate(
82115
block.timestamp, // no deadline
@@ -86,21 +119,20 @@ contract UseMorpho is SignetStd {
86119
}
87120
}
88121

89-
// This contract should be deployed on the host chain. It is used as a shortcut
90-
// to supply collateral to Morpho and can be invoked by the rollup via an Order.
91-
contract HostMorphoShortcut {
122+
abstract contract HostMorphoUser {
92123
IMorpho immutable morpho;
93124

94125
// This is an unrolled MarketParams struct.
95126
IERC20 immutable loanToken;
96127
IERC20 immutable collateralToken;
128+
97129
address immutable oracle;
98130
address immutable irm;
99131
uint256 immutable lltv;
100132

101-
address immutable onBehalf;
133+
error InsufficentTokensReceived(uint256 received, uint256 required);
102134

103-
constructor(IMorpho _morpho, MarketParams memory _params, address _onBehalf) {
135+
constructor(IMorpho _morpho, MarketParams memory _params) {
104136
morpho = _morpho;
105137

106138
loanToken = IERC20(_params.loanToken);
@@ -109,37 +141,56 @@ contract HostMorphoShortcut {
109141
irm = _params.irm;
110142
lltv = _params.lltv;
111143

112-
onBehalf = _onBehalf;
113-
144+
loanToken.approve(address(_morpho), type(uint256).max);
114145
collateralToken.approve(address(_morpho), type(uint256).max);
115146
}
116147

117-
fallback() external {
118-
MarketParams memory params;
148+
function checkReceived(uint256 received, uint256 required) internal pure {
149+
if (received < required) {
150+
revert InsufficentTokensReceived(received, required);
151+
}
152+
}
153+
154+
function loadParams() internal view returns (MarketParams memory params) {
119155
params.loanToken = address(loanToken);
120156
params.collateralToken = address(collateralToken);
121157
params.oracle = oracle;
122158
params.irm = irm;
123159
params.lltv = lltv;
160+
}
161+
}
162+
163+
// This contract should be deployed on the host chain. It is used as a shortcut
164+
// to supply collateral to Morpho and can be invoked by the rollup via an Order.
165+
contract HostMorphoRepay is HostMorphoUser {
166+
constructor(IMorpho _morpho, MarketParams memory _params) HostMorphoUser(_morpho, _params) {}
124167

168+
/// Uses the ERC20 transferFrom interface to invoke contract logic. This
169+
/// allows us to invoke logic from the Orders contract
170+
function transferFrom(address, address recipient, uint256 amount) external returns (bool) {
125171
uint256 loanTokenBalance = loanToken.balanceOf(address(this));
126-
uint256 collateralTokenBalance = collateralToken.balanceOf(address(this));
172+
checkReceived(loanTokenBalance, amount);
173+
morpho.repay(loadParams(), loanTokenBalance, 0, recipient, "");
174+
return true;
175+
}
176+
}
127177

128-
// If we have loan tokens, we are repaying a loan.
129-
if (loanTokenBalance > 0) {
130-
morpho.repay(params, loanTokenBalance, 0, onBehalf, "");
131-
return;
132-
}
178+
contract HostMorphoSupply is HostMorphoUser {
179+
constructor(IMorpho _morpho, MarketParams memory _params) HostMorphoUser(_morpho, _params) {}
133180

134-
// If we have collateral tokens, we are supplying collateral.
135-
if (collateralTokenBalance > 0) {
136-
morpho.supplyCollateral(params, collateralTokenBalance, onBehalf, "");
181+
/// Uses the ERC20 transferFrom interface to invoke contract logic. This
182+
/// allows us to invoke logic from the Orders contract
183+
function transferFrom(address, address recipient, uint256 amount) external returns (bool) {
184+
uint256 collateralTokenBalance = collateralToken.balanceOf(address(this));
137185

138-
// borrow and send to rollup?
186+
checkReceived(collateralTokenBalance, amount);
139187

140-
return;
141-
}
188+
morpho.supplyCollateral(loadParams(), collateralTokenBalance, recipient, "");
189+
190+
// Future extension:
191+
// borrow some amount of loanToken
192+
// and send it to the rollup
142193

143-
revert("No tokens received");
194+
return true;
144195
}
145196
}

0 commit comments

Comments
 (0)