@@ -6,10 +6,41 @@ import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
66import {SignetStd} from "../SignetStd.sol " ;
77import {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.
1140contract 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