From 7f2be9914268fa6bd391fce64b1e8b9ef4817938 Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:39:47 +0900 Subject: [PATCH 1/2] feat: MintRallyViewer.sol like ERC1155 without write functions --- hardhat/contracts/IERC1155MetadataURIView.sol | 46 ++++++++++++ hardhat/contracts/IMintNFT.sol | 11 +++ hardhat/contracts/MintRallyViewer.sol | 75 +++++++++++++++++++ hardhat/scripts/deploy_stg.ts | 2 + hardhat/scripts/deploy_viewer.ts | 36 +++++++++ hardhat/scripts/upgrades/upgrade_viewer.ts | 30 ++++++++ hardhat/scripts/utils/utils.ts | 41 ++++++++++ 7 files changed, 241 insertions(+) create mode 100644 hardhat/contracts/IERC1155MetadataURIView.sol create mode 100644 hardhat/contracts/MintRallyViewer.sol create mode 100644 hardhat/scripts/deploy_viewer.ts create mode 100644 hardhat/scripts/upgrades/upgrade_viewer.ts create mode 100644 hardhat/scripts/utils/utils.ts diff --git a/hardhat/contracts/IERC1155MetadataURIView.sol b/hardhat/contracts/IERC1155MetadataURIView.sol new file mode 100644 index 00000000..c89fec6c --- /dev/null +++ b/hardhat/contracts/IERC1155MetadataURIView.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol) + +pragma solidity ^0.8.9; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC-1155 compliant contract, as defined in the + * https://eips.ethereum.org/EIPS/eip-1155[ERC]. + */ +interface IERC1155MetadataURIView is IERC165 { + function supportsInterface(bytes4 interfaceId) external view returns (bool); + /** + * @dev Returns the value of tokens of token type `id` owned by `account`. + */ + function balanceOf(address account, uint256 id) external view returns (uint256); + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch( + address[] calldata accounts, + uint256[] calldata ids + ) external view returns (uint256[] memory); + + /** + * @dev Returns the URI for token type `id`. + * + * If the `\{id\}` substring is present in the URI, it must be replaced by + * clients with the actual token type ID. + */ + function uri(uint256 id) external view returns (string memory); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} \ No newline at end of file diff --git a/hardhat/contracts/IMintNFT.sol b/hardhat/contracts/IMintNFT.sol index f5eab4be..44e264be 100644 --- a/hardhat/contracts/IMintNFT.sol +++ b/hardhat/contracts/IMintNFT.sol @@ -99,4 +99,15 @@ interface IMintNFT { ) external; function transferOwnership(address newOwner) external; + + function isHoldingEventNFTByAddress( + address _addr, + uint256 _eventId + ) external view returns (bool); + + function getNFTAttributeRecordsByEventId( + uint256 _eventId, + uint256 _limit, + uint256 _offset + ) external view returns (NFTAttribute[] memory); } diff --git a/hardhat/contracts/MintRallyViewer.sol b/hardhat/contracts/MintRallyViewer.sol new file mode 100644 index 00000000..32f94e55 --- /dev/null +++ b/hardhat/contracts/MintRallyViewer.sol @@ -0,0 +1,75 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol"; +import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {IERC1155MetadataURIView} from "./IERC1155MetadataURIView.sol"; +import {IMintNFT} from "./IMintNFT.sol"; +import {IEventManager} from "./IEvent.sol"; + +contract MintRallyViewer is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155MetadataURIView, OwnableUpgradeable { + address private mintManagerAddr; + address private eventManagerAddr; + + // struct NFTAttribute { + // string metaDataURL; + // uint256 requiredParticipateCount; + // } + + function initialize( + address _owner, + address _mintManagerAddr, + address _eventManagerAddr + ) public initializer { + __Context_init(); + __ERC165_init(); + __Ownable_init(); + _transferOwnership(_owner); + mintManagerAddr = _mintManagerAddr; + eventManagerAddr = _eventManagerAddr; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC1155MetadataURIView) returns (bool) { + return + interfaceId == type(IERC1155).interfaceId || + interfaceId == type(IERC1155MetadataURI).interfaceId || + super.supportsInterface(interfaceId); + } + + function balanceOf(address account, uint256 id) external view override returns (uint256) { + return _balanceOf(account, id); + } + + function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view override returns (uint256[] memory) { + if (accounts.length != ids.length) { + revert ERC1155InvalidArrayLength(ids.length, accounts.length); + } + uint256[] memory batchBalances = new uint256[](accounts.length); + + for (uint256 i = 0; i < accounts.length; ++i) { + batchBalances[i] = _balanceOf(accounts[i], ids[i]); + } + + return batchBalances; + } + + function _balanceOf(address account, uint256 id) internal view returns (uint256) { + bool isHolidingEventNFT = IMintNFT(mintManagerAddr).isHoldingEventNFTByAddress(account, id); + if (isHolidingEventNFT) { + return 1; + } else { + return 0; + } + } + + function uri(uint256 id) external view override returns (string memory) { + IMintNFT.NFTAttribute[] memory attributes = IMintNFT(mintManagerAddr).getNFTAttributeRecordsByEventId(id, 1, 0); + + return attributes[0].metaDataURL; + } +} \ No newline at end of file diff --git a/hardhat/scripts/deploy_stg.ts b/hardhat/scripts/deploy_stg.ts index bd467c58..a929b97c 100644 --- a/hardhat/scripts/deploy_stg.ts +++ b/hardhat/scripts/deploy_stg.ts @@ -39,6 +39,7 @@ async function main() { const deployedMintNFT: any = await upgrades.deployProxy( MintNFTFactory, [ + "0xc5952da2d393d3421D56bd5FBCac1F8a3df40567", forwarder.address, secretPhraseVerifier.address, operationController.address, @@ -54,6 +55,7 @@ async function main() { const deployedEventManager: any = await upgrades.deployProxy( EventManagerFactory, [ + "0xc5952da2d393d3421D56bd5FBCac1F8a3df40567", process.env.MUMBAI_RELAYER_ADDRESS, 250000, 1000000, diff --git a/hardhat/scripts/deploy_viewer.ts b/hardhat/scripts/deploy_viewer.ts new file mode 100644 index 00000000..f753fcc0 --- /dev/null +++ b/hardhat/scripts/deploy_viewer.ts @@ -0,0 +1,36 @@ +import { ethers, upgrades } from "hardhat"; +import { + MintRallyViewer, +} from "../typechain"; + +async function main() { + const ownerAddr = "0xc5952da2d393d3421D56bd5FBCac1F8a3df40567"; + const mintNftAddr = "0x225B131690c2648EE58E7684e613C07D01A1B946"; + const eventManagerAddr = "0x71BAfD0812b483054b7e0c66dB428eB4AA54E13C"; + + let mintRallyViewer: MintRallyViewer; + + const MintRallyViewerFactory = await ethers.getContractFactory("MintRallyViewer"); + const deployedMintRallyViewer: any = await upgrades.deployProxy( + MintRallyViewerFactory, + [ + ownerAddr, + mintNftAddr, + eventManagerAddr, + ], + { + initializer: "initialize", + } + ); + mintRallyViewer = deployedMintRallyViewer; + await mintRallyViewer.deployed(); + + console.log("mintNFT address:", mintNftAddr); + console.log("eventManager address:", eventManagerAddr, "\n"); + console.log("mintRallyViewer address:", mintRallyViewer.address); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/hardhat/scripts/upgrades/upgrade_viewer.ts b/hardhat/scripts/upgrades/upgrade_viewer.ts new file mode 100644 index 00000000..df0f1478 --- /dev/null +++ b/hardhat/scripts/upgrades/upgrade_viewer.ts @@ -0,0 +1,30 @@ +import { ethers, upgrades } from "hardhat"; +import { + MintRallyViewer, +} from "../../typechain"; + +async function main() { + const ownerAddr = "0xc5952da2d393d3421D56bd5FBCac1F8a3df40567"; + const mintNftAddr = "0x225B131690c2648EE58E7684e613C07D01A1B946"; + const eventManagerAddr = "0x71BAfD0812b483054b7e0c66dB428eB4AA54E13C"; + const mintRallyViewerAddr = "0x283024996Bab0364A5Daa7a81A87eAF645a70eD6"; + + let mintRallyViewer: MintRallyViewer; + + const MintRallyViewerFactory = await ethers.getContractFactory("MintRallyViewer"); + const deployedMintRallyViewer: any = await upgrades.upgradeProxy( + mintRallyViewerAddr, + MintRallyViewerFactory + ); + mintRallyViewer = deployedMintRallyViewer; + await mintRallyViewer.deployed(); + + console.log("mintNFT address:", mintNftAddr); + console.log("eventManager address:", eventManagerAddr, "\n"); + console.log("mintRallyViewer address:", mintRallyViewer.address); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/hardhat/scripts/utils/utils.ts b/hardhat/scripts/utils/utils.ts new file mode 100644 index 00000000..6eefcbfa --- /dev/null +++ b/hardhat/scripts/utils/utils.ts @@ -0,0 +1,41 @@ +import axios from "axios"; +import { ethers } from "hardhat"; + +const MAX_GAS_GWEI = 150; +const NETWORKS = { + POLYGON: "polygon", + MUMBAI_STG: "mumbai-stg", + MUMBAI_DEV: "mumbai-dev", +}; + +/* + * polygon mainnet or mumbaiのガス代を計算する + * @param network ネットワーク名 + * @return ガス代 + */ +export const calcMaxGas = async (network: string) => { + let maxFeePerGas = ethers.utils.parseUnits(MAX_GAS_GWEI.toString(), "gwei"); + let maxPriorityFeePerGas = ethers.utils.parseUnits(MAX_GAS_GWEI.toString(), "gwei"); + try { + const { data } = await axios({ + method: "get", + url: + network === NETWORKS.POLYGON + ? "https://gasstation.polygon.technology/v2" + : network === NETWORKS.MUMBAI_STG || network === NETWORKS.MUMBAI_DEV + ? "https://gasstation-testnet.polygon.technology/v2" + : "", + }); + const maxFee = Math.ceil(data.fast.maxFee); + const maxPriorityFee = Math.ceil(data.fast.maxPriorityFee); + if (maxFee > MAX_GAS_GWEI) throw Error("maxFee が MAX_GAS_GWEI を超えています。"); + if (maxPriorityFee > MAX_GAS_GWEI) throw Error("maxFeePerGas が MAX_GAS_GWEI を超えています。"); + maxFeePerGas = ethers.utils.parseUnits(maxFee.toString(), "gwei"); + maxPriorityFeePerGas = ethers.utils.parseUnits(maxPriorityFee.toString(), "gwei"); + } catch (e) { + console.log(e); + if (e instanceof Error) throw Error(`"ガス代を取得できませんでした。\n\n${e.message}`); + throw Error("ガス代を取得できませんでした。"); + } + return { maxFeePerGas, maxPriorityFeePerGas }; +}; \ No newline at end of file From 37736375332f9aae57ed845962efec1056404ca7 Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:52:24 +0900 Subject: [PATCH 2/2] delete: unnecessary file --- hardhat/scripts/utils/utils.ts | 41 ---------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 hardhat/scripts/utils/utils.ts diff --git a/hardhat/scripts/utils/utils.ts b/hardhat/scripts/utils/utils.ts deleted file mode 100644 index 6eefcbfa..00000000 --- a/hardhat/scripts/utils/utils.ts +++ /dev/null @@ -1,41 +0,0 @@ -import axios from "axios"; -import { ethers } from "hardhat"; - -const MAX_GAS_GWEI = 150; -const NETWORKS = { - POLYGON: "polygon", - MUMBAI_STG: "mumbai-stg", - MUMBAI_DEV: "mumbai-dev", -}; - -/* - * polygon mainnet or mumbaiのガス代を計算する - * @param network ネットワーク名 - * @return ガス代 - */ -export const calcMaxGas = async (network: string) => { - let maxFeePerGas = ethers.utils.parseUnits(MAX_GAS_GWEI.toString(), "gwei"); - let maxPriorityFeePerGas = ethers.utils.parseUnits(MAX_GAS_GWEI.toString(), "gwei"); - try { - const { data } = await axios({ - method: "get", - url: - network === NETWORKS.POLYGON - ? "https://gasstation.polygon.technology/v2" - : network === NETWORKS.MUMBAI_STG || network === NETWORKS.MUMBAI_DEV - ? "https://gasstation-testnet.polygon.technology/v2" - : "", - }); - const maxFee = Math.ceil(data.fast.maxFee); - const maxPriorityFee = Math.ceil(data.fast.maxPriorityFee); - if (maxFee > MAX_GAS_GWEI) throw Error("maxFee が MAX_GAS_GWEI を超えています。"); - if (maxPriorityFee > MAX_GAS_GWEI) throw Error("maxFeePerGas が MAX_GAS_GWEI を超えています。"); - maxFeePerGas = ethers.utils.parseUnits(maxFee.toString(), "gwei"); - maxPriorityFeePerGas = ethers.utils.parseUnits(maxPriorityFee.toString(), "gwei"); - } catch (e) { - console.log(e); - if (e instanceof Error) throw Error(`"ガス代を取得できませんでした。\n\n${e.message}`); - throw Error("ガス代を取得できませんでした。"); - } - return { maxFeePerGas, maxPriorityFeePerGas }; -}; \ No newline at end of file