From adee4fd1d032d5ade1d755e7aac0fc9029c4d2f4 Mon Sep 17 00:00:00 2001 From: TomiOhl Date: Fri, 24 Jan 2025 14:54:18 +0100 Subject: [PATCH] feat: Sophon & Zero chain support (#1594) * feat: Sophon & Zero chain support * Add Zero NFT reward * chore(deps): update @guildxyz/types * feat(NFTreward): use supported chains and contract addresses from the types package * fix(AddRewardModal): provide supported chains as a mutable array * fix: replace sophon logo --------- Co-authored-by: BrickheadJohnny --- package-lock.json | 8 ++-- package.json | 2 +- public/networkLogos/sophon.svg | 23 +++++++++++ public/networkLogos/zero.svg | 11 +++++ .../CreateNftForm/CreateNftForm.tsx | 6 +-- .../CreateNftForm/components/NftDataForm.tsx | 12 +++--- .../CreateNftForm/hooks/useCreateNft.ts | 41 ++----------------- .../[guild]/collect/hooks/useGuildFee.ts | 4 +- src/hooks/useTokens.ts | 2 + .../api/nft/collectors/[chain]/[address].ts | 4 +- .../WalletActivity/WalletActivityForm.tsx | 4 ++ src/static/customChains.ts | 26 ++++++++++++ src/wagmiConfig/chains.ts | 20 +++++++++ src/wagmiConfig/index.ts | 6 +++ 14 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 public/networkLogos/sophon.svg create mode 100644 public/networkLogos/zero.svg diff --git a/package-lock.json b/package-lock.json index 6bd4708e57..a05af52b11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@emotion/styled": "^11.11.0", "@fuels/connectors": "^0.36.0", "@fuels/react": "^0.36.0", - "@guildxyz/types": "^1.10.35", + "@guildxyz/types": "^1.10.38", "@hcaptcha/react-hcaptcha": "^1.4.4", "@hookform/resolvers": "^3.3.4", "@lexical/code": "^0.12.0", @@ -5433,9 +5433,9 @@ } }, "node_modules/@guildxyz/types": { - "version": "1.10.35", - "resolved": "https://registry.npmjs.org/@guildxyz/types/-/types-1.10.35.tgz", - "integrity": "sha512-YWkdxKbtJRz7hC+ywvZeGQ+8KCdEtUQYW0klpt/LOwXkj6tEndhvkWu/NSOaPnyUv1+SBM5FEd74zvNULXqoeQ==", + "version": "1.10.38", + "resolved": "https://registry.npmjs.org/@guildxyz/types/-/types-1.10.38.tgz", + "integrity": "sha512-oPbRzmoOt3dkv2B0gdjX4fMhiwWxSG+n99loOI1AsvsGyDO9C2NdPDTa8HFULXZXctpPoUyf9VpY9BlsT8mKIQ==", "license": "ISC", "dependencies": { "zod": "^3.22.4" diff --git a/package.json b/package.json index 0e133d1074..aec143e2f7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@emotion/styled": "^11.11.0", "@fuels/connectors": "^0.36.0", "@fuels/react": "^0.36.0", - "@guildxyz/types": "^1.10.35", + "@guildxyz/types": "^1.10.38", "@hcaptcha/react-hcaptcha": "^1.4.4", "@hookform/resolvers": "^3.3.4", "@lexical/code": "^0.12.0", diff --git a/public/networkLogos/sophon.svg b/public/networkLogos/sophon.svg new file mode 100644 index 0000000000..771010f3c0 --- /dev/null +++ b/public/networkLogos/sophon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/networkLogos/zero.svg b/public/networkLogos/zero.svg new file mode 100644 index 0000000000..62d60eb24d --- /dev/null +++ b/public/networkLogos/zero.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/CreateNftForm.tsx b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/CreateNftForm.tsx index 95b64f1ea2..157999d6e0 100644 --- a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/CreateNftForm.tsx +++ b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/CreateNftForm.tsx @@ -1,3 +1,4 @@ +import { consts } from "@guildxyz/types" import { useAddRewardDiscardAlert } from "components/[guild]/AddRewardButton/hooks/useAddRewardDiscardAlert" import { useAddRewardContext } from "components/[guild]/AddRewardContext" import Button from "components/common/Button" @@ -7,7 +8,6 @@ import { useAccount } from "wagmi" import { Chains } from "wagmiConfig/chains" import NftDataForm, { CreateNftFormType } from "./components/NftDataForm" import useCreateNft, { - CONTRACT_CALL_SUPPORTED_CHAINS, ContractCallSupportedChain, CreateNFTResponse, } from "./hooks/useCreateNft" @@ -17,11 +17,11 @@ type Props = { } const getDefaultChain = (chainId: number) => - (CONTRACT_CALL_SUPPORTED_CHAINS.includes( + (consts.NFTRewardSupportedChains.includes( Chains[chainId] as ContractCallSupportedChain ) ? Chains[chainId] - : CONTRACT_CALL_SUPPORTED_CHAINS[0]) as ContractCallSupportedChain + : consts.NFTRewardSupportedChains[0]) as ContractCallSupportedChain const CreateNftForm = ({ onSuccess }: Props) => { const { isConnected: isEvmConnected, address, chainId } = useAccount() diff --git a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/components/NftDataForm.tsx b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/components/NftDataForm.tsx index 984a0f5d01..d828a841c9 100644 --- a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/components/NftDataForm.tsx +++ b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/components/NftDataForm.tsx @@ -26,6 +26,7 @@ import { Tooltip, useColorModeValue, } from "@chakra-ui/react" +import { consts } from "@guildxyz/types" import { ArrowSquareOut, Clock, @@ -51,10 +52,7 @@ import { ADDRESS_REGEX } from "utils/guildCheckout/constants" import { formatUnits } from "viem" import { useAccount } from "wagmi" import { CHAIN_CONFIG, Chains } from "wagmiConfig/chains" -import { - CONTRACT_CALL_SUPPORTED_CHAINS, - ContractCallSupportedChain, -} from "../hooks/useCreateNft" +import { ContractCallSupportedChain } from "../hooks/useCreateNft" import ImagePicker from "./ImagePicker" import MintPerAddressInput from "./MintPerAddressInput" import NftTypeInput from "./NftTypeInput" @@ -309,7 +307,11 @@ const NftDataForm = ({ isEditMode, submitButton }: Props) => { diff --git a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts index 5c72df6ed3..52e3bad7e0 100644 --- a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts +++ b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts @@ -1,4 +1,5 @@ import { usePostHogContext } from "@/components/Providers/PostHogProvider" +import { consts } from "@guildxyz/types" import { datetimeLocalToIsoString } from "components/[guild]/RolePlatforms/components/EditRewardAvailabilityModal/components/StartEndTimeForm" import { guildNftRewardMetadataSchema } from "components/[guild]/collect/hooks/useNftDetails" import useGuild from "components/[guild]/hooks/useGuild" @@ -16,45 +17,11 @@ import getEventsFromViemTxReceipt from "utils/getEventsFromViemTxReceipt" import processViemContractError from "utils/processViemContractError" import { TransactionReceipt, WriteContractParameters, parseUnits } from "viem" import { useAccount, usePublicClient, useWalletClient } from "wagmi" -import { CHAIN_CONFIG, Chain, Chains } from "wagmiConfig/chains" +import { CHAIN_CONFIG, Chains } from "wagmiConfig/chains" import { CreateNftFormType } from "../components/NftDataForm" -export const GUILD_REWARD_NFT_FACTORY_ADDRESSES = { - ETHEREUM: "0x6ee2dd02fbfb71f518827042b6adca242f1ba0b2", - BASE_MAINNET: "0x4205e56a69a0130a9e0828d45d0c84e45340a196", - OPTIMISM: "0xe6e6b676f94a6207882ac92b6014a391766fa96e", - BSC: "0xa445e7d3af54867d14467b44d5487352403d1e59", - CRONOS: "0x6c2c223b84724c4b8fd41ae0142c2369dfa7e319", - POLYGON: "0xc1c23618110277ffe6d529816eb23de42b24cc33", - MANTLE: "0x326f14942f8899406e3224bd63E9f250D275a52e", - ZKSYNC_ERA: "0x2a1eaf11a9753a871b15e2865d8a47cf17dd9450", - LINEA: "0x326f14942f8899406e3224bd63E9f250D275a52e", - CYBER: "0x097E05f7a194a30A482CC9616460498980bE79d3", - ARBITRUM: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - SCROLL: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - TAIKO: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - BLAST_MAINNET: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - X1: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - CORE_DAO: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - METIS: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - NEON_EVM: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - POLYGON_ZKEVM: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - ZETACHAIN: "0x13ec6B98362E43Add08f7CC4f6befd02fa52eE01", - MINT: "0x097E05f7a194a30A482CC9616460498980bE79d3", - MODE: "0x097E05f7a194a30A482CC9616460498980bE79d3", - AVALANCHE: "0x13ec6b98362e43add08f7cc4f6befd02fa52ee01", - LISK: "0x13ec6b98362e43add08f7cc4f6befd02fa52ee01", - INK: "0x13ec6b98362e43add08f7cc4f6befd02fa52ee01", - SEPOLIA: "0xa9e8e62266d449b766d305075248790bdd46facb", - IOTA: "0x097E05f7a194a30A482CC9616460498980bE79d3", - SONIC: "0x070cD1FD4de1ed0259871B7d0b85C0e237702691", -} as const satisfies Partial> - -export const CONTRACT_CALL_SUPPORTED_CHAINS = Object.keys( - GUILD_REWARD_NFT_FACTORY_ADDRESSES -) as (keyof typeof GUILD_REWARD_NFT_FACTORY_ADDRESSES)[] export type ContractCallSupportedChain = - (typeof CONTRACT_CALL_SUPPORTED_CHAINS)[number] + (typeof consts.NFTRewardSupportedChains)[number] export enum ContractCallFunction { // Kept the old one too, we can use it to determine if we need to show the old or the new UI for the availability-related features @@ -150,7 +117,7 @@ const useCreateNft = ( const { request } = await publicClient.simulateContract({ abi: guildRewardNFTFacotryAbi, - address: GUILD_REWARD_NFT_FACTORY_ADDRESSES[Chains[chainId]], + address: consts.NFTRewardFactoryAddresses[Chains[chainId]], functionName: "deployConfigurableNFT", args: contractCallParams, }) diff --git a/src/components/[guild]/collect/hooks/useGuildFee.ts b/src/components/[guild]/collect/hooks/useGuildFee.ts index 84ad7377d2..f5fc6135e9 100644 --- a/src/components/[guild]/collect/hooks/useGuildFee.ts +++ b/src/components/[guild]/collect/hooks/useGuildFee.ts @@ -1,4 +1,4 @@ -import { GUILD_REWARD_NFT_FACTORY_ADDRESSES } from "components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft" +import { consts } from "@guildxyz/types" import guildRewardNFTFactoryAbi from "static/abis/guildRewardNFTFactory" import { useReadContract } from "wagmi" import { Chain, Chains } from "wagmiConfig/chains" @@ -23,7 +23,7 @@ const useGuildFee = ( } = useReadContract({ abi: guildRewardNFTFactoryAbi, chainId: Chains[chain], - address: GUILD_REWARD_NFT_FACTORY_ADDRESSES[chain], + address: consts.NFTRewardFactoryAddresses[chain], functionName: contractAddress && chainsWithOverrides.includes(chain) ? "getFeeWithOverrides" diff --git a/src/hooks/useTokens.ts b/src/hooks/useTokens.ts index 7f3b05077d..a9b8b9724e 100644 --- a/src/hooks/useTokens.ts +++ b/src/hooks/useTokens.ts @@ -98,6 +98,8 @@ const TokenApiURLs: Record = { "https://raw.githubusercontent.com/MagicSea-Finance/tokenlist/main/token.default.json", ], SONIC: ["https://tokens.coingecko.com/sonic/all.json"], + SOPHON: [], + ZERO: [], } const fetchTokens = async ([_, chain]) => diff --git a/src/pages/api/nft/collectors/[chain]/[address].ts b/src/pages/api/nft/collectors/[chain]/[address].ts index 54dfec1df1..b1354a4c20 100644 --- a/src/pages/api/nft/collectors/[chain]/[address].ts +++ b/src/pages/api/nft/collectors/[chain]/[address].ts @@ -1,4 +1,4 @@ -import { CONTRACT_CALL_SUPPORTED_CHAINS } from "components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft" +import { consts } from "@guildxyz/types" import { env } from "env" import { NextApiHandler } from "next" import { topCollectorsSupportedChains } from "pages/[guild]/collect/[chain]/[address]" @@ -38,7 +38,7 @@ export const alchemyApiUrl: Record< // TODO: just use Zod. export const validateNftChain = (value: string | string[]): Chain => { const valueAsString = value?.toString()?.toUpperCase() - if (!value || !CONTRACT_CALL_SUPPORTED_CHAINS.includes(valueAsString as any)) + if (!value || !consts.NFTRewardSupportedChains.includes(valueAsString as any)) return null return valueAsString as Chain } diff --git a/src/requirements/WalletActivity/WalletActivityForm.tsx b/src/requirements/WalletActivity/WalletActivityForm.tsx index 3c80698fb9..d8c40c12b3 100644 --- a/src/requirements/WalletActivity/WalletActivityForm.tsx +++ b/src/requirements/WalletActivity/WalletActivityForm.tsx @@ -51,6 +51,8 @@ const COVALENT_CHAINS = new Set([ "INK", "IOTA", "SONIC", + "SOPHON", + "ZERO", ]) const walletActivityRequirementTypes: SelectOption[] = [ @@ -140,6 +142,8 @@ const WalletActivityForm = ({ "INK", "IOTA", "SONIC", + "SOPHON", + "ZERO", ] for (const covalentChain of COVALENT_CHAINS.values()) { diff --git a/src/static/customChains.ts b/src/static/customChains.ts index 73d8e985a1..bc6b827db1 100644 --- a/src/static/customChains.ts +++ b/src/static/customChains.ts @@ -454,3 +454,29 @@ export const ink = { }, testnet: false, } as const satisfies Chain + +export const zero = { + id: 543210, + name: "Zero", + nativeCurrency: { + decimals: 18, + name: "Ether", + symbol: "ETH", + }, + rpcUrls: { + default: { http: ["https://rpc.zerion.io/v1/zero"] }, + }, + blockExplorers: { + default: { + name: "Blockscout", + url: "https://explorer.zero.network", + }, + }, + contracts: { + multicall3: { + address: "0x0307F341a18f1FC1f63a7Ceeac970245A08C5a80", + blockCreated: 11517, + }, + }, + testnet: false, +} as const satisfies Chain diff --git a/src/wagmiConfig/chains.ts b/src/wagmiConfig/chains.ts index 863f35bb85..3ee75fd319 100644 --- a/src/wagmiConfig/chains.ts +++ b/src/wagmiConfig/chains.ts @@ -15,6 +15,7 @@ import { scrollAlpha, taikoKatlaTestnet, x1, + zero, } from "static/customChains" import { type Chain as ViemChain, @@ -67,6 +68,7 @@ import { sepolia, shimmer, sonic, + sophon, taiko, worldchain, x1Testnet, @@ -760,6 +762,22 @@ const CHAIN_CONFIG: Record = { dark: "/networkLogos/sonic.svg", }, }, + SOPHON: { + ...generateChainConfig(sophon, "/networkLogos/sophon.svg"), + iconUrl: "/networkLogos/sophon.svg", + blockExplorerIconUrl: { + light: "/networkLogos/sophon.svg", + dark: "/networkLogos/sophon.svg", + }, + }, + ZERO: { + ...generateChainConfig(zero, ETH_ICON), + iconUrl: "/networkLogos/zero.svg", + blockExplorerIconUrl: { + light: "/networkLogos/zero.svg", + dark: "/networkLogos/zero.svg", + }, + }, } enum Chains { @@ -835,6 +853,8 @@ enum Chains { INK = ink.id, IOTA = iota.id, SONIC = sonic.id, + SOPHON = sophon.id, + ZERO = zero.id, } export type Chain = keyof typeof Chains diff --git a/src/wagmiConfig/index.ts b/src/wagmiConfig/index.ts index ed543780b3..3b04ba129b 100644 --- a/src/wagmiConfig/index.ts +++ b/src/wagmiConfig/index.ts @@ -14,6 +14,7 @@ import { palm, taikoKatlaTestnet, x1, + zero, } from "static/customChains" import { http, @@ -73,6 +74,7 @@ import { sepolia, shimmer, sonic, + sophon, taiko, worldchain, x1Testnet, @@ -200,6 +202,8 @@ export const wagmiConfig = IS_TEST ink, iota, sonic, + sophon, + zero, ], transports: { [mainnet.id]: http(), @@ -270,6 +274,8 @@ export const wagmiConfig = IS_TEST [ink.id]: http(), [iota.id]: http(), [sonic.id]: http(), + [sophon.id]: http(), + [zero.id]: http(), }, ssr: true, connectors: [