文件 1 的 9:BlackDAOAccessControlled.sol
pragma solidity >=0.7.5;
import "../interfaces/IBlackDAOAuthority.sol";
abstract contract BlackDAOAccessControlled {
event AuthorityUpdated(IBlackDAOAuthority indexed authority);
string UNAUTHORIZED = "UNAUTHORIZED";
IBlackDAOAuthority public authority;
constructor(IBlackDAOAuthority _authority) {
authority = _authority;
emit AuthorityUpdated(_authority);
}
modifier onlyGovernor() {
require(msg.sender == authority.governor(), UNAUTHORIZED);
_;
}
modifier onlyGuardian() {
require(msg.sender == authority.guardian(), UNAUTHORIZED);
_;
}
modifier onlyPolicy() {
require(msg.sender == authority.policy(), UNAUTHORIZED);
_;
}
modifier onlyVault() {
require(msg.sender == authority.vault(), UNAUTHORIZED);
_;
}
function setAuthority(IBlackDAOAuthority _newAuthority) external onlyGovernor {
authority = _newAuthority;
emit AuthorityUpdated(_newAuthority);
}
}
文件 2 的 9:IBlackDAOAuthority.sol
pragma solidity >=0.7.5;
interface IBlackDAOAuthority {
event GovernorPushed(address indexed from, address indexed to, bool _effectiveImmediately);
event GuardianPushed(address indexed from, address indexed to, bool _effectiveImmediately);
event PolicyPushed(address indexed from, address indexed to, bool _effectiveImmediately);
event VaultPushed(address indexed from, address indexed to, bool _effectiveImmediately);
event GovernorPulled(address indexed from, address indexed to);
event GuardianPulled(address indexed from, address indexed to);
event PolicyPulled(address indexed from, address indexed to);
event VaultPulled(address indexed from, address indexed to);
function governor() external view returns (address);
function guardian() external view returns (address);
function policy() external view returns (address);
function vault() external view returns (address);
}
文件 3 的 9:IDistributor.sol
pragma solidity >=0.7.5;
interface IDistributor {
function distribute() external;
function bounty() external view returns (uint256);
function retrieveBounty() external returns (uint256);
function nextRewardAt(uint256 _rate) external view returns (uint256);
function nextRewardFor(address _recipient) external view returns (uint256);
function setBounty(uint256 _bounty) external;
function addRecipient(address _recipient, uint256 _rewardRate) external;
function removeRecipient(uint256 _index) external;
function setAdjustment(
uint256 _index,
bool _add,
uint256 _rate,
uint256 _target
) external;
}
文件 4 的 9:IERC20.sol
pragma solidity >=0.7.5;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 9:IgBLKD.sol
pragma solidity >=0.7.5;
import "./IERC20.sol";
interface IgBLKD is IERC20 {
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
function index() external view returns (uint256);
function balanceFrom(uint256 _amount) external view returns (uint256);
function balanceTo(uint256 _amount) external view returns (uint256);
function migrate(address _staking, address _sBLKD) external;
}
文件 6 的 9:IsBLKD.sol
pragma solidity >=0.7.5;
import "./IERC20.sol";
interface IsBLKD is IERC20 {
function rebase(uint256 blkdProfit_, uint256 epoch_) external returns (uint256);
function circulatingSupply() external view returns (uint256);
function gonsForBalance(uint256 amount) external view returns (uint256);
function balanceForGons(uint256 gons) external view returns (uint256);
function index() external view returns (uint256);
function toG(uint256 amount) external view returns (uint256);
function fromG(uint256 amount) external view returns (uint256);
function changeDebt(
uint256 amount,
address debtor,
bool add
) external;
function debtBalances(address _address) external view returns (uint256);
}
文件 7 的 9:SafeERC20.sol
pragma solidity >=0.7.5;
import {IERC20} from "../interfaces/IERC20.sol";
library SafeERC20 {
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
}
function safeTransfer(
IERC20 token,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(IERC20.transfer.selector, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
}
function safeApprove(
IERC20 token,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(IERC20.approve.selector, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED");
}
function safeTransferETH(address to, uint256 amount) internal {
(bool success, ) = to.call{value: amount}(new bytes(0));
require(success, "ETH_TRANSFER_FAILED");
}
}
文件 8 的 9:SafeMath.sol
pragma solidity ^0.7.5;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
assert(a == b * c + (a % b));
return c;
}
function sqrrt(uint256 a) internal pure returns (uint256 c) {
if (a > 3) {
c = a;
uint256 b = add(div(a, 2), 1);
while (b < c) {
c = b;
b = div(add(div(a, b), b), 2);
}
} else if (a != 0) {
c = 1;
}
}
}
文件 9 的 9:Staking.sol
pragma solidity ^0.7.5;
import "./libraries/SafeMath.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IsBLKD.sol";
import "./interfaces/IgBLKD.sol";
import "./interfaces/IDistributor.sol";
import "./types/BlackDAOAccessControlled.sol";
contract BlackDAOStaking is BlackDAOAccessControlled {
using SafeMath for uint256;
using SafeERC20 for IERC20;
using SafeERC20 for IsBLKD;
using SafeERC20 for IgBLKD;
event DistributorSet(address distributor);
event WarmupSet(uint256 warmup);
struct Epoch {
uint256 length;
uint256 number;
uint256 end;
uint256 distribute;
}
struct Claim {
uint256 deposit;
uint256 gons;
uint256 expiry;
bool lock;
}
IERC20 public immutable BLKD;
IsBLKD public immutable sBLKD;
IgBLKD public immutable gBLKD;
Epoch public epoch;
IDistributor public distributor;
mapping(address => Claim) public warmupInfo;
uint256 public warmupPeriod;
uint256 private gonsInWarmup;
constructor(
address _blkd,
address _sBLKD,
address _gBLKD,
uint256 _epochLength,
uint256 _firstEpochNumber,
uint256 _firstEpochTime,
address _authority
) BlackDAOAccessControlled(IBlackDAOAuthority(_authority)) {
require(_blkd != address(0), "Zero address: BLKD");
BLKD = IERC20(_blkd);
require(_sBLKD != address(0), "Zero address: sBLKD");
sBLKD = IsBLKD(_sBLKD);
require(_gBLKD != address(0), "Zero address: gBLKD");
gBLKD = IgBLKD(_gBLKD);
epoch = Epoch({length: _epochLength, number: _firstEpochNumber, end: _firstEpochTime, distribute: 0});
}
function stake(
address _to,
uint256 _amount,
bool _rebasing,
bool _claim
) external returns (uint256) {
BLKD.safeTransferFrom(msg.sender, address(this), _amount);
_amount = _amount.add(rebase());
if (_claim && warmupPeriod == 0) {
return _send(_to, _amount, _rebasing);
} else {
Claim memory info = warmupInfo[_to];
if (!info.lock) {
require(_to == msg.sender, "External deposits for account are locked");
}
warmupInfo[_to] = Claim({
deposit: info.deposit.add(_amount),
gons: info.gons.add(sBLKD.gonsForBalance(_amount)),
expiry: epoch.number.add(warmupPeriod),
lock: info.lock
});
gonsInWarmup = gonsInWarmup.add(sBLKD.gonsForBalance(_amount));
return _amount;
}
}
function claim(address _to, bool _rebasing) public returns (uint256) {
Claim memory info = warmupInfo[_to];
if (!info.lock) {
require(_to == msg.sender, "External claims for account are locked");
}
if (epoch.number >= info.expiry && info.expiry != 0) {
delete warmupInfo[_to];
gonsInWarmup = gonsInWarmup.sub(info.gons);
return _send(_to, sBLKD.balanceForGons(info.gons), _rebasing);
}
return 0;
}
function forfeit() external returns (uint256) {
Claim memory info = warmupInfo[msg.sender];
delete warmupInfo[msg.sender];
gonsInWarmup = gonsInWarmup.sub(info.gons);
BLKD.safeTransfer(msg.sender, info.deposit);
return info.deposit;
}
function toggleLock() external {
warmupInfo[msg.sender].lock = !warmupInfo[msg.sender].lock;
}
function unstake(
address _to,
uint256 _amount,
bool _trigger,
bool _rebasing
) external returns (uint256 amount_) {
amount_ = _amount;
uint256 bounty;
if (_trigger) {
bounty = rebase();
}
if (_rebasing) {
sBLKD.safeTransferFrom(msg.sender, address(this), _amount);
amount_ = amount_.add(bounty);
} else {
gBLKD.burn(msg.sender, _amount);
amount_ = gBLKD.balanceFrom(amount_).add(bounty);
}
require(amount_ <= BLKD.balanceOf(address(this)), "Insufficient BLKD balance in contract");
BLKD.safeTransfer(_to, amount_);
}
function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_) {
sBLKD.safeTransferFrom(msg.sender, address(this), _amount);
gBalance_ = gBLKD.balanceTo(_amount);
gBLKD.mint(_to, gBalance_);
}
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_) {
gBLKD.burn(msg.sender, _amount);
sBalance_ = gBLKD.balanceFrom(_amount);
sBLKD.safeTransfer(_to, sBalance_);
}
function rebase() public returns (uint256) {
uint256 bounty;
if (epoch.end <= block.timestamp) {
sBLKD.rebase(epoch.distribute, epoch.number);
epoch.end = epoch.end.add(epoch.length);
epoch.number++;
if (address(distributor) != address(0)) {
distributor.distribute();
bounty = distributor.retrieveBounty();
}
uint256 balance = BLKD.balanceOf(address(this));
uint256 staked = sBLKD.circulatingSupply();
if (balance <= staked.add(bounty)) {
epoch.distribute = 0;
} else {
epoch.distribute = balance.sub(staked).sub(bounty);
}
}
return bounty;
}
function _send(
address _to,
uint256 _amount,
bool _rebasing
) internal returns (uint256) {
if (_rebasing) {
sBLKD.safeTransfer(_to, _amount);
return _amount;
} else {
gBLKD.mint(_to, gBLKD.balanceTo(_amount));
return gBLKD.balanceTo(_amount);
}
}
function index() public view returns (uint256) {
return sBLKD.index();
}
function supplyInWarmup() public view returns (uint256) {
return sBLKD.balanceForGons(gonsInWarmup);
}
function secondsToNextEpoch() external view returns (uint256) {
return epoch.end.sub(block.timestamp);
}
function setDistributor(address _distributor) external onlyGovernor {
distributor = IDistributor(_distributor);
emit DistributorSet(_distributor);
}
function setWarmupLength(uint256 _warmupPeriod) external onlyGovernor {
warmupPeriod = _warmupPeriod;
emit WarmupSet(_warmupPeriod);
}
}
{
"compilationTarget": {
"Staking.sol": "BlackDAOStaking"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_blkd","type":"address"},{"internalType":"address","name":"_sBLKD","type":"address"},{"internalType":"address","name":"_gBLKD","type":"address"},{"internalType":"uint256","name":"_epochLength","type":"uint256"},{"internalType":"uint256","name":"_firstEpochNumber","type":"uint256"},{"internalType":"uint256","name":"_firstEpochTime","type":"uint256"},{"internalType":"address","name":"_authority","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IBlackDAOAuthority","name":"authority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"distributor","type":"address"}],"name":"DistributorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warmup","type":"uint256"}],"name":"WarmupSet","type":"event"},{"inputs":[],"name":"BLKD","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract IBlackDAOAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"bool","name":"_rebasing","type":"bool"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"contract IDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"length","type":"uint256"},{"internalType":"uint256","name":"number","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"uint256","name":"distribute","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forfeit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gBLKD","outputs":[{"internalType":"contract IgBLKD","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sBLKD","outputs":[{"internalType":"contract IsBLKD","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"secondsToNextEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IBlackDAOAuthority","name":"_newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_distributor","type":"address"}],"name":"setDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warmupPeriod","type":"uint256"}],"name":"setWarmupLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_rebasing","type":"bool"},{"internalType":"bool","name":"_claim","type":"bool"}],"name":"stake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyInWarmup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_trigger","type":"bool"},{"internalType":"bool","name":"_rebasing","type":"bool"}],"name":"unstake","outputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unwrap","outputs":[{"internalType":"uint256","name":"sBalance_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"warmupInfo","outputs":[{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"uint256","name":"gons","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"warmupPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"wrap","outputs":[{"internalType":"uint256","name":"gBalance_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]