Skip to content

Commit

Permalink
Add VITE_ALCHEMY_BASE environment variable and update tailwind.config.js
Browse files Browse the repository at this point in the history
  • Loading branch information
backmeupplz committed Dec 21, 2023
1 parent 8a16ef5 commit 9b9ebf1
Show file tree
Hide file tree
Showing 10 changed files with 3,311 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
VITE_CONTRACT=0x89Ca325bd05f851b2B91b3e9fb2d15A59d3D82C6
VITE_CONTRACT=0x89Ca325bd05f851b2B91b3e9fb2d15A59d3D82C6
VITE_ALCHEMY_BASE=123
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@
"preview": "yarn build && yarn vite preview"
},
"dependencies": {
"@borodutch/spam-contract": "^0.0.2",
"@rainbow-me/rainbowkit": "^1.3.1",
"envalid": "^8.0.0",
"ethers": "^6.9.1",
"jotai": "^2.6.0",
"preact": "^10.19.3"
"preact": "^10.19.3",
"viem": "^1.20.3",
"wagmi": "^1.4.12"
},
"devDependencies": {
"@preact/preset-vite": "^2.7.0",
Expand Down
21 changes: 18 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import Mint from 'components/Mint'
import Wallet from 'components/Wallet'

export default function () {
return (
<div className="container mx-auto max-w-prose p-10 prose">
<h1>$SPAM</h1>
</div>
<Wallet>
<div className="container mx-auto max-w-prose p-10 prose">
<h1>$SPAM</h1>
<div role="alert" class="alert alert-warning">
I'll add claiming $SPAM based on Farcaster activity soon. For now, you
can mint $SPAM below.
</div>
<p>
There will always be only 6,942,000 $SPAM that can be minted.
Farcaster claims don't use up the mint cap. For 1 ETH you will get
100,000 $SPAM.
</p>
<Mint />
</div>
</Wallet>
)
}
81 changes: 81 additions & 0 deletions src/components/Mint.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { ConnectButton } from '@rainbow-me/rainbowkit'
import { Spam__factory } from '@borodutch/spam-contract'
import { ethers } from 'ethers'
import { useAccount } from 'wagmi'
import { useEthersSigner } from 'hooks/useEthers'
import { useState } from 'preact/hooks'
import env from 'helpers/env'

export default function () {
const [amount, setAmount] = useState(1000)
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState(false)
const { isConnected } = useAccount()
const signer = useEthersSigner()

async function mint() {
setLoading(true)
setError('')
setSuccess(false)
try {
if (!signer) {
throw new Error('No signer')
}
const contract = Spam__factory.connect(env.VITE_CONTRACT, signer)
const tx = await contract.mint({
value: ethers.parseEther(`${amount / 100000}`),
})
await tx.wait()
setSuccess(true)
} catch (error) {
console.error(error)
setError(error instanceof Error ? error.message : `${error}`)
} finally {
setLoading(false)
}
}

return (
<>
<input
type="number"
class="input input-bordered w-full"
placeholder="How much $SPAM do you want?"
min={0}
value={amount}
onChange={(e) => setAmount(parseInt(e.currentTarget.value))}
/>
<div class="label">
<span class="label-text-alt">
Cost: ~{(amount / 100000).toFixed(3)} ETH
</span>
</div>
<div className="flex flex-col items-start gap-4 mt-8">
<ConnectButton />
{isConnected && (
<button
disabled={loading}
class="btn btn-primary btn-wide btn-lg"
onClick={mint}
>
{loading ? ' 🤔' : ''}The mint $SPAM button
</button>
)}
{success && (
<div role="alert" class="alert alert-success break-all">
<span role="img" aria-label="success">
🎉
</span>{' '}
You now have +{amount} $SPAM.
</div>
)}
{error && (
<div role="alert" class="alert alert-error break-all">
{error}
</div>
)}
</div>
</>
)
}
15 changes: 15 additions & 0 deletions src/components/SuspenseWithError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ComponentChildren } from 'preact'
import { PropsWithChildren, Suspense } from 'react'
import ErrorBoundary from 'components/ErrorBoundary'

export default function ({
errorText,
children,
fallback,
}: PropsWithChildren & { errorText: string; fallback?: ComponentChildren }) {
return (
<ErrorBoundary fallbackText={errorText}>
<Suspense fallback={fallback || <p>Loading...</p>}>{children}</Suspense>
</ErrorBoundary>
)
}
40 changes: 40 additions & 0 deletions src/components/Wallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import '@rainbow-me/rainbowkit/styles.css'

import { PropsWithChildren } from 'preact/compat'
import {
RainbowKitProvider,
darkTheme,
getDefaultWallets,
} from '@rainbow-me/rainbowkit'
import { WagmiConfig, configureChains, createConfig } from 'wagmi'
import { alchemyProvider } from 'wagmi/providers/alchemy'
import { base } from 'wagmi/chains'
import { publicProvider } from 'wagmi/providers/public'
import env from 'helpers/env'

const { chains, publicClient } = configureChains(
[base],
[alchemyProvider({ apiKey: env.VITE_ALCHEMY_BASE }), publicProvider()]
)

const { connectors } = getDefaultWallets({
appName: '$SPAM',
projectId: 'b5a3956f9dc9a3590b2244140b7caf82',
chains,
})

const wagmiConfig = createConfig({
autoConnect: true,
connectors,
publicClient,
})

export default function ({ children }: PropsWithChildren) {
return (
<WagmiConfig config={wagmiConfig}>
<RainbowKitProvider coolMode chains={chains} theme={darkTheme()}>
{children}
</RainbowKitProvider>
</WagmiConfig>
)
}
6 changes: 6 additions & 0 deletions src/helpers/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { cleanEnv, str } from 'envalid'

export default cleanEnv(import.meta.env, {
VITE_CONTRACT: str(),
VITE_ALCHEMY_BASE: str(),
})
42 changes: 42 additions & 0 deletions src/hooks/useEthers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { BrowserProvider, JsonRpcSigner } from 'ethers'
import { type WalletClient, useWalletClient } from 'wagmi'
import { useMemo } from 'preact/hooks'

export function walletClientToSigner(walletClient: WalletClient) {
const { account, chain, transport } = walletClient
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
}
const provider = new BrowserProvider(transport, network)
const signer = new JsonRpcSigner(provider, account.address)
return signer
}

export function walletClientToProvider(walletClient: WalletClient) {
const { chain, transport } = walletClient
const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
}
const provider = new BrowserProvider(transport, network)
return provider
}

export function useEthersSigner({ chainId }: { chainId?: number } = {}) {
const { data: walletClient } = useWalletClient({ chainId })
return useMemo(
() => (walletClient ? walletClientToSigner(walletClient) : undefined),
[walletClient]
)
}

export function useEthersProvider({ chainId }: { chainId?: number } = {}) {
const { data: walletClient } = useWalletClient({ chainId })
return useMemo(
() => (walletClient ? walletClientToProvider(walletClient) : undefined),
[walletClient]
)
}
3 changes: 3 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
module.exports = {
content: ['./index.html', './src/**/!(tailwind).{ts,tsx}'],
plugins: [require('@tailwindcss/typography'), require('daisyui')],
daisyui: {
themes: ['luxury'],
},
}
Loading

0 comments on commit 9b9ebf1

Please sign in to comment.