From 698ed93f407635c47f2bdb592e2f7202db15aedb Mon Sep 17 00:00:00 2001 From: neokry Date: Thu, 18 Jan 2024 11:23:08 -0800 Subject: [PATCH] Add support for manager v2 --- apps/web/src/data/contract/abis/ManagerV2.ts | 866 ++++++++++++++++++ .../ReviewAndDeploy/ReviewAndDeploy.tsx | 51 +- .../components/Upgrade/Upgrade.tsx | 3 +- .../components/Upgrade/versions/2.0.0.md | 21 + .../components/Upgrade/versions/index.ts | 1 + .../create-proposal/constants/versions.ts | 5 + .../src/pages/dao/[network]/[token]/index.tsx | 12 +- 7 files changed, 943 insertions(+), 16 deletions(-) create mode 100644 apps/web/src/data/contract/abis/ManagerV2.ts create mode 100644 apps/web/src/modules/create-proposal/components/Upgrade/versions/2.0.0.md diff --git a/apps/web/src/data/contract/abis/ManagerV2.ts b/apps/web/src/data/contract/abis/ManagerV2.ts new file mode 100644 index 000000000..77c33baa5 --- /dev/null +++ b/apps/web/src/data/contract/abis/ManagerV2.ts @@ -0,0 +1,866 @@ +export const managerV2Abi = [ + { + inputs: [ + { + internalType: 'address', + name: '_tokenImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_metadataImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_auctionImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_treasuryImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_governorImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_builderRewardsRecipient', + type: 'address', + }, + ], + stateMutability: 'payable', + type: 'constructor', + }, + { + inputs: [], + name: 'ADDRESS_ZERO', + type: 'error', + }, + { + inputs: [], + name: 'ALREADY_INITIALIZED', + type: 'error', + }, + { + inputs: [], + name: 'DELEGATE_CALL_FAILED', + type: 'error', + }, + { + inputs: [], + name: 'FOUNDER_REQUIRED', + type: 'error', + }, + { + inputs: [], + name: 'INITIALIZING', + type: 'error', + }, + { + inputs: [], + name: 'INVALID_TARGET', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'impl', + type: 'address', + }, + ], + name: 'INVALID_UPGRADE', + type: 'error', + }, + { + inputs: [], + name: 'NOT_INITIALIZING', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_CALL', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_DELEGATECALL', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_OWNER', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_PENDING_OWNER', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_PROXY', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_TOKEN_OWNER', + type: 'error', + }, + { + inputs: [], + name: 'ONLY_UUPS', + type: 'error', + }, + { + inputs: [], + name: 'UNSUPPORTED_UUID', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'metadata', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'auction', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'treasury', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'governor', + type: 'address', + }, + ], + name: 'DAODeployed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'version', + type: 'uint256', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'renderer', + type: 'address', + }, + ], + name: 'MetadataRendererUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'canceledOwner', + type: 'address', + }, + ], + name: 'OwnerCanceled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'pendingOwner', + type: 'address', + }, + ], + name: 'OwnerPending', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'prevOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnerUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'baseImpl', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'upgradeImpl', + type: 'address', + }, + ], + name: 'UpgradeRegistered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'baseImpl', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'upgradeImpl', + type: 'address', + }, + ], + name: 'UpgradeRemoved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'impl', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'acceptOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'auctionImpl', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'builderRewardsRecipient', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'cancelOwnershipTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'contractVersion', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'wallet', + type: 'address', + }, + { + internalType: 'uint256', + name: 'ownershipPct', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'vestExpiry', + type: 'uint256', + }, + ], + internalType: 'struct IManager.FounderParams[]', + name: '_founderParams', + type: 'tuple[]', + }, + { + components: [ + { + internalType: 'bytes', + name: 'initStrings', + type: 'bytes', + }, + { + internalType: 'address', + name: 'metadataRenderer', + type: 'address', + }, + { + internalType: 'uint256', + name: 'reservedUntilTokenId', + type: 'uint256', + }, + ], + internalType: 'struct IManager.TokenParams', + name: '_tokenParams', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint256', + name: 'reservePrice', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'duration', + type: 'uint256', + }, + { + internalType: 'address', + name: 'founderRewardRecipent', + type: 'address', + }, + { + internalType: 'uint16', + name: 'founderRewardBps', + type: 'uint16', + }, + ], + internalType: 'struct IManager.AuctionParams', + name: '_auctionParams', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint256', + name: 'timelockDelay', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'votingDelay', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'votingPeriod', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'proposalThresholdBps', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'quorumThresholdBps', + type: 'uint256', + }, + { + internalType: 'address', + name: 'vetoer', + type: 'address', + }, + ], + internalType: 'struct IManager.GovParams', + name: '_govParams', + type: 'tuple', + }, + ], + name: 'deploy', + outputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + internalType: 'address', + name: 'metadata', + type: 'address', + }, + { + internalType: 'address', + name: 'auction', + type: 'address', + }, + { + internalType: 'address', + name: 'treasury', + type: 'address', + }, + { + internalType: 'address', + name: 'governor', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address', + }, + ], + name: 'getAddresses', + outputs: [ + { + internalType: 'address', + name: 'metadata', + type: 'address', + }, + { + internalType: 'address', + name: 'auction', + type: 'address', + }, + { + internalType: 'address', + name: 'treasury', + type: 'address', + }, + { + internalType: 'address', + name: 'governor', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + ], + name: 'getDAOVersions', + outputs: [ + { + components: [ + { + internalType: 'string', + name: 'token', + type: 'string', + }, + { + internalType: 'string', + name: 'metadata', + type: 'string', + }, + { + internalType: 'string', + name: 'auction', + type: 'string', + }, + { + internalType: 'string', + name: 'treasury', + type: 'string', + }, + { + internalType: 'string', + name: 'governor', + type: 'string', + }, + ], + internalType: 'struct IManager.DAOVersionInfo', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLatestVersions', + outputs: [ + { + components: [ + { + internalType: 'string', + name: 'token', + type: 'string', + }, + { + internalType: 'string', + name: 'metadata', + type: 'string', + }, + { + internalType: 'string', + name: 'auction', + type: 'string', + }, + { + internalType: 'string', + name: 'treasury', + type: 'string', + }, + { + internalType: 'string', + name: 'governor', + type: 'string', + }, + ], + internalType: 'struct IManager.DAOVersionInfo', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'governorImpl', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_newOwner', + type: 'address', + }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_baseImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_upgradeImpl', + type: 'address', + }, + ], + name: 'isRegisteredUpgrade', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'metadataImpl', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'pendingOwner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_baseImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_upgradeImpl', + type: 'address', + }, + ], + name: 'registerUpgrade', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_baseImpl', + type: 'address', + }, + { + internalType: 'address', + name: '_upgradeImpl', + type: 'address', + }, + ], + name: 'removeUpgrade', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_newOwner', + type: 'address', + }, + ], + name: 'safeTransferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address', + }, + { + internalType: 'address', + name: '_newRendererImpl', + type: 'address', + }, + { + internalType: 'bytes', + name: '_setupRenderer', + type: 'bytes', + }, + ], + name: 'setMetadataRenderer', + outputs: [ + { + internalType: 'address', + name: 'metadata', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'tokenImpl', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'treasuryImpl', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_newImpl', + type: 'address', + }, + ], + name: 'upgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_newImpl', + type: 'address', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, +] as const diff --git a/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx b/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx index cd5c6d5e6..3a0e815fd 100644 --- a/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx +++ b/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx @@ -3,7 +3,7 @@ import { ethers } from 'ethers' import { getFetchableUrl } from 'ipfs-service' import React, { useState } from 'react' import { getAddress, parseEther } from 'viem' -import { useAccount } from 'wagmi' +import { useAccount, useContractRead } from 'wagmi' import { WriteContractUnpreparedArgs, prepareWriteContract, @@ -17,6 +17,7 @@ import { Icon } from 'src/components/Icon' import { PUBLIC_MANAGER_ADDRESS } from 'src/constants/addresses' import { NULL_ADDRESS } from 'src/constants/addresses' import { managerAbi } from 'src/data/contract/abis' +import { managerV2Abi } from 'src/data/contract/abis/ManagerV2' import { formatAuctionDuration, formatFounderAllocation } from 'src/modules/create-dao' import { useChainStore } from 'src/stores/useChainStore' import { @@ -61,6 +62,12 @@ export const ReviewAndDeploy: React.FC = ({ title }) => { const [hasConfirmedChain, setHasConfirmedChain] = useState(false) const [deploymentError, setDeploymentError] = useState() const chain = useChainStore((x) => x.chain) + const { data: version, isLoading: isVersionLoading } = useContractRead({ + abi: managerAbi, + address: PUBLIC_MANAGER_ADDRESS[chain.id], + functionName: 'contractVersion', + chainId: chain.id, + }) const { address } = useAccount() const { @@ -106,7 +113,9 @@ export const ReviewAndDeploy: React.FC = ({ title }) => { ] ) - const tokenParams = { initStrings: ethers.utils.hexlify(tokenParamsHex) as AddressType } + const tokenParams = { + initStrings: ethers.utils.hexlify(tokenParamsHex) as AddressType, + } const auctionParams = { reservePrice: auctionSettings.auctionReservePrice @@ -163,13 +172,34 @@ export const ReviewAndDeploy: React.FC = ({ title }) => { setIsPendingTransaction(true) let transaction try { - const config = await prepareWriteContract({ - address: PUBLIC_MANAGER_ADDRESS[chain.id], - chainId: chain.id, - abi: managerAbi, - functionName: 'deploy', - args: [founderParams, tokenParams, auctionParams, govParams], - }) + let config: any + if (version?.startsWith('2')) { + config = await prepareWriteContract({ + address: PUBLIC_MANAGER_ADDRESS[chain.id], + chainId: chain.id, + abi: managerV2Abi, + functionName: 'deploy', + args: [ + founderParams, + { ...tokenParams, reservedUntilTokenId: 0n, metadataRenderer: NULL_ADDRESS }, + { + ...auctionParams, + founderRewardRecipent: NULL_ADDRESS, + founderRewardBps: 0, + }, + govParams, + ], + }) + } else { + config = await prepareWriteContract({ + address: PUBLIC_MANAGER_ADDRESS[chain.id], + chainId: chain.id, + abi: managerAbi, + functionName: 'deploy', + args: [founderParams, tokenParams, auctionParams, govParams], + }) + } + const tx = await writeContract(config) if (tx.hash) transaction = await waitForTransaction({ hash: tx.hash }) } catch (e) { @@ -356,7 +386,8 @@ export const ReviewAndDeploy: React.FC = ({ title }) => { !address || !hasConfirmedTerms || !hasConfirmedChain || - isPendingTransaction + isPendingTransaction || + isVersionLoading } className={ deployContractButtonStyle[isPendingTransaction ? 'pending' : 'default'] diff --git a/apps/web/src/modules/create-proposal/components/Upgrade/Upgrade.tsx b/apps/web/src/modules/create-proposal/components/Upgrade/Upgrade.tsx index 4a1dcd2e9..0bc6db30c 100644 --- a/apps/web/src/modules/create-proposal/components/Upgrade/Upgrade.tsx +++ b/apps/web/src/modules/create-proposal/components/Upgrade/Upgrade.tsx @@ -10,9 +10,10 @@ import { DaoContractAddresses } from '../../../dao' import { useAvailableUpgrade } from '../../hooks' import { useProposalStore } from '../../stores' import { UpgradeCard } from '../UpgradeCard' -import { v1_1_0, v1_2_0 } from './versions' +import { v1_1_0, v1_2_0, v2_0_0 } from './versions' export const VERSION_PROPOSAL_SUMMARY: { [key: string]: string } = { + '2.0.0': v2_0_0, '1.2.0': v1_2_0, '1.1.0': v1_1_0, } diff --git a/apps/web/src/modules/create-proposal/components/Upgrade/versions/2.0.0.md b/apps/web/src/modules/create-proposal/components/Upgrade/versions/2.0.0.md new file mode 100644 index 000000000..647cb1eac --- /dev/null +++ b/apps/web/src/modules/create-proposal/components/Upgrade/versions/2.0.0.md @@ -0,0 +1,21 @@ +![](https://i.imgur.com/HrQKZMG.png) + +## Summary + +This proposal upgrades the DAO to V2 "Bali" to add several features, improvements and bug fixes. + +### Token Reserve + +Reserve n tokens for claiming when a DAO is created. Used for DAOs that want to offer reserved token claims via minters. + +### Merkle Reserve Minter + +Allow reserved token claiming via merkle proofs. Used for DAOs that want to include a preset list of members to claim tokens. + +### Alternate Metadata Renderers: + +Allow current DAOs to set a new metadata renderer. Allow new deployments to choose an alternate renderer. + +### Protocol Rewards: + +Rewards taken as a percent of protocol auctions distributed to bid referrals, DAO founders and BuilderDAO diff --git a/apps/web/src/modules/create-proposal/components/Upgrade/versions/index.ts b/apps/web/src/modules/create-proposal/components/Upgrade/versions/index.ts index 33b43804e..8751441db 100644 --- a/apps/web/src/modules/create-proposal/components/Upgrade/versions/index.ts +++ b/apps/web/src/modules/create-proposal/components/Upgrade/versions/index.ts @@ -1,2 +1,3 @@ export { default as v1_1_0 } from './1.1.0.md' export { default as v1_2_0 } from './1.2.0.md' +export { default as v2_0_0 } from './2.0.0.md' diff --git a/apps/web/src/modules/create-proposal/constants/versions.ts b/apps/web/src/modules/create-proposal/constants/versions.ts index 9386f6749..e74e74704 100644 --- a/apps/web/src/modules/create-proposal/constants/versions.ts +++ b/apps/web/src/modules/create-proposal/constants/versions.ts @@ -46,6 +46,11 @@ export const CONTRACT_VERSION_ADDRESSES = { } export const CONTRACT_VERSION_DETAILS: ContractVersion = { + '2.0.0': { + description: + 'This optional release upgrades the DAO to V2 to add custom metadata renderers, protocol rewards and more.', + date: '2024-01-17', + }, '1.2.0': { description: 'This release upgrades the DAO to V1.2 to add several features, improvements and bug fixes.', diff --git a/apps/web/src/pages/dao/[network]/[token]/index.tsx b/apps/web/src/pages/dao/[network]/[token]/index.tsx index f0a8754e9..ecca0d6bb 100644 --- a/apps/web/src/pages/dao/[network]/[token]/index.tsx +++ b/apps/web/src/pages/dao/[network]/[token]/index.tsx @@ -23,26 +23,28 @@ import { useDaoStore, } from 'src/modules/dao' import { NextPageWithLayout } from 'src/pages/_app' -import { AddressType, Chain } from 'src/typings' +import { useChainStore } from 'src/stores/useChainStore' +import { AddressType, CHAIN_ID } from 'src/typings' import { unpackOptionalArray } from 'src/utils/helpers' interface DaoPageProps { - chain: Chain + chainId: CHAIN_ID addresses: DaoContractAddresses collectionAddress: AddressType } -const DaoPage: NextPageWithLayout = ({ chain, collectionAddress }) => { +const DaoPage: NextPageWithLayout = ({ chainId, collectionAddress }) => { const { query } = useRouter() const { address: signerAddress } = useAccount() const { addresses } = useDaoStore() + const chain = useChainStore((x) => x.chain) const { data: owner } = useContractRead({ abi: auctionAbi, address: addresses.auction, functionName: 'owner', - chainId: chain.id, + chainId: chainId, }) const sections = [ @@ -157,7 +159,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { if (!initialized) { return { props: { - chain, + chainId: chain.id, addresses, collectionAddress, },