主页EIPs周刊
EIPsERC-5247
ERC-5247

Smart Contract Executable Proposal Interface

An interface to create and execute proposals.
ReviewStandards Track: ERC
创建时间: 2022-07-13
Zainan Victor Zhou (@xinbenlv)
社区讨论原文链接编辑
1 分钟了解
欢迎补充好内容
去提交
相关视频
欢迎补充好内容
去提交
正文

Abstract

This EIP presents an interface for "smart contract executable proposals": proposals that are submitted to, recorded on, and possibly executed on-chain. Such proposals include a series of information about function calls including the target contract address, ether value to be transmitted, gas limits and calldatas.

Motivation

It is oftentimes necessary to separate the code that is to be executed from the actual execution of the code.

A typical use case for this EIP is in a Decentralized Autonomous Organization (DAO). A proposer will create a smart proposal and advocate for it. Members will then choose whether or not to endorse the proposal and vote accordingly (see ERC-1202). Finallym when consensus has been formed, the proposal is executed.

A second typical use-case is that one could have someone who they trust, such as a delegator, trustee, or an attorney-in-fact, or any bilateral collaboration format, where a smart proposal will be first composed, discussed, approved in some way, and then put into execution.

A third use-case is that a person could make an "offer" to a second person, potentially with conditions. The smart proposal can be presented as an offer and the second person can execute it if they choose to accept this proposal.

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; interface IERC5247 { event ProposalCreated( address indexed proposer, uint256 indexed proposalId, address[] targets, uint256[] values, uint256[] gasLimits, bytes[] calldatas, bytes extraParams ); event ProposalExecuted( address indexed executor, uint256 indexed proposalId, bytes extraParams ); function createProposal( uint256 proposalId, address[] calldata targets, uint256[] calldata values, uint256[] calldata gasLimits, bytes[] calldata calldatas, bytes calldata extraParams ) external returns (uint256 registeredProposalId); function executeProposal(uint256 proposalId, bytes calldata extraParams) external; }

Rationale

  • Originally, this interface was part of part of ERC-1202. However, the proposal itself can potentially have many use cases outside of voting. It is possible that voting may not need to be upon a proposal in any particular format. Hence, we decide to decouple the voting interface and proposal interface.
  • Arrays were used for targets, values, calldatas instead of single variables, allowing a proposal to carry arbitrarily long multiple functional calls.
  • registeredProposalId is returned in createProposal so the standard can support implementation to decide their own format of proposal id.

Test Cases

A simple test case can be found as

it("Should work for a simple case", async function () { const { contract, erc721, owner } = await loadFixture(deployFixture); const callData1 = erc721.interface.encodeFunctionData("mint", [owner.address, 1]); const callData2 = erc721.interface.encodeFunctionData("mint", [owner.address, 2]); await contract.connect(owner) .createProposal( 0, [erc721.address, erc721.address], [0,0], [0,0], [callData1, callData2], []); expect(await erc721.balanceOf(owner.address)).to.equal(0); await contract.connect(owner).executeProposal(0, []); expect(await erc721.balanceOf(owner.address)).to.equal(2); });

See testProposalRegistry.ts for the whole testset.

Reference Implementation

A simple reference implementation can be found.

function createProposal( uint256 proposalId, address[] calldata targets, uint256[] calldata values, uint256[] calldata gasLimits, bytes[] calldata calldatas, bytes calldata extraParams ) external returns (uint256 registeredProposalId) { require(targets.length == values.length, "GeneralForwarder: targets and values length mismatch"); require(targets.length == gasLimits.length, "GeneralForwarder: targets and gasLimits length mismatch"); require(targets.length == calldatas.length, "GeneralForwarder: targets and calldatas length mismatch"); registeredProposalId = proposalCount; proposalCount++; proposals[registeredProposalId] = Proposal({ by: msg.sender, proposalId: proposalId, targets: targets, values: values, calldatas: calldatas, gasLimits: gasLimits }); emit ProposalCreated(msg.sender, proposalId, targets, values, gasLimits, calldatas, extraParams); return registeredProposalId; } function executeProposal(uint256 proposalId, bytes calldata extraParams) external { Proposal storage proposal = proposals[proposalId]; address[] memory targets = proposal.targets; string memory errorMessage = "Governor: call reverted without message"; for (uint256 i = 0; i < targets.length; ++i) { (bool success, bytes memory returndata) = proposal.targets[i].call{value: proposal.values[i]}(proposal.calldatas[i]); Address.verifyCallResult(success, returndata, errorMessage); } emit ProposalExecuted(msg.sender, proposalId, extraParams); }

See ProposalRegistry.sol for more information.

Security Considerations

Needs discussion.

Copyright and related rights waived via CC0.

扩展阅读
欢迎补充好内容
去提交
相关项目
欢迎补充好内容
去提交

不想错过最新的 EIP 动态?

订阅 EIPs Fun 周刊以跟进相关更新,建⽴你与 EIP 之间的连接 ,更好地建设以太坊。

详情
支持以太坊贡献者,推动生态建设
资源
GitHub
支持社区