文件 1 的 1:DoraID.sol
pragma solidity 0.8.2;
interface IERC20 {
function balanceOf(address who) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}
contract DoraID {
using SafeMath for uint256;
IERC20 public DORAYAKI;
uint256 constant public MIN_AUTH_STAKING = 10 ether;
uint256 constant public MIN_AUTH_DURATION = 30 days;
uint256 constant public FORCE_QUIT_THRESHOLD = 10000 ether;
uint256 constant public FORCE_QUIT_DURATION = 30 days;
uint256 constant public ACTIVATION_FEE = 10 ether * 10 days;
uint256 constant public MAX_STORED_POS = 20 * ACTIVATION_FEE;
struct UserInfo {
address parent;
address[] children;
bool authenticated;
uint256 stakingAmount;
uint256 stakingEndTime;
uint256 proofOfStake;
uint256 lastSeen;
}
mapping(address => UserInfo) internal _users;
mapping(address => uint256) internal _tips;
mapping(address => address) internal _entrusteds;
bool private _rentrancyLock;
event Activate(address indexed _parent, address indexed _child);
event Stake(address indexed _user, uint256 _totalAmount, uint256 _endTime);
constructor(IERC20 _dora, address[] memory _initUserList, uint256[] memory _initPOSList) {
DORAYAKI = _dora;
require(_initUserList.length == _initPOSList.length, "Parameter array length mismatch");
for (uint256 i = 0; i < _initUserList.length; i++) {
address addr = _initUserList[i];
uint256 POS = _initPOSList[i];
_users[addr].proofOfStake = POS;
_activate(address(this), addr);
}
}
modifier nonReentrant() {
require(!_rentrancyLock, "Reentrant error");
_rentrancyLock = true;
_;
_rentrancyLock = false;
}
function statusOf(address _user) public view returns (bool authenticated, uint256 stakingAmount, uint256 stakingEndTime) {
UserInfo storage user = _users[_user];
authenticated = user.authenticated;
stakingAmount = user.stakingAmount;
stakingEndTime = user.stakingEndTime;
}
function proofOf(address _user) public view returns (uint256 proof) {
UserInfo storage user = _users[_user];
if (!user.authenticated) {
return 0;
}
proof = user.proofOfStake.add((block.timestamp.sub(user.lastSeen)).mul(user.stakingAmount));
if (user.proofOfStake <= MAX_STORED_POS && proof > MAX_STORED_POS) {
proof = MAX_STORED_POS;
}
}
function tipOf(address _user) public view returns (uint256 tip, address entrusted) {
tip = _tips[_user];
entrusted = _entrusteds[_user];
}
function parentOf(address _user) public view returns (address) {
return _users[_user].parent;
}
function childrenSizeOf(address _user) public view returns (uint256) {
return _users[_user].children.length;
}
function childOf(address _user, uint256 _index) public view returns (address) {
uint256 size = childrenSizeOf(_user);
require(_index < size, "Overflow");
return _users[_user].children[_index];
}
function stake(uint256 _amount, uint256 _endTime, uint256 _tip, address _entrusted) external {
uint256 tip = _tips[msg.sender];
if (tip > 0) {
_tips[msg.sender] = 0;
require(DORAYAKI.transfer(msg.sender, tip), "ERC20 transfer error");
}
_tips[msg.sender] = _tip;
require(DORAYAKI.transferFrom(msg.sender, address(this), _tip), "ERC20 transfer error");
_entrusteds[msg.sender] = _entrusted;
stake(_amount, _endTime);
}
function stake(uint256 _amount, uint256 _endTime) public nonReentrant {
require(DORAYAKI.transferFrom(msg.sender, address(this), _amount), "ERC20 transfer error");
UserInfo storage user = _users[msg.sender];
_updatePOS(user);
require(user.stakingEndTime <= _endTime || user.stakingAmount == 0, "Can not set an earlier staking time");
uint256 totalStaking = user.stakingAmount.add(_amount);
user.stakingAmount = totalStaking;
user.stakingEndTime = _endTime;
emit Stake(msg.sender, totalStaking, _endTime);
}
function unstake(uint256 _amount) external {
unstake(_amount, 0);
}
function unstake(uint256 _amount, uint256 _endTime) public nonReentrant {
UserInfo storage user = _users[msg.sender];
_updatePOS(user);
require(user.stakingEndTime < block.timestamp, "Unfinished staking");
require(user.stakingEndTime <= _endTime || _endTime == 0, "Can not set an earlier staking time");
uint256 tip = _tips[msg.sender];
if (tip > 0) {
_tips[msg.sender] = 0;
require(DORAYAKI.transfer(msg.sender, tip), "ERC20 transfer error");
}
uint256 remainder = user.stakingAmount.sub(_amount);
user.stakingAmount = remainder;
user.stakingEndTime = _endTime;
require(DORAYAKI.transfer(msg.sender, _amount), "ERC20 transfer error");
emit Stake(msg.sender, remainder, _endTime);
}
function activate(address _newUser, uint256 _withTip) external {
uint256 tip = _tips[_newUser];
require(tip >= _withTip, "The current tip is lower than expected");
activate(_newUser);
}
function activate(address _newUser) public nonReentrant {
UserInfo storage user = _users[msg.sender];
_updatePOS(user);
UserInfo storage newUser = _users[_newUser];
address entrusted = _entrusteds[_newUser];
require(entrusted == address(0) || entrusted == msg.sender, "The new user specifies the activator");
require(user.authenticated, "No permission");
require(user.proofOfStake >= ACTIVATION_FEE, "Insufficient POS");
user.proofOfStake -= ACTIVATION_FEE;
require(!newUser.authenticated, "The user has been activated");
require(newUser.stakingAmount >= MIN_AUTH_STAKING, "User's staking amount is not up to standard");
require(newUser.stakingEndTime >= block.timestamp.add(MIN_AUTH_DURATION), "User's staking time is not up to standard");
uint256 tip = _tips[_newUser];
if (tip > 0) {
_tips[_newUser] = 0;
DORAYAKI.transfer(msg.sender, tip);
}
newUser.lastSeen = block.timestamp;
_activate(msg.sender, _newUser);
}
function forceQuit() external {
UserInfo storage user = _users[msg.sender];
_updatePOS(user);
require(user.stakingAmount >= FORCE_QUIT_THRESHOLD, "Staking amount is not up to standard");
user.stakingEndTime = block.timestamp + FORCE_QUIT_DURATION;
emit Stake(msg.sender, user.stakingAmount, user.stakingEndTime);
}
function _activate(address _parent, address _child) internal {
_users[_parent].children.push(_child);
_users[_child].parent = _parent;
_users[_child].authenticated = true;
emit Activate(_parent, _child);
}
function _updatePOS(UserInfo storage _user) internal returns (uint256 proof) {
if (_user.authenticated) {
proof = _user.proofOfStake.add((block.timestamp.sub(_user.lastSeen)).mul(_user.stakingAmount));
}
if (_user.proofOfStake <= MAX_STORED_POS && proof > MAX_STORED_POS) {
proof = MAX_STORED_POS;
}
_user.proofOfStake = proof;
_user.lastSeen = block.timestamp;
}
}
{
"compilationTarget": {
"DoraID.sol": "DoraID"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}