diff --git a/.env.dev b/.env.dev index 41d2e0e31d..232c4218bf 100644 --- a/.env.dev +++ b/.env.dev @@ -30,8 +30,9 @@ NEXT_PUBLIC_TWITTER_CLIENT_ID=X3d6Szg5bnVCMm5wRWxSVmhXUTc6MTpjaQ NEXT_PUBLIC_FACEBOOK_CLIENT_ID=1072677713882819 NEXT_PUBLIC_CF_TURNSTILE_SITE_KEY=0x4AAAAAAAKiedvR5qiLUhIs NEXT_PUBLIC_NOMAD_MATTERS_CAMPAIGN_LINK=/@rsdyobw/22048-launching-the-nomad-matters-initiative -NEXT_PUBLIC_BILLBOARD_ADDRESS=0x6a72820E1CCCba1B1FE02E37881cEa3F9Aa6375C -NEXT_PUBLIC_BILLBOARD_TOKEN_ID=6 +NEXT_PUBLIC_BILLBOARD_OPERATOR_ADDRESS=0xBcEAD54De463C083F348b530305e2471652D59A3 +NEXT_PUBLIC_BILLBOARD_REGISTRY_ADDRESS=0xEb23327533aa993069D61b8E1d6001B1cce0E216 +NEXT_PUBLIC_BILLBOARD_TOKEN_ID=1 NEXT_PUBLIC_BILLBOARD_IMAGE_URL=matters-billboard-ad-dev.s3.ap-southeast-1.amazonaws.com/ PLAYWRIGHT_RUNTIME_ENV=ci PLAYWRIGHT_TEST_BASE_URL=https://web-develop.matters.town diff --git a/.env.prod b/.env.prod index 7f8e1ac6d5..27428c7fd4 100644 --- a/.env.prod +++ b/.env.prod @@ -31,6 +31,7 @@ NEXT_PUBLIC_TWITTER_CLIENT_ID=cmdKbUlyd1ZZZDZYa3dTampidGo6MTpjaQ NEXT_PUBLIC_FACEBOOK_CLIENT_ID=161556310354354 NEXT_PUBLIC_NOMAD_MATTERS_CAMPAIGN_LINK_EN=/@hi176/476405-a-guide-to-invite NEXT_PUBLIC_NOMAD_MATTERS_CAMPAIGN_LINK=/@hi176/476404 -NEXT_PUBLIC_BILLBOARD_ADDRESS=0x88EA16c2a69f50B9bc2E8f7684D425f33f29225F +NEXT_PUBLIC_BILLBOARD_OPERATOR_ADDRESS=0x92a117aeA74963Cd0CEdF9C50f99435451a291F7 +NEXT_PUBLIC_BILLBOARD_REGISTRY_ADDRESS=0x95bEFe8E08a56dCEBBa8d40BE3e9c3cb2fF81806 NEXT_PUBLIC_BILLBOARD_TOKEN_ID=1 NEXT_PUBLIC_BILLBOARD_IMAGE_URL=matters-billboard-ad.s3.ap-southeast-1.amazonaws.com/ diff --git a/src/common/utils/abis/billboard.ts b/src/common/utils/abis/billboard.ts index f738fb68aa..f980a781be 100644 --- a/src/common/utils/abis/billboard.ts +++ b/src/common/utils/abis/billboard.ts @@ -1,53 +1,119 @@ -export const BillboardABI = [ +export const BillboardOperatorABI = [ { + type: 'function', + name: 'getLatestEpoch', inputs: [ { + name: 'tokenId_', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: 'epoch', + type: 'uint256', internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getBid', + inputs: [ + { name: 'tokenId_', type: 'uint256', + internalType: 'uint256', + }, + { + name: 'epoch_', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'bidder_', + type: 'address', + internalType: 'address', }, ], - name: 'getBoard', outputs: [ { + name: 'bid', + type: 'tuple', + internalType: 'struct IBillboardRegistry.Bid', components: [ { - internalType: 'address', - name: 'creator', - type: 'address', + name: 'price', + type: 'uint256', + internalType: 'uint256', }, { - internalType: 'string', - name: 'name', - type: 'string', + name: 'tax', + type: 'uint256', + internalType: 'uint256', }, { - internalType: 'string', - name: 'description', + name: 'contentURI', type: 'string', + internalType: 'string', }, { - internalType: 'string', - name: 'location', + name: 'redirectURI', type: 'string', + internalType: 'string', }, { - internalType: 'string', - name: 'contentURI', - type: 'string', + name: 'placedAt', + type: 'uint256', + internalType: 'uint256', }, { - internalType: 'string', - name: 'redirectURI', - type: 'string', + name: 'updatedAt', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'isWon', + type: 'bool', + internalType: 'bool', + }, + { + name: 'isWithdrawn', + type: 'bool', + internalType: 'bool', }, ], - internalType: 'struct IBillboardRegistry.Board', - name: 'board', - type: 'tuple', }, ], stateMutability: 'view', + }, +] as const + +export const BillboardRegistryABI = [ + { type: 'function', + name: 'highestBidder', + inputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'address', + internalType: 'address', + }, + ], + stateMutability: 'view', }, ] as const diff --git a/src/components/Billboard/index.tsx b/src/components/Billboard/index.tsx index 3f36121ef6..d5636ba8b7 100644 --- a/src/components/Billboard/index.tsx +++ b/src/components/Billboard/index.tsx @@ -1,13 +1,13 @@ import { FormattedMessage, useIntl } from 'react-intl' -import { useContractRead } from 'wagmi' import { ReactComponent as IconInfo } from '@/public/static/icons/24px/information.svg' -import { analytics, BillboardABI, featureSupportedChains } from '~/common/utils' +import { analytics, featureSupportedChains } from '~/common/utils' import { BillboardDialog, BillboardExposureTracker, Icon, TextIcon, + useBillboard, } from '~/components' import styles from './styles.module.css' @@ -20,17 +20,18 @@ type BillboardProps = { export const Billboard = ({ tokenId, type }: BillboardProps) => { // collect vars const id = !isNaN(Number(tokenId)) ? Number(tokenId) : 0 - const address = process.env.NEXT_PUBLIC_BILLBOARD_ADDRESS as `0x${string}` + const operatorAddress = process.env + .NEXT_PUBLIC_BILLBOARD_OPERATOR_ADDRESS as `0x${string}` + const registryAddress = process.env + .NEXT_PUBLIC_BILLBOARD_REGISTRY_ADDRESS as `0x${string}` const network = featureSupportedChains.billboard[0] const intl = useIntl() - const { data, isError, isLoading } = useContractRead({ - address, - abi: BillboardABI, - functionName: 'getBoard', + const { data, isError, isLoading } = useBillboard({ + id, chainId: network.id, - args: [BigInt(id)], - cacheTime: 60_000, + operatorAddress, + registryAddress, }) if (!id || isError || isLoading || !data || !data.contentURI) { diff --git a/src/components/Hook/index.ts b/src/components/Hook/index.ts index 944ece3603..828ad28fb8 100644 --- a/src/components/Hook/index.ts +++ b/src/components/Hook/index.ts @@ -1,3 +1,4 @@ +export * from './useBillboard' export * from './useCarousel' export * from './useColorThief' export * from './useCountdown' diff --git a/src/components/Hook/useBillboard.ts b/src/components/Hook/useBillboard.ts new file mode 100644 index 0000000000..9a952d33cf --- /dev/null +++ b/src/components/Hook/useBillboard.ts @@ -0,0 +1,88 @@ +import { readContract } from '@wagmi/core' +import { useEffect, useState } from 'react' + +import { BillboardOperatorABI, BillboardRegistryABI } from '~/common/utils' + +// custom hook level enums +enum BillboardQueryStatus { + IDLE = 'idle', + LOADING = 'loading', + LOADED = 'loaded', + ERROR = 'error', +} + +type Props = { + id: number + chainId: number + operatorAddress: `0x${string}` + registryAddress: `0x${string}` +} + +export const useBillboard = ({ + id, + chainId, + operatorAddress, + registryAddress, +}: Props) => { + const [status, setStatus] = useState( + BillboardQueryStatus.IDLE + ) + const [data, setData] = useState>({}) + + const isLoading = status === BillboardQueryStatus.LOADING + const isError = status === BillboardQueryStatus.ERROR + + useEffect(() => { + if (isLoading) { + return + } + + ;(async () => { + try { + setStatus(BillboardQueryStatus.LOADING) + + const tokenId = BigInt(id) + const currEpoch = await readContract({ + abi: BillboardOperatorABI, + address: operatorAddress, + chainId, + functionName: 'getLatestEpoch', + args: [tokenId], + }) + + if (currEpoch < 2n) { + return + } + + const epoch = currEpoch - 2n + const bidder = await readContract({ + abi: BillboardRegistryABI, + address: registryAddress, + chainId, + functionName: 'highestBidder', + args: [tokenId, epoch], + }) + const bid = await readContract({ + abi: BillboardOperatorABI, + address: operatorAddress, + chainId, + functionName: 'getBid', + args: [tokenId, epoch, bidder], + }) + + if (bid && bid.isWon) { + setData({ + ...(bid.contentURI ? { contentURI: bid.contentURI } : {}), + ...(bid.redirectURI ? { redirectURI: bid.redirectURI } : {}), + }) + } + + setStatus(BillboardQueryStatus.LOADED) + } catch (error) { + setStatus(BillboardQueryStatus.ERROR) + } + })() + }, []) + + return { data, isLoading, isError } +}