Address
0xcd2da1b1f58e145dbcbf79de2dda2f6f79f66d8aCurrent Holdings
$0.00
TXs sent
not counted
First Active
2026-03-20
block 26,073,335
Last Active
169 days ago
block 26,148,657
Funded By
not identified
Net worth historyi
6 snapshots · to block 27,427,957coverage change 26 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Aug
exact matchVoodooDAOsolc 0.8.20+commit.a1b79de6runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/*
██╗░░░██╗░█████╗░░█████╗░██████╗░░█████╗░░█████╗░ ██████╗░░█████╗░░█████╗░
██║░░░██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔══██╗
╚██╗░██╔╝██║░░██║██║░░██║██║░░██║██║░░██║██║░░██║ ██║░░██║███████║██║░░██║
░╚████╔╝░██║░░██║██║░░██║██║░░██║██║░░██║██║░░██║ ██║░░██║██╔══██║██║░░██║
░░╚██╔╝░░╚█████╔╝╚█████╔╝██████╔╝╚█████╔╝╚█████╔╝ ██████╔╝██║░░██║╚█████╔╝
░░░╚═╝░░░░╚════╝░░╚════╝░╚═════╝░░╚════╝░░╚════╝░ ╚═════╝░╚═╝░░╚═╝░╚════╝░
*/
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract VoodooDAO is Ownable {
IERC20 public immutable vdoToken;
mapping(address => bool) public allowedTargets;
bool public paused;
struct Proposal {
address proposer;
address target;
bytes data;
string description;
uint256 yesVotes;
uint256 noVotes;
uint256 endTime;
uint256 queueTime;
bool executed;
bool canceled;
bool queued;
}
Proposal[] public proposals;
mapping(uint256 => mapping(address => bool)) public hasVoted;
mapping(address => uint256) public lockedBalance;
mapping(address => mapping(uint256 => uint256)) public lockedInProposal;
uint256 public votingDuration = 3 days;
uint256 public timelockDelay = 2 days;
uint256 public minTokensToPropose = 10_000 * 10**18;
uint256 public quorumPercentage = 10;
uint256 public majorityPercentage = 51;
event ProposalCreated(uint256 indexed id, address proposer, string description);
event Voted(uint256 indexed id, address voter, bool support, uint256 weight);
event ProposalQueued(uint256 indexed id, uint256 queueTime);
event ProposalExecuted(uint256 indexed id);
event ProposalCanceled(uint256 indexed id);
event TargetWhitelisted(address indexed target, bool allowed);
event TokensLocked(address indexed user, uint256 proposalId, uint256 amount);
event TokensUnlocked(address indexed user, uint256 proposalId, uint256 amount);
constructor(address _vdoToken, address[] memory _initialTargets) Ownable(msg.sender) {
vdoToken = IERC20(_vdoToken);
for (uint256 i = 0; i < _initialTargets.length; i++) {
require(_initialTargets[i] != address(0));
allowedTargets[_initialTargets[i]] = true;
emit TargetWhitelisted(_initialTargets[i], true);
}
allowedTargets[address(this)] = true;
emit TargetWhitelisted(address(this), true);
}
modifier whenNotPaused() {
require(!paused, "Contract is paused");
_;
}
function setPaused(bool _paused) external onlyOwner {
paused = _paused;
}
function _addTargetToWhitelist(address _newTarget) external {
require(msg.sender == address(this), "Only self-call allowed");
require(!allowedTargets[_newTarget], "Target already allowed");
allowedTargets[_newTarget] = true;
emit TargetWhitelisted(_newTarget, true);
}
function proposeNewTargetWhitelist(address _newTarget, string calldata _description) external whenNotPaused {
bytes memory data = abi.encodeWithSelector(this._addTargetToWhitelist.selector, _newTarget);
_createProposal(address(this), data, _description);
}
function _createProposal(address _target, bytes memory _data, string memory _description) internal whenNotPaused {
require(allowedTargets[_target], "Target not allowed");
uint256 power = vdoToken.balanceOf(msg.sender);
require(power >= minTokensToPropose, "Insufficient VDO balance");
uint256 id = proposals.length;
proposals.push(Proposal({
proposer: msg.sender,
target: _target,
data: _data,
description: _description,
yesVotes: 0,
noVotes: 0,
endTime: block.timestamp + votingDuration,
queueTime: 0,
executed: false,
canceled: false,
queued: false
}));
emit ProposalCreated(id, msg.sender, _description);
_lockTokens(msg.sender, id, power);
}
function propose(address _target, bytes calldata _data, string calldata _description) external whenNotPaused {
_createProposal(_target, _data, _description);
}
function vote(uint256 _proposalId, bool _support) external whenNotPaused {
Proposal storage p = proposals[_proposalId];
require(block.timestamp < p.endTime, "Voting period ended");
require(!hasVoted[_proposalId][msg.sender], "Already voted");
require(!p.executed && !p.canceled, "Proposal closed");
uint256 weight = vdoToken.balanceOf(msg.sender);
require(weight > 0, "No voting power");
if (_support) {
p.yesVotes += weight;
} else {
p.noVotes += weight;
}
hasVoted[_proposalId][msg.sender] = true;
emit Voted(_proposalId, msg.sender, _support, weight);
if (lockedInProposal[msg.sender][_proposalId] == 0) {
_lockTokens(msg.sender, _proposalId, weight);
}
}
function _lockTokens(address user, uint256 proposalId, uint256 amount) internal {
lockedBalance[user] += amount;
lockedInProposal[user][proposalId] += amount;
require(vdoToken.transferFrom(user, address(this), amount), "Token transfer failed");
emit TokensLocked(user, proposalId, amount);
}
function _unlockTokens(address user, uint256 proposalId) internal {
uint256 amount = lockedInProposal[user][proposalId];
if (amount > 0) {
lockedBalance[user] -= amount;
lockedInProposal[user][proposalId] = 0;
require(vdoToken.transfer(user, amount), "Token transfer back failed");
emit TokensUnlocked(user, proposalId, amount);
}
}
function queue(uint256 _proposalId) external whenNotPaused {
Proposal storage p = proposals[_proposalId];
require(block.timestamp >= p.endTime, "Voting not ended");
require(!p.queued && !p.executed && !p.canceled, "Invalid state");
uint256 totalVotes = p.yesVotes + p.noVotes;
uint256 totalSupply = vdoToken.totalSupply();
require(totalVotes >= (totalSupply * quorumPercentage) / 100, "Quorum not reached");
require(p.yesVotes * 100 >= totalVotes * majorityPercentage, "Majority not reached");
p.queued = true;
p.queueTime = block.timestamp + timelockDelay;
emit ProposalQueued(_proposalId, p.queueTime);
}
function execute(uint256 _proposalId) external whenNotPaused {
Proposal storage p = proposals[_proposalId];
require(p.queued, "Proposal not queued");
require(block.timestamp >= p.queueTime, "Timelock not passed");
require(!p.executed && !p.canceled, "Proposal already finalized");
p.executed = true;
(bool success, bytes memory result) = p.target.call{value: 0}(p.data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
emit ProposalExecuted(_proposalId);
}
function cancel(uint256 _proposalId) external whenNotPaused {
Proposal storage p = proposals[_proposalId];
require(!p.executed && !p.canceled, "Proposal already finalized");
require(msg.sender == p.proposer || msg.sender == owner(), "Not authorized");
p.canceled = true;
emit ProposalCanceled(_proposalId);
}
function withdrawLockedTokens(uint256 _proposalId) external {
Proposal storage p = proposals[_proposalId];
require(
p.executed || p.canceled || block.timestamp > p.endTime + timelockDelay,
"Proposal still active"
);
_unlockTokens(msg.sender, _proposalId);
}
function getTotalLockedTokens() external view returns (uint256) {
return vdoToken.balanceOf(address(this));
}
function getLockedBalance(address user) external view returns (uint256) {
return lockedBalance[user];
}
function getProposalCount() external view returns (uint256) {
return proposals.length;
}
function getProposal(uint256 id) external view returns (
address proposer,
address target,
bytes memory data,
string memory description,
uint256 yesVotes,
uint256 noVotes,
uint256 endTime,
uint256 queueTime,
bool executed,
bool canceled,
bool queued
) {
Proposal storage p = proposals[id];
return (
p.proposer,
p.target,
p.data,
p.description,
p.yesVotes,
p.noVotes,
p.endTime,
p.queueTime,
p.executed,
p.canceled,
p.queued
);
}
}