Open6

OpenZeppelinのガバナンス関連のコントラクトを読む

MingleMingle(Thurendous)MingleMingle(Thurendous)

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.1/contracts/governance/IGovernor.sol

これはGovernor.solのコントラクトのインターフェースとなる。

  • IGovernorコントラクトはまずIERC165, IERC6372を継承している
  • enum ProposalStateが提案の状態を定義している。全部で8つの状態がある。
  • event ProposalCreatedはproposalが作られると、吐かれるイベントを定義。
MingleMingle(Thurendous)MingleMingle(Thurendous)
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";

contract MyGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
    constructor(IVotes _token, TimelockController _timelock)
        Governor("MyGovernor")
        GovernorSettings(7200 /* 1 day */, 50400 /* 1 week */, 0)
        GovernorVotes(_token)
        GovernorVotesQuorumFraction(4)
        GovernorTimelockControl(_timelock)
    {}

    // The following functions are overrides required by Solidity.

    function votingDelay()
        public
        view
        override(Governor, GovernorSettings)
        returns (uint256)
    {
        return super.votingDelay();
    }

    function votingPeriod()
        public
        view
        override(Governor, GovernorSettings)
        returns (uint256)
    {
        return super.votingPeriod();
    }

    function quorum(uint256 blockNumber)
        public
        view
        override(Governor, GovernorVotesQuorumFraction)
        returns (uint256)
    {
        return super.quorum(blockNumber);
    }

    function state(uint256 proposalId)
        public
        view
        override(Governor, GovernorTimelockControl)
        returns (ProposalState)
    {
        return super.state(proposalId);
    }

    function proposalNeedsQueuing(uint256 proposalId)
        public
        view
        override(Governor, GovernorTimelockControl)
        returns (bool)
    {
        return super.proposalNeedsQueuing(proposalId);
    }

    function proposalThreshold()
        public
        view
        override(Governor, GovernorSettings)
        returns (uint256)
    {
        return super.proposalThreshold();
    }

    function _queueOperations(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
        internal
        override(Governor, GovernorTimelockControl)
        returns (uint48)
    {
        return super._queueOperations(proposalId, targets, values, calldatas, descriptionHash);
    }

    function _executeOperations(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
        internal
        override(Governor, GovernorTimelockControl)
    {
        super._executeOperations(proposalId, targets, values, calldatas, descriptionHash);
    }

    function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
        internal
        override(Governor, GovernorTimelockControl)
        returns (uint256)
    {
        return super._cancel(targets, values, calldatas, descriptionHash);
    }

    function _executor()
        internal
        view
        override(Governor, GovernorTimelockControl)
        returns (address)
    {
        return super._executor();
    }
}

wizardを使って生成したガバナンスを実現するためのコントラクトはこれ。

MingleMingle(Thurendous)MingleMingle(Thurendous)

EIP-6372について

多くのコントラクトが時間へ依存することがあり、歴史歴なデータを保存するニーズがあるが、それに関するスタンダードがほとんどない状況である。

とあるコントラクトがtimestampへ依存し、とあるコントラクトがblocknumberへ依存することが多々あります。このEIPはこれをスタンダード化してコントラクト双方のコンポザビリティを促進するためのものとされている。このように、EIP6372は時間依存周りを規定をするためのEIPである。

このEIPは以下のインターフェースを規定している。必ずこれらの関数を実装することが求められる。


interface IERC6372 {
  function clock() external view returns (uint48);
  function CLOCK_MODE() external view returns (string);
}

clock

function clock() external view returns (uint48);

この関数は今の時間を返すためのもの。この関数は数字が増加する関数でなければならない。例:block.timestamp, block.numberなどがある。

CLOCK_MODE

function CLOCK_MODE() external view returns (string);

これは今の時間を記録するモードを返すための関数。タイムスタンプ、ブロック番号、または他の時間計測方法のいずれかが返ってきます。

この情報は機械可読な文字列形式で提供され、JavaScriptでnew URLSearchParams(CLOCK_MODE)を用いてデコードできる形式でなければなりません。
この関数を使用することで、他のスマートコントラクトやプログラムが、正確な時間情報を理解できるようになります。

実装例

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

/**
 * @title TimestampClock
 * @dev An implementation of the EIP-6372 interface that uses timestamps as the time representation.
 */
contract TimestampClock {
    /**
     * @notice Returns the current block timestamp as the contract's time representation.
     * @return uint48 The current block timestamp.
     */
    function clock() external view returns (uint48) {
        return uint48(block.timestamp);
    }

    /**
     * @notice Returns the time mode the contract is using.
     * @return string The mode of the clock, indicating "mode=timestamp" for this contract.
     */
    function CLOCK_MODE() external pure returns (string memory) {
        return "mode=timestamp";
    }
}

参考

https://qiita.com/cardene/items/eacc6a32cbac790df52d

MingleMingle(Thurendous)MingleMingle(Thurendous)

DAOを作ろうとしてどこから考えればいいかわからなかった話

参考となる記事を見つけたので、こちら:
https://qiita.com/lowzzy/items/5753c424399be5c41c30

DAOって何?
DAOとはDecentralized Autonomous Organizationの略で日本語で言うと自立分散型組織です。
有名な例で言うと↓

AAVE
Maker
Decred
Compound
Uniswap
PancakeSwap
eCash
参考
ちなみに日本で有名なDAOはこの辺らしい↓

Ninja DAO
國光DAO
和組DAO
SUPER SAPIENSS
MZ CLUB
HENKAKU Discord Community
参考

MingleMingle(Thurendous)MingleMingle(Thurendous)

Governor.solの解説


    struct ProposalCore {
        address proposer;
        uint48 voteStart;
        uint32 voteDuration;
        bool executed;
        bool canceled;
        uint48 etaSeconds;
    }

proposalはこのstructを使って管理している。
以前のstructより進化している。
etaSecondsは実施できるタイムスタンプを意味している。

これはproposalEta関数のことで、実施できるタイミングを返している。

    /**
     * @dev See {IGovernor-proposalEta}.
     */
    function proposalEta(uint256 proposalId) public view virtual returns (uint256) {
        return _proposals[proposalId].etaSeconds;
    }

このstate関数はproposalの状態を返している。

  1. まず、提案をストレージとしておいておく。
  2. その中にあるexecuted, canceledのステートを取り出す。
    3. もしそういったステートがあるなら、それらを返す。executed, canceledは排他的だから、どちらかにしかならない。
    /**
     * @dev See {IGovernor-state}.
     */
    function state(uint256 proposalId) public view virtual returns (ProposalState) {
        // We read the struct fields into the stack at once so Solidity emits a single SLOAD
        ProposalCore storage proposal = _proposals[proposalId];
        bool proposalExecuted = proposal.executed;
        bool proposalCanceled = proposal.canceled;

        if (proposalExecuted) {
            return ProposalState.Executed;
        }

        if (proposalCanceled) {
            return ProposalState.Canceled;
        }

        uint256 snapshot = proposalSnapshot(proposalId);

        if (snapshot == 0) {
            revert GovernorNonexistentProposal(proposalId);
        }

        uint256 currentTimepoint = clock();

        if (snapshot >= currentTimepoint) {
            return ProposalState.Pending;
        }

        uint256 deadline = proposalDeadline(proposalId);

        if (deadline >= currentTimepoint) {
            return ProposalState.Active;
        } else if (!_quorumReached(proposalId) || !_voteSucceeded(proposalId)) {
            return ProposalState.Defeated;
        } else if (proposalEta(proposalId) == 0) {
            return ProposalState.Succeeded;
        } else {
            return ProposalState.Queued;
        }
    }



    /**
     * @dev See {IGovernor-proposalSnapshot}.
     */
    function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256) {
        return _proposals[proposalId].voteStart;
    }