@sablier/bob 1.0.1
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/CHANGELOG.md +19 -0
- package/LICENSE-BUSL.md +82 -0
- package/LICENSE-GPL.md +470 -0
- package/LICENSE.md +9 -0
- package/README.md +81 -0
- package/artifacts/BobVaultShare.json +936 -0
- package/artifacts/SablierBob.json +1683 -0
- package/artifacts/SablierEscrow.json +1316 -0
- package/artifacts/SablierLidoAdapter.json +1649 -0
- package/artifacts/erc20/IERC20.json +226 -0
- package/artifacts/interfaces/IBobVaultShare.json +393 -0
- package/artifacts/interfaces/ISablierBob.json +1171 -0
- package/artifacts/interfaces/ISablierEscrow.json +999 -0
- package/artifacts/interfaces/ISablierLidoAdapter.json +1141 -0
- package/artifacts/interfaces/external/ICurveStETHPool.json +128 -0
- package/artifacts/interfaces/external/ILidoWithdrawalQueue.json +209 -0
- package/artifacts/interfaces/external/IStETH.json +262 -0
- package/artifacts/interfaces/external/IWETH9.json +259 -0
- package/artifacts/interfaces/external/IWstETH.json +311 -0
- package/artifacts/libraries/Errors.json +868 -0
- package/package.json +68 -0
- package/src/BobVaultShare.sol +119 -0
- package/src/SablierBob.sol +543 -0
- package/src/SablierEscrow.sol +288 -0
- package/src/SablierLidoAdapter.sol +549 -0
- package/src/abstracts/SablierBobState.sol +156 -0
- package/src/abstracts/SablierEscrowState.sol +159 -0
- package/src/interfaces/IBobVaultShare.sol +51 -0
- package/src/interfaces/ISablierBob.sol +261 -0
- package/src/interfaces/ISablierBobAdapter.sol +157 -0
- package/src/interfaces/ISablierBobState.sol +74 -0
- package/src/interfaces/ISablierEscrow.sol +148 -0
- package/src/interfaces/ISablierEscrowState.sol +77 -0
- package/src/interfaces/ISablierLidoAdapter.sol +110 -0
- package/src/interfaces/external/ICurveStETHPool.sol +31 -0
- package/src/interfaces/external/ILidoWithdrawalQueue.sol +67 -0
- package/src/interfaces/external/IStETH.sol +18 -0
- package/src/interfaces/external/IWETH9.sol +19 -0
- package/src/interfaces/external/IWstETH.sol +32 -0
- package/src/libraries/Errors.sol +189 -0
- package/src/types/Bob.sol +49 -0
- package/src/types/Escrow.sol +49 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
pragma solidity >=0.8.22;
|
|
3
|
+
|
|
4
|
+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
5
|
+
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol";
|
|
7
|
+
import { Comptrollerable } from "@sablier/evm-utils/src/Comptrollerable.sol";
|
|
8
|
+
|
|
9
|
+
import { SablierEscrowState } from "./abstracts/SablierEscrowState.sol";
|
|
10
|
+
import { ISablierEscrow } from "./interfaces/ISablierEscrow.sol";
|
|
11
|
+
import { Errors } from "./libraries/Errors.sol";
|
|
12
|
+
import { Escrow } from "./types/Escrow.sol";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
|
|
16
|
+
███████╗ █████╗ ██████╗ ██╗ ██╗███████╗██████╗ ███████╗███████╗ ██████╗██████╗ ██████╗ ██╗ ██╗
|
|
17
|
+
██╔════╝██╔══██╗██╔══██╗██║ ██║██╔════╝██╔══██╗ ██╔════╝██╔════╝██╔════╝██╔══██╗██╔═══██╗██║ ██║
|
|
18
|
+
███████╗███████║██████╔╝██║ ██║█████╗ ██████╔╝ █████╗ ███████╗██║ ██████╔╝██║ ██║██║ █╗ ██║
|
|
19
|
+
╚════██║██╔══██║██╔══██╗██║ ██║██╔══╝ ██╔══██╗ ██╔══╝ ╚════██║██║ ██╔══██╗██║ ██║██║███╗██║
|
|
20
|
+
███████║██║ ██║██████╔╝███████╗██║███████╗██║ ██║ ███████╗███████║╚██████╗██║ ██║╚██████╔╝╚███╔███╔╝
|
|
21
|
+
╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝╚══════╝╚═╝ ╚═╝ ╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝
|
|
22
|
+
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/// @title SablierEscrow
|
|
26
|
+
/// @notice See the documentation in {ISablierEscrow}.
|
|
27
|
+
contract SablierEscrow is
|
|
28
|
+
Comptrollerable, // 1 inherited component
|
|
29
|
+
ISablierEscrow, // 2 inherited components
|
|
30
|
+
SablierEscrowState // 1 inherited component
|
|
31
|
+
{
|
|
32
|
+
using SafeERC20 for IERC20;
|
|
33
|
+
|
|
34
|
+
/*//////////////////////////////////////////////////////////////////////////
|
|
35
|
+
CONSTRUCTOR
|
|
36
|
+
//////////////////////////////////////////////////////////////////////////*/
|
|
37
|
+
|
|
38
|
+
/// @param initialComptroller The address of the initial comptroller contract.
|
|
39
|
+
/// @param initialTradeFee The initial trade fee percentage.
|
|
40
|
+
constructor(
|
|
41
|
+
address initialComptroller,
|
|
42
|
+
UD60x18 initialTradeFee
|
|
43
|
+
)
|
|
44
|
+
Comptrollerable(initialComptroller)
|
|
45
|
+
SablierEscrowState(initialTradeFee)
|
|
46
|
+
{ }
|
|
47
|
+
|
|
48
|
+
/*//////////////////////////////////////////////////////////////////////////
|
|
49
|
+
USER-FACING STATE-CHANGING FUNCTIONS
|
|
50
|
+
//////////////////////////////////////////////////////////////////////////*/
|
|
51
|
+
|
|
52
|
+
/// @inheritdoc ISablierEscrow
|
|
53
|
+
function cancelOrder(uint256 orderId) external override notNull(orderId) {
|
|
54
|
+
Escrow.Order storage order = _orders[orderId];
|
|
55
|
+
|
|
56
|
+
// Check: the caller is the seller.
|
|
57
|
+
if (msg.sender != order.seller) {
|
|
58
|
+
revert Errors.SablierEscrow_CallerNotAuthorized(orderId, msg.sender, order.seller);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check: the order has not been filled.
|
|
62
|
+
if (order.wasFilled) {
|
|
63
|
+
revert Errors.SablierEscrow_OrderFilled(orderId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check: the order has not been canceled.
|
|
67
|
+
if (order.wasCanceled) {
|
|
68
|
+
revert Errors.SablierEscrow_OrderCancelled(orderId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Effect: mark the order as canceled.
|
|
72
|
+
order.wasCanceled = true;
|
|
73
|
+
|
|
74
|
+
// Cache the sell amount for this order.
|
|
75
|
+
uint128 sellAmount = order.sellAmount;
|
|
76
|
+
|
|
77
|
+
// Interaction: transfer sell tokens to caller.
|
|
78
|
+
order.sellToken.safeTransfer(msg.sender, sellAmount);
|
|
79
|
+
|
|
80
|
+
// Log the event.
|
|
81
|
+
emit CancelOrder({ orderId: orderId, seller: msg.sender, sellAmount: sellAmount });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// @inheritdoc ISablierEscrow
|
|
85
|
+
function createOrder(
|
|
86
|
+
IERC20 sellToken,
|
|
87
|
+
uint128 sellAmount,
|
|
88
|
+
IERC20 buyToken,
|
|
89
|
+
uint128 minBuyAmount,
|
|
90
|
+
address buyer,
|
|
91
|
+
uint40 expiryTime
|
|
92
|
+
)
|
|
93
|
+
external
|
|
94
|
+
override
|
|
95
|
+
returns (uint256 orderId)
|
|
96
|
+
{
|
|
97
|
+
// Check: sell token is not the zero address.
|
|
98
|
+
if (address(sellToken) == address(0)) {
|
|
99
|
+
revert Errors.SablierEscrow_SellTokenZero();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check: sell token is not the native token.
|
|
103
|
+
if (address(sellToken) == nativeToken) {
|
|
104
|
+
revert Errors.SablierEscrow_ForbidNativeToken(address(sellToken));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check: buy token is not the zero address.
|
|
108
|
+
if (address(buyToken) == address(0)) {
|
|
109
|
+
revert Errors.SablierEscrow_BuyTokenZero();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check: buy token is not the native token.
|
|
113
|
+
if (address(buyToken) == nativeToken) {
|
|
114
|
+
revert Errors.SablierEscrow_ForbidNativeToken(address(buyToken));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check: sell and buy tokens are not the same.
|
|
118
|
+
if (sellToken == buyToken) {
|
|
119
|
+
revert Errors.SablierEscrow_SameToken(sellToken);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check: sell amount is not zero.
|
|
123
|
+
if (sellAmount == 0) {
|
|
124
|
+
revert Errors.SablierEscrow_SellAmountZero();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check: minimum buy amount is not zero.
|
|
128
|
+
if (minBuyAmount == 0) {
|
|
129
|
+
revert Errors.SablierEscrow_MinBuyAmountZero();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check: expiryTime is in the future. Zero is sentinel for orders that never expire.
|
|
133
|
+
if (expiryTime > 0 && expiryTime <= block.timestamp) {
|
|
134
|
+
revert Errors.SablierEscrow_ExpiryTimeInPast(expiryTime, uint40(block.timestamp));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Use the current next order ID as this order's ID.
|
|
138
|
+
orderId = nextOrderId;
|
|
139
|
+
|
|
140
|
+
// Effect: increment the next order ID.
|
|
141
|
+
unchecked {
|
|
142
|
+
nextOrderId = orderId + 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Effect: create the order.
|
|
146
|
+
_orders[orderId] = Escrow.Order({
|
|
147
|
+
seller: msg.sender,
|
|
148
|
+
buyer: buyer,
|
|
149
|
+
sellToken: sellToken,
|
|
150
|
+
buyToken: buyToken,
|
|
151
|
+
sellAmount: sellAmount,
|
|
152
|
+
minBuyAmount: minBuyAmount,
|
|
153
|
+
expiryTime: expiryTime,
|
|
154
|
+
wasCanceled: false,
|
|
155
|
+
wasFilled: false
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Interaction: transfer sell tokens from caller to this contract.
|
|
159
|
+
sellToken.safeTransferFrom(msg.sender, address(this), sellAmount);
|
|
160
|
+
|
|
161
|
+
// Log the event.
|
|
162
|
+
emit CreateOrder({
|
|
163
|
+
orderId: orderId,
|
|
164
|
+
seller: msg.sender,
|
|
165
|
+
buyer: buyer,
|
|
166
|
+
sellToken: sellToken,
|
|
167
|
+
buyToken: buyToken,
|
|
168
|
+
sellAmount: sellAmount,
|
|
169
|
+
minBuyAmount: minBuyAmount,
|
|
170
|
+
expiryTime: expiryTime
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// @inheritdoc ISablierEscrow
|
|
175
|
+
function fillOrder(
|
|
176
|
+
uint256 orderId,
|
|
177
|
+
uint128 buyAmount
|
|
178
|
+
)
|
|
179
|
+
external
|
|
180
|
+
override
|
|
181
|
+
notNull(orderId)
|
|
182
|
+
returns (
|
|
183
|
+
uint128 amountToTransferToSeller,
|
|
184
|
+
uint128 amountToTransferToBuyer,
|
|
185
|
+
uint128 feeDeductedFromBuyerAmount,
|
|
186
|
+
uint128 feeDeductedFromSellerAmount
|
|
187
|
+
)
|
|
188
|
+
{
|
|
189
|
+
// Check: the order is open.
|
|
190
|
+
Escrow.Status status = _statusOf(orderId);
|
|
191
|
+
if (status != Escrow.Status.OPEN) {
|
|
192
|
+
revert Errors.SablierEscrow_OrderNotOpen(orderId, status);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Escrow.Order storage order = _orders[orderId];
|
|
196
|
+
|
|
197
|
+
// Check: if the order has buyer specified, the caller must be the buyer.
|
|
198
|
+
if (order.buyer != address(0) && msg.sender != order.buyer) {
|
|
199
|
+
revert Errors.SablierEscrow_CallerNotAuthorized(orderId, msg.sender, order.buyer);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check: the buy amount meets the minimum asked.
|
|
203
|
+
if (buyAmount < order.minBuyAmount) {
|
|
204
|
+
revert Errors.SablierEscrow_InsufficientBuyAmount(buyAmount, order.minBuyAmount);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Effect: mark the order as filled.
|
|
208
|
+
order.wasFilled = true;
|
|
209
|
+
|
|
210
|
+
// Get the trade fee from storage.
|
|
211
|
+
UD60x18 currentTradeFee = tradeFee;
|
|
212
|
+
|
|
213
|
+
amountToTransferToSeller = buyAmount;
|
|
214
|
+
amountToTransferToBuyer = order.sellAmount;
|
|
215
|
+
|
|
216
|
+
// If the fee is non-zero, deduct the fee from both sides.
|
|
217
|
+
if (currentTradeFee.unwrap() > 0) {
|
|
218
|
+
// Calculate the fee on the sell amount.
|
|
219
|
+
feeDeductedFromBuyerAmount = ud(amountToTransferToBuyer).mul(currentTradeFee).intoUint128();
|
|
220
|
+
amountToTransferToBuyer -= feeDeductedFromBuyerAmount;
|
|
221
|
+
|
|
222
|
+
// Calculate the fee on the buy amount.
|
|
223
|
+
feeDeductedFromSellerAmount = ud(buyAmount).mul(currentTradeFee).intoUint128();
|
|
224
|
+
amountToTransferToSeller -= feeDeductedFromSellerAmount;
|
|
225
|
+
|
|
226
|
+
// Set comptroller as the fee recipient.
|
|
227
|
+
address feeRecipient = address(comptroller);
|
|
228
|
+
|
|
229
|
+
// Interaction: transfer fees to the fee recipient.
|
|
230
|
+
order.buyToken.safeTransferFrom(msg.sender, feeRecipient, feeDeductedFromSellerAmount);
|
|
231
|
+
order.sellToken.safeTransfer(feeRecipient, feeDeductedFromBuyerAmount);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Cache the seller address.
|
|
235
|
+
address seller = order.seller;
|
|
236
|
+
|
|
237
|
+
// Interaction: transfer buy token to the seller.
|
|
238
|
+
order.buyToken.safeTransferFrom(msg.sender, seller, amountToTransferToSeller);
|
|
239
|
+
|
|
240
|
+
// Interaction: transfer sell tokens to the buyer.
|
|
241
|
+
order.sellToken.safeTransfer(msg.sender, amountToTransferToBuyer);
|
|
242
|
+
|
|
243
|
+
// Log the event.
|
|
244
|
+
emit FillOrder({
|
|
245
|
+
orderId: orderId,
|
|
246
|
+
buyer: msg.sender,
|
|
247
|
+
seller: seller,
|
|
248
|
+
sellAmount: amountToTransferToBuyer,
|
|
249
|
+
buyAmount: amountToTransferToSeller,
|
|
250
|
+
feeDeductedFromBuyerAmount: feeDeductedFromBuyerAmount,
|
|
251
|
+
feeDeductedFromSellerAmount: feeDeductedFromSellerAmount
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// @inheritdoc ISablierEscrow
|
|
256
|
+
function setNativeToken(address newNativeToken) external override onlyComptroller {
|
|
257
|
+
// Check: provided token is not zero address.
|
|
258
|
+
if (newNativeToken == address(0)) {
|
|
259
|
+
revert Errors.SablierEscrow_NativeTokenZeroAddress();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check: native token is not set.
|
|
263
|
+
if (nativeToken != address(0)) {
|
|
264
|
+
revert Errors.SablierEscrow_NativeTokenAlreadySet(nativeToken);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Effect: set the native token.
|
|
268
|
+
nativeToken = newNativeToken;
|
|
269
|
+
|
|
270
|
+
// Log the update.
|
|
271
|
+
emit SetNativeToken({ comptroller: msg.sender, nativeToken: newNativeToken });
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// @inheritdoc ISablierEscrow
|
|
275
|
+
function setTradeFee(UD60x18 newTradeFee) external override onlyComptroller {
|
|
276
|
+
// Check: the new trade fee does not exceed the maximum trade fee.
|
|
277
|
+
_notTooHigh(newTradeFee);
|
|
278
|
+
|
|
279
|
+
// Cache the current trade fee for the event.
|
|
280
|
+
UD60x18 previousTradeFee = tradeFee;
|
|
281
|
+
|
|
282
|
+
// Effect: set the new trade fee.
|
|
283
|
+
tradeFee = newTradeFee;
|
|
284
|
+
|
|
285
|
+
// Log the event.
|
|
286
|
+
emit SetTradeFee({ caller: msg.sender, previousTradeFee: previousTradeFee, newTradeFee: newTradeFee });
|
|
287
|
+
}
|
|
288
|
+
}
|