Skip to content

Commit

Permalink
Deploy Controller contract locally (#180)
Browse files Browse the repository at this point in the history
Why:
* We want to start integrating the UI with the contracts

How:
* Switching from using the Fractal Registry contract for KYC
  verification to using a Merkle Root with Merkle Proofs validation
* Updating the tests
* Increasing the `code-size-limit` for local `anvil`
  • Loading branch information
DavideSilva authored Mar 27, 2024
1 parent 67d9ab8 commit 9963622
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 97 deletions.
3 changes: 2 additions & 1 deletion packages/contracts/Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ eth:
# just test
sleep 0.2 && just eth-deploy &
just wagmi &
anvil --host 0.0.0.0
anvil --code-size-limit 100000 --host 0.0.0.0

build-contracts:
forge build
Expand Down Expand Up @@ -79,6 +79,7 @@ forge-script script:
forge script \
$1 \
--fork-url http://localhost:8545 \
--code-size-limit 100000 \
--broadcast \
--mnemonics "{{ mnemonic }}" \
--sender "{{ sender }}" \
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts/contracts/discovery/Batch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ contract Batch is IBatch, ICommon, ProjectVoting {
investmentEnd = votingPeriod.end + extraInvestmentDuration;
}

function vote(address projectAddress)
function vote(address projectAddress, bytes32[] calldata _merkleProof)
external
votingPeriodIsSet
inVotingPeriod
{
require(
IController(controller).canVote(msg.sender),
IController(controller).canVote(msg.sender, _merkleProof),
"not allowed to vote"
);

Expand Down
49 changes: 24 additions & 25 deletions packages/contracts/contracts/discovery/Controller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

import {IController} from "./interfaces/IController.sol";
import {IProject} from "./interfaces/IProject.sol";
import {IBatch} from "./interfaces/IBatch.sol";
import {IStaking} from "./interfaces/IStaking.sol";
import {Project} from "./Project.sol";
import {Batch} from "./Batch.sol";
import {FractalRegistry} from "../fractal_registry/FractalRegistry.sol";

contract Controller is IController, ERC165, AccessControl {
using SafeERC20 for IERC20;
Expand Down Expand Up @@ -47,21 +47,17 @@ contract Controller is IController, ERC165, AccessControl {
// CTND staking contract
address public staking;

// Fractal Registry contract
address public registry;

// CTND token contract
address public token;

// Merkle root to pass to the projects
bytes32 public merkleRoot;

constructor(
address _registry,
address _staking,
address _token,
bytes32 _merkleRoot
) {
registry = _registry;
staking = _staking;
token = _token;
merkleRoot = _merkleRoot;
Expand Down Expand Up @@ -133,40 +129,37 @@ contract Controller is IController, ERC165, AccessControl {
}

/// @inheritdoc IController
function canInvestInStakersPool(address _user)
external
view
override(IController)
returns (bool)
{
function canInvestInStakersPool(
address _user,
bytes32[] calldata _merkleProof
) external view override(IController) returns (bool) {
return
_hasKYC(_user) &&
_hasKYC(_user, _merkleProof) &&
_belongsToDAO(_user) &&
IStaking(staking).hasStaked(_user);
}

/// @inheritdoc IController
function canInvestInPeoplesPool(address _project, address _user)
external
view
override(IController)
returns (bool)
{
function canInvestInPeoplesPool(
address _project,
address _user,
bytes32[] calldata _merkleProof
) external view override(IController) returns (bool) {
Batch batch = Batch(projectsToBatches[_project]);

return
_hasKYC(_user) &&
_hasKYC(_user, _merkleProof) &&
_belongsToDAO(_user) &&
batch.userHasVotedForProject(_project, _user);
}

function canVote(address _user)
function canVote(address _user, bytes32[] calldata _merkleProof)
external
view
override(IController)
returns (bool)
{
return _hasKYC(_user) && _belongsToDAO(_user);
return _hasKYC(_user, _merkleProof) && _belongsToDAO(_user);
}

/// @inheritdoc IController
Expand All @@ -179,9 +172,15 @@ contract Controller is IController, ERC165, AccessControl {
Batch(batch).setVotingPeriod(start, end, extraInvestmentDuration);
}

function _hasKYC(address _user) internal view returns (bool) {
bytes32 fractalId = FractalRegistry(registry).getFractalId(_user);
return fractalId != 0;
function _hasKYC(address _user, bytes32[] calldata _merkleProof)
internal
view
returns (bool)
{
bytes32 leaf = keccak256(abi.encodePacked(_user));
bool isValid = MerkleProof.verify(_merkleProof, merkleRoot, leaf);

return isValid;
}

function _belongsToDAO(address _user) internal view returns (bool) {
Expand Down
3 changes: 2 additions & 1 deletion packages/contracts/contracts/discovery/interfaces/IBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
pragma solidity ^0.8.20;

interface IBatch {
function vote(address projectAddress) external;
function vote(address projectAddress, bytes32[] calldata _merkleProof)
external;
}
17 changes: 12 additions & 5 deletions packages/contracts/contracts/discovery/interfaces/IController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,24 @@ interface IController {
returns (bool);

/// Checks if a user can invest in the staker's pool of a project
function canInvestInStakersPool(address _user) external view returns (bool);
function canInvestInStakersPool(
address _user,
bytes32[] calldata _merkleProof
) external view returns (bool);

/// Checks if a user can invest in the people's pool of a project
function canInvestInPeoplesPool(address _project, address _user)
function canInvestInPeoplesPool(
address _project,
address _user,
bytes32[] calldata _merkleProof
) external view returns (bool);

/// Checks if a user can vote
function canVote(address _user, bytes32[] calldata _merkleProof)
external
view
returns (bool);

/// Checks if a user can vote
function canVote(address _user) external view returns (bool);

/// Sets the voting period for a Batch
function setBatchVotingPeriod(
address batch,
Expand Down
5 changes: 2 additions & 3 deletions packages/contracts/deploy/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@ const func: DeployFunction = async function (hre) {
const { deployer } = await hre.getNamedAccounts();
const { get } = hre.deployments;

const registry = await get("FractalRegistry");
const citizend = await get("Citizend");
const staking = await get("Staking");
const merkleRoot =
"0xa5c09e2a9128afef7246a5900cfe02c4bd2cfcac8ac4286f0159a699c8455a49";

await acalaDeploy(hre, "Controller", {
from: deployer,
args: [registry.address, staking.address, citizend.address, merkleRoot],
args: [staking.address, citizend.address, merkleRoot],
log: true,
});
};

func.id = "controller";
func.tags = ["controller"];
func.dependencies = ["ctnd.token", "staking", "fractal-registry"];
func.dependencies = ["ctnd.token", "staking"];

export default func;
8 changes: 6 additions & 2 deletions packages/contracts/script/DevDeploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
pragma solidity ^0.8.20;

import {Script} from "lib/forge-std/src/Script.sol";

import {Citizend} from "contracts/token/Citizend.sol";
import {Controller} from "contracts/discovery/Controller.sol";
import {Staking} from "contracts/discovery/Staking.sol";
import {Project} from "contracts/discovery/Project.sol";

Expand All @@ -24,10 +25,13 @@ contract DevDeployScript is Script {
function run() public {
vm.startBroadcast();

bytes32 merkleRoot = 0xa5c09e2a9128afef7246a5900cfe02c4bd2cfcac8ac4286f0159a699c8455a49;

Citizend citizend = new Citizend(alice);
Staking staking = new Staking(address(citizend));

bytes32 merkleRoot = 0xa5c09e2a9128afef7246a5900cfe02c4bd2cfcac8ac4286f0159a699c8455a49;
Controller controller = new Controller(address(staking), address(citizend), merkleRoot);

Project project = new Project("token sale project", address(citizend), 1000, 1, address(0), merkleRoot);

for (uint256 i; i < testAccounts.length; i++) {
Expand Down
Loading

0 comments on commit 9963622

Please sign in to comment.