Files
smom-dbis-138/contracts/cw-settlement/CWVotingEscrow.sol

75 lines
2.5 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title CWVotingEscrow
* @notice Minimal vote-escrow (veCW) for emission-direction governance.
*/
contract CWVotingEscrow is AccessControl {
using SafeERC20 for IERC20;
IERC20 public immutable escrowToken;
uint256 public constant MAX_LOCK_DURATION = 4 * 365 days;
struct Lock {
uint256 amount;
uint256 unlockAt;
uint256 votingPower;
}
mapping(address => Lock[]) public locks;
event Locked(address indexed account, uint256 amount, uint256 unlockAt, uint256 votingPower);
event Withdrawn(address indexed account, uint256 amount);
error ZeroAddress();
error InvalidLock();
error NothingToWithdraw();
constructor(address admin, address escrowToken_) {
if (admin == address(0) || escrowToken_ == address(0)) {
revert ZeroAddress();
}
_grantRole(DEFAULT_ADMIN_ROLE, admin);
escrowToken = IERC20(escrowToken_);
}
function createLock(uint256 amount, uint256 lockDuration) external {
if (amount == 0 || lockDuration == 0 || lockDuration > MAX_LOCK_DURATION) {
revert InvalidLock();
}
escrowToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 unlockAt = block.timestamp + lockDuration;
uint256 votingPower = (amount * lockDuration) / MAX_LOCK_DURATION;
locks[msg.sender].push(Lock({amount: amount, unlockAt: unlockAt, votingPower: votingPower}));
emit Locked(msg.sender, amount, unlockAt, votingPower);
}
function withdraw(uint256 lockIndex) external {
Lock storage entry = locks[msg.sender][lockIndex];
if (entry.amount == 0 || block.timestamp < entry.unlockAt) {
revert NothingToWithdraw();
}
uint256 amount = entry.amount;
entry.amount = 0;
entry.votingPower = 0;
escrowToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function votingPowerOf(address account) external view returns (uint256 total) {
Lock[] storage userLocks = locks[account];
for (uint256 i = 0; i < userLocks.length; i++) {
if (userLocks[i].amount > 0 && block.timestamp < userLocks[i].unlockAt) {
total += userLocks[i].votingPower;
}
}
}
}