forked from ethereum/EIPs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update EIP-5247 add assets (ethereum#6109)
* Update EIP * Add refimpl
- Loading branch information
Showing
3 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: MIT | ||
// A fully runnalbe version can be found in https://github.com/ercref/ercref-contracts/tree/869843f23dc4da793f0d9d018ed92e3950da8f75 | ||
pragma solidity ^0.8.17; | ||
|
||
import "./IERC5247.sol"; | ||
import "@openzeppelin/contracts/utils/Address.sol"; | ||
|
||
struct Proposal { | ||
address by; | ||
uint256 proposalId; | ||
address[] targets; | ||
uint256[] values; | ||
uint256[] gasLimits; | ||
bytes[] calldatas; | ||
} | ||
|
||
contract ProposalRegistry is IERC5247 { | ||
using Address for address; | ||
mapping(uint256 => Proposal) public proposals; | ||
uint256 private proposalCount; | ||
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); | ||
} | ||
|
||
function getProposal(uint256 proposalId) external view returns (Proposal memory) { | ||
return proposals[proposalId]; | ||
} | ||
|
||
function getProposalCount() external view returns (uint256) { | ||
return proposalCount; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// A fully runnalbe version can be found in https://github.com/ercref/ercref-contracts/tree/869843f23dc4da793f0d9d018ed92e3950da8f75 | ||
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; | ||
import { expect } from "chai"; | ||
import { hexlify } from "ethers/lib/utils"; | ||
import { ethers } from "hardhat"; | ||
|
||
describe("ProposalRegistry", function () { | ||
async function deployFixture() { | ||
// Contracts are deployed using the first signer/account by default | ||
const [owner, otherAccount] = await ethers.getSigners(); | ||
|
||
const ProposalRegistry = await ethers.getContractFactory("ProposalRegistry"); | ||
const contract = await ProposalRegistry.deploy(); | ||
|
||
const ERC721ForTesting = await ethers.getContractFactory("ERC721ForTesting"); | ||
const erc721 = await ERC721ForTesting.deploy(); | ||
|
||
const SimpleForwarder = await ethers.getContractFactory("SimpleForwarder"); | ||
const forwarder = await SimpleForwarder.deploy(); | ||
return { contract, erc721, forwarder, owner, otherAccount }; | ||
} | ||
|
||
describe("Deployment", function () { | ||
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); | ||
}); | ||
const Ns = [0, 50, 100, 150, 200]; | ||
for (let n of Ns) { | ||
|
||
it(`Should work for a proposal case of ${n}`, async function () { | ||
const { contract, erc721, owner } = await loadFixture(deployFixture); | ||
const numOfMint = n; | ||
const calldatas = []; | ||
for (let i = 0 ; i < numOfMint; i++) { | ||
const callData = erc721.interface.encodeFunctionData("mint", [owner.address, i]); | ||
calldatas.push(callData); | ||
} | ||
let txCreate = await contract.connect(owner) | ||
.createProposal( | ||
0, | ||
Array(numOfMint).fill(erc721.address), | ||
Array(numOfMint).fill(0), | ||
Array(numOfMint).fill(0), | ||
calldatas, | ||
[]); | ||
let txCreateWaited = await txCreate.wait(); | ||
console.log(`Creation TX gas`, txCreateWaited.cumulativeGasUsed.toString()); | ||
console.log(`Gas per mint`, parseInt(txCreateWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
expect(await erc721.balanceOf(owner.address)).to.equal(0); | ||
let txExecute = await contract.connect(owner).executeProposal(0, []); | ||
let txExecuteWaited = await txExecute.wait(); | ||
console.log(`Execution TX gas`, txExecuteWaited.cumulativeGasUsed.toString()); | ||
console.log(`Gas per mint`, parseInt(txExecuteWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
expect(await erc721.balanceOf(owner.address)).to.equal(numOfMint); | ||
}); | ||
} | ||
}); | ||
describe("Benchmark", function () { | ||
it(`Should work for a forwarding case`, async function () { | ||
const { forwarder, erc721, owner } = await loadFixture(deployFixture); | ||
const numOfMint = 200; | ||
const calldatas = []; | ||
for (let i = 0 ; i < numOfMint; i++) { | ||
const callData = erc721.interface.encodeFunctionData("mint", [owner.address, i]); | ||
calldatas.push(callData); | ||
} | ||
expect(await erc721.balanceOf(owner.address)).to.equal(0); | ||
let txForward = await forwarder.connect(owner) | ||
.forward( | ||
Array(numOfMint).fill(erc721.address), | ||
Array(numOfMint).fill(0), | ||
Array(numOfMint).fill(0), | ||
calldatas); | ||
let txForwardWaited = await txForward.wait(); | ||
|
||
console.log(`txForwardWaited TX gas`, txForwardWaited.cumulativeGasUsed.toString()); | ||
|
||
console.log(`Gas per mint`, parseInt(txForwardWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
expect(await erc721.balanceOf(owner.address)).to.equal(numOfMint); | ||
|
||
}); | ||
|
||
|
||
it(`Should work for erc721 batchMint with same addresses`, async function () { | ||
const { erc721, owner } = await loadFixture(deployFixture); | ||
const numOfMint = 200; | ||
const tokenIds = []; | ||
const addresses = []; | ||
|
||
for (let i = 0 ; i < numOfMint; i++) { | ||
addresses.push(owner.address);// addresses.push(hexlify(ethers.utils.randomBytes(20))); | ||
tokenIds.push(i); | ||
} | ||
const tx = await erc721.connect(owner).batchMint(addresses, tokenIds); | ||
const txWaited = await tx.wait(); | ||
console.log(`batchMint TX gas`, txWaited.cumulativeGasUsed.toString()); | ||
console.log(`At ${numOfMint} Gas per mint`, parseInt(txWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
}) | ||
|
||
it(`Should work for erc721 batchMint with different addresses`, async function () { | ||
const { erc721, owner } = await loadFixture(deployFixture); | ||
const numOfMint = 200; | ||
const tokenIds = []; | ||
const addresses = []; | ||
|
||
for (let i = 0 ; i < numOfMint; i++) { | ||
addresses.push(hexlify(ethers.utils.randomBytes(20))); | ||
tokenIds.push(i); | ||
} | ||
const tx = await erc721.connect(owner).batchMint(addresses, tokenIds); | ||
const txWaited = await tx.wait(); | ||
console.log(`batchMint TX gas`, txWaited.cumulativeGasUsed.toString()); | ||
console.log(`At ${numOfMint} Gas per mint`, parseInt(txWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
}); | ||
|
||
|
||
it(`Should work for erc721 batchSafeMint with same addresses`, async function () { | ||
const { erc721, owner } = await loadFixture(deployFixture); | ||
const numOfMint = 400; | ||
const tokenIds = []; | ||
const addresses = []; | ||
|
||
for (let i = 0 ; i < numOfMint; i++) { | ||
addresses.push(owner.address);// addresses.push(hexlify(ethers.utils.randomBytes(20))); | ||
tokenIds.push(i); | ||
} | ||
const tx = await erc721.connect(owner).batchSafeMint(addresses, tokenIds); | ||
const txWaited = await tx.wait(); | ||
console.log(`batchSafeMint TX gas`, txWaited.cumulativeGasUsed.toString()); | ||
console.log(`At ${numOfMint} Gas per mint`, parseInt(txWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
}); | ||
|
||
it(`Should work for erc721 batchSafeMint with different addresses`, async function () { | ||
const { erc721, owner } = await loadFixture(deployFixture); | ||
const numOfMint = 400; | ||
const tokenIds = []; | ||
const addresses = []; | ||
|
||
for (let i = 0 ; i < numOfMint; i++) { | ||
addresses.push(hexlify(ethers.utils.randomBytes(20))); | ||
tokenIds.push(i); | ||
} | ||
const tx = await erc721.connect(owner).batchSafeMint(addresses, tokenIds); | ||
const txWaited = await tx.wait(); | ||
console.log(`batchSafeMint TX gas`, txWaited.cumulativeGasUsed.toString()); | ||
console.log(`At ${numOfMint} the Gas per mint`, parseInt(txWaited.cumulativeGasUsed.toString()) / numOfMint); | ||
}); | ||
}); | ||
}); |