-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4e64ba8
commit c388934
Showing
13 changed files
with
732 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/** | ||
* @authors: [@shotaronowhere] | ||
* @reviewers: [] | ||
* @auditors: [] | ||
*/ | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "./interfaces/ISortitionStrategy.sol"; | ||
import "./interfaces/ICore.sol"; | ||
|
||
|
||
/** | ||
* @title Core | ||
* @author ShotaroNowhere - <[email protected]> | ||
* @dev Simple core stake management contract | ||
*/ | ||
contract Core is ICore{ | ||
|
||
IERC20 public immutable token; | ||
address public governor; | ||
|
||
constructor(IERC20 _token){ | ||
token = _token; | ||
governor = msg.sender; | ||
freeAccountIndex = 1; | ||
} | ||
|
||
|
||
struct Pool { | ||
uint96 parent; // The parent sortition pool. | ||
uint256 minStake; // Minimum tokens needed to stake in the pool. | ||
} | ||
|
||
modifier onlyByGovernor() { | ||
require(governor == msg.sender, "Access not allowed: Governor only."); | ||
_; | ||
} | ||
|
||
struct SortitionRequest { | ||
uint96 subpool; // The ID of the subpool the request is in. | ||
address requester; // The sortition requester contract. | ||
uint64 numDraws; | ||
uint32 strategyIndex; // The index of approved strategy. | ||
address[] drawnAccounts; // packed selected staker indicies | ||
} | ||
|
||
Pool[] public pools; // The subpools. | ||
ISortitionStrategy[] public strategies; | ||
SortitionRequest[] public sortitionRequests; | ||
uint48 public freeAccountIndex; | ||
mapping(bytes32 => Account) public stakeIDToAccounts; | ||
mapping(uint48 => bytes32) public override indexToStakeID; | ||
|
||
function getStakeIDToAccounts(bytes32 stakeID) external view override returns (Account memory){ | ||
return stakeIDToAccounts[stakeID]; | ||
} | ||
|
||
function setStake(uint256 _stake, uint96 _subpoolID) external { | ||
require(_setStake(msg.sender, _stake, _subpoolID), "Staking Failed"); | ||
} | ||
|
||
function _setStake(address _account, uint256 _stake, uint96 _subpoolID) internal returns (bool){ | ||
bytes32 stakeID = accountAndSubpoolIDToStakeID(_account, _subpoolID); | ||
Account memory account = stakeIDToAccounts[stakeID]; | ||
|
||
uint256 currentStake = account.stakedTokens; | ||
uint256 transferredAmount; | ||
|
||
if (_stake >= currentStake) { | ||
unchecked{ | ||
transferredAmount = _stake - currentStake; | ||
} | ||
if (transferredAmount > 0) { | ||
if (!token.transferFrom(_account, address(this), transferredAmount)) return false; | ||
} | ||
} else if (_stake == 0) { | ||
if (!token.transfer(_account, _stake)) return false; | ||
} else { | ||
unchecked{ | ||
transferredAmount = currentStake - _stake; | ||
} | ||
if (transferredAmount > 0) { | ||
if (!token.transfer(_account, transferredAmount)) return false; | ||
} | ||
} | ||
|
||
if (account.index == 0){ | ||
uint48 _freeAccountIndex = freeAccountIndex; | ||
account.index = _freeAccountIndex; | ||
freeAccountIndex = _freeAccountIndex + 1; | ||
} | ||
|
||
account.stakedTokens = _stake; // new stake | ||
account.time = uint32(block.timestamp); | ||
stakeIDToAccounts[stakeID] = account; | ||
|
||
return true; | ||
} | ||
|
||
/** @dev Add a new supported sortition strategy module. | ||
* @param _sortitionStrategy The address of the sortition strategy contract. | ||
*/ | ||
function addNewSortitionStrategy(ISortitionStrategy _sortitionStrategy) external onlyByGovernor { | ||
strategies.push(_sortitionStrategy); | ||
} | ||
|
||
/** @dev Creates a subpool under a specified parent pool. | ||
* @param _parent The `parent` property value of the subpool. | ||
* @param _minStake The min stake. | ||
*/ | ||
function createSubpool( | ||
uint96 _parent, | ||
uint96 _minStake | ||
) external onlyByGovernor { | ||
require( | ||
pools[_parent].minStake <= _minStake, | ||
"A subpool cannot be a child of a subpool with a higher minimum stake." | ||
); | ||
|
||
uint96 subpoolID = uint96(pools.length); | ||
|
||
pools.push( | ||
Pool({ | ||
parent: _parent, | ||
minStake: _minStake | ||
}) | ||
); | ||
} | ||
|
||
/** @dev Creates a Sortition Request. | ||
* @param _subpool The subpool to draw from | ||
* @param _numDraws Number of addresses to draw | ||
* @param _strategyIndex The index of the strategy | ||
* @return sortitionRequestID The ID of the created dispute. | ||
*/ | ||
function createSortitionRequest(uint96 _subpool, uint32 _strategyIndex, uint64 _numDraws) external payable returns (uint64 sortitionRequestID){ | ||
uint256 sortitionRequestID = sortitionRequests.length; | ||
require(_numDraws > 0); | ||
sortitionRequests.push(SortitionRequest({ | ||
subpool: _subpool, | ||
requester: msg.sender, | ||
numDraws: _numDraws, | ||
strategyIndex: _strategyIndex, | ||
drawnAccounts: new address[](0) | ||
}) | ||
); | ||
//strategies[_strategyIndex].createSortitionRequest(sortitionRequestID, _numDraws); | ||
} | ||
|
||
/** @dev Draw stakers according to a strategy | ||
*/ | ||
function draw(uint64 _sortitionRequestID, uint256 iterations) external { | ||
ISortitionStrategy strategy = strategies[sortitionRequests[_sortitionRequestID].strategyIndex]; | ||
require(strategy.isDrawing(), "Wrong phase."); | ||
|
||
uint256 startIndex = sortitionRequests[_sortitionRequestID].drawnAccounts.length; | ||
uint256 endIndex = startIndex + iterations; | ||
if (endIndex > sortitionRequests[_sortitionRequestID].numDraws) | ||
endIndex = sortitionRequests[_sortitionRequestID].numDraws; | ||
|
||
for (uint i = startIndex; i < endIndex; i++) { | ||
address drawnAccount = strategy.draw(_sortitionRequestID); | ||
require(drawnAccount != address(0), "Dispute Kit Draw Failed."); | ||
sortitionRequests[_sortitionRequestID].drawnAccounts.push(drawnAccount); | ||
} | ||
} | ||
|
||
|
||
/** @dev Packs an account and a subpool ID into a stake ID. | ||
* @param _account The address of the juror to pack. | ||
* @param _subpoolID The subpool ID to pack. | ||
* @return stakePathID The stake ID. | ||
*/ | ||
function accountAndSubpoolIDToStakeID(address _account, uint96 _subpoolID) | ||
internal | ||
pure | ||
returns (bytes32 stakePathID) | ||
{ | ||
assembly { | ||
// solium-disable-line security/no-inline-assembly | ||
let ptr := mload(0x40) | ||
for {let i := 0x00} lt(i, 0x14) {i := add(i, 0x01)} { | ||
mstore8(add(ptr, i), byte(add(0x0c, i), _account)) | ||
} | ||
for {let i := 0x14} lt(i, 0x20) {i := add(i, 0x01)} { | ||
mstore8(add(ptr, i), byte(i, _subpoolID)) | ||
} | ||
stakePathID := mload(ptr) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* @authors: [@shotaronowhere] | ||
* @reviewers: [] | ||
* @auditors: [] | ||
*/ | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @title ISortitionStrategy | ||
* @author ShotaroNowhere - <[email protected]> | ||
* @dev Strategy Interface for drawing. | ||
*/ | ||
interface ICore{ | ||
|
||
struct Account { | ||
uint256 stakedTokens; // The account's total amount of tokens staked in subpools. | ||
uint48 index; // indexing the account | ||
uint32 time; // The time when the juror staked | ||
} | ||
|
||
function indexToStakeID(uint48) external view returns (bytes32); | ||
function getStakeIDToAccounts(bytes32) external view returns (Account memory); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* @authors: [@shotaronowhere] | ||
* @reviewers: [] | ||
* @auditors: [] | ||
*/ | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @title ISortitionStrategy | ||
* @author ShotaroNowhere - <[email protected]> | ||
* @dev Strategy Interface for drawing. | ||
*/ | ||
interface ISortitionStrategy{ | ||
|
||
function isResolving() external view returns (bool); // Current phase of this dispute kit. | ||
function isDrawing() external view returns (bool); // Current phase of this dispute kit. | ||
function draw(uint256 _sortitionRequestID) external returns (address); | ||
function createSortitionRequest(uint96 _subpool, uint64 _numDraws) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
/** | ||
* @title Constant Number Generator | ||
* @author Clément Lesaege - <[email protected]> | ||
* @dev A Random Number Generator which always return the same number. Usefull in order to make tests. | ||
*/ | ||
|
||
pragma solidity ^0.8; | ||
import "./RNG.sol"; | ||
|
||
contract ConstantNG is RNG { | ||
uint256 public immutable number; | ||
|
||
/** | ||
* @dev Constructor. | ||
* @param _number The number to always return. | ||
*/ | ||
constructor(uint256 _number) { | ||
number = _number; | ||
} | ||
|
||
/** | ||
* @dev Contribute to the reward of a random number. All the ETH will be lost forever. | ||
* @param _block Block the random number is linked to. | ||
*/ | ||
function contribute(uint256 _block) public payable override {} | ||
|
||
/** | ||
* @dev Get the "random number" (which is always the same). | ||
* @param _block Block the random number is linked to. | ||
* @return RN Random Number. If the number is not ready or has not been required 0 instead. | ||
*/ | ||
function getRN(uint256 _block) public view override returns (uint256 RN) { | ||
return number; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
/** | ||
* @title Incremental Number Generator | ||
* @author JayBuidl <[email protected]> | ||
* @dev A Random Number Generator which returns a number incremented by 1 each time. Useful as a fallback method. | ||
*/ | ||
|
||
pragma solidity ^0.8; | ||
import "./RNG.sol"; | ||
|
||
contract IncrementalNG is RNG { | ||
uint256 public number; | ||
|
||
constructor(uint256 _start) { | ||
number = _start; | ||
} | ||
|
||
/** | ||
* @dev Contribute to the reward of a random number. All the ETH will be lost forever. | ||
* @param _block Block the random number is linked to. | ||
*/ | ||
function contribute(uint256 _block) public payable override { | ||
/* NOP */ | ||
} | ||
|
||
/** | ||
* @dev Get the "random number", which is predictable. | ||
* @param _block Block the random number is linked to. | ||
* @return RN Random Number. If the number is not ready or has not been required 0 instead. | ||
*/ | ||
function getRN(uint256 _block) public override returns (uint256 RN) { | ||
unchecked { | ||
return number++; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
/** | ||
* @authors: [@clesaege] | ||
* @reviewers: [@remedcu] | ||
* @auditors: [] | ||
* @bounties: [] | ||
* @deployments: [] | ||
*/ | ||
|
||
pragma solidity ^0.8; | ||
|
||
/** | ||
* @title Random Number Generator Standard | ||
* @author Clément Lesaege - <[email protected]> | ||
* @dev This is an abstract contract | ||
*/ | ||
abstract contract RNG { | ||
/** | ||
* @dev Contribute to the reward of a random number. | ||
* @param _block Block the random number is linked to. | ||
*/ | ||
function contribute(uint256 _block) public payable virtual; | ||
|
||
/** | ||
* @dev Request a random number. | ||
* @param _block Block linked to the request. | ||
*/ | ||
function requestRN(uint256 _block) public payable { | ||
contribute(_block); | ||
} | ||
|
||
/** | ||
* @dev Get the random number. | ||
* @param _block Block the random number is linked to. | ||
* @return RN Random Number. If the number is not ready or has not been required 0 instead. | ||
*/ | ||
function getRN(uint256 _block) public virtual returns (uint256 RN); | ||
|
||
/** | ||
* @dev Get a uncorrelated random number. Act like getRN but give a different number for each sender. | ||
* This is to prevent users from getting correlated numbers. | ||
* @param _block Block the random number is linked to. | ||
* @return RN Random Number. If the number is not ready or has not been required 0 instead. | ||
*/ | ||
function getUncorrelatedRN(uint256 _block) public returns (uint256 RN) { | ||
uint256 baseRN = getRN(_block); | ||
if (baseRN == 0) return 0; | ||
else return uint256(keccak256(abi.encode(msg.sender, baseRN))); | ||
} | ||
} |
Oops, something went wrong.