-
Notifications
You must be signed in to change notification settings - Fork 17
/
TimeswapV2Token.sol
274 lines (219 loc) · 13.1 KB
/
TimeswapV2Token.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "forge-std/console.sol";
import {ITimeswapV2Option} from "@timeswap-labs/v2-option/src/interfaces/ITimeswapV2Option.sol";
import {OptionFactoryLibrary} from "@timeswap-labs/v2-option/src/libraries/OptionFactory.sol";
import {ReentrancyGuard} from "@timeswap-labs/v2-pool/src/libraries/ReentrancyGuard.sol";
import {TimeswapV2OptionPosition} from "@timeswap-labs/v2-option/src/enums/Position.sol";
import {ITimeswapV2Token} from "./interfaces/ITimeswapV2Token.sol";
import {ITimeswapV2TokenMintCallback} from "./interfaces/callbacks/ITimeswapV2TokenMintCallback.sol";
import {ITimeswapV2TokenBurnCallback} from "./interfaces/callbacks/ITimeswapV2TokenBurnCallback.sol";
import {ERC1155Enumerable} from "./base/ERC1155Enumerable.sol";
import {TimeswapV2TokenPosition, PositionLibrary} from "./structs/Position.sol";
import {TimeswapV2TokenMintParam, TimeswapV2TokenBurnParam, ParamLibrary} from "./structs/Param.sol";
import {TimeswapV2TokenMintCallbackParam, TimeswapV2TokenBurnCallbackParam} from "./structs/CallbackParam.sol";
import {Error} from "@timeswap-labs/v2-library/src/Error.sol";
/// @title
/// @author Timeswap Labs
/// @notice TimeswapV2Token tokenizes the TimeswapV2 native option positions (long0, long1, short)
contract TimeswapV2Token is ITimeswapV2Token, ERC1155Enumerable {
using ReentrancyGuard for uint96;
using PositionLibrary for TimeswapV2TokenPosition;
address public immutable optionFactory;
mapping(bytes32 => uint96) private reentrancyGuards;
mapping(uint256 => TimeswapV2TokenPosition) private _timeswapV2TokenPositions;
mapping(bytes32 => uint256) private _timeswapV2TokenPositionIds;
constructor(address chosenOptionFactory) ERC1155("Timeswap V2 address") {
optionFactory = chosenOptionFactory;
}
function changeInteractedIfNecessary(address token0, address token1, uint256 strike, uint256 maturity) private {
bytes32 key = keccak256(abi.encode(token0, token1, strike, maturity));
if (reentrancyGuards[key] == ReentrancyGuard.NOT_INTERACTED) reentrancyGuards[key] = ReentrancyGuard.NOT_ENTERED;
}
/// @dev internal function to start the reentrancy guard
function raiseGuard(address token0, address token1, uint256 strike, uint256 maturity) private {
bytes32 key = keccak256(abi.encode(token0, token1, strike, maturity));
reentrancyGuards[key].check();
reentrancyGuards[key] = ReentrancyGuard.ENTERED;
}
/// @dev internal function to end the reentrancy guard
function lowerGuard(address token0, address token1, uint256 strike, uint256 maturity) private {
bytes32 key = keccak256(abi.encode(token0, token1, strike, maturity));
reentrancyGuards[key] = ReentrancyGuard.NOT_ENTERED;
}
/// @inheritdoc ITimeswapV2Token
function positionOf(address owner, TimeswapV2TokenPosition calldata timeswapV2TokenPosition) public view returns (uint256 amount) {
amount = ERC1155.balanceOf(owner, _timeswapV2TokenPositionIds[timeswapV2TokenPosition.toKey()]);
}
/// @inheritdoc ITimeswapV2Token
function transferTokenPositionFrom(address from, address to, TimeswapV2TokenPosition calldata timeswapV2TokenPosition, uint256 amount) external override {
safeTransferFrom(from, to, _timeswapV2TokenPositionIds[timeswapV2TokenPosition.toKey()], (amount), bytes(""));
}
/// @inheritdoc ITimeswapV2Token
function mint(TimeswapV2TokenMintParam calldata param) external override returns (bytes memory data) {
ParamLibrary.check(param);
changeInteractedIfNecessary(param.token0, param.token1, param.strike, param.maturity);
raiseGuard(param.token0, param.token1, param.strike, param.maturity);
address optionPair = OptionFactoryLibrary.getWithCheck(optionFactory, param.token0, param.token1);
uint256 long0BalanceTarget;
// mints TimeswapV2Token in case of the long0 position
if (param.long0Amount != 0) {
// get the initial balance of the long0 position and add the long0 amount to mint
long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + param.long0Amount;
TimeswapV2TokenPosition memory timeswapV2TokenPosition = TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Long0
});
bytes32 key = timeswapV2TokenPosition.toKey();
// get the unique id of the TimeswapV2Token position
uint256 id = _timeswapV2TokenPositionIds[key];
// if the id is 0, it means that the position has not been minted yet
if (id == 0) {
id = totalSupply() + 1;
_timeswapV2TokenPositions[id] = timeswapV2TokenPosition;
_timeswapV2TokenPositionIds[key] = id;
}
// mint the TimeswapV2Token long0 position
console.log("reaches right before mint in timeswapv2Tokne::mint");
_mint(param.long0To, id, (param.long0Amount), bytes(""));
}
uint256 long1BalanceTarget;
// mints TimeswapV2Token in case of the long1 position
if (param.long1Amount != 0) {
// get the initial balance of the long1 position and add the long1 amount to mint
long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + param.long1Amount;
TimeswapV2TokenPosition memory timeswapV2TokenPosition = TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Long1
});
bytes32 key = timeswapV2TokenPosition.toKey();
// get the unique id of the TimeswapV2Token position
uint256 id = _timeswapV2TokenPositionIds[key];
// if the id is 0, it means that the position has not been minted yet
if (id == 0) {
id = totalSupply() + 1;
_timeswapV2TokenPositions[id] = timeswapV2TokenPosition;
_timeswapV2TokenPositionIds[key] = id;
}
// mint the TimeswapV2Token long1 position
_mint(param.long1To, id, (param.long1Amount), bytes(""));
}
uint256 shortBalanceTarget;
// mints TimeswapV2Token in case of the short position
if (param.shortAmount != 0) {
// get the initial balance of the short position and add the short amount to mint
shortBalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + param.shortAmount;
TimeswapV2TokenPosition memory timeswapV2TokenPosition = TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Short
});
bytes32 key = timeswapV2TokenPosition.toKey();
// get the unique id of the TimeswapV2Token position
uint256 id = _timeswapV2TokenPositionIds[key];
// if the id is 0, it means that the position has not been minted yet
if (id == 0) {
id = totalSupply() + 1;
_timeswapV2TokenPositions[id] = timeswapV2TokenPosition;
_timeswapV2TokenPositionIds[key] = id;
}
// mint the TimeswapV2Token short position
_mint(param.shortTo, id, (param.shortAmount), bytes(""));
}
// console.log()
// ask the msg.sender to transfer the long0/long1/short amount to the this contract
data = ITimeswapV2TokenMintCallback(msg.sender).timeswapV2TokenMintCallback(
TimeswapV2TokenMintCallbackParam({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
long0Amount: param.long0Amount,
long1Amount: param.long1Amount,
shortAmount: param.shortAmount,
data: param.data
})
);
// check if the long0 position token balance target is achieved. If not, revert the transaction
if (param.long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget);
// check if the long1 position token balance target is achieved. If not, revert the transaction
if (param.long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget);
// check if the short position token balance target is achieved. If not, revert the transaction
if (param.shortAmount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), shortBalanceTarget);
lowerGuard(param.token0, param.token1, param.strike, param.maturity);
}
/// @inheritdoc ITimeswapV2Token
function burn(TimeswapV2TokenBurnParam calldata param) external override returns (bytes memory data) {
ParamLibrary.check(param);
raiseGuard(param.token0, param.token1, param.strike, param.maturity);
address optionPair = OptionFactoryLibrary.getWithCheck(optionFactory, param.token0, param.token1);
// case when the long0 position is to be burned
if (param.long0Amount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long0To, TimeswapV2OptionPosition.Long0, param.long0Amount);
// case when the long1 position is to be burned
if (param.long1Amount != 0)
// transfer the underlying equivalent long1 position amount to address of the recipient of long1 position.
ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long1To, TimeswapV2OptionPosition.Long1, param.long1Amount);
// case when the short position is to be burned
if (param.shortAmount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.shortTo, TimeswapV2OptionPosition.Short, param.shortAmount);
if (param.data.length != 0)
data = ITimeswapV2TokenBurnCallback(msg.sender).timeswapV2TokenBurnCallback(
TimeswapV2TokenBurnCallbackParam({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
long0Amount: param.long0Amount,
long1Amount: param.long1Amount,
shortAmount: param.shortAmount,
data: param.data
})
);
// case when the long0 position is to be burned
if (param.long0Amount != 0) {
TimeswapV2TokenPosition memory timeswapV2TokenPosition = TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Long0
});
// burn the TimeswapV2Token representing long0 position
_burn(msg.sender, _timeswapV2TokenPositionIds[timeswapV2TokenPosition.toKey()], param.long0Amount);
}
// case when the long1 position is to be burned
if (param.long1Amount != 0) {
TimeswapV2TokenPosition memory timeswapV2TokenPosition = TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Long1
});
// burn the TimeswapV2Token representing long1 position
_burn(msg.sender, _timeswapV2TokenPositionIds[timeswapV2TokenPosition.toKey()], param.long1Amount);
}
// case when the short position is to be burned
if (param.shortAmount != 0) {
TimeswapV2TokenPosition memory timeswapV2TokenPosition = TimeswapV2TokenPosition({
token0: param.token0,
token1: param.token1,
strike: param.strike,
maturity: param.maturity,
position: TimeswapV2OptionPosition.Short
});
// burn the TimeswapV2Token representing short position
_burn(msg.sender, _timeswapV2TokenPositionIds[timeswapV2TokenPosition.toKey()], param.shortAmount);
}
// stop the guard of reentrancy
lowerGuard(param.token0, param.token1, param.strike, param.maturity);
}
}