This repository was archived by the owner on Jan 25, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathStakedCap.sol
More file actions
120 lines (103 loc) · 4.75 KB
/
Copy pathStakedCap.sol
File metadata and controls
120 lines (103 loc) · 4.75 KB
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
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import { Access } from "../access/Access.sol";
import { IStakedCap } from "../interfaces/IStakedCap.sol";
import { StakedCapStorageUtils } from "../storage/StakedCapStorageUtils.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC20PermitUpgradeable } from
"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {
ERC20Upgradeable,
ERC4626Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/// @title Staked Cap Token
/// @author kexley, Cap Labs
/// @notice Slow releasing yield-bearing token that distributes the yield accrued from agents
/// borrowing from the underlying assets.
/// @dev Calling notify permissionlessly will start the linear unlock
contract StakedCap is
IStakedCap,
UUPSUpgradeable,
ERC4626Upgradeable,
ERC20PermitUpgradeable,
Access,
StakedCapStorageUtils
{
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @inheritdoc IStakedCap
function initialize(address _accessControl, address _asset, uint256 _lockDuration) external initializer {
string memory _name = string.concat("Staked ", IERC20Metadata(_asset).name());
string memory _symbol = string.concat("st", IERC20Metadata(_asset).symbol());
__ERC4626_init(IERC20(_asset));
__ERC20_init(_name, _symbol);
__ERC20Permit_init(_name);
__Access_init(_accessControl);
__UUPSUpgradeable_init();
getStakedCapStorage().lockDuration = _lockDuration;
}
/// @inheritdoc IStakedCap
function notify() external {
StakedCapStorage storage $ = getStakedCapStorage();
if ($.lastNotify + $.lockDuration > block.timestamp) revert StillVesting();
uint256 total = IERC20(asset()).balanceOf(address(this));
if (total > $.storedTotal) {
uint256 diff = total - $.storedTotal;
$.totalLocked = diff;
$.storedTotal = total;
$.lastNotify = block.timestamp;
emit Notify(msg.sender, diff);
}
}
/// @inheritdoc ERC4626Upgradeable
function decimals() public view override(ERC20Upgradeable, ERC4626Upgradeable) returns (uint8 _decimals) {
_decimals = ERC4626Upgradeable.decimals();
}
/// @inheritdoc IStakedCap
function lastNotify() external view returns (uint256 _lastNotify) {
_lastNotify = getStakedCapStorage().lastNotify;
}
/// @inheritdoc IStakedCap
function lockDuration() external view returns (uint256 _lockDuration) {
_lockDuration = getStakedCapStorage().lockDuration;
}
/// @inheritdoc IStakedCap
function lockedProfit() public view returns (uint256 locked) {
StakedCapStorage storage $ = getStakedCapStorage();
if ($.lockDuration == 0) return 0;
uint256 elapsed = block.timestamp - $.lastNotify;
uint256 remaining = elapsed < $.lockDuration ? $.lockDuration - elapsed : 0;
locked = $.totalLocked * remaining / $.lockDuration;
}
/// @inheritdoc ERC4626Upgradeable
function totalAssets() public view override returns (uint256 total) {
total = getStakedCapStorage().storedTotal - lockedProfit();
}
/// @dev Overridden to update the total assets including unvested tokens
/// @param _caller Caller of the deposit
/// @param _receiver Receiver of the staked cap tokens
/// @param _assets Amount of cap tokens to pull from the caller
/// @param _shares Amount of staked cap tokens to send to receiver
function _deposit(address _caller, address _receiver, uint256 _assets, uint256 _shares) internal override {
super._deposit(_caller, _receiver, _assets, _shares);
getStakedCapStorage().storedTotal += _assets;
}
/// @dev Overridden to reduce the total assets including unvested tokens
/// @param _caller Caller of the withdrawal
/// @param _receiver Receiver of the cap tokens
/// @param _owner Owner of the staked cap tokens being burnt
/// @param _assets Amount of cap tokens to send to the receiver
/// @param _shares Amount of staked cap tokens to burn from the owner
function _withdraw(address _caller, address _receiver, address _owner, uint256 _assets, uint256 _shares)
internal
override
{
super._withdraw(_caller, _receiver, _owner, _assets, _shares);
getStakedCapStorage().storedTotal -= _assets;
}
/// @inheritdoc UUPSUpgradeable
function _authorizeUpgrade(address) internal view override checkAccess(bytes4(0)) { }
}