@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE-BUSL.md +82 -0
  3. package/LICENSE-GPL.md +470 -0
  4. package/LICENSE.md +9 -0
  5. package/README.md +81 -0
  6. package/artifacts/BobVaultShare.json +936 -0
  7. package/artifacts/SablierBob.json +1683 -0
  8. package/artifacts/SablierEscrow.json +1316 -0
  9. package/artifacts/SablierLidoAdapter.json +1649 -0
  10. package/artifacts/erc20/IERC20.json +226 -0
  11. package/artifacts/interfaces/IBobVaultShare.json +393 -0
  12. package/artifacts/interfaces/ISablierBob.json +1171 -0
  13. package/artifacts/interfaces/ISablierEscrow.json +999 -0
  14. package/artifacts/interfaces/ISablierLidoAdapter.json +1141 -0
  15. package/artifacts/interfaces/external/ICurveStETHPool.json +128 -0
  16. package/artifacts/interfaces/external/ILidoWithdrawalQueue.json +209 -0
  17. package/artifacts/interfaces/external/IStETH.json +262 -0
  18. package/artifacts/interfaces/external/IWETH9.json +259 -0
  19. package/artifacts/interfaces/external/IWstETH.json +311 -0
  20. package/artifacts/libraries/Errors.json +868 -0
  21. package/package.json +68 -0
  22. package/src/BobVaultShare.sol +119 -0
  23. package/src/SablierBob.sol +543 -0
  24. package/src/SablierEscrow.sol +288 -0
  25. package/src/SablierLidoAdapter.sol +549 -0
  26. package/src/abstracts/SablierBobState.sol +156 -0
  27. package/src/abstracts/SablierEscrowState.sol +159 -0
  28. package/src/interfaces/IBobVaultShare.sol +51 -0
  29. package/src/interfaces/ISablierBob.sol +261 -0
  30. package/src/interfaces/ISablierBobAdapter.sol +157 -0
  31. package/src/interfaces/ISablierBobState.sol +74 -0
  32. package/src/interfaces/ISablierEscrow.sol +148 -0
  33. package/src/interfaces/ISablierEscrowState.sol +77 -0
  34. package/src/interfaces/ISablierLidoAdapter.sol +110 -0
  35. package/src/interfaces/external/ICurveStETHPool.sol +31 -0
  36. package/src/interfaces/external/ILidoWithdrawalQueue.sol +67 -0
  37. package/src/interfaces/external/IStETH.sol +18 -0
  38. package/src/interfaces/external/IWETH9.sol +19 -0
  39. package/src/interfaces/external/IWstETH.sol +32 -0
  40. package/src/libraries/Errors.sol +189 -0
  41. package/src/types/Bob.sol +49 -0
  42. package/src/types/Escrow.sol +49 -0
@@ -0,0 +1,156 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ pragma solidity >=0.8.22;
3
+
4
+ import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
5
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+
7
+ import { IBobVaultShare } from "../interfaces/IBobVaultShare.sol";
8
+ import { ISablierBobAdapter } from "../interfaces/ISablierBobAdapter.sol";
9
+ import { ISablierBobState } from "../interfaces/ISablierBobState.sol";
10
+ import { Errors } from "../libraries/Errors.sol";
11
+ import { Bob } from "../types/Bob.sol";
12
+
13
+ /// @title SablierBobState
14
+ /// @notice See the documentation in {ISablierBobState}.
15
+ abstract contract SablierBobState is ISablierBobState {
16
+ /*//////////////////////////////////////////////////////////////////////////
17
+ STATE VARIABLES
18
+ //////////////////////////////////////////////////////////////////////////*/
19
+
20
+ /// @dev Default adapters mapped by token address.
21
+ mapping(IERC20 token => ISablierBobAdapter adapter) internal _defaultAdapters;
22
+
23
+ /// @inheritdoc ISablierBobState
24
+ address public override nativeToken;
25
+
26
+ /// @inheritdoc ISablierBobState
27
+ uint256 public override nextVaultId;
28
+
29
+ /// @dev Vaults mapped by unsigned integers.
30
+ mapping(uint256 vaultId => Bob.Vault vault) internal _vaults;
31
+
32
+ /*//////////////////////////////////////////////////////////////////////////
33
+ MODIFIERS
34
+ //////////////////////////////////////////////////////////////////////////*/
35
+
36
+ /// @dev Checks that `vaultId` does not reference a null vault.
37
+ modifier notNull(uint256 vaultId) {
38
+ _notNull(vaultId);
39
+ _;
40
+ }
41
+
42
+ /*//////////////////////////////////////////////////////////////////////////
43
+ CONSTRUCTOR
44
+ //////////////////////////////////////////////////////////////////////////*/
45
+
46
+ /// @notice Initializes the state variables.
47
+ constructor() {
48
+ // Set the next vault ID to 1.
49
+ nextVaultId = 1;
50
+ }
51
+
52
+ /*//////////////////////////////////////////////////////////////////////////
53
+ USER-FACING READ-ONLY FUNCTIONS
54
+ //////////////////////////////////////////////////////////////////////////*/
55
+
56
+ /// @inheritdoc ISablierBobState
57
+ function getAdapter(uint256 vaultId) external view override notNull(vaultId) returns (ISablierBobAdapter adapter) {
58
+ adapter = _vaults[vaultId].adapter;
59
+ }
60
+
61
+ /// @inheritdoc ISablierBobState
62
+ function getDefaultAdapterFor(IERC20 token) external view override returns (ISablierBobAdapter adapter) {
63
+ adapter = _defaultAdapters[token];
64
+ }
65
+
66
+ /// @inheritdoc ISablierBobState
67
+ function getExpiry(uint256 vaultId) external view override notNull(vaultId) returns (uint40 expiry) {
68
+ expiry = _vaults[vaultId].expiry;
69
+ }
70
+
71
+ /// @inheritdoc ISablierBobState
72
+ function getLastSyncedAt(uint256 vaultId) external view override notNull(vaultId) returns (uint40 lastSyncedAt) {
73
+ lastSyncedAt = _vaults[vaultId].lastSyncedAt;
74
+ }
75
+
76
+ /// @inheritdoc ISablierBobState
77
+ function getLastSyncedPrice(uint256 vaultId)
78
+ external
79
+ view
80
+ override
81
+ notNull(vaultId)
82
+ returns (uint128 lastSyncedPrice)
83
+ {
84
+ lastSyncedPrice = _vaults[vaultId].lastSyncedPrice;
85
+ }
86
+
87
+ /// @inheritdoc ISablierBobState
88
+ function getOracle(uint256 vaultId) external view override notNull(vaultId) returns (AggregatorV3Interface oracle) {
89
+ oracle = _vaults[vaultId].oracle;
90
+ }
91
+
92
+ /// @inheritdoc ISablierBobState
93
+ function getShareToken(uint256 vaultId)
94
+ external
95
+ view
96
+ override
97
+ notNull(vaultId)
98
+ returns (IBobVaultShare shareToken)
99
+ {
100
+ shareToken = _vaults[vaultId].shareToken;
101
+ }
102
+
103
+ /// @inheritdoc ISablierBobState
104
+ function getTargetPrice(uint256 vaultId) external view override notNull(vaultId) returns (uint128 targetPrice) {
105
+ targetPrice = _vaults[vaultId].targetPrice;
106
+ }
107
+
108
+ /// @inheritdoc ISablierBobState
109
+ function getUnderlyingToken(uint256 vaultId) external view override notNull(vaultId) returns (IERC20 token) {
110
+ token = _vaults[vaultId].token;
111
+ }
112
+
113
+ /// @inheritdoc ISablierBobState
114
+ function isStakedInAdapter(uint256 vaultId) external view override notNull(vaultId) returns (bool stakedInAdapter) {
115
+ stakedInAdapter = _vaults[vaultId].isStakedInAdapter;
116
+ }
117
+
118
+ /// @inheritdoc ISablierBobState
119
+ function statusOf(uint256 vaultId) external view override notNull(vaultId) returns (Bob.Status status) {
120
+ status = _statusOf(vaultId);
121
+ }
122
+
123
+ /*//////////////////////////////////////////////////////////////////////////
124
+ INTERNAL READ-ONLY FUNCTIONS
125
+ //////////////////////////////////////////////////////////////////////////*/
126
+
127
+ /// @dev Retrieves the vault's status without performing a null check.
128
+ function _statusOf(uint256 vaultId) internal view returns (Bob.Status) {
129
+ // Return EXPIRED if the vault has expired.
130
+ if (block.timestamp >= _vaults[vaultId].expiry) {
131
+ return Bob.Status.EXPIRED;
132
+ }
133
+
134
+ // Return SETTLED if the last synced price is greater than or equal to the target price.
135
+ if (_vaults[vaultId].lastSyncedPrice >= _vaults[vaultId].targetPrice) {
136
+ return Bob.Status.SETTLED;
137
+ }
138
+
139
+ // Otherwise, return ACTIVE.
140
+ return Bob.Status.ACTIVE;
141
+ }
142
+
143
+ /*//////////////////////////////////////////////////////////////////////////
144
+ PRIVATE READ-ONLY FUNCTIONS
145
+ //////////////////////////////////////////////////////////////////////////*/
146
+
147
+ /// @dev Reverts if `vaultId` references a null vault.
148
+ /// @dev A private function is used instead of inlining this logic in a modifier because Solidity copies modifiers
149
+ /// into every function that uses them.
150
+ function _notNull(uint256 vaultId) private view {
151
+ // A vault is considered null if its token address is zero, as tokens are always checked on creation.
152
+ if (address(_vaults[vaultId].token) == address(0)) {
153
+ revert Errors.SablierBobState_Null(vaultId);
154
+ }
155
+ }
156
+ }
@@ -0,0 +1,159 @@
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 { UD60x18 } from "@prb/math/src/UD60x18.sol";
6
+
7
+ import { ISablierEscrowState } from "../interfaces/ISablierEscrowState.sol";
8
+ import { Errors } from "../libraries/Errors.sol";
9
+ import { Escrow } from "../types/Escrow.sol";
10
+
11
+ /// @title SablierEscrowState
12
+ /// @notice See the documentation in {ISablierEscrowState}.
13
+ abstract contract SablierEscrowState is ISablierEscrowState {
14
+ /*//////////////////////////////////////////////////////////////////////////
15
+ CONSTANTS
16
+ //////////////////////////////////////////////////////////////////////////*/
17
+
18
+ /// @inheritdoc ISablierEscrowState
19
+ UD60x18 public constant override MAX_TRADE_FEE = UD60x18.wrap(0.02e18);
20
+
21
+ /*//////////////////////////////////////////////////////////////////////////
22
+ STATE VARIABLES
23
+ //////////////////////////////////////////////////////////////////////////*/
24
+
25
+ /// @inheritdoc ISablierEscrowState
26
+ address public override nativeToken;
27
+
28
+ /// @inheritdoc ISablierEscrowState
29
+ uint256 public override nextOrderId;
30
+
31
+ /// @inheritdoc ISablierEscrowState
32
+ UD60x18 public override tradeFee;
33
+
34
+ /// @dev Orders mapped by order ID.
35
+ mapping(uint256 orderId => Escrow.Order order) internal _orders;
36
+
37
+ /*//////////////////////////////////////////////////////////////////////////
38
+ CONSTRUCTOR
39
+ //////////////////////////////////////////////////////////////////////////*/
40
+
41
+ /// @notice Initializes the state variables.
42
+ /// @param initialTradeFee The initial trade fee percentage.
43
+ constructor(UD60x18 initialTradeFee) {
44
+ // Check: the trade fee is not greater than the maximum trade fee.
45
+ _notTooHigh(initialTradeFee);
46
+
47
+ // Set the next order ID to 1.
48
+ nextOrderId = 1;
49
+
50
+ // Set the initial trade fee.
51
+ tradeFee = initialTradeFee;
52
+ }
53
+
54
+ /*//////////////////////////////////////////////////////////////////////////
55
+ MODIFIERS
56
+ //////////////////////////////////////////////////////////////////////////*/
57
+
58
+ /// @dev Checks that `orderId` does not reference a null order.
59
+ modifier notNull(uint256 orderId) {
60
+ _notNull(orderId);
61
+ _;
62
+ }
63
+
64
+ /*//////////////////////////////////////////////////////////////////////////
65
+ USER-FACING READ-ONLY FUNCTIONS
66
+ //////////////////////////////////////////////////////////////////////////*/
67
+
68
+ /// @inheritdoc ISablierEscrowState
69
+ function getBuyer(uint256 orderId) external view override notNull(orderId) returns (address buyer) {
70
+ buyer = _orders[orderId].buyer;
71
+ }
72
+
73
+ /// @inheritdoc ISablierEscrowState
74
+ function getBuyToken(uint256 orderId) external view override notNull(orderId) returns (IERC20 buyToken) {
75
+ buyToken = _orders[orderId].buyToken;
76
+ }
77
+
78
+ /// @inheritdoc ISablierEscrowState
79
+ function getExpiryTime(uint256 orderId) external view override notNull(orderId) returns (uint40 expiryTime) {
80
+ expiryTime = _orders[orderId].expiryTime;
81
+ }
82
+
83
+ /// @inheritdoc ISablierEscrowState
84
+ function getMinBuyAmount(uint256 orderId) external view override notNull(orderId) returns (uint128 minBuyAmount) {
85
+ minBuyAmount = _orders[orderId].minBuyAmount;
86
+ }
87
+
88
+ /// @inheritdoc ISablierEscrowState
89
+ function getSellAmount(uint256 orderId) external view override notNull(orderId) returns (uint128 sellAmount) {
90
+ sellAmount = _orders[orderId].sellAmount;
91
+ }
92
+
93
+ /// @inheritdoc ISablierEscrowState
94
+ function getSeller(uint256 orderId) external view override notNull(orderId) returns (address seller) {
95
+ seller = _orders[orderId].seller;
96
+ }
97
+
98
+ /// @inheritdoc ISablierEscrowState
99
+ function getSellToken(uint256 orderId) external view override notNull(orderId) returns (IERC20 sellToken) {
100
+ sellToken = _orders[orderId].sellToken;
101
+ }
102
+
103
+ /// @inheritdoc ISablierEscrowState
104
+ function statusOf(uint256 orderId) external view override notNull(orderId) returns (Escrow.Status status) {
105
+ status = _statusOf(orderId);
106
+ }
107
+
108
+ /// @inheritdoc ISablierEscrowState
109
+ function wasCanceled(uint256 orderId) external view override notNull(orderId) returns (bool result) {
110
+ result = _orders[orderId].wasCanceled;
111
+ }
112
+
113
+ /// @inheritdoc ISablierEscrowState
114
+ function wasFilled(uint256 orderId) external view override notNull(orderId) returns (bool result) {
115
+ result = _orders[orderId].wasFilled;
116
+ }
117
+
118
+ /*//////////////////////////////////////////////////////////////////////////
119
+ INTERNAL READ-ONLY FUNCTIONS
120
+ //////////////////////////////////////////////////////////////////////////*/
121
+
122
+ /// @dev Reverts if `newTradeFee` is greater than the maximum trade fee.
123
+ function _notTooHigh(UD60x18 newTradeFee) internal pure {
124
+ if (newTradeFee.gt(MAX_TRADE_FEE)) {
125
+ revert Errors.SablierEscrowState_NewTradeFeeTooHigh(newTradeFee, MAX_TRADE_FEE);
126
+ }
127
+ }
128
+
129
+ /// @dev Return the order status without performing a null check.
130
+ function _statusOf(uint256 orderId) internal view returns (Escrow.Status) {
131
+ if (_orders[orderId].wasFilled) {
132
+ return Escrow.Status.FILLED;
133
+ }
134
+ if (_orders[orderId].wasCanceled) {
135
+ return Escrow.Status.CANCELLED;
136
+ }
137
+
138
+ uint40 expiryTime = _orders[orderId].expiryTime;
139
+
140
+ // Return EXPIRED if the order has an expiry timestamp and it has expired.
141
+ if (expiryTime != 0 && block.timestamp >= expiryTime) {
142
+ return Escrow.Status.EXPIRED;
143
+ }
144
+
145
+ return Escrow.Status.OPEN;
146
+ }
147
+
148
+ /*//////////////////////////////////////////////////////////////////////////
149
+ PRIVATE READ-ONLY FUNCTIONS
150
+ //////////////////////////////////////////////////////////////////////////*/
151
+
152
+ /// @dev Reverts if `orderId` references a null order.
153
+ function _notNull(uint256 orderId) private view {
154
+ // An order is considered null if its seller address is zero.
155
+ if (_orders[orderId].seller == address(0)) {
156
+ revert Errors.SablierEscrowState_Null(orderId);
157
+ }
158
+ }
159
+ }
@@ -0,0 +1,51 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ pragma solidity >=0.8.22;
3
+
4
+ import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
+
6
+ /// @title IBobVaultShare
7
+ /// @notice Interface for the ERC-20 token representing shares in a Bob vault.
8
+ interface IBobVaultShare is IERC20Metadata {
9
+ /*//////////////////////////////////////////////////////////////////////////
10
+ READ-ONLY FUNCTIONS
11
+ //////////////////////////////////////////////////////////////////////////*/
12
+
13
+ /// @notice Returns the address of the Bob contract with the authority to mint and burn tokens.
14
+ /// @dev This is an immutable state variable.
15
+ function SABLIER_BOB() external view returns (address);
16
+
17
+ /// @notice Returns the vault ID this share token represents.
18
+ /// @dev This is an immutable state variable.
19
+ function VAULT_ID() external view returns (uint256);
20
+
21
+ /*//////////////////////////////////////////////////////////////////////////
22
+ STATE-CHANGING FUNCTIONS
23
+ //////////////////////////////////////////////////////////////////////////*/
24
+
25
+ /// @notice Mints `amount` tokens to `to`.
26
+ ///
27
+ /// @dev Emits a {Transfer} event.
28
+ ///
29
+ /// Requirements:
30
+ /// - The caller must be the SablierBob contract.
31
+ /// - `vaultId` must be equal to the {VAULT_ID}.
32
+ ///
33
+ /// @param vaultId The vault ID that this share token represents.
34
+ /// @param to The address to mint tokens to.
35
+ /// @param amount The amount of tokens to mint.
36
+ function mint(uint256 vaultId, address to, uint256 amount) external;
37
+
38
+ /// @notice Burns `amount` tokens from `from`.
39
+ ///
40
+ /// @dev Emits a {Transfer} event.
41
+ ///
42
+ /// Requirements:
43
+ /// - The caller must be the SablierBob contract.
44
+ /// - `vaultId` must be equal to the {VAULT_ID}.
45
+ /// - `from` must have at least `amount` tokens.
46
+ ///
47
+ /// @param vaultId The vault ID that this share token represents.
48
+ /// @param from The address to burn tokens from.
49
+ /// @param amount The amount of tokens to burn.
50
+ function burn(uint256 vaultId, address from, uint256 amount) external;
51
+ }
@@ -0,0 +1,261 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ pragma solidity >=0.8.22;
3
+
4
+ import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
5
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+ import { IComptrollerable } from "@sablier/evm-utils/src/interfaces/IComptrollerable.sol";
7
+
8
+ import { IBobVaultShare } from "./IBobVaultShare.sol";
9
+ import { ISablierBobAdapter } from "./ISablierBobAdapter.sol";
10
+ import { ISablierBobState } from "./ISablierBobState.sol";
11
+
12
+ /// @title ISablierBob
13
+ /// @notice Price-gated vaults that unlock deposited tokens when the price returned by the oracle is greater than or
14
+ /// equal to the target price set by the vault creator. The tokens are also unlocked if the vault expires. When a vault
15
+ /// is configured with a adapter, the protocol automatically stakes the tokens via adapter and earns yield on the
16
+ /// deposit amount.
17
+ interface ISablierBob is IComptrollerable, ISablierBobState {
18
+ /*//////////////////////////////////////////////////////////////////////////
19
+ EVENTS
20
+ //////////////////////////////////////////////////////////////////////////*/
21
+
22
+ /// @notice Emitted when a new vault is created.
23
+ event CreateVault(
24
+ uint256 indexed vaultId,
25
+ IERC20 indexed token,
26
+ AggregatorV3Interface indexed oracle,
27
+ ISablierBobAdapter adapter,
28
+ IBobVaultShare shareToken,
29
+ uint128 targetPrice,
30
+ uint40 expiry
31
+ );
32
+
33
+ /// @notice Emitted when a user deposits tokens into a vault.
34
+ event Enter(uint256 indexed vaultId, address indexed user, uint128 amountReceived, uint128 sharesMinted);
35
+
36
+ /// @notice Emitted when a user redeems their shares from a settled vault.
37
+ event Redeem(
38
+ uint256 indexed vaultId,
39
+ address indexed user,
40
+ uint128 amountReceived,
41
+ uint128 sharesBurned,
42
+ uint256 fee
43
+ );
44
+
45
+ /// @notice Emitted when the comptroller sets a new default adapter for a token.
46
+ event SetDefaultAdapter(IERC20 indexed token, ISablierBobAdapter indexed adapter);
47
+
48
+ /// @notice Emitted when the native token address is set by the comptroller.
49
+ event SetNativeToken(address indexed comptroller, address nativeToken);
50
+
51
+ /// @notice Emitted when a vault's price is synced from the oracle.
52
+ event SyncPriceFromOracle(
53
+ uint256 indexed vaultId,
54
+ AggregatorV3Interface indexed oracle,
55
+ uint128 latestPrice,
56
+ uint40 syncedAt
57
+ );
58
+
59
+ /// @notice Emitted when tokens staked in the adapter for a given vault are unstaked.
60
+ event UnstakeFromAdapter(
61
+ uint256 indexed vaultId,
62
+ ISablierBobAdapter indexed adapter,
63
+ uint128 wrappedTokenUnstakedAmount,
64
+ uint128 amountReceivedFromAdapter
65
+ );
66
+
67
+ /*//////////////////////////////////////////////////////////////////////////
68
+ READ-ONLY FUNCTIONS
69
+ //////////////////////////////////////////////////////////////////////////*/
70
+
71
+ /// @notice Calculates the minimum fee in wei required to redeem from the given vault ID. Returns 0 for vaults with
72
+ /// an adapter, since the fee is taken from yield generated.
73
+ /// @dev Reverts if `vaultId` references a null vault.
74
+ /// @param vaultId The vault ID for the query.
75
+ function calculateMinFeeWei(uint256 vaultId) external view returns (uint256 minFeeWei);
76
+
77
+ /*//////////////////////////////////////////////////////////////////////////
78
+ STATE-CHANGING FUNCTIONS
79
+ //////////////////////////////////////////////////////////////////////////*/
80
+
81
+ /// @notice Creates a new vault with the specified parameters.
82
+ ///
83
+ /// @dev Emits a {CreateVault} event.
84
+ ///
85
+ /// Notes:
86
+ /// - A new ERC-20 share token is deployed for each vault to represent user's share of deposits in the vault.
87
+ /// - The default adapter for the token is copied as the vault adapter. Any change in the default adapter does not
88
+ /// affect existing vaults.
89
+ /// - Vault creator is responsible for choosing a valid oracle. They should use Chainlink oracles, as the
90
+ /// integration is based on their API.
91
+ ///
92
+ /// Requirements:
93
+ /// - `token` must not be the zero address.
94
+ /// - `token` must implement `symbol()` and `decimals()` functions.
95
+ /// - `expiry` must be in the future.
96
+ /// - `oracle` must implement the Chainlink's {AggregatorV3Interface} interface.
97
+ /// - `oracle` must return a positive price when `latestRoundData()` is called.
98
+ /// - `oracle` must return a non-zero value no greater than 36 when `decimals()` is called.
99
+ /// - `targetPrice` must not be zero or greater than the current price returned by the provided oracle.
100
+ ///
101
+ /// @param token The address of the ERC-20 token that will be accepted for deposits.
102
+ /// @param oracle The address of the price feed oracle for the deposit token.
103
+ /// @param expiry The Unix timestamp when the vault expires.
104
+ /// @param targetPrice The target price at which the vault settles, denominated in Chainlink's 8-decimal format for
105
+ /// USD prices, where 1e8 is $1.
106
+ /// @return vaultId The ID of the newly created vault.
107
+ function createVault(
108
+ IERC20 token,
109
+ AggregatorV3Interface oracle,
110
+ uint40 expiry,
111
+ uint128 targetPrice
112
+ )
113
+ external
114
+ returns (uint256 vaultId);
115
+
116
+ /// @notice Enter into a vault by depositing tokens into it and minting share tokens to the caller.
117
+ ///
118
+ /// @dev Emits an {Enter} event.
119
+ ///
120
+ /// Notes:
121
+ /// - If an adapter is configured for the vault, tokens are automatically staked for yield using the adapter.
122
+ /// - Share tokens are minted 1:1 with the deposited amount.
123
+ ///
124
+ /// Requirements:
125
+ /// - `vaultId` must not reference a null vault.
126
+ /// - The vault must have ACTIVE status.
127
+ /// - `amount` must be greater than zero.
128
+ /// - The caller must have approved this contract to transfer `amount` tokens.
129
+ ///
130
+ /// @param vaultId The ID of the vault to deposit into.
131
+ /// @param amount The amount of tokens to deposit.
132
+ function enter(uint256 vaultId, uint128 amount) external;
133
+
134
+ /// @notice Enter into a vault by depositing native token (such as ETH, POL, etc.) into it and minting share tokens
135
+ /// to the caller.
136
+ ///
137
+ /// @dev Emits an {Enter} event.
138
+ ///
139
+ /// Notes:
140
+ /// - `msg.value` is used as the deposit amount.
141
+ /// - See notes for {enter}.
142
+ ///
143
+ /// Requirements:
144
+ /// - See requirements for {enter}.
145
+ /// - `msg.value` must be greater than zero and must not exceed `type(uint128).max`.
146
+ ///
147
+ /// @param vaultId The ID of the vault to deposit into.
148
+ function enterWithNativeToken(uint256 vaultId) external payable;
149
+
150
+ /// @notice Called by adapter when share tokens for a given vault are transferred between users. This is required
151
+ /// for accounting of the yield generated by the adapter.
152
+ ///
153
+ /// Requirements:
154
+ /// - The caller must be the share token contract stored in the given vault.
155
+ /// - The calculated wstETH transfer amount must not be zero.
156
+ ///
157
+ /// @param vaultId The ID of the vault.
158
+ /// @param from The address transferring share tokens.
159
+ /// @param to The address receiving share tokens.
160
+ /// @param amount The number of share tokens being transferred.
161
+ /// @param fromBalanceBefore The number of share tokens the sender had before the transfer.
162
+ function onShareTransfer(
163
+ uint256 vaultId,
164
+ address from,
165
+ address to,
166
+ uint256 amount,
167
+ uint256 fromBalanceBefore
168
+ )
169
+ external;
170
+
171
+ /// @notice Redeem the tokens by burning user shares.
172
+ ///
173
+ /// @dev Emits a {Redeem} event.
174
+ ///
175
+ /// Notes:
176
+ /// - If no adapter is configured for the vault, a fee in the native token is applied.
177
+ /// - If an adapter is configured for the vault, a fee, in the deposit token, is deducted from yield generated by
178
+ /// the adapter.
179
+ /// - If unstake via Lido withdrawal queue contract is triggered, redeem will revert until the withdrawal from the
180
+ /// Lido queue is finalized.
181
+ ///
182
+ /// Requirements:
183
+ /// - `vaultId` must not reference a null vault.
184
+ /// - Either block timestamp must be greater than or equal to the vault expiry or the latest price from the oracle
185
+ /// must be greater than or equal to the target price.
186
+ /// - The share balance of the caller must be greater than zero.
187
+ /// - If no adapter is configured for the vault, `msg.value` must be greater than or equal to the min fee required
188
+ /// in the native token.
189
+ ///
190
+ /// @param vaultId The ID of the vault to redeem from.
191
+ /// @return transferAmount The amount of tokens transferred to the caller, after fees are deducted (only applicable
192
+ /// if adapter is set).
193
+ /// @return feeAmountDeductedFromYield The fee amount deducted from the yield. Zero if no adapter is set.
194
+ function redeem(uint256 vaultId)
195
+ external
196
+ payable
197
+ returns (uint128 transferAmount, uint128 feeAmountDeductedFromYield);
198
+
199
+ /// @notice Sets the default adapter for a specific token.
200
+ ///
201
+ /// @dev Emits a {SetDefaultAdapter} event.
202
+ ///
203
+ /// Notes:
204
+ /// - This only affects future vaults.
205
+ ///
206
+ /// Requirements:
207
+ /// - The caller must be the comptroller.
208
+ /// - If new adapter is not zero address, it must implement {ISablierBobAdapter} interface.
209
+ ///
210
+ /// @param token The token address to set the adapter for.
211
+ /// @param newAdapter The address of the new adapter.
212
+ function setDefaultAdapter(IERC20 token, ISablierBobAdapter newAdapter) external;
213
+
214
+ /// @notice Sets the native token address. Once set, it cannot be changed.
215
+ /// @dev For more information, see the documentation for {nativeToken}.
216
+ ///
217
+ /// Emits a {SetNativeToken} event.
218
+ ///
219
+ /// Requirements:
220
+ /// - `msg.sender` must be the comptroller.
221
+ /// - `newNativeToken` must not be zero address.
222
+ /// - The native token must not be already set.
223
+ /// @param newNativeToken The address of the native token.
224
+ function setNativeToken(address newNativeToken) external;
225
+
226
+ /// @notice Fetches the latest price from the oracle set for a vault and updates it in the vault storage.
227
+ ///
228
+ /// @dev Emits a {SyncPriceFromOracle} event.
229
+ ///
230
+ /// Notes:
231
+ /// - Oracle staleness is not validated on-chain when calling this function. Any price returned by the oracle is
232
+ /// accepted.
233
+ /// - Useful for syncing the price from oracle without calling {redeem} or {enter}. This function can be called by
234
+ /// anyone to settle vault when the price is above the target price.
235
+ ///
236
+ /// Requirements:
237
+ /// - `vaultId` must not reference a null vault.
238
+ /// - The vault must have ACTIVE status.
239
+ /// - The oracle must return a positive price.
240
+ ///
241
+ /// @param vaultId The ID of the vault to sync.
242
+ /// @return latestPrice The latest price fetched from the oracle, denominated in Chainlink's 8-decimal format for
243
+ /// USD prices, where 1e8 is $1.
244
+ function syncPriceFromOracle(uint256 vaultId) external returns (uint128 latestPrice);
245
+
246
+ /// @notice Unstake all tokens from the adapter for a given vault.
247
+ ///
248
+ /// @dev Emits an {UnstakeFromAdapter} event.
249
+ ///
250
+ /// Requirements:
251
+ /// - `vaultId` must not reference a null vault.
252
+ /// - The adapter set in the vault must not be zero address.
253
+ /// - Either block timestamp must be greater than or equal to the vault expiry or the latest price from the oracle
254
+ /// must be greater than or equal to the target price.
255
+ /// - The vault must not have been unstaked already.
256
+ /// - The amount staked must be greater than zero.
257
+ ///
258
+ /// @param vaultId The ID of the vault.
259
+ /// @return amountReceivedFromAdapter The amount of tokens received from the adapter.
260
+ function unstakeTokensViaAdapter(uint256 vaultId) external returns (uint128 amountReceivedFromAdapter);
261
+ }