Skip to content

Commit

Permalink
feat: Initial Implementation Sprint
Browse files Browse the repository at this point in the history
  • Loading branch information
shotaronowhere committed Jun 20, 2022
1 parent 4e64ba8 commit c388934
Show file tree
Hide file tree
Showing 13 changed files with 732 additions and 0 deletions.
193 changes: 193 additions & 0 deletions contracts/Core.sol
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)
}
}
}
24 changes: 24 additions & 0 deletions contracts/interfaces/ICore.sol
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);
}
20 changes: 20 additions & 0 deletions contracts/interfaces/ISortitionStrategy.sol
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;
}
37 changes: 37 additions & 0 deletions contracts/rng/ConstantNG.sol
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;
}
}
37 changes: 37 additions & 0 deletions contracts/rng/IncrementalNG.sol
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++;
}
}
}
51 changes: 51 additions & 0 deletions contracts/rng/RNG.sol
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)));
}
}
Loading

0 comments on commit c388934

Please sign in to comment.