文件 1 的 11:Address.sol
pragma solidity 0.7.5;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(
address(this).balance >= amount,
"Address: insufficient balance"
);
(bool success, ) = recipient.call{value: amount}("");
require(
success,
"Address: unable to send value, recipient may have reverted"
);
}
function functionCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
value,
"Address: low-level call with value failed"
);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(
address(this).balance >= value,
"Address: insufficient balance for call"
);
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(
data
);
return _verifyCallResult(success, returndata, errorMessage);
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 weiValue,
string memory errorMessage
) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: weiValue}(
data
);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
function functionStaticCall(
address target,
bytes memory data
) internal view returns (bytes memory) {
return
functionStaticCall(
target,
data,
"Address: low-level static call failed"
);
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return
functionDelegateCall(
target,
data,
"Address: low-level delegate call failed"
);
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
function addressToString(
address _address
) internal pure returns (string memory) {
bytes32 _bytes = bytes32(uint256(_address));
bytes memory HEX = "0123456789abcdef";
bytes memory _addr = new bytes(42);
_addr[0] = "0";
_addr[1] = "x";
for (uint256 i = 0; i < 20; i++) {
_addr[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)];
_addr[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)];
}
return string(_addr);
}
}
文件 2 的 11:IDistributor.sol
pragma solidity 0.7.5;
interface IDistributor {
function nextRewardAt(uint _rate) external view returns (uint);
function nextRewardFor(address _recipient) external view returns (uint);
function distribute() external returns (bool);
function addRecipient(address _recipient, uint _rewardRate) external;
function removeRecipient(uint _index, address _recipient) external;
function setAdjustment(
uint _index,
bool _add,
uint _rate,
uint _target
) external;
function updateCurrentRate(uint _index, uint _rate) external;
}
文件 3 的 11:IERC20.sol
pragma solidity 0.7.5;
interface IERC20 {
function decimals() external view returns (uint8);
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
);
}
文件 4 的 11:IOwnableManagement.sol
pragma solidity 0.7.5;
interface IOwnableManagement {
function manager() external view returns (address);
function renounceManagement() external;
function pushManagement(address newOwner_) external;
function pullManagement() external;
}
文件 5 的 11:ISyncus.sol
pragma solidity 0.7.5;
interface ISyncus {
function burn(uint256 amount) external;
function burnFrom(address account_, uint256 amount_) external;
}
文件 6 的 11:IVESYNC.sol
pragma solidity 0.7.5;
interface IVESYNC {
function rebase( uint256 ohmProfit_, uint epoch_) external returns (uint256);
function circulatingSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function gonsForBalance( uint amount ) external view returns ( uint );
function balanceForGons( uint gons ) external view returns ( uint );
function index() external view returns ( uint );
}
文件 7 的 11:IWarmup.sol
pragma solidity 0.7.5;
interface IWarmup {
function retrieve( address staker_, uint amount_ ) external;
}
文件 8 的 11:OwnableManagement.sol
pragma solidity 0.7.5;
import "./lib/IOwnableManagement.sol";
contract OwnableManagement is IOwnableManagement {
address internal _owner;
address internal _newOwner;
event OwnershipPushed(address indexed previousOwner, address indexed newOwner);
event OwnershipPulled(address indexed previousOwner, address indexed newOwner);
constructor () {
_owner = msg.sender;
emit OwnershipPushed( address(0), _owner );
}
function manager() public view override returns (address) {
return _owner;
}
modifier onlyManager() {
require( _owner == msg.sender, "Ownable: caller is not the owner" );
_;
}
function renounceManagement() public virtual override onlyManager() {
emit OwnershipPushed( _owner, address(0) );
_owner = address(0);
}
function pushManagement( address newOwner_ ) public virtual override onlyManager() {
require( newOwner_ != address(0), "Ownable: new owner is the zero address");
emit OwnershipPushed( _owner, newOwner_ );
_newOwner = newOwner_;
}
function pullManagement() public virtual override {
require( msg.sender == _newOwner, "Ownable: must be new owner to pull");
emit OwnershipPulled( _owner, _newOwner );
_owner = _newOwner;
}
}
文件 9 的 11:SafeERC20.sol
pragma solidity 0.7.5;
import "./SafeMath.sol";
import "./Address.sol";
import "./IERC20.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function callOptionalReturn(IERC20 token, bytes memory data) private {
require(address(token).isContract(), "SafeERC20: call to non-contract");
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 10 的 11: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 add32(uint32 a, uint32 b) internal pure returns (uint32) {
uint32 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 sub32(uint32 a, uint32 b) internal pure returns (uint32) {
return sub32(a, b, "SafeMath: subtraction overflow");
}
function sub32(
uint32 a,
uint32 b,
string memory errorMessage
) internal pure returns (uint32) {
require(b <= a, errorMessage);
uint32 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 mul32(uint32 a, uint32 b) internal pure returns (uint32) {
if (a == 0) {
return 0;
}
uint32 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;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
function sqrrt(uint256 a) internal pure returns (uint c) {
if (a > 3) {
c = a;
uint 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;
}
}
function percentageAmount(
uint256 total_,
uint8 percentage_
) internal pure returns (uint256 percentAmount_) {
return div(mul(total_, percentage_), 1000);
}
function substractPercentage(
uint256 total_,
uint8 percentageToSub_
) internal pure returns (uint256 result_) {
return sub(total_, div(mul(total_, percentageToSub_), 1000));
}
function percentageOfTotal(
uint256 part_,
uint256 total_
) internal pure returns (uint256 percent_) {
return div(mul(part_, 100), total_);
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
}
function quadraticPricing(
uint256 payment_,
uint256 multiplier_
) internal pure returns (uint256) {
return sqrrt(mul(multiplier_, payment_));
}
function bondingCurve(
uint256 supply_,
uint256 multiplier_
) internal pure returns (uint256) {
return mul(multiplier_, supply_);
}
}
文件 11 的 11:SyncusStaking.sol
pragma solidity 0.7.5;
import "./lib/SafeMath.sol";
import "./lib/Address.sol";
import "./lib/SafeERC20.sol";
import "./OwnableManagement.sol";
import "./lib/IERC20.sol";
import "./lib/ISyncus.sol";
import "./lib/IVESYNC.sol";
import "./lib/IDistributor.sol";
import "./lib/IWarmup.sol";
contract SyncusStaking is OwnableManagement {
using SafeMath for uint256;
using SafeMath for uint32;
using SafeERC20 for IERC20;
address public immutable Sync;
address public immutable veSync;
struct Epoch {
uint number;
uint distribute;
uint32 length;
uint32 endTime;
}
Epoch public epoch;
address public distributor;
address public locker;
uint public totalBonus;
address public warmupContract;
uint public warmupPeriod;
address taxReceiver;
uint public taxOnStake = 250;
uint public taxOnUnstake = 250;
function setTaxOnStake(uint _taxOnStake) external onlyManager {
require(_taxOnStake <= 10000, "Tax cannot be greater than 100%");
taxOnStake = _taxOnStake;
}
function setTaxOnUnstake(uint _taxOnUnstake) external onlyManager {
require(_taxOnUnstake <= 10000, "Tax cannot be greater than 100%");
taxOnUnstake = _taxOnUnstake;
}
function setTaxReceiver(address _taxReceiver) external onlyManager {
taxReceiver = _taxReceiver;
}
modifier onlyDistributor() {
require(msg.sender == distributor, "Only distributor");
_;
}
constructor(
address _Sync,
address _veSync,
uint32 _epochLength,
uint _firstEpochNumber,
uint32 _firstEpochTime,
address _taxReceiver
) {
require(_Sync != address(0));
Sync = _Sync;
require(_veSync != address(0));
veSync = _veSync;
epoch = Epoch({
length: _epochLength,
number: _firstEpochNumber,
endTime: _firstEpochTime,
distribute: 0
});
taxReceiver = _taxReceiver;
}
struct Claim {
uint deposit;
uint gons;
uint expiry;
bool lock;
}
mapping(address => Claim) public warmupInfo;
function stake(uint _amount, address _recipient) external returns (bool) {
rebase();
uint tax = _amount.mul(taxOnStake).div(10000);
uint amountAfterTax = _amount.sub(tax);
IERC20(Sync).safeTransferFrom(
msg.sender,
address(this),
amountAfterTax
);
IERC20(Sync).safeTransferFrom(msg.sender, taxReceiver, tax);
Claim memory info = warmupInfo[_recipient];
require(!info.lock, "Deposits for account are locked");
warmupInfo[_recipient] = Claim({
deposit: info.deposit.add(amountAfterTax),
gons: info.gons.add(IVESYNC(veSync).gonsForBalance(amountAfterTax)),
expiry: epoch.number.add(warmupPeriod),
lock: false
});
IERC20(veSync).safeTransfer(warmupContract, amountAfterTax);
return true;
}
function claim(address _recipient) public {
Claim memory info = warmupInfo[_recipient];
if (epoch.number >= info.expiry && info.expiry != 0) {
delete warmupInfo[_recipient];
IWarmup(warmupContract).retrieve(
_recipient,
IVESYNC(veSync).balanceForGons(info.gons)
);
}
}
function forfeit() external {
Claim memory info = warmupInfo[msg.sender];
delete warmupInfo[msg.sender];
IWarmup(warmupContract).retrieve(
address(this),
IVESYNC(veSync).balanceForGons(info.gons)
);
IERC20(Sync).safeTransfer(msg.sender, info.deposit);
}
function toggleDepositLock() external {
warmupInfo[msg.sender].lock = !warmupInfo[msg.sender].lock;
}
function unstake(uint _amount, bool _trigger) external {
if (_trigger) {
rebase();
}
IERC20(veSync).safeTransferFrom(msg.sender, address(this), _amount);
uint tax = _amount.mul(taxOnUnstake).div(10000);
uint amountAfterTax = _amount.sub(tax);
IERC20(Sync).safeTransfer(msg.sender, amountAfterTax);
IERC20(Sync).safeTransfer(taxReceiver, tax);
}
function index() public view returns (uint) {
return IVESYNC(veSync).index();
}
function rebase() public {
if (epoch.endTime <= uint32(block.timestamp)) {
IVESYNC(veSync).rebase(epoch.distribute, epoch.number);
epoch.endTime = epoch.endTime.add32(epoch.length);
epoch.number++;
if (distributor != address(0)) {
IDistributor(distributor).distribute();
}
uint balance = contractBalance();
uint staked = IVESYNC(veSync).circulatingSupply();
if (balance <= staked) {
epoch.distribute = 0;
} else {
epoch.distribute = balance.sub(staked);
}
}
}
function contractBalance() public view returns (uint) {
return IERC20(Sync).balanceOf(address(this)).add(totalBonus);
}
function giveLockBonus(uint _amount) external {
require(msg.sender == locker);
totalBonus = totalBonus.add(_amount);
IERC20(veSync).safeTransfer(locker, _amount);
}
function returnLockBonus(uint _amount) external {
require(msg.sender == locker);
totalBonus = totalBonus.sub(_amount);
IERC20(veSync).safeTransferFrom(locker, address(this), _amount);
}
enum CONTRACTS {
DISTRIBUTOR,
WARMUP,
LOCKER
}
function setContract(
CONTRACTS _contract,
address _address
) external onlyManager {
if (_contract == CONTRACTS.DISTRIBUTOR) {
distributor = _address;
} else if (_contract == CONTRACTS.WARMUP) {
require(
warmupContract == address(0),
"Warmup cannot be set more than once"
);
warmupContract = _address;
} else if (_contract == CONTRACTS.LOCKER) {
require(
locker == address(0),
"Locker cannot be set more than once"
);
locker = _address;
}
}
function setWarmup(uint _warmupPeriod) external onlyManager {
warmupPeriod = _warmupPeriod;
}
}
{
"compilationTarget": {
"src/SyncusStaking.sol": "SyncusStaking"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/"
]
}
[{"inputs":[{"internalType":"address","name":"_Sync","type":"address"},{"internalType":"address","name":"_veSync","type":"address"},{"internalType":"uint32","name":"_epochLength","type":"uint32"},{"internalType":"uint256","name":"_firstEpochNumber","type":"uint256"},{"internalType":"uint32","name":"_firstEpochTime","type":"uint32"},{"internalType":"address","name":"_taxReceiver","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipPulled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipPushed","type":"event"},{"inputs":[],"name":"Sync","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"number","type":"uint256"},{"internalType":"uint256","name":"distribute","type":"uint256"},{"internalType":"uint32","name":"length","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forfeit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"giveLockBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"locker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pullManagement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner_","type":"address"}],"name":"pushManagement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceManagement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"returnLockBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum SyncusStaking.CONTRACTS","name":"_contract","type":"uint8"},{"internalType":"address","name":"_address","type":"address"}],"name":"setContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_taxOnStake","type":"uint256"}],"name":"setTaxOnStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_taxOnUnstake","type":"uint256"}],"name":"setTaxOnUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_taxReceiver","type":"address"}],"name":"setTaxReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warmupPeriod","type":"uint256"}],"name":"setWarmup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"stake","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"taxOnStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxOnUnstake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleDepositLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_trigger","type":"bool"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"veSync","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"warmupContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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"}]