use solana connector
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -9,4 +9,7 @@ test-ledger
|
||||
.vscode
|
||||
|
||||
spec.md
|
||||
app_old
|
||||
app_old
|
||||
|
||||
connectorkit
|
||||
create-solana-dapp-template
|
||||
@@ -4,7 +4,6 @@ const nextConfig: NextConfig = {
|
||||
transpilePackages: [
|
||||
'@tamagui/lucide-icons-2',
|
||||
'@descro/sdk',
|
||||
'@solana/wallet-adapter-react-ui',
|
||||
],
|
||||
turbopack: {
|
||||
resolveAlias: {
|
||||
|
||||
@@ -9,10 +9,7 @@
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.32.1",
|
||||
"@descro/sdk": "workspace:*",
|
||||
"@solana/wallet-adapter-base": "^0.9.27",
|
||||
"@solana/wallet-adapter-react": "^0.15.39",
|
||||
"@solana/wallet-adapter-react-ui": "^0.9.39",
|
||||
"@solana/wallet-adapter-wallets": "^0.19.38",
|
||||
"@solana/connector": "^0.2.4",
|
||||
"@solana/web3.js": "^1.98.4",
|
||||
"@tamagui/config": "^2.0.0-rc.42",
|
||||
"@tamagui/next-theme": "^2.0.0-rc.42",
|
||||
|
||||
@@ -1,54 +1,37 @@
|
||||
'use client'
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react'
|
||||
import { WalletAdapterNetwork, type WalletError } from '@solana/wallet-adapter-base'
|
||||
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'
|
||||
import { clusterApiUrl } from '@solana/web3.js'
|
||||
import { UnsafeBurnerWalletAdapter } from '@solana/wallet-adapter-wallets'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { AppProvider } from '@solana/connector/react'
|
||||
import { getDefaultConfig } from '@solana/connector/headless'
|
||||
|
||||
export type Network = 'devnet' | 'localnet'
|
||||
|
||||
interface NetworkContextValue {
|
||||
network: WalletAdapterNetwork
|
||||
setNetwork: (n: WalletAdapterNetwork) => void
|
||||
function getOrigin() {
|
||||
if (typeof window !== 'undefined') return window.location.origin
|
||||
return 'http://localhost:3000'
|
||||
}
|
||||
|
||||
export const NetworkContext = React.createContext<NetworkContextValue>({
|
||||
network: WalletAdapterNetwork.Devnet,
|
||||
setNetwork: () => undefined,
|
||||
})
|
||||
|
||||
export function WalletProviders({ children }: { children: React.ReactNode }) {
|
||||
const [network, setNetwork] = useState<WalletAdapterNetwork>(WalletAdapterNetwork.Devnet)
|
||||
|
||||
const endpoint = useMemo(
|
||||
export function WalletProviders({ children }: { children: ReactNode }) {
|
||||
const connectorConfig = useMemo(
|
||||
() =>
|
||||
(network as string) === 'localnet'
|
||||
? 'http://localhost:8899'
|
||||
: clusterApiUrl(network),
|
||||
[network],
|
||||
);
|
||||
|
||||
const wallets = useMemo(
|
||||
() => network as string === 'localnet' ? [new UnsafeBurnerWalletAdapter()] : [],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[network]
|
||||
);
|
||||
|
||||
const onError = useCallback((error: WalletError) => {
|
||||
console.warn('[wallet]', error.name, error.message, error.error)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<NetworkContext.Provider value={{ network, setNetwork }}>
|
||||
<ConnectionProvider endpoint={endpoint}>
|
||||
<WalletProvider wallets={wallets} autoConnect onError={onError}>
|
||||
<WalletModalProvider>
|
||||
{children}
|
||||
</WalletModalProvider>
|
||||
</WalletProvider>
|
||||
</ConnectionProvider>
|
||||
</NetworkContext.Provider>
|
||||
getDefaultConfig({
|
||||
appName: 'Descro',
|
||||
appUrl: getOrigin(),
|
||||
autoConnect: true,
|
||||
clusters: [
|
||||
{
|
||||
id: 'solana:devnet' as const,
|
||||
label: 'Devnet',
|
||||
url: 'https://api.devnet.solana.com',
|
||||
},
|
||||
{
|
||||
id: 'solana:localnet' as const,
|
||||
label: 'Localnet',
|
||||
url: 'http://localhost:8899',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[],
|
||||
)
|
||||
|
||||
return <AppProvider connectorConfig={connectorConfig}>{children}</AppProvider>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import '../../public/tamagui.generated.css'
|
||||
import '@solana/wallet-adapter-react-ui/styles.css'
|
||||
import { Metadata } from 'next'
|
||||
import { NextTamaguiProvider } from './NextTamaguiProvider'
|
||||
import { WalletProviders } from './WalletProviders'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useContext, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { YStack, XStack, Text, Button, Spinner, Separator } from 'tamagui'
|
||||
import { WalletButton } from '@/components/WalletButton'
|
||||
import { CreateEscrowForm } from '@/components/CreateEscrowForm'
|
||||
@@ -8,14 +8,12 @@ import { EscrowCard } from '@/components/EscrowCard'
|
||||
import { EscrowDetail } from '@/components/EscrowDetail'
|
||||
import { ResolverPanel } from '@/components/ResolverPanel'
|
||||
import { useEscrows } from '@/hooks/useEscrows'
|
||||
import { NetworkContext } from './WalletProviders'
|
||||
import type { Network } from './WalletProviders'
|
||||
import { useWallet, useCluster } from '@solana/connector/react'
|
||||
import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
|
||||
export default function PlaygroundPage() {
|
||||
const { network, setNetwork } = useContext(NetworkContext)
|
||||
const { publicKey } = useWallet()
|
||||
const { cluster, clusters, setCluster, isDevnet } = useCluster()
|
||||
const { account } = useWallet()
|
||||
const { escrows, loading, error, refresh } = useEscrows()
|
||||
const [selected, setSelected] = useState<EscrowAccountWithPda | null>(null)
|
||||
const [showResolvers, setShowResolvers] = useState(false)
|
||||
@@ -43,8 +41,8 @@ export default function PlaygroundPage() {
|
||||
Network:
|
||||
</Text>
|
||||
<select
|
||||
value={network}
|
||||
onChange={(e) => setNetwork(e.target.value as Network)}
|
||||
value={cluster?.id ?? ''}
|
||||
onChange={(e) => setCluster(e.target.value as never)}
|
||||
style={{
|
||||
padding: '6px 10px',
|
||||
borderRadius: 6,
|
||||
@@ -53,12 +51,15 @@ export default function PlaygroundPage() {
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<option value="devnet">Devnet</option>
|
||||
<option value="localnet">Localnet</option>
|
||||
{clusters.map((c) => (
|
||||
<option key={c.id} value={c.id}>
|
||||
{c.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</XStack>
|
||||
|
||||
{network === 'devnet' && (
|
||||
{isDevnet && (
|
||||
<a
|
||||
href="https://faucet.solana.com/"
|
||||
target="_blank"
|
||||
@@ -100,7 +101,7 @@ export default function PlaygroundPage() {
|
||||
</Button>
|
||||
</XStack>
|
||||
|
||||
{!publicKey && (
|
||||
{!account && (
|
||||
<Text color="$color10" fontSize={13}>
|
||||
Connect your wallet to see escrows.
|
||||
</Text>
|
||||
@@ -114,7 +115,7 @@ export default function PlaygroundPage() {
|
||||
</XStack>
|
||||
)}
|
||||
|
||||
{publicKey && !loading && escrows.length === 0 && (
|
||||
{account && !loading && escrows.length === 0 && (
|
||||
<Text color="$color10" fontSize={13}>
|
||||
No escrows found for your wallet.
|
||||
</Text>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { YStack, XStack, Text, Button, Input, Spinner } from 'tamagui'
|
||||
import { useWallet, useConnection } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey, Transaction } from '@solana/web3.js'
|
||||
import { PublicKey, Transaction, Connection } from '@solana/web3.js'
|
||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
||||
import { useTransactionSigner, useDisconnectWallet, useCluster } from '@solana/connector/react'
|
||||
import { useWalletAdapterCompat } from '@solana/connector/compat'
|
||||
import BN from 'bn.js'
|
||||
import { EscrowClient } from '@descro/sdk'
|
||||
|
||||
@@ -22,8 +23,17 @@ interface CreateEscrowFormProps {
|
||||
}
|
||||
|
||||
export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||
const { publicKey, sendTransaction, signTransaction } = useWallet()
|
||||
const { connection } = useConnection()
|
||||
const { signer } = useTransactionSigner()
|
||||
const { disconnect } = useDisconnectWallet()
|
||||
const { cluster } = useCluster()
|
||||
const walletAdapter = useWalletAdapterCompat(signer, disconnect)
|
||||
|
||||
const connection = useMemo(
|
||||
() => (cluster?.url ? new Connection(cluster.url, 'confirmed') : null),
|
||||
[cluster?.url],
|
||||
)
|
||||
|
||||
const publicKey = walletAdapter.publicKey ? new PublicKey(walletAdapter.publicKey.toString()) : null
|
||||
|
||||
const [buyer, setBuyer] = useState('')
|
||||
const [amountSol, setAmountSol] = useState('')
|
||||
@@ -37,7 +47,7 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||
setError(null)
|
||||
setSuccess(null)
|
||||
|
||||
if (!publicKey) {
|
||||
if (!publicKey || !connection) {
|
||||
setError('Connect your wallet first.')
|
||||
return
|
||||
}
|
||||
@@ -70,8 +80,8 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||
try {
|
||||
const provider = new AnchorProvider(
|
||||
connection,
|
||||
{ publicKey, signTransaction: signTransaction! } as any,
|
||||
{}
|
||||
{ publicKey, signTransaction: walletAdapter.signTransaction!.bind(walletAdapter) } as never,
|
||||
{},
|
||||
)
|
||||
const client = new EscrowClient(provider)
|
||||
const escrowId = getNextEscrowId(publicKey.toBase58())
|
||||
@@ -85,9 +95,10 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||
escrowId,
|
||||
})
|
||||
|
||||
const tx = new Transaction().add(ix)
|
||||
const sig = await sendTransaction(tx, connection)
|
||||
await connection.confirmTransaction(sig, 'confirmed')
|
||||
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
||||
const tx = new Transaction({ blockhash, lastValidBlockHeight, feePayer: publicKey }).add(ix)
|
||||
const sig = await walletAdapter.sendTransaction(tx, connection)
|
||||
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, 'confirmed')
|
||||
|
||||
setSuccess(`Escrow #${escrowId.toString()} created! Tx: ${sig.slice(0, 16)}…`)
|
||||
setBuyer('')
|
||||
@@ -112,7 +123,7 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||
value={buyer}
|
||||
onChangeText={setBuyer}
|
||||
placeholder="Pubkey..."
|
||||
fontFamily="monospace"
|
||||
style={{ fontFamily: 'monospace' }}
|
||||
fontSize={13}
|
||||
/>
|
||||
</YStack>
|
||||
@@ -134,7 +145,7 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||
value={resolver}
|
||||
onChangeText={setResolver}
|
||||
placeholder="Pubkey..."
|
||||
fontFamily="monospace"
|
||||
style={{ fontFamily: 'monospace' }}
|
||||
fontSize={13}
|
||||
/>
|
||||
</YStack>
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { YStack, XStack, Text, Button, Spinner, Separator } from 'tamagui'
|
||||
import { useWallet, useConnection } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey, Transaction } from '@solana/web3.js'
|
||||
import { PublicKey, Transaction, Connection } from '@solana/web3.js'
|
||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
||||
import { useTransactionSigner, useDisconnectWallet, useCluster } from '@solana/connector/react'
|
||||
import { useWalletAdapterCompat } from '@solana/connector/compat'
|
||||
import { StatusBadge } from './StatusBadge'
|
||||
import {
|
||||
EscrowClient,
|
||||
isAwaitingDeposit,
|
||||
isActive,
|
||||
isDisputed,
|
||||
} from '@descro/sdk'
|
||||
import { EscrowClient, isAwaitingDeposit, isActive, isDisputed } from '@descro/sdk'
|
||||
import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||
import { useEscrowDetail } from '@/hooks/useEscrowDetail'
|
||||
|
||||
@@ -29,8 +25,18 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
||||
const { account, loading: detailLoading } = useEscrowDetail(pda)
|
||||
const escrow = account ?? item.account
|
||||
|
||||
const { publicKey, sendTransaction, signTransaction } = useWallet()
|
||||
const { connection } = useConnection()
|
||||
const { signer } = useTransactionSigner()
|
||||
const { disconnect } = useDisconnectWallet()
|
||||
const { cluster } = useCluster()
|
||||
const walletAdapter = useWalletAdapterCompat(signer, disconnect)
|
||||
|
||||
const connection = useMemo(
|
||||
() => (cluster?.url ? new Connection(cluster.url, 'confirmed') : null),
|
||||
[cluster?.url],
|
||||
)
|
||||
|
||||
const publicKey = walletAdapter.publicKey ? new PublicKey(walletAdapter.publicKey.toString()) : null
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [success, setSuccess] = useState<string | null>(null)
|
||||
@@ -39,13 +45,12 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
||||
const isSeller = publicKey?.equals(escrow.seller) ?? false
|
||||
const isBuyer = publicKey?.equals(escrow.buyer) ?? false
|
||||
const isResolver =
|
||||
escrow.disputeResolver != null &&
|
||||
publicKey?.equals(escrow.disputeResolver) === true
|
||||
escrow.disputeResolver != null && publicKey?.equals(escrow.disputeResolver) === true
|
||||
|
||||
async function runTx(
|
||||
buildFn: (client: EscrowClient) => Promise<import('@solana/web3.js').TransactionInstruction>
|
||||
buildFn: (client: EscrowClient) => Promise<import('@solana/web3.js').TransactionInstruction>,
|
||||
) {
|
||||
if (!publicKey || !signTransaction) {
|
||||
if (!publicKey || !connection || !walletAdapter.signTransaction) {
|
||||
setError('Connect your wallet first.')
|
||||
return
|
||||
}
|
||||
@@ -55,14 +60,15 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
||||
try {
|
||||
const provider = new AnchorProvider(
|
||||
connection,
|
||||
{ publicKey, signTransaction } as any,
|
||||
{}
|
||||
{ publicKey, signTransaction: walletAdapter.signTransaction.bind(walletAdapter) } as never,
|
||||
{},
|
||||
)
|
||||
const client = new EscrowClient(provider)
|
||||
const ix = await buildFn(client)
|
||||
const tx = new Transaction().add(ix)
|
||||
const sig = await sendTransaction(tx, connection)
|
||||
await connection.confirmTransaction(sig, 'confirmed')
|
||||
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
||||
const tx = new Transaction({ blockhash, lastValidBlockHeight, feePayer: publicKey }).add(ix)
|
||||
const sig = await walletAdapter.sendTransaction(tx, connection)
|
||||
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, 'confirmed')
|
||||
setSuccess(`Done! Tx: ${sig.slice(0, 20)}…`)
|
||||
onAction?.()
|
||||
} catch (err: unknown) {
|
||||
@@ -72,25 +78,21 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleDeposit() {
|
||||
const handleDeposit = () =>
|
||||
runTx((client) => client.buildDeposit({ buyer: publicKey!, escrowPda: pda }))
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
const handleCancel = () =>
|
||||
runTx((client) => client.buildCancel({ seller: publicKey!, escrowPda: pda }))
|
||||
}
|
||||
|
||||
function handleComplete() {
|
||||
const handleComplete = () =>
|
||||
runTx((client) =>
|
||||
client.buildComplete({ buyer: publicKey!, seller: escrow.seller, escrowPda: pda })
|
||||
client.buildComplete({ buyer: publicKey!, seller: escrow.seller, escrowPda: pda }),
|
||||
)
|
||||
}
|
||||
|
||||
function handleDispute() {
|
||||
const handleDispute = () =>
|
||||
runTx((client) => client.buildDispute({ initiator: publicKey!, escrowPda: pda }))
|
||||
}
|
||||
|
||||
function handleResolve(winner: 'buyer' | 'seller') {
|
||||
const handleResolve = (winner: 'buyer' | 'seller') => {
|
||||
const winnerPubkey = winner === 'buyer' ? escrow.buyer : escrow.seller
|
||||
runTx((client) =>
|
||||
client.buildResolve({
|
||||
@@ -100,18 +102,14 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
||||
seller: escrow.seller,
|
||||
escrowPda: pda,
|
||||
disputeResolver: escrow.disputeResolver,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
void detailLoading
|
||||
|
||||
return (
|
||||
<YStack
|
||||
padding="$4"
|
||||
borderRadius="$4"
|
||||
borderWidth={1}
|
||||
borderColor="$borderColor"
|
||||
gap="$3"
|
||||
>
|
||||
<YStack padding="$4" borderRadius="$4" borderWidth={1} borderColor="$borderColor" gap="$3">
|
||||
<XStack justifyContent="space-between" alignItems="center">
|
||||
<Text fontSize={18} fontWeight="700">
|
||||
Escrow #{escrow.escrowId.toString()}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect, useMemo } from 'react'
|
||||
import { YStack, XStack, Text, Button, Input, Spinner, Separator } from 'tamagui'
|
||||
import { useWallet, useConnection } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey, Transaction } from '@solana/web3.js'
|
||||
import { PublicKey, Transaction, Connection } from '@solana/web3.js'
|
||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
||||
import { useTransactionSigner, useDisconnectWallet, useCluster } from '@solana/connector/react'
|
||||
import { useWalletAdapterCompat } from '@solana/connector/compat'
|
||||
import { RegistryClient, resolverTypeLabel } from '@descro/sdk'
|
||||
import type { ResolverEntryWithPda, ResolverType } from '@descro/sdk'
|
||||
|
||||
@@ -22,8 +23,17 @@ function truncPk(pk: PublicKey): string {
|
||||
}
|
||||
|
||||
export function ResolverPanel() {
|
||||
const { publicKey, sendTransaction, signTransaction } = useWallet()
|
||||
const { connection } = useConnection()
|
||||
const { signer } = useTransactionSigner()
|
||||
const { disconnect } = useDisconnectWallet()
|
||||
const { cluster } = useCluster()
|
||||
const walletAdapter = useWalletAdapterCompat(signer, disconnect)
|
||||
|
||||
const connection = useMemo(
|
||||
() => (cluster?.url ? new Connection(cluster.url, 'confirmed') : null),
|
||||
[cluster?.url],
|
||||
)
|
||||
|
||||
const publicKey = walletAdapter.publicKey ? new PublicKey(walletAdapter.publicKey.toString()) : null
|
||||
|
||||
const [resolvers, setResolvers] = useState<ResolverEntryWithPda[]>([])
|
||||
const [resolversLoading, setResolversLoading] = useState(false)
|
||||
@@ -40,9 +50,10 @@ export function ResolverPanel() {
|
||||
const [success, setSuccess] = useState<string | null>(null)
|
||||
|
||||
async function loadResolvers() {
|
||||
if (!connection) return
|
||||
setResolversLoading(true)
|
||||
try {
|
||||
const provider = new AnchorProvider(connection, {} as any, {})
|
||||
const provider = new AnchorProvider(connection, {} as never, {})
|
||||
const client = new RegistryClient(provider)
|
||||
const all = await client.fetchAllResolvers()
|
||||
setResolvers(all)
|
||||
@@ -63,7 +74,7 @@ export function ResolverPanel() {
|
||||
setError(null)
|
||||
setSuccess(null)
|
||||
|
||||
if (!publicKey || !signTransaction) {
|
||||
if (!publicKey || !connection || !walletAdapter.signTransaction) {
|
||||
setError('Connect your wallet first.')
|
||||
return
|
||||
}
|
||||
@@ -92,8 +103,8 @@ export function ResolverPanel() {
|
||||
try {
|
||||
const provider = new AnchorProvider(
|
||||
connection,
|
||||
{ publicKey, signTransaction } as any,
|
||||
{}
|
||||
{ publicKey, signTransaction: walletAdapter.signTransaction.bind(walletAdapter) } as never,
|
||||
{},
|
||||
)
|
||||
const client = new RegistryClient(provider)
|
||||
const resolverType = RESOLVER_TYPE_OPTIONS[resolverTypeIdx].value
|
||||
@@ -108,9 +119,10 @@ export function ResolverPanel() {
|
||||
metadataUri: metadataUri.trim(),
|
||||
})
|
||||
|
||||
const tx = new Transaction().add(ix)
|
||||
const sig = await sendTransaction(tx, connection)
|
||||
await connection.confirmTransaction(sig, 'confirmed')
|
||||
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
||||
const tx = new Transaction({ blockhash, lastValidBlockHeight, feePayer: publicKey }).add(ix)
|
||||
const sig = await walletAdapter.sendTransaction(tx, connection)
|
||||
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, 'confirmed')
|
||||
|
||||
setSuccess(`Registered! Tx: ${sig.slice(0, 20)}…`)
|
||||
setName('')
|
||||
@@ -164,7 +176,7 @@ export function ResolverPanel() {
|
||||
<Text fontSize={12} color="$green9">B: {account.ruledForBuyer.toString()}</Text>
|
||||
<Text fontSize={12} color="$orange9">S: {account.ruledForSeller.toString()}</Text>
|
||||
</XStack>
|
||||
<Text fontSize={11} fontFamily="monospace" color="$color10">
|
||||
<Text fontSize={11} color="$color10" style={{ fontFamily: 'monospace' }}>
|
||||
{truncPk(account.authority)}
|
||||
</Text>
|
||||
</YStack>
|
||||
@@ -207,7 +219,7 @@ export function ResolverPanel() {
|
||||
|
||||
<YStack gap="$1">
|
||||
<Text fontSize={13} color="$color11">Fee Recipient (default: your wallet)</Text>
|
||||
<Input value={feeRecipient} onChangeText={setFeeRecipient} placeholder="Pubkey or leave blank" fontFamily="monospace" fontSize={13} />
|
||||
<Input value={feeRecipient} onChangeText={setFeeRecipient} placeholder="Pubkey or leave blank" fontSize={13} />
|
||||
</YStack>
|
||||
|
||||
<YStack gap="$1">
|
||||
|
||||
@@ -1,22 +1,79 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'
|
||||
import { useState } from 'react'
|
||||
import { useConnector, useConnectWallet, useDisconnectWallet, useWalletConnectors } from '@solana/connector/react'
|
||||
|
||||
export function WalletButton() {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
useEffect(() => setMounted(true), [])
|
||||
if (!mounted) return null
|
||||
const { isConnected, isConnecting, account, connector } = useConnector()
|
||||
const { connect } = useConnectWallet()
|
||||
const { disconnect } = useDisconnectWallet()
|
||||
const connectors = useWalletConnectors()
|
||||
const [showList, setShowList] = useState(false)
|
||||
|
||||
const baseStyle: React.CSSProperties = {
|
||||
backgroundColor: '#512da8',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: 8,
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
padding: '8px 16px',
|
||||
cursor: 'pointer',
|
||||
}
|
||||
|
||||
if (isConnected && account) {
|
||||
const short = `${account.slice(0, 4)}…${account.slice(-4)}`
|
||||
return (
|
||||
<button style={{ ...baseStyle, backgroundColor: '#2e7d32' }} onClick={() => disconnect()}>
|
||||
{connector?.icon && (
|
||||
<img src={connector.icon} width={16} height={16} alt="" style={{ marginRight: 6, verticalAlign: 'middle', borderRadius: 4 }} />
|
||||
)}
|
||||
{short} · Disconnect
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
if (showList) {
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div style={{
|
||||
position: 'absolute', top: 0, right: 0, zIndex: 100,
|
||||
backgroundColor: 'white', border: '1px solid #ddd', borderRadius: 8,
|
||||
padding: 8, minWidth: 200, boxShadow: '0 4px 16px rgba(0,0,0,0.15)',
|
||||
}}>
|
||||
{connectors.length === 0 && (
|
||||
<div style={{ fontSize: 13, color: '#666', padding: '8px 12px' }}>No wallets detected</div>
|
||||
)}
|
||||
{connectors.map((w) => (
|
||||
<button
|
||||
key={w.id}
|
||||
onClick={() => { connect(w.id); setShowList(false) }}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8, width: '100%',
|
||||
padding: '8px 12px', border: 'none', background: 'none',
|
||||
cursor: 'pointer', fontSize: 13, borderRadius: 6,
|
||||
}}
|
||||
onMouseEnter={(e) => { (e.currentTarget as HTMLButtonElement).style.backgroundColor = '#f5f5f5' }}
|
||||
onMouseLeave={(e) => { (e.currentTarget as HTMLButtonElement).style.backgroundColor = 'transparent' }}
|
||||
>
|
||||
{w.icon && <img src={w.icon} width={20} height={20} alt="" style={{ borderRadius: 4 }} />}
|
||||
{w.name}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
onClick={() => setShowList(false)}
|
||||
style={{ width: '100%', padding: '4px', border: 'none', background: 'none', cursor: 'pointer', fontSize: 12, color: '#999', marginTop: 4 }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<WalletMultiButton
|
||||
style={{
|
||||
backgroundColor: '#512da8',
|
||||
borderRadius: 8,
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
padding: '8px 16px',
|
||||
}}
|
||||
/>
|
||||
<button style={baseStyle} onClick={() => setShowList(true)} disabled={isConnecting}>
|
||||
{isConnecting ? 'Connecting…' : 'Connect Wallet'}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import { useConnection } from '@solana/wallet-adapter-react'
|
||||
import { useState, useEffect, useRef, useMemo } from 'react'
|
||||
import { PublicKey, Connection } from '@solana/web3.js'
|
||||
import { useCluster } from '@solana/connector/react'
|
||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
||||
import { EscrowClient, subscribeEscrow } from '@descro/sdk'
|
||||
import type { EscrowAccount } from '@descro/sdk'
|
||||
|
||||
export function useEscrowDetail(pda: PublicKey | null) {
|
||||
const { connection } = useConnection()
|
||||
const { cluster } = useCluster()
|
||||
const connection = useMemo(
|
||||
() => (cluster?.url ? new Connection(cluster.url) : null),
|
||||
[cluster?.url],
|
||||
)
|
||||
|
||||
const [account, setAccount] = useState<EscrowAccount | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const unsubRef = useRef<(() => void) | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!pda) {
|
||||
if (!pda || !connection) {
|
||||
setAccount(null)
|
||||
return
|
||||
}
|
||||
|
||||
let cancelled = false
|
||||
|
||||
setLoading(true)
|
||||
const provider = new AnchorProvider(connection, {} as any, {})
|
||||
const provider = new AnchorProvider(connection, {} as never, {})
|
||||
const client = new EscrowClient(provider)
|
||||
|
||||
client
|
||||
.fetchEscrow(pda)
|
||||
.then((acc) => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { useWallet, useConnection } from '@solana/wallet-adapter-react'
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
|
||||
import { Connection, PublicKey } from '@solana/web3.js'
|
||||
import { useTransactionSigner, useCluster } from '@solana/connector/react'
|
||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
||||
import { EscrowClient } from '@descro/sdk'
|
||||
import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||
@@ -9,24 +10,31 @@ import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||
const POLL_INTERVAL_MS = 5000
|
||||
|
||||
export function useEscrows() {
|
||||
const { publicKey } = useWallet()
|
||||
const { connection } = useConnection()
|
||||
const { address } = useTransactionSigner()
|
||||
const { cluster } = useCluster()
|
||||
|
||||
const publicKey = useMemo(() => (address ? new PublicKey(address) : null), [address])
|
||||
const connection = useMemo(
|
||||
() => (cluster?.url ? new Connection(cluster.url) : null),
|
||||
[cluster?.url],
|
||||
)
|
||||
|
||||
const [escrows, setEscrows] = useState<EscrowAccountWithPda[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
const fetchEscrows = useCallback(async () => {
|
||||
if (!publicKey) {
|
||||
if (!publicKey || !connection) {
|
||||
setEscrows([])
|
||||
return
|
||||
}
|
||||
try {
|
||||
const provider = new AnchorProvider(connection, {} as any, {})
|
||||
const provider = new AnchorProvider(connection, {} as never, {})
|
||||
const client = new EscrowClient(provider)
|
||||
const results = await client.fetchEscrowsForWallet(publicKey)
|
||||
results.sort((a, b) =>
|
||||
Number(b.account.escrowId.toString()) - Number(a.account.escrowId.toString())
|
||||
results.sort(
|
||||
(a, b) => Number(b.account.escrowId.toString()) - Number(a.account.escrowId.toString()),
|
||||
)
|
||||
setEscrows(results)
|
||||
setError(null)
|
||||
@@ -38,9 +46,7 @@ export function useEscrows() {
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
fetchEscrows().finally(() => setLoading(false))
|
||||
|
||||
intervalRef.current = setInterval(fetchEscrows, POLL_INTERVAL_MS)
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current !== null) clearInterval(intervalRef.current)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user