Skip to content

Instantly share code, notes, and snippets.

@eMarchenko
Created April 26, 2022 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eMarchenko/e773b49bf9a60ef12d91823706b5b185 to your computer and use it in GitHub Desktop.
Save eMarchenko/e773b49bf9a60ef12d91823706b5b185 to your computer and use it in GitHub Desktop.
Prototype of an efficient staking smart contract
/*
Warning! The code is not production ready. It is provided for educational purposes only.
*/
pragma solidity ^0.8.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract Staking is ERC20 {
using SafeERC20 for IERC20;
// the contract lacks over- and underflow checks on type casts.
// we recommend using a library to safely switch between signed and unsigned integers.
// potentially the same token
IERC20 public immutable stakedToken;
IERC20 public immutable rewardToken;
uint public rewardPerToken;
mapping(address => int256) private corrections;
constructor(IERC20 s, IERC20 r) ERC20("StakingToken", "stkn") {
stakedToken = s;
rewardToken = r;
}
function deposit(uint amount) external {
_mint(msg.sender, amount);
stakedToken.safeTransferFrom(msg.sender, address(this), amount);
}
function withdraw(uint amount) external {
_burn(msg.sender, amount);
stakedToken.safeTransfer(msg.sender, amount);
}
function addReward(uint amount) external {
// do smth if totalSupply == 0
rewardPerToken += amount * 1e18 / totalSupply(); // 1e18 prevents rounding to 0 for smaller rewards
rewardToken.safeTransferFrom(msg.sender, address(this), amount);
}
function reward() public view returns (int) {
return (int(rewardPerToken * balanceOf(msg.sender)) + corrections[msg.sender]) ;
}
function claimReward() external {
int reward = reward();
corrections[msg.sender] -= reward;
rewardToken.safeTransfer(msg.sender, uint(reward / 1e18));
}
// the function is called from `_mint`, `_burn`, and `_transfer`
// we use it to manage reward corrections
function _beforeTokenTransfer(address from, address to, uint256 amount ) internal override virtual {
if (from != address(0)) {
corrections[from] += int(rewardPerToken*amount);
}
if (to != address(0)) {
corrections[to] -= int(rewardPerToken*amount);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment