From 8a0d2526564b2830fac7570ae08b1aca125ff415 Mon Sep 17 00:00:00 2001 From: Dennison Bertram Date: Wed, 27 Apr 2022 08:08:11 -0400 Subject: [PATCH 1/2] new update --- packages/wizard-frontend/abi/minter.js | 0 .../wizard-frontend/components/Minter.tsx | 16 + .../wizard-frontend/components/NavbarLinks.ts | 10 + .../components/admin/GovernanceAdmin.tsx | 47 ++ .../components/admin/MinterAdmin.tsx | 439 ++++++++++++++++++ .../admin/MinterAdminMaxMintsView.tsx | 54 +++ .../admin/PaymentSplitterAdminForm.tsx | 55 +++ .../components/admin/TokenAdmin.tsx | 271 +++++++++++ .../components/minter/IncrementalMinter.tsx | 14 + .../minter/IncrementalMinterMintForm.tsx | 92 ++++ .../components/minter/SpecificIdMinter.tsx | 14 + .../minter/SpecificIdMinterMintForm.tsx | 51 ++ .../lib/contractWrappers/deployer.ts | 16 + .../lib/contractWrappers/minter.ts | 250 ++++++++++ .../lib/contractWrappers/mintingFilter.ts | 39 ++ .../lib/contractWrappers/timelock.ts | 6 + .../lib/contractWrappers/token.ts | 125 +++++ packages/wizard-frontend/pages/admin.tsx | 37 ++ packages/wizard-frontend/pages/mint.tsx | 22 + 19 files changed, 1558 insertions(+) create mode 100644 packages/wizard-frontend/abi/minter.js create mode 100644 packages/wizard-frontend/components/Minter.tsx create mode 100644 packages/wizard-frontend/components/NavbarLinks.ts create mode 100644 packages/wizard-frontend/components/admin/GovernanceAdmin.tsx create mode 100644 packages/wizard-frontend/components/admin/MinterAdmin.tsx create mode 100644 packages/wizard-frontend/components/admin/MinterAdminMaxMintsView.tsx create mode 100644 packages/wizard-frontend/components/admin/PaymentSplitterAdminForm.tsx create mode 100644 packages/wizard-frontend/components/admin/TokenAdmin.tsx create mode 100644 packages/wizard-frontend/components/minter/IncrementalMinter.tsx create mode 100644 packages/wizard-frontend/components/minter/IncrementalMinterMintForm.tsx create mode 100644 packages/wizard-frontend/components/minter/SpecificIdMinter.tsx create mode 100644 packages/wizard-frontend/components/minter/SpecificIdMinterMintForm.tsx create mode 100644 packages/wizard-frontend/lib/contractWrappers/deployer.ts create mode 100644 packages/wizard-frontend/lib/contractWrappers/minter.ts create mode 100644 packages/wizard-frontend/lib/contractWrappers/mintingFilter.ts create mode 100644 packages/wizard-frontend/lib/contractWrappers/timelock.ts create mode 100644 packages/wizard-frontend/lib/contractWrappers/token.ts create mode 100644 packages/wizard-frontend/pages/admin.tsx create mode 100644 packages/wizard-frontend/pages/mint.tsx diff --git a/packages/wizard-frontend/abi/minter.js b/packages/wizard-frontend/abi/minter.js new file mode 100644 index 00000000..e69de29b diff --git a/packages/wizard-frontend/components/Minter.tsx b/packages/wizard-frontend/components/Minter.tsx new file mode 100644 index 00000000..7a724ed9 --- /dev/null +++ b/packages/wizard-frontend/components/Minter.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import { IncrementalMinter } from './minter/IncrementalMinter' +import { SpecificIdMinter } from './minter/SpecificIdMinter' +import { config, MinterType } from '../config' + +// export const Minter = () => { +// if (config.minterType === MinterType.FixedPriceSequentialMinter) { +// return +// } else if (config.minterType === MinterType.FixedPriceSpecificIDMinter) { +// return +// } +// } + +export const Minter = () => { + return +} diff --git a/packages/wizard-frontend/components/NavbarLinks.ts b/packages/wizard-frontend/components/NavbarLinks.ts new file mode 100644 index 00000000..9017a477 --- /dev/null +++ b/packages/wizard-frontend/components/NavbarLinks.ts @@ -0,0 +1,10 @@ +export const NavbarLinks = [ + { + href: '/', + label: 'Mint', + }, + { + href: '/admin', + label: 'Admin', + }, +] diff --git a/packages/wizard-frontend/components/admin/GovernanceAdmin.tsx b/packages/wizard-frontend/components/admin/GovernanceAdmin.tsx new file mode 100644 index 00000000..3344b81e --- /dev/null +++ b/packages/wizard-frontend/components/admin/GovernanceAdmin.tsx @@ -0,0 +1,47 @@ +import { VStack } from '@chakra-ui/layout' +import { Heading, Text } from '@chakra-ui/react' +import { config } from '../../config' +import { useTimelockETHBalance } from '../../lib/contractWrappers/timelock' +import { showEther } from '@create-nft-dao/shared' +import { EtherscanLink } from '../EtherscanLink' +import { TallyLink } from '../TallyLink' + +export const GovernanceAdmin = () => { + const timelockETHBalance = useTimelockETHBalance() + + return ( + + + + Governor + + + Contract address:{' '} + + + + + + + + + Timelock + + + The treasury and executor of Governor. + + + Contract address:{' '} + + + Balance: {showEther(timelockETHBalance)} + + + ) +} diff --git a/packages/wizard-frontend/components/admin/MinterAdmin.tsx b/packages/wizard-frontend/components/admin/MinterAdmin.tsx new file mode 100644 index 00000000..9cdff2c1 --- /dev/null +++ b/packages/wizard-frontend/components/admin/MinterAdmin.tsx @@ -0,0 +1,439 @@ +import { Box, HStack, VStack } from '@chakra-ui/layout' +import { + Button, + NumberInput, + NumberInputField, + Heading, + Text, + Input, + FormControl, + FormLabel, + FormHelperText, +} from '@chakra-ui/react' +import { parseEther } from 'ethers/lib/utils' +import React, { useEffect, useState } from 'react' +import { config, MinterType } from '../../config' +import { + useFixedPriceSupplyMinterFunction, + useIncrementalMinterMintPrice, + useIsSaleActive, + useIncrementalMinterIsTokenPriceLocked, + useIncrementalMinterMaxTokens, + useIncrementalMinterIsMaxTokensLocked, + useStartingBlock, + useIsStartingBlockLocked, + useIsOwnerMintLocked, + useFixedPriceSequentialMinterFunction, + usePauseSale, + useUnpauseSale, + useMintingFilter, + useFixedPriceSpecificIDMinterFunction, + usePayeeGetter, + useSetMintingFilter, +} from '../../lib/contractWrappers/minter' +import { + useCloneAndInitMintingFilter, + NEW_MINTING_FILTER_CLONE_EVENT, +} from '../../lib/contractWrappers/deployer' +import { createMintingFilterInitCallData } from '../../lib/contractWrappers/mintingFilter' +import { MintingFilterEditForm } from '../MintingFilterEditForm' +import { PaymentSplitterAdminForm } from './PaymentSplitterAdminForm' +import { showEther } from '@create-nft-dao/shared' +import { useEthers } from '@usedapp/core' +import { MinterAdminMaxMintsView } from './MinterAdminMaxMintsView' + +export const MinterAdmin = () => { + const { account } = useEthers() + + const [formTokenPrice, setFormTokenPrice] = useState('') + const tokenPrice = useIncrementalMinterMintPrice() + const isTokenPriceLocked = useIncrementalMinterIsTokenPriceLocked() + const { send: setTokenPrice, state: setTokenPriceState } = + useFixedPriceSupplyMinterFunction('setTokenPrice') + const { send: lockTokenPrice, state: lockTokenPriceState } = + useFixedPriceSupplyMinterFunction('lockTokenPrice') + + const [formMaxTokens, setFormMaxTokens] = useState('') + const maxTokens = useIncrementalMinterMaxTokens() + const isMaxTokensLocked = useIncrementalMinterIsMaxTokensLocked() + const { send: setMaxTokens, state: setMaxTokensState } = + useFixedPriceSupplyMinterFunction('setMaxTokens') + const { send: lockMaxTokens, state: lockMaxTokensState } = + useFixedPriceSupplyMinterFunction('lockMaxTokens') + + const [formStartingBlock, setFormStartingBlock] = useState('') + const startingBlock = useStartingBlock() + const isStartingBlockLocked = useIsStartingBlockLocked() + const { send: setStartingBlock, state: setStartingBlockState } = + useFixedPriceSupplyMinterFunction('setStartingBlock') + const { send: lockStartingBlock, state: lockStartingBlockState } = + useFixedPriceSupplyMinterFunction('lockStartingBlock') + + const [ownerMintTo, setOwnerMintTo] = useState('') + const [ownerMintAmount, setOwnerMintAmount] = useState('') + const [ownerMintTokenID, setOwnerMintTokenID] = useState('') + const isOwnerMintLocked = useIsOwnerMintLocked() + const { send: sequentialOwnerMint, state: sequentialOwnerMintState } = + useFixedPriceSequentialMinterFunction('ownerMint') + const { send: idOwnerMint, state: idOwnerMintState } = + useFixedPriceSpecificIDMinterFunction('ownerMint') + const { send: lockOwnerMint, state: lockOwnerMintState } = + useFixedPriceSupplyMinterFunction('lockOwnerMint') + + const isSaleActive = useIsSaleActive() + const { send: pauseSale, state: pauseSaleState } = usePauseSale() + const { send: unpauseSale, state: unpauseSaleState } = useUnpauseSale() + + const mintingFilterAddress = useMintingFilter() + const creatorPayeeAddress = usePayeeGetter(0) + const daoPayeeAddress = usePayeeGetter(1) + + const onTokenPriceSubmit = (e) => { + e.preventDefault() + setTokenPrice(parseEther(formTokenPrice)) + } + + const onLockTokenPriceClick = () => { + lockTokenPrice() + } + + const onMaxTokensSubmit = (e) => { + e.preventDefault() + setMaxTokens(parseInt(formMaxTokens)) + } + + const onLockMaxTokensClick = () => { + lockMaxTokens() + } + + const onStartingBlockSubmit = (e) => { + e.preventDefault() + setStartingBlock(parseInt(formStartingBlock)) + } + + const onLockStartingBlockClick = () => { + lockStartingBlock() + } + + const onOwnerMintSubmit = (e) => { + e.preventDefault() + if (config.minterType === MinterType.FixedPriceSequentialMinter) { + sequentialOwnerMint(ownerMintTo, parseInt(ownerMintAmount)) + } else { + idOwnerMint(ownerMintTo, parseInt(ownerMintTokenID)) + } + } + + const onLockOwnerMintClick = () => { + lockOwnerMint() + } + + const onSaleStatusClick = () => { + if (isSaleActive) { + pauseSale() + } else { + unpauseSale() + } + } + + // MintingFilter Clone + Set on Minter + + const { + send: cloneAndInitMintingFilter, + state: cloneAndInitMintingFilterState, + events: cloneAndInitMintingFilterEvents, + } = useCloneAndInitMintingFilter() + const { send: setMintingFilter, state: setMintingFilterState } = + useSetMintingFilter() + const isDeployMintingFilterLoading = + cloneAndInitMintingFilterState.status === 'Mining' || + setMintingFilterState.status === 'Mining' + + const onDeployMintingFilter = (tokenAddresses, minBalances) => { + const initCallData = createMintingFilterInitCallData( + account, + tokenAddresses, + minBalances + ) + cloneAndInitMintingFilter({ + useMintingFilter: true, + implementationIndex: 0, + initCallData: initCallData, + }) + } + + useEffect(() => { + if ( + cloneAndInitMintingFilterEvents === undefined || + cloneAndInitMintingFilterEvents.length === 0 + ) { + return + } + + const cloneEvent = cloneAndInitMintingFilterEvents.find( + (e) => e.name === NEW_MINTING_FILTER_CLONE_EVENT + ) + + setMintingFilter(cloneEvent.args.mintingFilter) + }, [cloneAndInitMintingFilterEvents]) + + return ( + <> + Contract address: {config.minterAddress} + + + + Sale status + + + Sale status: {isSaleActive ? 'Open' : 'Paused'} + + + + + + Token price + + + Current: {showEther(tokenPrice)} +
+ + { + setFormTokenPrice(s) + }} + > + + + + +
+
+ + + Lock status: {isTokenPriceLocked ? 'Locked' : 'Unlocked'} + + + +
+ + + Max token supply + + + + Current: {maxTokens !== undefined ? maxTokens.toString() : ''} + +
+ + { + setFormMaxTokens(s) + }} + > + + + + +
+
+ + + Lock status: {isMaxTokensLocked ? 'Locked' : 'Unlocked'} + + + +
+ {config.minterType === MinterType.FixedPriceSequentialMinter ? ( + + ) : ( + <> + )} + + + Sale start block + + + + Current:{' '} + {startingBlock !== undefined ? startingBlock.toString() : ''} + +
+ + { + setFormStartingBlock(s) + }} + > + + + + +
+
+ + + Lock status: {isStartingBlockLocked ? 'Locked' : 'Unlocked'} + + + +
+ + + Owner mint + +
+ + + To + { + setOwnerMintTo(s.target.value) + }} + /> + + The Ethereum wallet address of the recipient. + + + + + {config.minterType === MinterType.FixedPriceSequentialMinter + ? 'Amount' + : 'Token ID'} + + { + if ( + config.minterType === + MinterType.FixedPriceSequentialMinter + ) { + setOwnerMintAmount(s) + } else { + setOwnerMintTokenID(s) + } + }} + > + + + + {config.minterType === MinterType.FixedPriceSequentialMinter + ? 'How many tokens to mint to the recipient.' + : 'The ID of the token to mint.'} + + + + +
+ + + Lock status: {isOwnerMintLocked ? 'Locked' : 'Unlocked'} + + + +
+ {mintingFilterAddress !== undefined ? ( + + + Buyer filtering + + + + ) : ( + <> + )} + + + Balance and funds release + + {creatorPayeeAddress !== undefined && + daoPayeeAddress !== undefined ? ( + + ) : ( + <> + )} + +
+ + ) +} diff --git a/packages/wizard-frontend/components/admin/MinterAdminMaxMintsView.tsx b/packages/wizard-frontend/components/admin/MinterAdminMaxMintsView.tsx new file mode 100644 index 00000000..eb3b0cd6 --- /dev/null +++ b/packages/wizard-frontend/components/admin/MinterAdminMaxMintsView.tsx @@ -0,0 +1,54 @@ +import { + VStack, + Heading, + Text, + HStack, + NumberInput, + NumberInputField, + Button, +} from '@chakra-ui/react' +import { useState } from 'react' +import { + useFixedPriceSequentialMinterFunction, + useMaxMintPerTx, +} from '../../lib/contractWrappers/minter' + +export const MinterAdminMaxMintsView = () => { + const maxMintsPerTx = useMaxMintPerTx() + const [formMaxMintsPerTx, setFormMaxMintsPerTx] = useState('') + const { send: setMaxMintsPerTx, state: setMaxMintsPerTxState } = + useFixedPriceSequentialMinterFunction('setMaxMintsPerTx') + + const onMaxMintsPerTxSubmit = (e) => { + e.preventDefault() + setMaxMintsPerTx(parseInt(formMaxMintsPerTx)) + } + + return ( + + + Max mints per transaction + + + Current: {maxMintsPerTx} +
+ + setFormMaxMintsPerTx(s)} + > + + + + +
+
+
+ ) +} diff --git a/packages/wizard-frontend/components/admin/PaymentSplitterAdminForm.tsx b/packages/wizard-frontend/components/admin/PaymentSplitterAdminForm.tsx new file mode 100644 index 00000000..23fc4c24 --- /dev/null +++ b/packages/wizard-frontend/components/admin/PaymentSplitterAdminForm.tsx @@ -0,0 +1,55 @@ +import { VStack, HStack } from '@chakra-ui/layout' +import { Button, Text } from '@chakra-ui/react' +import { showEther } from '@create-nft-dao/shared' +import { + useMinterETHBalance, + useSharesGetter, + useTotalShares, + useFixedPriceSupplyMinterFunction, +} from '../../lib/contractWrappers/minter' + +export const PaymentSplitterAdminForm = ({ + creatorPayeeAddress, + daoPayeeAddress, +}) => { + const ethBalance = useMinterETHBalance() + + const creatorShares = useSharesGetter(creatorPayeeAddress) + const daoShares = useSharesGetter(daoPayeeAddress) + const totalShares = useTotalShares() + + const { send: releaseFunds, state: releaseFundsState } = + useFixedPriceSupplyMinterFunction('release') + + const releaseCreatorFunds = () => { + releaseFunds(creatorPayeeAddress) + } + + const releaseDAOFunds = () => { + releaseFunds(daoPayeeAddress) + } + + return ( + + Current minter balance: {showEther(ethBalance)} + + Creator shares: {`${(creatorShares / totalShares) * 100}%`} + + + + DAO shares: {`${(daoShares / totalShares) * 100}%`} + + + + ) +} diff --git a/packages/wizard-frontend/components/admin/TokenAdmin.tsx b/packages/wizard-frontend/components/admin/TokenAdmin.tsx new file mode 100644 index 00000000..0ddf52e1 --- /dev/null +++ b/packages/wizard-frontend/components/admin/TokenAdmin.tsx @@ -0,0 +1,271 @@ +import { HStack, VStack } from '@chakra-ui/layout' +import { Heading, Text, Button, Input } from '@chakra-ui/react' +import { config } from '../../config' +import { OpenSeaLink } from '../OpenSeaLink' +import { RaribleLink } from '../RaribleLink' +import { + useNFTName, + useTotalSupply, + useBaseURIEnabled, + useBaseURI, + useSetBaseURIEnabled, + useSetBaseURI, + useContractInfoURI, + useSetContractInfoURI, + useRoyaltyInfo, + useSetRoyalties, + useProxyRegistryEnabled, + useSetProxyRegistryEnabled, + useProxyRegistry, + useSetProxyRegistryAndEnable, +} from '../../lib/contractWrappers/token' +import { useState } from 'react' +import { RoyaltiesForm, RoyaltiesParams } from '@create-nft-dao/shared' + +export const TokenAdmin = () => { + const totalSupply = useTotalSupply() + const tokenName = useNFTName() + const isBaseURIEnabled = useBaseURIEnabled() + const baseURI = useBaseURI() + const contractInfoURI = useContractInfoURI() + + const [baseURIFormValue, setBaseURIFormValue] = useState('') + const [contractInfoURIFormValue, setContractInfoURIFormValue] = useState('') + + const { send: setBaseURIEnabled, state: setBaseURIEnabledState } = + useSetBaseURIEnabled() + const { send: setBaseURI, state: setBaseURIState } = useSetBaseURI() + const { send: setContractInfoURI, state: setContractInfoURIState } = + useSetContractInfoURI() + + const { royaltiesRecipient, royaltiesBPs } = useRoyaltyInfo() + const [royaltiesFormValues, setRoyaltiesFormValues] = useState({}) + const { send: setRoyalties } = useSetRoyalties() + + const [proxyRegistryFormValue, setProxyRegistryFormValue] = useState('') + const isProxyRegistryEnabled = useProxyRegistryEnabled() + const proxyRegistryAddress = useProxyRegistry() + const { send: setProxyRegistryEnabled, state: setProxyRegistryEnabledState } = + useSetProxyRegistryEnabled() + const { + send: setProxyRegistryAndEnable, + state: setProxyRegistryAndEnableState, + } = useSetProxyRegistryAndEnable() + + const onBaseURIToggleClick = () => { + if (isBaseURIEnabled === undefined) { + return + } + setBaseURIEnabled(!isBaseURIEnabled) + } + + const onBaseURISubmit = (e) => { + e.preventDefault() + setBaseURI(baseURIFormValue) + } + + const onContractInfoURISubmit = (e) => { + e.preventDefault() + setContractInfoURI(contractInfoURIFormValue) + } + + const onUpdateRoyaltiesClick = (_) => { + const params = royaltiesFormValues as RoyaltiesParams + + let recipient = config.timelockAddress + if (params.isRoyaltiesRecipientOverrideEnabled) { + recipient = params.royaltiesRecipientOverride + } + + setRoyalties(recipient, params.royaltiesBPs) + } + + const onProxyRegistryEnabledToggleClick = () => { + if (isProxyRegistryEnabled === undefined) { + return + } + setProxyRegistryEnabled(!isProxyRegistryEnabled) + } + + const onProxyRegistrySubmit = (e) => { + e.preventDefault() + setProxyRegistryAndEnable(proxyRegistryFormValue) + } + + return ( + + + + Name: {tokenName} + + Contract address: {config.tokenAddress} + + + + + + Total supply (minted so far): {totalSupply && totalSupply.toString()} + + + + + Assets URI + + + + Base URI Enabled + + + + Current value: {isBaseURIEnabled ? 'Enabled' : 'Disabled'} + + + + + When Base URI is disabled, your NFTs use an auto-generated + placeholder image that contains the name of your NFT and the ID of + the asset. + + + + + Base URI + + Current value: {baseURI} +
+ + { + setBaseURIFormValue(e.target.value) + }} + /> + + +
+
+ + + Contract Info URI + + Current value: {contractInfoURI} +
+ + { + setContractInfoURIFormValue(e.target.value) + }} + /> + + +
+
+
+ + + Royalties + + + + Current values + + + Recipient:{' '} + {royaltiesRecipient + ? royaltiesRecipient.toLowerCase() === + config.timelockAddress.toLowerCase() + ? 'The DAO' + : royaltiesRecipient + : ''} + + + Royalties:{' '} + {royaltiesBPs + ? (royaltiesBPs.toNumber() / 100).toString() + '%' + : ''} + + + + + Update values + + + + + + + + OpenSea proxy registry + + + This feature makes trading on OpenSea cheaper, since it removes the + gas cost of submitting a transaction that approves OpenSea to trade + your NFT on behalf of their users. + + + + Current state: {isProxyRegistryEnabled ? 'Enabled' : 'Disabled'} + + + + Current proxy address: {proxyRegistryAddress} + +
+ + { + setProxyRegistryFormValue(e.target.value) + }} + /> + + +
+ + On the Ethereum mainnet the OpenSea proxy registry is at + 0xa5409ec958C83C3f309868babACA7c86DCB077c1. On Rinkeby it's at + 0xF57B2c51dED3A29e6891aba85459d600256Cf317. + +
+
+
+ ) +} diff --git a/packages/wizard-frontend/components/minter/IncrementalMinter.tsx b/packages/wizard-frontend/components/minter/IncrementalMinter.tsx new file mode 100644 index 00000000..ce2f5d1a --- /dev/null +++ b/packages/wizard-frontend/components/minter/IncrementalMinter.tsx @@ -0,0 +1,14 @@ +import { HStack } from '@chakra-ui/react' +import React from 'react' +import { useIncrementalMinterMintPrice } from '../../lib/contractWrappers/minter' +import { IncrementalMinterMintForm } from './IncrementalMinterMintForm' + +export const IncrementalMinter = () => { + const tokenPrice = useIncrementalMinterMintPrice() + + return ( + + + + ) +} diff --git a/packages/wizard-frontend/components/minter/IncrementalMinterMintForm.tsx b/packages/wizard-frontend/components/minter/IncrementalMinterMintForm.tsx new file mode 100644 index 00000000..2d615512 --- /dev/null +++ b/packages/wizard-frontend/components/minter/IncrementalMinterMintForm.tsx @@ -0,0 +1,92 @@ +import { + Box, + Button, + FormControl, + FormLabel, + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, + VStack, +} from '@chakra-ui/react' +import { FixedPriceSequentialMinterABI } from '@create-nft-dao/hardhat' +import { useContractFunction } from '@usedapp/core' +import { formatEther } from 'ethers/lib/utils' +import { Contract, utils } from 'ethers' + +import React, { useState, useEffect } from 'react' +import { useMaxMintPerTx } from '../../lib/contractWrappers/minter' +import { useRouter } from 'next/router' + +export const IncrementalMinterMintForm = ({ tokenPrice }) => { + const router = useRouter() + const [minter, setMinter] = useState(null) + useEffect(() => { + if (router?.query?.minter) { + setMinter(router.query.minter) + } + }, [router]) + + + const maxMintPerTx = useMaxMintPerTx({ minterAddress: minter }) + const [tokensToMint, setTokensToMint] = useState(1) + const valueToSend = tokenPrice && tokenPrice.mul(tokensToMint) + + const [contract, setContract] = useState(null) + + + useEffect(() => { + if (minter) { + setContract( + new Contract( + '0xac23a272C3a1f9F0E6047Ac1B90EffDf51A4071b', + FixedPriceSequentialMinterABI.abi + ) + ) + } + }, [minter]) + + const { send: mint, state: mintState } = useContractFunction(contract, 'mint') + + const mintClicked = () => { + mint(tokensToMint, { value: valueToSend }) + } + + return ( + + + Tokens to mint (max {maxMintPerTx}): + setTokensToMint(n)} + > + + + + + + + + + ETH: {valueToSend && formatEther(valueToSend)} + + + + {mintState.status !== 'None' ? `tx status: ${mintState.status}` : ''} + + + {mintState.status === 'Exception' ? mintState.errorMessage : ''} + + + ) +} diff --git a/packages/wizard-frontend/components/minter/SpecificIdMinter.tsx b/packages/wizard-frontend/components/minter/SpecificIdMinter.tsx new file mode 100644 index 00000000..0c51a84b --- /dev/null +++ b/packages/wizard-frontend/components/minter/SpecificIdMinter.tsx @@ -0,0 +1,14 @@ +import { HStack } from '@chakra-ui/react' +import React from 'react' +import { useSpecificIdMinterMintPrice } from '../../lib/contractWrappers/minter' +import { SpecificIdMinterMintForm } from './SpecificIdMinterMintForm' + +export const SpecificIdMinter = () => { + const tokenPrice = useSpecificIdMinterMintPrice() + + return ( + + + + ) +} diff --git a/packages/wizard-frontend/components/minter/SpecificIdMinterMintForm.tsx b/packages/wizard-frontend/components/minter/SpecificIdMinterMintForm.tsx new file mode 100644 index 00000000..f4a894bb --- /dev/null +++ b/packages/wizard-frontend/components/minter/SpecificIdMinterMintForm.tsx @@ -0,0 +1,51 @@ +import { + Box, + Button, + FormControl, + FormLabel, + NumberInput, + NumberInputField, + VStack, +} from '@chakra-ui/react' +import { formatEther } from '@ethersproject/units' +import React, { useState } from 'react' +import { useSpecificIdMinterMint } from '../../lib/contractWrappers/minter' + +export const SpecificIdMinterMintForm = ({ tokenPrice }) => { + const [tokenIdToMint, setTokenIdToMint] = useState(1234) + const valueToSend = tokenPrice + + const { send: mint, state: mintState } = useSpecificIdMinterMint() + + const mintClicked = () => { + mint(tokenIdToMint, { value: valueToSend }) + } + + return ( + + + Token id to mint: + setTokenIdToMint(n)} + > + + + + + ETH: {valueToSend && formatEther(valueToSend)} + + + + {mintState.status !== 'None' ? `tx status: ${mintState.status}` : ''} + + + {mintState.status === 'Exception' ? mintState.errorMessage : ''} + + + ) +} diff --git a/packages/wizard-frontend/lib/contractWrappers/deployer.ts b/packages/wizard-frontend/lib/contractWrappers/deployer.ts new file mode 100644 index 00000000..d172cb16 --- /dev/null +++ b/packages/wizard-frontend/lib/contractWrappers/deployer.ts @@ -0,0 +1,16 @@ +import { useContractFunction } from '@usedapp/core' +import { Contract, utils } from 'ethers' +import { Interface } from 'ethers/lib/utils' +import { ERC721DAODeployerABI } from '@create-nft-dao/hardhat' +import { config } from '../../config' + +export const NEW_MINTING_FILTER_CLONE_EVENT = 'NewMintingFilterClone' + +const ERC721DAODeployerAbi: Interface = new utils.Interface( + ERC721DAODeployerABI.abi +) + +export const useCloneAndInitMintingFilter = () => { + const contract = new Contract(config.deployerAddress, ERC721DAODeployerAbi) + return useContractFunction(contract, 'cloneAndInitMintingFilter') +} diff --git a/packages/wizard-frontend/lib/contractWrappers/minter.ts b/packages/wizard-frontend/lib/contractWrappers/minter.ts new file mode 100644 index 00000000..714d25ca --- /dev/null +++ b/packages/wizard-frontend/lib/contractWrappers/minter.ts @@ -0,0 +1,250 @@ +import { + useContractCall, + useContractFunction, + useEtherBalance, +} from '@usedapp/core' +import { Contract, utils } from 'ethers' +import { Interface } from 'ethers/lib/utils' +import { + ERC721MinterABI, + FixedPriceSequentialMinterABI, + FixedPriceSpecificIDMinterABI, + FixedPriceFixedSupplyMinterABI, +} from '@create-nft-dao/hardhat' + +import { config } from '../../config' + +const ERC721MinterAbi: Interface = new utils.Interface(ERC721MinterABI.abi) +const FixedPriceSequentialMinterAbi: Interface = new utils.Interface( + FixedPriceSequentialMinterABI.abi +) +const FixedPriceSpecificIDMinterAbi: Interface = new utils.Interface( + FixedPriceSpecificIDMinterABI.abi +) + +const FixedPriceFixedSupplyMinterAbi: Interface = new utils.Interface( + FixedPriceFixedSupplyMinterABI.abi +) + +export const useIsSaleActive = () => { + const [paused] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'paused', + args: [], + }) || [] + return !paused +} + +export const useIncrementalMinterMintPrice = () => { + return useMintPrice(FixedPriceSequentialMinterAbi) +} + +export const useSpecificIdMinterMintPrice = () => { + return useMintPrice(FixedPriceSpecificIDMinterAbi) +} + +function useMintPrice(abi: utils.Interface, minterAddress) { + const [tokenPrice] = + useContractCall({ + abi: abi, + address: minterAddress, + method: 'tokenPrice', + args: [], + }) || [] + return tokenPrice +} + +export const useIncrementalMinterIsTokenPriceLocked = () => { + return useIsTokenPriceLocked(FixedPriceSequentialMinterAbi) +} + +function useIsTokenPriceLocked(abi: utils.Interface) { + const [isTokenPriceLocked] = + useContractCall({ + abi: abi, + address: config.minterAddress, + method: 'isTokenPriceLocked', + args: [], + }) || [] + return isTokenPriceLocked +} + +export const useIncrementalMinterMaxTokens = () => { + return useMaxTokens(FixedPriceSequentialMinterAbi) +} + +function useMaxTokens(abi: utils.Interface) { + const [maxTokens] = + useContractCall({ + abi: abi, + address: config.minterAddress, + method: 'maxTokens', + args: [], + }) || [] + return maxTokens +} + +export const useIncrementalMinterIsMaxTokensLocked = () => { + return useIsMaxTokensLocked(FixedPriceSequentialMinterAbi) +} + +function useIsMaxTokensLocked(abi: utils.Interface) { + const [isMaxTokensLocked] = + useContractCall({ + abi: abi, + address: config.minterAddress, + method: 'isMaxTokensLocked', + args: [], + }) || [] + return isMaxTokensLocked +} + +export const useMaxMintPerTx = ({ minterAddress }) => { + const [maxMintPerTx] = + useContractCall({ + abi: FixedPriceSequentialMinterAbi, + address: minterAddress, + method: 'maxMintsPerTx', + args: [], + }) || [] + return maxMintPerTx && maxMintPerTx.toNumber() +} + +export const useIncrementalMinterMint = ({ minterAddress }) => { + + const contract = new Contract( + "0xac23a272C3a1f9F0E6047Ac1B90EffDf51A4071b", + FixedPriceSequentialMinterAbi + ) + return useContractFunction(contract, 'mint') +} + +export const useSpecificIdMinterMint = () => { + const contract = new Contract( + config.minterAddress, + FixedPriceSpecificIDMinterAbi + ) + return useContractFunction(contract, 'mint') +} + +export const usePauseSale = () => { + const contract = new Contract(config.minterAddress, ERC721MinterAbi) + return useContractFunction(contract, 'pause') +} + +export const useUnpauseSale = () => { + const contract = new Contract(config.minterAddress, ERC721MinterAbi) + return useContractFunction(contract, 'unpause') +} + +export const useStartingBlock = () => { + const [startBlock] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'startingBlock', + args: [], + }) || [] + return startBlock && startBlock.toNumber() +} + +export const useIsStartingBlockLocked = () => { + const [isStartingBlockLocked] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'isStartingBlockLocked', + args: [], + }) || [] + return isStartingBlockLocked +} + +export const useFixedPriceSupplyMinterFunction = (functionName: string) => { + const contract = new Contract( + config.minterAddress, + FixedPriceFixedSupplyMinterAbi + ) + return useContractFunction(contract, functionName) +} + +export const useFixedPriceSequentialMinterFunction = (functionName: string) => { + const contract = new Contract( + config.minterAddress, + FixedPriceSequentialMinterAbi + ) + return useContractFunction(contract, functionName) +} + +export const useFixedPriceSpecificIDMinterFunction = (functionName: string) => { + const contract = new Contract( + config.minterAddress, + FixedPriceSpecificIDMinterAbi + ) + return useContractFunction(contract, functionName) +} + +export const useIsOwnerMintLocked = () => { + const [isOwnerMintLocked] = + useContractCall({ + abi: FixedPriceFixedSupplyMinterAbi, + address: config.minterAddress, + method: 'isOwnerMintLocked', + args: [], + }) || [] + return isOwnerMintLocked +} + +export const useMintingFilter = () => { + const [mintingFilter] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'mintingFilter', + args: [], + }) || [] + return mintingFilter +} + +export const useMinterETHBalance = () => { + return useEtherBalance(config.minterAddress) +} + +export const usePayeeGetter = (index: number) => { + const [address] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'payee', + args: [index], + }) || [] + return address +} + +export const useSharesGetter = (address: string) => { + const [shares] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'shares', + args: [address], + }) || [] + return shares && shares.toNumber() +} + +export const useTotalShares = () => { + const [totalShares] = + useContractCall({ + abi: ERC721MinterAbi, + address: config.minterAddress, + method: 'totalShares', + args: [], + }) || [] + return totalShares && totalShares.toNumber() +} + +export const useSetMintingFilter = () => { + const contract = new Contract(config.minterAddress, ERC721MinterAbi) + return useContractFunction(contract, 'setMintingFilter') +} diff --git a/packages/wizard-frontend/lib/contractWrappers/mintingFilter.ts b/packages/wizard-frontend/lib/contractWrappers/mintingFilter.ts new file mode 100644 index 00000000..ff7de683 --- /dev/null +++ b/packages/wizard-frontend/lib/contractWrappers/mintingFilter.ts @@ -0,0 +1,39 @@ +import { useContractCall, useContractFunction } from '@usedapp/core' +import { utils, Contract } from 'ethers' +import { Interface } from 'ethers/lib/utils' +import { NFTsMintingFilterABI } from '@create-nft-dao/hardhat' + +const NFTsMintingFilterAbi: Interface = new utils.Interface( + NFTsMintingFilterABI.abi +) + +export const useGetTokenFilters = (address: string) => { + const [tokenFilters] = + useContractCall({ + abi: NFTsMintingFilterAbi, + address: address, + method: 'getTokenFilters', + args: [], + }) || [] + return tokenFilters +} + +export const useMintingFilterFunction = ( + address: string, + functionName: string +) => { + const contract = new Contract(address, NFTsMintingFilterAbi) + return useContractFunction(contract, functionName) +} + +export const createMintingFilterInitCallData = ( + creatorAddress, + tokenAddresses, + minBalances +) => { + return NFTsMintingFilterAbi.encodeFunctionData('initialize', [ + creatorAddress, + tokenAddresses, + minBalances, + ]) +} diff --git a/packages/wizard-frontend/lib/contractWrappers/timelock.ts b/packages/wizard-frontend/lib/contractWrappers/timelock.ts new file mode 100644 index 00000000..f33a0c05 --- /dev/null +++ b/packages/wizard-frontend/lib/contractWrappers/timelock.ts @@ -0,0 +1,6 @@ +import { config } from '../../config' +import { useEtherBalance } from '@usedapp/core' + +export const useTimelockETHBalance = () => { + return useEtherBalance(config.timelockAddress) +} diff --git a/packages/wizard-frontend/lib/contractWrappers/token.ts b/packages/wizard-frontend/lib/contractWrappers/token.ts new file mode 100644 index 00000000..db37e265 --- /dev/null +++ b/packages/wizard-frontend/lib/contractWrappers/token.ts @@ -0,0 +1,125 @@ +import { useContractCall, useContractFunction } from '@usedapp/core' +import { utils, Contract } from 'ethers' +import { Interface } from 'ethers/lib/utils' +import { ERC721DAOTokenABI } from '@create-nft-dao/hardhat' +import { config } from '../../config' + +const tokenAbi: Interface = new utils.Interface(ERC721DAOTokenABI.abi) + +export const useNFTName = () => { + const [nftName] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'name', + args: [], + }) || [] + return nftName +} + +export const useTotalSupply = () => { + const [totalSupply] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'totalSupply', + args: [], + }) || [] + return totalSupply +} + +export const useBaseURIEnabled = () => { + const [baseURIEnabled] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'baseURIEnabled', + args: [], + }) || [] + return baseURIEnabled +} + +export const useBaseURI = () => { + const [baseURI] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'baseURI', + args: [], + }) || [] + return baseURI +} + +export const useSetBaseURIEnabled = () => { + const contract = new Contract(config.tokenAddress, tokenAbi) + return useContractFunction(contract, 'setBaseURIEnabled') +} + +export const useSetBaseURI = () => { + const contract = new Contract(config.tokenAddress, tokenAbi) + return useContractFunction(contract, 'setBaseURI') +} + +export const useContractInfoURI = () => { + const [contractInfoURI] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'contractInfoURI', + args: [], + }) || [] + return contractInfoURI +} + +export const useSetContractInfoURI = () => { + const contract = new Contract(config.tokenAddress, tokenAbi) + return useContractFunction(contract, 'setContractInfoURI') +} + +export const useRoyaltyInfo = () => { + const [royaltiesRecipient, royaltiesBPs] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'royaltyInfo', + args: [0, 10000], + }) || [] + return { royaltiesRecipient, royaltiesBPs } +} + +export const useSetRoyalties = () => { + const contract = new Contract(config.tokenAddress, tokenAbi) + return useContractFunction(contract, 'setRoyalties') +} + +export const useProxyRegistryEnabled = () => { + const [proxyRegistryEnabled] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'proxyRegistryEnabled', + args: [], + }) || [] + return proxyRegistryEnabled +} + +export const useSetProxyRegistryEnabled = () => { + const contract = new Contract(config.tokenAddress, tokenAbi) + return useContractFunction(contract, 'setProxyRegistryEnabled') +} + +export const useProxyRegistry = () => { + const [proxyRegistry] = + useContractCall({ + abi: tokenAbi, + address: config.tokenAddress, + method: 'proxyRegistry', + args: [], + }) || [] + return proxyRegistry +} + +export const useSetProxyRegistryAndEnable = () => { + const contract = new Contract(config.tokenAddress, tokenAbi) + return useContractFunction(contract, 'setProxyRegistryAndEnable') +} diff --git a/packages/wizard-frontend/pages/admin.tsx b/packages/wizard-frontend/pages/admin.tsx new file mode 100644 index 00000000..d3cb8279 --- /dev/null +++ b/packages/wizard-frontend/pages/admin.tsx @@ -0,0 +1,37 @@ +import { Layout } from '@create-nft-dao/shared' +import { Heading, VStack } from '@chakra-ui/react' +import React from 'react' +import { MinterAdmin } from '../components/admin/MinterAdmin' +import { TokenAdmin } from '../components/admin/TokenAdmin' +import { GovernanceAdmin } from '../components/admin/GovernanceAdmin' +import { NavbarLinks } from '../components/NavbarLinks' + +const Mint = () => { + const layoutProps = { + title: 'Create NFT DAO: Admin', + } + + return ( + + + + Admin + + + Minter contract + + + + NFT contract + + + + Governance + + + + + ) +} + +export default Mint diff --git a/packages/wizard-frontend/pages/mint.tsx b/packages/wizard-frontend/pages/mint.tsx new file mode 100644 index 00000000..f5198119 --- /dev/null +++ b/packages/wizard-frontend/pages/mint.tsx @@ -0,0 +1,22 @@ +import { Layout } from '@create-nft-dao/shared' +import { Minter } from '../components/Minter' +import { Center, Container } from '@chakra-ui/react' +import { NavbarLinks } from '../components/NavbarLinks' + +const Mint = (props) => { + const layoutProps = { + title: 'Mint', + } + + return ( + + +
+ +
+
+
+ ) +} + +export default Mint From 463f973f5254eccf98f885e0b57872ad96c1b374 Mon Sep 17 00:00:00 2001 From: Dennison Bertram Date: Wed, 4 May 2022 20:13:57 -0400 Subject: [PATCH 2/2] add polygon --- packages/hardhat/.env.example | 7 ---- .../deployed_contracts.polygon.json | 42 +++++++++++++++++++ packages/hardhat/hardhat.base.config.ts | 8 ++++ 3 files changed, 50 insertions(+), 7 deletions(-) delete mode 100644 packages/hardhat/.env.example create mode 100644 packages/hardhat/deployed_contracts/deployed_contracts.polygon.json diff --git a/packages/hardhat/.env.example b/packages/hardhat/.env.example deleted file mode 100644 index 87d15910..00000000 --- a/packages/hardhat/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -MNEMONIC= -ALCHEMY_API_KEY= -ETHERSCAN_API_KEY= -POLYGONSCAN_API_KEY= -MUMBAI_RPC_URL= -MAINNET_RPC_URL= -COINMARKETCAP_KEY= \ No newline at end of file diff --git a/packages/hardhat/deployed_contracts/deployed_contracts.polygon.json b/packages/hardhat/deployed_contracts/deployed_contracts.polygon.json new file mode 100644 index 00000000..62aa28de --- /dev/null +++ b/packages/hardhat/deployed_contracts/deployed_contracts.polygon.json @@ -0,0 +1,42 @@ +[ + { + "name": "ERC721DAOToken", + "address": "0x913F5DBBE810798A3319879328936004E81B9F6A" + }, + { + "name": "ERC721Timelock", + "address": "0x0D585EA7b14DFc355C71076d63810395057D0BEF" + }, + { + "name": "ERC721Governor", + "address": "0x0B4c96B65b17706461a54611BB5461C17b5aB267" + }, + { + "name": "SVGPlaceholder", + "address": "0x5Cad586c443E42B53A99bfDb26503CB5CD498230" + }, + { + "name": "FixedPriceSequentialMinter", + "address": "0xac9a22EEB2FBd580999671BCAE9CEb44A8f8D4cA" + }, + { + "name": "FixedPriceSpecificIDMinter", + "address": "0x1D4b43903532EFC06d4Ba3b87626Cb60E6aE3afF" + }, + { + "name": "RequiredNFTsMintingFilter", + "address": "0xdDaD8E66Ff347C064D0f4C4EcCD0b72b12cf9237" + }, + { + "name": "RejectedNFTsMintingFilter", + "address": "0x6EE522634d6d0eB32e32469d6d82d1ad1aa65352" + }, + { + "name": "CompositeMintingFilter", + "address": "0xBE370EADC3941e96d325628431B03b4D860cC691" + }, + { + "name": "ERC721DAODeployer", + "address": "0xc693e79EEBdFB5766c0E6A0c9AA1e45C58f9506c" + } +] \ No newline at end of file diff --git a/packages/hardhat/hardhat.base.config.ts b/packages/hardhat/hardhat.base.config.ts index 084b1d04..d8a5d2c2 100644 --- a/packages/hardhat/hardhat.base.config.ts +++ b/packages/hardhat/hardhat.base.config.ts @@ -6,6 +6,7 @@ import "hardhat-gas-reporter"; import "hardhat-contract-sizer"; import { task } from "hardhat/config"; import { HardhatUserConfig } from "hardhat/types"; +import { string } from "hardhat/internal/core/params/argumentTypes"; require("dotenv").config(); // This is a sample Hardhat task. To learn how to create your own go to @@ -62,6 +63,12 @@ const config: HardhatUserConfig = { url: "https://rpc-mumbai.matic.today", accounts: { mnemonic: process.env.MNEMONIC || "dummy-value" }, }, + polygon: { + url: `https://polygon-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, + gas: 2100000, + gasPrice: 90000000000, + accounts: ['a389f1f95283f727f94dae0aea4d4fc668273bd6fbb6fa254df9cf2f2baceedf'] + } }, etherscan: { apiKey: { @@ -69,6 +76,7 @@ const config: HardhatUserConfig = { rinkeby: process.env.ETHERSCAN_API_KEY, optimisticKovan: process.env.ETHERSCAN_OPTIMISM_API_KEY, polygonMumbai: process.env.POLYGONSCAN_API_KEY, + polygon: process.env.POLYGONSCAN_API_KEY, arbitrumTestnet: process.env.ARBISCAN_API_KEY, }, },