Skip to content

Commit 6179287

Browse files
Staking updates (#298)
* more efficient way of storing state for Staking contracts * [M-1] Block gas limit can be exceeded during setTimeUnit() and setRewardsPerUnit() when staker count grows * [C-1] Contract admins can lock staked tokens in the contract * [M-1] revised fix for Staking1155 * [H-1] TokenStake.sol rewards can be over- or under-awarded when the staking and reward tokens have different decimals * [M-2] ERC721 and ERC1155 tokens safe-transferred directly to contract will be locked and unrecoverable * [C-1] revised fix for large rewardsPerUnitTime * [L-1] Incorrect ERC165 implementation for NFTStake and EditionStake * [Q-2] Normalize support for ERC2771 trusted forwarder * [Q-3] Reentrancy init called twice * [Q-5] unitTime and rewardsPerUnitTime setter functions don’t check for new input data * [Q-6] getStakeInfo should be marked as external * [G-1] Halt array iteration after staker removed during withdraw() * [G-2] Loop reading from storage array length * [Q-7] Missing reward balance information * [M-3] TokenStake.sol: Double entry-point ERC20 tokens could be drained from the staking contract * [H-2] TokenStake.sol: Tokens with a tax on transfer will account for inaccurate amounts * virtual functions for bases * docs * v3.2.9 * handle native token * rename to stakingToken * remove beta tag * interfaces Co-authored-by: Krishang <[email protected]>
1 parent 2b3f5e1 commit 6179287

36 files changed

+3621
-543
lines changed

contracts/base/Staking1155Base.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import "../extension/Staking1155.sol";
99
import "../eip/interface/IERC20.sol";
1010

1111
/**
12-
* note: This is a Beta release.
1312
*
1413
* EXTENSION: Staking1155
1514
*
@@ -39,9 +38,9 @@ contract Staking1155Base is ContractMetadata, Multicall, Ownable, Staking1155 {
3938
constructor(
4039
uint256 _defaultTimeUnit,
4140
uint256 _defaultRewardsPerUnitTime,
42-
address _edition,
41+
address _stakingToken,
4342
address _rewardToken
44-
) Staking1155(_edition) {
43+
) Staking1155(_stakingToken) {
4544
_setupOwner(msg.sender);
4645
_setDefaultStakingCondition(_defaultTimeUnit, _defaultRewardsPerUnitTime);
4746

contracts/base/Staking20Base.sol

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import "../eip/interface/IERC20.sol";
1010
import "../eip/interface/IERC20Metadata.sol";
1111

1212
/**
13-
* note: This is a Beta release.
1413
*
1514
* EXTENSION: Staking20
1615
*
@@ -42,8 +41,16 @@ contract Staking20Base is ContractMetadata, Multicall, Ownable, Staking20 {
4241
uint256 _rewardRatioNumerator,
4342
uint256 _rewardRatioDenominator,
4443
address _stakingToken,
45-
address _rewardToken
46-
) Staking20(_stakingToken, IERC20Metadata(_stakingToken).decimals(), IERC20Metadata(_rewardToken).decimals()) {
44+
address _rewardToken,
45+
address _nativeTokenWrapper
46+
)
47+
Staking20(
48+
_nativeTokenWrapper,
49+
_stakingToken,
50+
IERC20Metadata(_stakingToken).decimals(),
51+
IERC20Metadata(_rewardToken).decimals()
52+
)
53+
{
4754
_setupOwner(msg.sender);
4855
_setStakingCondition(_timeUnit, _rewardRatioNumerator, _rewardRatioDenominator);
4956

contracts/base/Staking721Base.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import "../extension/Staking721.sol";
99
import "../eip/interface/IERC20.sol";
1010

1111
/**
12-
* note: This is a Beta release.
1312
*
1413
* EXTENSION: Staking721
1514
*
@@ -39,9 +38,9 @@ contract Staking721Base is ContractMetadata, Multicall, Ownable, Staking721 {
3938
constructor(
4039
uint128 _timeUnit,
4140
uint128 _rewardsPerUnitTime,
42-
address _nftCollection,
41+
address _stakingToken,
4342
address _rewardToken
44-
) Staking721(_nftCollection) {
43+
) Staking721(_stakingToken) {
4544
_setupOwner(msg.sender);
4645
_setStakingCondition(_timeUnit, _rewardsPerUnitTime);
4746

contracts/extension/Staking1155.sol

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,13 @@ import "../eip/interface/IERC1155.sol";
77

88
import "./interface/IStaking1155.sol";
99

10-
/**
11-
* note: This is a Beta release.
12-
*/
13-
1410
abstract contract Staking1155 is ReentrancyGuard, IStaking1155 {
1511
/*///////////////////////////////////////////////////////////////
1612
State variables / Mappings
1713
//////////////////////////////////////////////////////////////*/
1814

1915
///@dev Address of ERC1155 contract -- staked tokens belong to this contract.
20-
address public edition;
16+
address public stakingToken;
2117

2218
/// @dev Flag to check direct transfers of staking tokens.
2319
uint8 internal isStaking = 1;
@@ -46,9 +42,9 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 {
4642
/// @dev Mapping from token-id to list of accounts that have staked that token-id.
4743
mapping(uint256 => address[]) public stakersArray;
4844

49-
constructor(address _edition) ReentrancyGuard() {
50-
require(address(_edition) != address(0), "address 0");
51-
edition = _edition;
45+
constructor(address _stakingToken) ReentrancyGuard() {
46+
require(address(_stakingToken) != address(0), "address 0");
47+
stakingToken = _stakingToken;
5248
}
5349

5450
/*///////////////////////////////////////////////////////////////
@@ -270,7 +266,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 {
270266
/// @dev Staking logic. Override to add custom logic.
271267
function _stake(uint256 _tokenId, uint256 _amount) internal virtual {
272268
require(_amount != 0, "Staking 0 tokens");
273-
address _edition = edition;
269+
address _stakingToken = stakingToken;
274270

275271
if (stakers[_tokenId][_stakeMsgSender()].amountStaked > 0) {
276272
_updateUnclaimedRewardsForStaker(_tokenId, _stakeMsgSender());
@@ -285,12 +281,12 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 {
285281
}
286282

287283
require(
288-
IERC1155(_edition).balanceOf(_stakeMsgSender(), _tokenId) >= _amount &&
289-
IERC1155(_edition).isApprovedForAll(_stakeMsgSender(), address(this)),
284+
IERC1155(_stakingToken).balanceOf(_stakeMsgSender(), _tokenId) >= _amount &&
285+
IERC1155(_stakingToken).isApprovedForAll(_stakeMsgSender(), address(this)),
290286
"Not balance or approved"
291287
);
292288
isStaking = 2;
293-
IERC1155(_edition).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, "");
289+
IERC1155(_stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, "");
294290
isStaking = 1;
295291
// stakerAddress[_tokenIds[i]] = _stakeMsgSender();
296292
stakers[_tokenId][_stakeMsgSender()].amountStaked += _amount;
@@ -323,7 +319,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 {
323319
}
324320
stakers[_tokenId][_stakeMsgSender()].amountStaked -= _amount;
325321

326-
IERC1155(edition).safeTransferFrom(address(this), _stakeMsgSender(), _tokenId, _amount, "");
322+
IERC1155(stakingToken).safeTransferFrom(address(this), _stakeMsgSender(), _tokenId, _amount, "");
327323

328324
emit TokensWithdrawn(_stakeMsgSender(), _tokenId, _amount);
329325
}

contracts/extension/Staking1155Upgradeable.sol

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,13 @@ import "../eip/interface/IERC1155.sol";
77

88
import "./interface/IStaking1155.sol";
99

10-
/**
11-
* note: This is a Beta release.
12-
*/
13-
1410
abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking1155 {
1511
/*///////////////////////////////////////////////////////////////
1612
State variables / Mappings
1713
//////////////////////////////////////////////////////////////*/
1814

1915
///@dev Address of ERC1155 contract -- staked tokens belong to this contract.
20-
address public edition;
16+
address public stakingToken;
2117

2218
/// @dev Flag to check direct transfers of staking tokens.
2319
uint8 internal isStaking = 1;
@@ -46,11 +42,11 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking
4642
/// @dev Mapping from token-id to list of accounts that have staked that token-id.
4743
mapping(uint256 => address[]) public stakersArray;
4844

49-
function __Staking1155_init(address _edition) internal onlyInitializing {
45+
function __Staking1155_init(address _stakingToken) internal onlyInitializing {
5046
__ReentrancyGuard_init();
5147

52-
require(address(_edition) != address(0), "address 0");
53-
edition = _edition;
48+
require(address(_stakingToken) != address(0), "address 0");
49+
stakingToken = _stakingToken;
5450
}
5551

5652
/*///////////////////////////////////////////////////////////////
@@ -272,7 +268,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking
272268
/// @dev Staking logic. Override to add custom logic.
273269
function _stake(uint256 _tokenId, uint256 _amount) internal virtual {
274270
require(_amount != 0, "Staking 0 tokens");
275-
address _edition = edition;
271+
address _stakingToken = stakingToken;
276272

277273
if (stakers[_tokenId][_stakeMsgSender()].amountStaked > 0) {
278274
_updateUnclaimedRewardsForStaker(_tokenId, _stakeMsgSender());
@@ -287,12 +283,12 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking
287283
}
288284

289285
require(
290-
IERC1155(_edition).balanceOf(_stakeMsgSender(), _tokenId) >= _amount &&
291-
IERC1155(_edition).isApprovedForAll(_stakeMsgSender(), address(this)),
286+
IERC1155(_stakingToken).balanceOf(_stakeMsgSender(), _tokenId) >= _amount &&
287+
IERC1155(_stakingToken).isApprovedForAll(_stakeMsgSender(), address(this)),
292288
"Not balance or approved"
293289
);
294290
isStaking = 2;
295-
IERC1155(_edition).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, "");
291+
IERC1155(_stakingToken).safeTransferFrom(_stakeMsgSender(), address(this), _tokenId, _amount, "");
296292
isStaking = 1;
297293
// stakerAddress[_tokenIds[i]] = _stakeMsgSender();
298294
stakers[_tokenId][_stakeMsgSender()].amountStaked += _amount;
@@ -325,7 +321,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking
325321
}
326322
stakers[_tokenId][_stakeMsgSender()].amountStaked -= _amount;
327323

328-
IERC1155(edition).safeTransferFrom(address(this), _stakeMsgSender(), _tokenId, _amount, "");
324+
IERC1155(stakingToken).safeTransferFrom(address(this), _stakeMsgSender(), _tokenId, _amount, "");
329325

330326
emit TokensWithdrawn(_stakeMsgSender(), _tokenId, _amount);
331327
}

contracts/extension/Staking20.sol

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,16 @@ import "../lib/CurrencyTransferLib.sol";
88

99
import "./interface/IStaking20.sol";
1010

11-
/**
12-
* note: This is a Beta release.
13-
*/
14-
1511
abstract contract Staking20 is ReentrancyGuard, IStaking20 {
1612
/*///////////////////////////////////////////////////////////////
1713
State variables / Mappings
1814
//////////////////////////////////////////////////////////////*/
1915

16+
/// @dev The address of the native token wrapper contract.
17+
address internal immutable nativeTokenWrapper;
18+
2019
///@dev Address of ERC20 contract -- staked tokens belong to this contract.
21-
address public token;
20+
address public stakingToken;
2221

2322
/// @dev Decimals of staking token.
2423
uint256 public stakingTokenDecimals;
@@ -42,14 +41,16 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 {
4241
mapping(uint256 => StakingCondition) private stakingConditions;
4342

4443
constructor(
45-
address _token,
44+
address _nativeTokenWrapper,
45+
address _stakingToken,
4646
uint256 _stakingTokenDecimals,
4747
uint256 _rewardTokenDecimals
4848
) ReentrancyGuard() {
49-
require(address(_token) != address(0), "address 0");
49+
require(_stakingToken != address(0) && _nativeTokenWrapper != address(0), "address 0");
5050
require(_stakingTokenDecimals != 0 && _rewardTokenDecimals != 0, "decimals 0");
5151

52-
token = _token;
52+
nativeTokenWrapper = _nativeTokenWrapper;
53+
stakingToken = _stakingToken;
5354
stakingTokenDecimals = _stakingTokenDecimals;
5455
rewardTokenDecimals = _rewardTokenDecimals;
5556
}
@@ -65,7 +66,7 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 {
6566
*
6667
* @param _amount Amount to stake.
6768
*/
68-
function stake(uint256 _amount) external nonReentrant {
69+
function stake(uint256 _amount) external payable nonReentrant {
6970
_stake(_amount);
7071
}
7172

@@ -170,7 +171,14 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 {
170171
/// @dev Staking logic. Override to add custom logic.
171172
function _stake(uint256 _amount) internal virtual {
172173
require(_amount != 0, "Staking 0 tokens");
173-
address _token = token;
174+
175+
address _stakingToken;
176+
if (stakingToken == CurrencyTransferLib.NATIVE_TOKEN) {
177+
_stakingToken = nativeTokenWrapper;
178+
} else {
179+
require(msg.value == 0, "Value not 0");
180+
_stakingToken = stakingToken;
181+
}
174182

175183
if (stakers[_stakeMsgSender()].amountStaked > 0) {
176184
_updateUnclaimedRewardsForStaker(_stakeMsgSender());
@@ -180,9 +188,15 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 {
180188
stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1;
181189
}
182190

183-
uint256 balanceBefore = IERC20(_token).balanceOf(address(this));
184-
CurrencyTransferLib.transferCurrency(_token, _stakeMsgSender(), address(this), _amount);
185-
uint256 actualAmount = IERC20(_token).balanceOf(address(this)) - balanceBefore;
191+
uint256 balanceBefore = IERC20(_stakingToken).balanceOf(address(this));
192+
CurrencyTransferLib.transferCurrencyWithWrapper(
193+
stakingToken,
194+
_stakeMsgSender(),
195+
address(this),
196+
_amount,
197+
nativeTokenWrapper
198+
);
199+
uint256 actualAmount = IERC20(_stakingToken).balanceOf(address(this)) - balanceBefore;
186200

187201
stakers[_stakeMsgSender()].amountStaked += actualAmount;
188202
stakingTokenBalance += actualAmount;
@@ -211,7 +225,13 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 {
211225
stakers[_stakeMsgSender()].amountStaked -= _amount;
212226
stakingTokenBalance -= _amount;
213227

214-
CurrencyTransferLib.transferCurrency(token, address(this), _stakeMsgSender(), _amount);
228+
CurrencyTransferLib.transferCurrencyWithWrapper(
229+
stakingToken,
230+
address(this),
231+
_stakeMsgSender(),
232+
_amount,
233+
nativeTokenWrapper
234+
);
215235

216236
emit TokensWithdrawn(_stakeMsgSender(), _amount);
217237
}

0 commit comments

Comments
 (0)