contract VoteDelegate {
    mapping(address => uint256) public stake;
    address     public immutable delegate;
    TokenLike   public immutable gov;
    TokenLike   public immutable iou;
    ChiefLike   public immutable chief;
    PollingLike public immutable polling;
    uint256     public immutable expiration;

    event Lock(address indexed usr, uint256 wad);
    event Free(address indexed usr, uint256 wad);

    constructor(address _chief, address _polling, address _delegate) public {
        chief = ChiefLike(_chief);
        polling = PollingLike(_polling);
        delegate = _delegate;
        expiration = block.timestamp + 365 days;

        TokenLike _gov = gov = ChiefLike(_chief).GOV();
        TokenLike _iou = iou = ChiefLike(_chief).IOU();

        _gov.approve(_chief, type(uint256).max);
        _iou.approve(_chief, type(uint256).max);
    }

    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "VoteDelegate/add-overflow");
    }

    modifier delegate_auth() {
        require(msg.sender == delegate, "VoteDelegate/sender-not-delegate");
        _;
    }

    modifier live() {
        require(block.timestamp < expiration, "VoteDelegate/delegation-contract-expired");
        _;
    }

    function lock(uint256 wad) external live {
        stake[msg.sender] = add(stake[msg.sender], wad);
        gov.pull(msg.sender, wad);
        chief.lock(wad);
        iou.push(msg.sender, wad);

        emit Lock(msg.sender, wad);
    }

    function free(uint256 wad) external {
        require(stake[msg.sender] >= wad, "VoteDelegate/insufficient-stake");

        stake[msg.sender] -= wad;
        iou.pull(msg.sender, wad);
        chief.free(wad);
        gov.push(msg.sender, wad);

        emit Free(msg.sender, wad);
    }

    function vote(address[] memory yays) external delegate_auth live returns (bytes32 result) {
        result = chief.vote(yays);
    }

    function vote(bytes32 slate) external delegate_auth live {
        chief.vote(slate);
    }

    // Polling vote
    function votePoll(uint256 pollId, uint256 optionId) external delegate_auth live {
        polling.vote(pollId, optionId);
    }

    function votePoll(uint256[] calldata pollIds, uint256[] calldata optionIds) external delegate_auth live {
        polling.vote(pollIds, optionIds);
    }
}
function votePoll(uint256 pollId, uint256 optionId) external delegate_auth live {
    polling.vote(pollId, optionId);
}

// Polling vote