@juicedollar/jusd 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/MintingHubV2/MintingHub.sol +58 -17
- package/contracts/MintingHubV2/Position.sol +94 -24
- package/contracts/MintingHubV2/PositionFactory.sol +2 -2
- package/contracts/MintingHubV2/PositionRoller.sol +160 -42
- package/contracts/MintingHubV2/interface/IMintingHub.sol +3 -1
- package/contracts/MintingHubV2/interface/IPosition.sol +5 -3
- package/contracts/gateway/MintingHubGateway.sol +14 -19
- package/contracts/gateway/interface/IMintingHubGateway.sol +2 -2
- package/contracts/interface/IWrappedNative.sol +10 -0
- package/contracts/test/PositionExpirationTest.sol +3 -3
- package/contracts/test/ReentrantAttacker.sol +74 -0
- package/contracts/test/RejectNative.sol +17 -0
- package/dist/index.d.mts +225 -497
- package/dist/index.d.ts +225 -497
- package/dist/index.js +286 -641
- package/dist/index.mjs +286 -640
- package/exports/abis/MintingHubV2/PositionRoller.ts +90 -18
- package/exports/abis/MintingHubV2/PositionV2.ts +68 -26
- package/exports/abis/core/MintingHubGateway.ts +41 -76
- package/exports/abis/utils/MintingHubV2.ts +36 -37
- package/exports/address.config.ts +10 -13
- package/exports/index.ts +0 -1
- package/package.json +1 -1
- package/contracts/gateway/CoinLendingGateway.sol +0 -223
- package/contracts/gateway/interface/ICoinLendingGateway.sol +0 -73
- package/exports/abis/core/CoinLendingGateway.ts +0 -427
|
@@ -8,6 +8,7 @@ import {IMintingHubGateway} from "../gateway/interface/IMintingHubGateway.sol";
|
|
|
8
8
|
import {IMintingHub} from "./interface/IMintingHub.sol";
|
|
9
9
|
import {IPosition} from "./interface/IPosition.sol";
|
|
10
10
|
import {IReserve} from "../interface/IReserve.sol";
|
|
11
|
+
import {IWrappedNative} from "../interface/IWrappedNative.sol";
|
|
11
12
|
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -15,13 +16,19 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
15
16
|
*
|
|
16
17
|
* Helper to roll over a debt from an old position to a new one.
|
|
17
18
|
* Both positions should have the same collateral. Otherwise, it does not make much sense.
|
|
19
|
+
*
|
|
20
|
+
* For standard ERC20 positions, use roll/rollFully/rollFullyWithExpiration.
|
|
21
|
+
* Collateral flows through the user's wallet and requires approval.
|
|
22
|
+
*
|
|
23
|
+
* For native coin positions (e.g., WCBTC), use rollNative/rollFullyNative/rollFullyNativeWithExpiration.
|
|
24
|
+
* Collateral flows through the roller, no approval needed, and excess is returned as native coin.
|
|
18
25
|
*/
|
|
19
26
|
contract PositionRoller {
|
|
20
27
|
IJuiceDollar private jusd;
|
|
21
28
|
|
|
22
29
|
error NotOwner(address pos);
|
|
23
30
|
error NotPosition(address pos);
|
|
24
|
-
error
|
|
31
|
+
error NativeTransferFailed();
|
|
25
32
|
|
|
26
33
|
event Roll(address source, uint256 collWithdraw, uint256 repay, address target, uint256 collDeposit, uint256 mint);
|
|
27
34
|
|
|
@@ -32,8 +39,8 @@ contract PositionRoller {
|
|
|
32
39
|
/**
|
|
33
40
|
* Convenience method to roll an old position into a new one.
|
|
34
41
|
*
|
|
35
|
-
* Pre-condition: an allowance for the roller to spend the collateral asset
|
|
36
|
-
* i.e.,
|
|
42
|
+
* Pre-condition: an allowance for the roller to spend the collateral asset
|
|
43
|
+
* on behalf of the caller, i.e., collateral.approve(roller, collateral.balanceOf(sourcePosition)).
|
|
37
44
|
*
|
|
38
45
|
* The following is assumed:
|
|
39
46
|
* - If the limit of the target position permits, the user wants to roll everything.
|
|
@@ -49,21 +56,12 @@ contract PositionRoller {
|
|
|
49
56
|
*/
|
|
50
57
|
function rollFullyWithExpiration(IPosition source, IPosition target, uint40 expiration) public {
|
|
51
58
|
require(source.collateral() == target.collateral());
|
|
52
|
-
uint256
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
uint256 depositAmount = (mintAmount * 10 ** 18 + targetPrice - 1) / targetPrice; // round up
|
|
59
|
-
if (depositAmount > collateralToWithdraw) {
|
|
60
|
-
// If we need more collateral than available from the old position, we opt for taking
|
|
61
|
-
// the missing funds from the caller instead of requiring additional collateral.
|
|
62
|
-
depositAmount = collateralToWithdraw;
|
|
63
|
-
mintAmount = (depositAmount * target.price()) / 10 ** 18; // round down, rest will be taken from caller
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
roll(source, principal + interest, collateralToWithdraw, target, mintAmount, depositAmount, expiration);
|
|
59
|
+
(uint256 repay, uint256 collWithdraw, uint256 mint, uint256 collDeposit) = _calculateRollParams(
|
|
60
|
+
source,
|
|
61
|
+
target,
|
|
62
|
+
0
|
|
63
|
+
);
|
|
64
|
+
roll(source, repay, collWithdraw, target, mint, collDeposit, expiration);
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
/**
|
|
@@ -72,12 +70,12 @@ contract PositionRoller {
|
|
|
72
70
|
* It is the responsibility of the caller to ensure that both positions are valid contracts.
|
|
73
71
|
*
|
|
74
72
|
* @param source The source position, must be owned by the msg.sender.
|
|
75
|
-
* @param repay The amount of principal to repay from the source position using a flash loan
|
|
76
|
-
* @param collWithdraw Collateral to
|
|
73
|
+
* @param repay The amount of principal to repay from the source position using a flash loan.
|
|
74
|
+
* @param collWithdraw Collateral to withdraw from the source position.
|
|
77
75
|
* @param target The target position. If not owned by msg.sender or if it does not have the desired expiration,
|
|
78
76
|
* it is cloned to create a position owned by the msg.sender.
|
|
79
|
-
* @param mint The amount to be minted from the target position
|
|
80
|
-
* @param collDeposit The amount of collateral to
|
|
77
|
+
* @param mint The amount to be minted from the target position.
|
|
78
|
+
* @param collDeposit The amount of collateral to deposit into the target position.
|
|
81
79
|
* @param expiration The desired expiration date for the target position.
|
|
82
80
|
*/
|
|
83
81
|
function roll(
|
|
@@ -94,9 +92,10 @@ contract PositionRoller {
|
|
|
94
92
|
source.withdrawCollateral(msg.sender, collWithdraw);
|
|
95
93
|
if (mint > 0) {
|
|
96
94
|
IERC20 targetCollateral = IERC20(target.collateral());
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
targetCollateral.
|
|
95
|
+
bool needsClone = Ownable(address(target)).owner() != msg.sender || expiration != target.expiration();
|
|
96
|
+
if (needsClone) {
|
|
97
|
+
targetCollateral.transferFrom(msg.sender, address(this), collDeposit);
|
|
98
|
+
targetCollateral.approve(target.hub(), collDeposit);
|
|
100
99
|
target = _cloneTargetPosition(target, source, collDeposit, mint, expiration);
|
|
101
100
|
} else {
|
|
102
101
|
// We can roll into the provided existing position.
|
|
@@ -116,10 +115,125 @@ contract PositionRoller {
|
|
|
116
115
|
emit Roll(address(source), collWithdraw, repay, address(target), collDeposit, mint);
|
|
117
116
|
}
|
|
118
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Convenience method to roll a native coin position into a new one.
|
|
120
|
+
*
|
|
121
|
+
* No collateral approval is needed - collateral flows through the roller
|
|
122
|
+
* and excess is returned as native coin.
|
|
123
|
+
*
|
|
124
|
+
* Additional collateral can be provided via msg.value.
|
|
125
|
+
*/
|
|
126
|
+
function rollFullyNative(IPosition source, IPosition target) external payable {
|
|
127
|
+
rollFullyNativeWithExpiration(source, target, target.expiration());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Like rollFullyNative, but with a custom expiration date for the new position.
|
|
132
|
+
*/
|
|
133
|
+
function rollFullyNativeWithExpiration(IPosition source, IPosition target, uint40 expiration) public payable {
|
|
134
|
+
require(source.collateral() == target.collateral());
|
|
135
|
+
(uint256 repay, uint256 collWithdraw, uint256 mint, uint256 collDeposit) = _calculateRollParams(
|
|
136
|
+
source,
|
|
137
|
+
target,
|
|
138
|
+
msg.value
|
|
139
|
+
);
|
|
140
|
+
rollNative(source, repay, collWithdraw, target, mint, collDeposit, expiration);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Rolls a native coin position into the target position using a flash loan.
|
|
145
|
+
* Collateral is routed through the roller and returned as native coin,
|
|
146
|
+
* eliminating the need for users to interact with wrapped native tokens directly.
|
|
147
|
+
*
|
|
148
|
+
* If additional collateral is needed (collDeposit > collWithdraw), it can be provided
|
|
149
|
+
* as native coin via msg.value.
|
|
150
|
+
*
|
|
151
|
+
* @param source The source position, must be owned by the msg.sender.
|
|
152
|
+
* @param repay The amount of principal to repay from the source position using a flash loan.
|
|
153
|
+
* @param collWithdraw Collateral to withdraw from the source position.
|
|
154
|
+
* @param target The target position. If not owned by msg.sender or if it does not have the desired expiration,
|
|
155
|
+
* it is cloned to create a position owned by the msg.sender.
|
|
156
|
+
* @param mint The amount to be minted from the target position.
|
|
157
|
+
* @param collDeposit The amount of collateral to deposit into the target position.
|
|
158
|
+
* @param expiration The desired expiration date for the target position.
|
|
159
|
+
*/
|
|
160
|
+
function rollNative(
|
|
161
|
+
IPosition source,
|
|
162
|
+
uint256 repay,
|
|
163
|
+
uint256 collWithdraw,
|
|
164
|
+
IPosition target,
|
|
165
|
+
uint256 mint,
|
|
166
|
+
uint256 collDeposit,
|
|
167
|
+
uint40 expiration
|
|
168
|
+
) public payable valid(source) valid(target) own(source) {
|
|
169
|
+
address collateral = address(source.collateral());
|
|
170
|
+
|
|
171
|
+
jusd.mint(address(this), repay); // take a flash loan
|
|
172
|
+
uint256 used = source.repay(repay);
|
|
173
|
+
source.withdrawCollateral(address(this), collWithdraw);
|
|
174
|
+
if (msg.value > 0) {
|
|
175
|
+
IWrappedNative(collateral).deposit{value: msg.value}();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (mint > 0) {
|
|
179
|
+
IERC20 targetCollateral = IERC20(collateral);
|
|
180
|
+
bool needsClone = Ownable(address(target)).owner() != msg.sender || expiration != target.expiration();
|
|
181
|
+
if (needsClone) {
|
|
182
|
+
targetCollateral.approve(target.hub(), collDeposit);
|
|
183
|
+
target = _cloneTargetPosition(target, source, collDeposit, mint, expiration);
|
|
184
|
+
} else {
|
|
185
|
+
targetCollateral.transfer(address(target), collDeposit);
|
|
186
|
+
target.mint(msg.sender, mint);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Transfer remaining flash loan to caller for repayment
|
|
191
|
+
if (repay > used) {
|
|
192
|
+
jusd.transfer(msg.sender, repay - used);
|
|
193
|
+
}
|
|
194
|
+
jusd.burnFrom(msg.sender, repay); // repay the flash loan
|
|
195
|
+
|
|
196
|
+
// Return excess as native coin
|
|
197
|
+
uint256 remaining = IERC20(collateral).balanceOf(address(this));
|
|
198
|
+
if (remaining > 0) {
|
|
199
|
+
IWrappedNative(collateral).withdraw(remaining);
|
|
200
|
+
(bool success, ) = msg.sender.call{value: remaining}("");
|
|
201
|
+
if (!success) revert NativeTransferFailed();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
emit Roll(address(source), collWithdraw, repay, address(target), collDeposit, mint);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Calculates the parameters for a roll operation.
|
|
209
|
+
* @param extraCollateral Additional collateral provided via msg.value (for native rolls).
|
|
210
|
+
*/
|
|
211
|
+
function _calculateRollParams(
|
|
212
|
+
IPosition source,
|
|
213
|
+
IPosition target,
|
|
214
|
+
uint256 extraCollateral
|
|
215
|
+
) internal view returns (uint256 repay, uint256 collWithdraw, uint256 mint, uint256 collDeposit) {
|
|
216
|
+
uint256 principal = source.principal();
|
|
217
|
+
uint256 interest = source.getInterest();
|
|
218
|
+
uint256 usableMint = source.getUsableMint(principal) + interest;
|
|
219
|
+
uint256 mintAmount = target.getMintAmount(usableMint);
|
|
220
|
+
uint256 collateralAvailable = IERC20(source.collateral()).balanceOf(address(source));
|
|
221
|
+
uint256 totalAvailable = collateralAvailable + extraCollateral;
|
|
222
|
+
uint256 targetPrice = target.price();
|
|
223
|
+
uint256 depositAmount = (mintAmount * 10 ** 18 + targetPrice - 1) / targetPrice;
|
|
224
|
+
|
|
225
|
+
if (depositAmount > totalAvailable) {
|
|
226
|
+
depositAmount = totalAvailable;
|
|
227
|
+
mintAmount = (depositAmount * target.price()) / 10 ** 18;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return (principal + interest, collateralAvailable, mintAmount, depositAmount);
|
|
231
|
+
}
|
|
232
|
+
|
|
119
233
|
/**
|
|
120
234
|
* Clones the target position and mints the specified amount using the given collateral.
|
|
121
235
|
*/
|
|
122
|
-
function _cloneTargetPosition
|
|
236
|
+
function _cloneTargetPosition(
|
|
123
237
|
IPosition target,
|
|
124
238
|
IPosition source,
|
|
125
239
|
uint256 collDeposit,
|
|
@@ -127,23 +241,24 @@ contract PositionRoller {
|
|
|
127
241
|
uint40 expiration
|
|
128
242
|
) internal returns (IPosition) {
|
|
129
243
|
if (IERC165(target.hub()).supportsInterface(type(IMintingHubGateway).interfaceId)) {
|
|
130
|
-
bytes32 frontendCode = IMintingHubGateway(target.hub()).GATEWAY().getPositionFrontendCode(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
244
|
+
bytes32 frontendCode = IMintingHubGateway(target.hub()).GATEWAY().getPositionFrontendCode(address(source));
|
|
245
|
+
return
|
|
246
|
+
IPosition(
|
|
247
|
+
IMintingHubGateway(target.hub()).clone(
|
|
248
|
+
msg.sender,
|
|
249
|
+
address(target),
|
|
250
|
+
collDeposit,
|
|
251
|
+
mint,
|
|
252
|
+
expiration,
|
|
253
|
+
0, // inherit price from parent
|
|
254
|
+
frontendCode // use the same frontend code
|
|
255
|
+
)
|
|
256
|
+
);
|
|
143
257
|
} else {
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
|
|
258
|
+
return
|
|
259
|
+
IPosition(
|
|
260
|
+
IMintingHub(target.hub()).clone(msg.sender, address(target), collDeposit, mint, expiration, 0)
|
|
261
|
+
);
|
|
147
262
|
}
|
|
148
263
|
}
|
|
149
264
|
|
|
@@ -156,4 +271,7 @@ contract PositionRoller {
|
|
|
156
271
|
if (jusd.getPositionParent(address(pos)) == address(0x0)) revert NotPosition(address(pos));
|
|
157
272
|
_;
|
|
158
273
|
}
|
|
274
|
+
|
|
275
|
+
/// @notice Required to receive native coin when unwrapping
|
|
276
|
+
receive() external payable {}
|
|
159
277
|
}
|
|
@@ -10,6 +10,8 @@ interface IMintingHub {
|
|
|
10
10
|
|
|
11
11
|
function ROLLER() external view returns (PositionRoller);
|
|
12
12
|
|
|
13
|
+
function WCBTC() external view returns (address);
|
|
14
|
+
|
|
13
15
|
function challenge(
|
|
14
16
|
address _positionAddr,
|
|
15
17
|
uint256 _collateralAmount,
|
|
@@ -22,5 +24,5 @@ interface IMintingHub {
|
|
|
22
24
|
|
|
23
25
|
function buyExpiredCollateral(IPosition pos, uint256 upToAmount) external returns (uint256);
|
|
24
26
|
|
|
25
|
-
function clone(address owner, address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration) external returns (address);
|
|
27
|
+
function clone(address owner, address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration, uint256 _liqPrice) external payable returns (address);
|
|
26
28
|
}
|
|
@@ -56,13 +56,13 @@ interface IPosition {
|
|
|
56
56
|
|
|
57
57
|
function getMintAmount(uint256 usableMint) external view returns (uint256);
|
|
58
58
|
|
|
59
|
-
function adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice) external;
|
|
59
|
+
function adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice, bool withdrawAsNative) external payable;
|
|
60
60
|
|
|
61
61
|
function adjustPrice(uint256 newPrice) external;
|
|
62
62
|
|
|
63
63
|
function adjustPriceWithReference(uint256 newPrice, address referencePosition) external;
|
|
64
64
|
|
|
65
|
-
function
|
|
65
|
+
function adjustWithReference(uint256 newMinted, uint256 newCollateral, uint256 newPrice, address referencePosition, bool withdrawAsNative) external payable;
|
|
66
66
|
|
|
67
67
|
function isValidPriceReference(address referencePosition, uint256 newPrice) external view returns (bool);
|
|
68
68
|
|
|
@@ -80,10 +80,12 @@ interface IPosition {
|
|
|
80
80
|
|
|
81
81
|
function forceSale(address buyer, uint256 colAmount, uint256 proceeds) external;
|
|
82
82
|
|
|
83
|
-
function
|
|
83
|
+
function rescueToken(address token, address target, uint256 amount) external;
|
|
84
84
|
|
|
85
85
|
function withdrawCollateral(address target, uint256 amount) external;
|
|
86
86
|
|
|
87
|
+
function withdrawCollateralAsNative(address target, uint256 amount) external;
|
|
88
|
+
|
|
87
89
|
function transferChallengedCollateral(address target, uint256 amount) external;
|
|
88
90
|
|
|
89
91
|
function challengeData() external view returns (uint256 liqPrice, uint40 phase);
|
|
@@ -11,10 +11,11 @@ contract MintingHubGateway is MintingHub, IMintingHubGateway {
|
|
|
11
11
|
constructor(
|
|
12
12
|
address _jusd,
|
|
13
13
|
address _leadrate,
|
|
14
|
-
address _roller,
|
|
14
|
+
address payable _roller,
|
|
15
15
|
address _factory,
|
|
16
|
-
address _gateway
|
|
17
|
-
|
|
16
|
+
address _gateway,
|
|
17
|
+
address _wcbtc
|
|
18
|
+
) MintingHub(_jusd, _leadrate, _roller, _factory, _wcbtc) {
|
|
18
19
|
GATEWAY = IFrontendGateway(_gateway);
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -30,8 +31,8 @@ contract MintingHubGateway is MintingHub, IMintingHubGateway {
|
|
|
30
31
|
uint256 _liqPrice,
|
|
31
32
|
uint24 _reservePPM,
|
|
32
33
|
bytes32 _frontendCode
|
|
33
|
-
) public returns (address) {
|
|
34
|
-
address position = openPosition(
|
|
34
|
+
) public payable returns (address) {
|
|
35
|
+
address position = MintingHub.openPosition(
|
|
35
36
|
_collateralAddress,
|
|
36
37
|
_minCollateral,
|
|
37
38
|
_initialCollateral,
|
|
@@ -47,19 +48,12 @@ contract MintingHubGateway is MintingHub, IMintingHubGateway {
|
|
|
47
48
|
return position;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
function clone(
|
|
51
|
-
address parent,
|
|
52
|
-
uint256 _initialCollateral,
|
|
53
|
-
uint256 _initialMint,
|
|
54
|
-
uint40 expiration,
|
|
55
|
-
bytes32 frontendCode
|
|
56
|
-
) public returns (address) {
|
|
57
|
-
return clone(msg.sender, parent, _initialCollateral, _initialMint, expiration, frontendCode);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
51
|
/**
|
|
61
|
-
* @notice Clones an existing position and immediately tries to mint the specified amount using the given collateral.
|
|
62
|
-
* @dev
|
|
52
|
+
* @notice Clones an existing position and immediately tries to mint the specified amount using the given collateral.
|
|
53
|
+
* @dev For native coin positions (WcBTC), send msg.value equal to _initialCollateral.
|
|
54
|
+
* For ERC20 collateral, ensure prior approval for the minting hub to transfer _initialCollateral.
|
|
55
|
+
* @param _liqPrice Optionally adjust price of new position after minting. Set to 0 to inherit parent's price.
|
|
56
|
+
* @param frontendCode Optionally register the position with a frontend code.
|
|
63
57
|
*/
|
|
64
58
|
function clone(
|
|
65
59
|
address owner,
|
|
@@ -67,9 +61,10 @@ contract MintingHubGateway is MintingHub, IMintingHubGateway {
|
|
|
67
61
|
uint256 _initialCollateral,
|
|
68
62
|
uint256 _initialMint,
|
|
69
63
|
uint40 expiration,
|
|
64
|
+
uint256 _liqPrice,
|
|
70
65
|
bytes32 frontendCode
|
|
71
|
-
) public returns (address) {
|
|
72
|
-
address position = clone(owner, parent, _initialCollateral, _initialMint, expiration);
|
|
66
|
+
) public payable returns (address) {
|
|
67
|
+
address position = MintingHub.clone(owner, parent, _initialCollateral, _initialMint, expiration, _liqPrice);
|
|
73
68
|
GATEWAY.registerPosition(position, frontendCode);
|
|
74
69
|
return position;
|
|
75
70
|
}
|
|
@@ -7,6 +7,6 @@ import {IFrontendGateway} from "./IFrontendGateway.sol";
|
|
|
7
7
|
interface IMintingHubGateway {
|
|
8
8
|
function GATEWAY() external view returns (IFrontendGateway);
|
|
9
9
|
function notifyInterestPaid(uint256 amount) external;
|
|
10
|
-
function openPosition(address _collateralAddress, uint256 _minCollateral, uint256 _initialCollateral, uint256 _mintingMaximum, uint40 _initPeriodSeconds, uint40 _expirationSeconds, uint40 _challengeSeconds, uint24 _riskPremium, uint256 _liqPrice, uint24 _reservePPM, bytes32 _frontendCode) external returns (address);
|
|
11
|
-
function clone(address owner, address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration, bytes32 frontendCode) external returns (address);
|
|
10
|
+
function openPosition(address _collateralAddress, uint256 _minCollateral, uint256 _initialCollateral, uint256 _mintingMaximum, uint40 _initPeriodSeconds, uint40 _expirationSeconds, uint40 _challengeSeconds, uint24 _riskPremium, uint256 _liqPrice, uint24 _reservePPM, bytes32 _frontendCode) external payable returns (address);
|
|
11
|
+
function clone(address owner, address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration, uint256 _liqPrice, bytes32 frontendCode) external payable returns (address);
|
|
12
12
|
}
|
|
@@ -54,7 +54,7 @@ contract PositionExpirationTest {
|
|
|
54
54
|
200000
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
|
-
Position(pos).transferOwnership(owner);
|
|
57
|
+
Position(payable(pos)).transferOwnership(owner);
|
|
58
58
|
return pos;
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -63,10 +63,10 @@ contract PositionExpirationTest {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function forceBuy(address pos, uint256 amount) public {
|
|
66
|
-
uint256 price = hub.expiredPurchasePrice(Position(pos));
|
|
66
|
+
uint256 price = hub.expiredPurchasePrice(Position(payable(pos)));
|
|
67
67
|
uint256 balanceBefore = jusd.balanceOf(address(this));
|
|
68
68
|
uint256 colBalBefore = col.balanceOf(address(this));
|
|
69
|
-
amount = hub.buyExpiredCollateral(Position(pos), amount);
|
|
69
|
+
amount = hub.buyExpiredCollateral(Position(payable(pos)), amount);
|
|
70
70
|
uint256 balanceAfter = jusd.balanceOf(address(this));
|
|
71
71
|
uint256 colBalAfter = col.balanceOf(address(this));
|
|
72
72
|
require(colBalAfter - colBalBefore == amount, "collateral amount");
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {IPosition} from "../MintingHubV2/interface/IPosition.sol";
|
|
5
|
+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title ReentrantAttacker
|
|
9
|
+
* @notice Test contract that attempts reentrancy attack on Position.withdrawCollateralAsNative()
|
|
10
|
+
* @dev Used to verify Position is safe against reentrancy
|
|
11
|
+
*/
|
|
12
|
+
contract ReentrantAttacker is Ownable {
|
|
13
|
+
IPosition public targetPosition;
|
|
14
|
+
uint256 public attackCount;
|
|
15
|
+
uint256 public withdrawAmount;
|
|
16
|
+
bool public attackSucceeded;
|
|
17
|
+
string public lastRevertReason;
|
|
18
|
+
|
|
19
|
+
event AttackAttempted(uint256 count, bool success, string reason);
|
|
20
|
+
|
|
21
|
+
constructor() Ownable(msg.sender) {}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @notice Sets the target position for the attack
|
|
25
|
+
* @param _position The position contract to attack
|
|
26
|
+
*/
|
|
27
|
+
function setTarget(address _position) external onlyOwner {
|
|
28
|
+
targetPosition = IPosition(_position);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @notice Initiates the reentrancy attack
|
|
33
|
+
* @param _amount Amount to withdraw in each attempt
|
|
34
|
+
*/
|
|
35
|
+
function attack(uint256 _amount) external onlyOwner {
|
|
36
|
+
require(address(targetPosition) != address(0), "Target not set");
|
|
37
|
+
withdrawAmount = _amount;
|
|
38
|
+
attackCount = 0;
|
|
39
|
+
attackSucceeded = false;
|
|
40
|
+
lastRevertReason = "";
|
|
41
|
+
|
|
42
|
+
targetPosition.withdrawCollateralAsNative(address(this), _amount);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @notice Called when receiving native coin - attempts reentrancy
|
|
47
|
+
*/
|
|
48
|
+
receive() external payable {
|
|
49
|
+
attackCount++;
|
|
50
|
+
|
|
51
|
+
if (attackCount < 2) {
|
|
52
|
+
// Attempt reentrancy on second receive
|
|
53
|
+
try targetPosition.withdrawCollateralAsNative(address(this), withdrawAmount) {
|
|
54
|
+
// If this succeeds, reentrancy attack worked
|
|
55
|
+
attackSucceeded = true;
|
|
56
|
+
emit AttackAttempted(attackCount, true, "Attack succeeded - VULNERABILITY!");
|
|
57
|
+
} catch Error(string memory reason) {
|
|
58
|
+
lastRevertReason = reason;
|
|
59
|
+
emit AttackAttempted(attackCount, false, reason);
|
|
60
|
+
} catch (bytes memory) {
|
|
61
|
+
lastRevertReason = "Unknown revert";
|
|
62
|
+
emit AttackAttempted(attackCount, false, "Unknown revert");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @notice Allows owner to withdraw any native coin from this contract
|
|
69
|
+
*/
|
|
70
|
+
function withdrawAll() external onlyOwner {
|
|
71
|
+
(bool success, ) = owner().call{value: address(this).balance}("");
|
|
72
|
+
require(success, "Withdraw failed");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title RejectNative
|
|
6
|
+
* @notice Test helper contract that rejects all native coin transfers
|
|
7
|
+
* @dev Used to test NativeTransferFailed error in Position.withdrawCollateralAsNative()
|
|
8
|
+
*/
|
|
9
|
+
contract RejectNative {
|
|
10
|
+
receive() external payable {
|
|
11
|
+
revert("I reject native coin");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fallback() external payable {
|
|
15
|
+
revert("I reject native coin");
|
|
16
|
+
}
|
|
17
|
+
}
|