update app to use solana kit
This commit is contained in:
1
app/.gitignore
vendored
1
app/.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
public/tamagui.generated.css
|
public/tamagui.generated.css
|
||||||
.env*.local
|
.env*.local
|
||||||
|
tsconfig.tsbuildinfo
|
||||||
@@ -7,10 +7,9 @@
|
|||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coral-xyz/anchor": "^0.32.1",
|
|
||||||
"@descro/sdk": "workspace:*",
|
"@descro/sdk": "workspace:*",
|
||||||
"@solana/connector": "^0.2.4",
|
"@solana/connector": "^0.2.4",
|
||||||
"@solana/web3.js": "^1.98.4",
|
"@solana/kit": "^6.0.0",
|
||||||
"@tamagui/config": "^2.0.0-rc.42",
|
"@tamagui/config": "^2.0.0-rc.42",
|
||||||
"@tamagui/next-theme": "^2.0.0-rc.42",
|
"@tamagui/next-theme": "^2.0.0-rc.42",
|
||||||
"next": "^16.2.6",
|
"next": "^16.2.6",
|
||||||
|
|||||||
@@ -124,9 +124,9 @@ export default function PlaygroundPage() {
|
|||||||
<YStack gap="$2">
|
<YStack gap="$2">
|
||||||
{escrows.map((item) => (
|
{escrows.map((item) => (
|
||||||
<EscrowCard
|
<EscrowCard
|
||||||
key={item.pda.toBase58()}
|
key={item.pda}
|
||||||
item={item}
|
item={item}
|
||||||
selected={selected?.pda.equals(item.pda) ?? false}
|
selected={selected?.pda === item.pda}
|
||||||
onSelect={() => setSelected(item)}
|
onSelect={() => setSelected(item)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,21 +1,32 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useMemo } from 'react'
|
import { useState } from 'react'
|
||||||
import { YStack, XStack, Text, Button, Input, Spinner } from 'tamagui'
|
import { YStack, XStack, Text, Button, Input, Spinner } from 'tamagui'
|
||||||
import { PublicKey, Transaction, Connection } from '@solana/web3.js'
|
import {
|
||||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
createSolanaRpc,
|
||||||
import { useTransactionSigner, useDisconnectWallet, useCluster } from '@solana/connector/react'
|
createSolanaRpcSubscriptions,
|
||||||
import { useWalletAdapterCompat } from '@solana/connector/compat'
|
pipe,
|
||||||
import BN from 'bn.js'
|
createTransactionMessage,
|
||||||
import { EscrowClient } from '@descro/sdk'
|
setTransactionMessageFeePayerSigner,
|
||||||
|
setTransactionMessageLifetimeUsingBlockhash,
|
||||||
|
appendTransactionMessageInstructions,
|
||||||
|
signTransactionMessageWithSigners,
|
||||||
|
sendAndConfirmTransactionFactory,
|
||||||
|
assertIsTransactionWithBlockhashLifetime,
|
||||||
|
getSignatureFromTransaction,
|
||||||
|
address as kitAddress,
|
||||||
|
} from '@solana/kit'
|
||||||
|
import { useKitTransactionSigner, useWallet, useCluster } from '@solana/connector/react'
|
||||||
|
import { getCreateEscrowInstructionAsync } from '@descro/sdk'
|
||||||
|
import type { Address } from '@descro/sdk'
|
||||||
|
|
||||||
function getNextEscrowId(walletPubkey: string): BN {
|
function getNextEscrowId(walletAddress: string): bigint {
|
||||||
const key = `descro_escrow_id_${walletPubkey}`
|
const key = `descro_escrow_id_${walletAddress}`
|
||||||
const stored = localStorage.getItem(key)
|
const stored = localStorage.getItem(key)
|
||||||
const current = stored ? parseInt(stored, 10) : 0
|
const current = stored ? parseInt(stored, 10) : 0
|
||||||
const next = current + 1
|
const next = current + 1
|
||||||
localStorage.setItem(key, next.toString())
|
localStorage.setItem(key, next.toString())
|
||||||
return new BN(next)
|
return BigInt(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateEscrowFormProps {
|
interface CreateEscrowFormProps {
|
||||||
@@ -23,17 +34,10 @@ interface CreateEscrowFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
||||||
const { signer } = useTransactionSigner()
|
const { signer } = useKitTransactionSigner()
|
||||||
const { disconnect } = useDisconnectWallet()
|
const { account: address } = useWallet()
|
||||||
const { cluster } = useCluster()
|
const { cluster } = useCluster()
|
||||||
const walletAdapter = useWalletAdapterCompat(signer, disconnect)
|
const rpcUrl = cluster?.url ?? null
|
||||||
|
|
||||||
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 [buyer, setBuyer] = useState('')
|
||||||
const [amountSol, setAmountSol] = useState('')
|
const [amountSol, setAmountSol] = useState('')
|
||||||
@@ -47,29 +51,30 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
|||||||
setError(null)
|
setError(null)
|
||||||
setSuccess(null)
|
setSuccess(null)
|
||||||
|
|
||||||
if (!publicKey || !connection) {
|
if (!signer || !address || !rpcUrl) {
|
||||||
setError('Connect your wallet first.')
|
setError('Connect your wallet first.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let buyerPk: PublicKey
|
let buyerAddress: Address
|
||||||
try {
|
try {
|
||||||
buyerPk = new PublicKey(buyer)
|
buyerAddress = kitAddress(buyer)
|
||||||
} catch {
|
} catch {
|
||||||
setError('Invalid buyer public key.')
|
setError('Invalid buyer public key.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const amountLamports = parseFloat(amountSol)
|
const amountSolNum = parseFloat(amountSol)
|
||||||
if (isNaN(amountLamports) || amountLamports <= 0) {
|
if (isNaN(amountSolNum) || amountSolNum <= 0) {
|
||||||
setError('Invalid SOL amount.')
|
setError('Invalid SOL amount.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const amountLamports = BigInt(Math.round(amountSolNum * 1e9))
|
||||||
|
|
||||||
let resolverPk: PublicKey | null = null
|
let resolverAddress: Address | null = null
|
||||||
if (resolver.trim()) {
|
if (resolver.trim()) {
|
||||||
try {
|
try {
|
||||||
resolverPk = new PublicKey(resolver.trim())
|
resolverAddress = kitAddress(resolver.trim())
|
||||||
} catch {
|
} catch {
|
||||||
setError('Invalid resolver public key.')
|
setError('Invalid resolver public key.')
|
||||||
return
|
return
|
||||||
@@ -78,29 +83,33 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
|||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const provider = new AnchorProvider(
|
const rpc = createSolanaRpc(rpcUrl)
|
||||||
connection,
|
const rpcSubscriptions = createSolanaRpcSubscriptions(rpcUrl.replace('http', 'ws'))
|
||||||
{ publicKey, signTransaction: walletAdapter.signTransaction!.bind(walletAdapter) } as never,
|
const escrowId = getNextEscrowId(address)
|
||||||
{},
|
|
||||||
)
|
|
||||||
const client = new EscrowClient(provider)
|
|
||||||
const escrowId = getNextEscrowId(publicKey.toBase58())
|
|
||||||
const amount = new BN(Math.round(amountLamports * 1e9))
|
|
||||||
|
|
||||||
const ix = await client.buildCreateEscrow({
|
const ix = await getCreateEscrowInstructionAsync({
|
||||||
seller: publicKey,
|
seller: signer,
|
||||||
buyer: buyerPk,
|
buyer: buyerAddress,
|
||||||
amount,
|
amount: amountLamports,
|
||||||
disputeResolver: resolverPk,
|
disputeResolver: resolverAddress,
|
||||||
escrowId,
|
escrowId,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send()
|
||||||
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)}…`)
|
const txMsg = pipe(
|
||||||
|
createTransactionMessage({ version: 0 }),
|
||||||
|
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
|
||||||
|
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
||||||
|
(tx) => appendTransactionMessageInstructions([ix as never], tx),
|
||||||
|
)
|
||||||
|
|
||||||
|
const signed = await signTransactionMessageWithSigners(txMsg)
|
||||||
|
assertIsTransactionWithBlockhashLifetime(signed)
|
||||||
|
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signed, { commitment: 'confirmed' })
|
||||||
|
const sig = getSignatureFromTransaction(signed)
|
||||||
|
|
||||||
|
setSuccess(`Escrow #${escrowId} created! Tx: ${sig.slice(0, 16)}…`)
|
||||||
setBuyer('')
|
setBuyer('')
|
||||||
setAmountSol('')
|
setAmountSol('')
|
||||||
setResolver('')
|
setResolver('')
|
||||||
@@ -164,7 +173,7 @@ export function CreateEscrowForm({ onCreated }: CreateEscrowFormProps) {
|
|||||||
<Button
|
<Button
|
||||||
theme="active"
|
theme="active"
|
||||||
onPress={handleSubmit as unknown as () => void}
|
onPress={handleSubmit as unknown as () => void}
|
||||||
disabled={loading || !publicKey}
|
disabled={loading || !address}
|
||||||
icon={loading ? <Spinner /> : undefined}
|
icon={loading ? <Spinner /> : undefined}
|
||||||
>
|
>
|
||||||
{loading ? 'Creating…' : 'Create Escrow'}
|
{loading ? 'Creating…' : 'Create Escrow'}
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ import React from 'react'
|
|||||||
import { YStack, XStack, Text } from 'tamagui'
|
import { YStack, XStack, Text } from 'tamagui'
|
||||||
import { StatusBadge } from './StatusBadge'
|
import { StatusBadge } from './StatusBadge'
|
||||||
import type { EscrowAccountWithPda } from '@descro/sdk'
|
import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||||
import { PublicKey } from '@solana/web3.js'
|
function truncAddr(addr: string): string {
|
||||||
|
return `${addr.slice(0, 4)}…${addr.slice(-4)}`
|
||||||
function truncPubkey(pk: PublicKey): string {
|
|
||||||
const s = pk.toBase58()
|
|
||||||
return `${s.slice(0, 4)}…${s.slice(-4)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EscrowCardProps {
|
interface EscrowCardProps {
|
||||||
@@ -47,16 +44,16 @@ export function EscrowCard({ item, selected, onSelect }: EscrowCardProps) {
|
|||||||
<XStack gap="$4">
|
<XStack gap="$4">
|
||||||
<YStack>
|
<YStack>
|
||||||
<Text fontSize={11} color="$color10">Seller</Text>
|
<Text fontSize={11} color="$color10">Seller</Text>
|
||||||
<Text fontSize={12} fontFamily="monospace">{truncPubkey(account.seller)}</Text>
|
<Text fontSize={12} fontFamily="monospace">{truncAddr(account.seller)}</Text>
|
||||||
</YStack>
|
</YStack>
|
||||||
<YStack>
|
<YStack>
|
||||||
<Text fontSize={11} color="$color10">Buyer</Text>
|
<Text fontSize={11} color="$color10">Buyer</Text>
|
||||||
<Text fontSize={12} fontFamily="monospace">{truncPubkey(account.buyer)}</Text>
|
<Text fontSize={12} fontFamily="monospace">{truncAddr(account.buyer)}</Text>
|
||||||
</YStack>
|
</YStack>
|
||||||
</XStack>
|
</XStack>
|
||||||
|
|
||||||
<Text fontSize={11} color="$color10" fontFamily="monospace">
|
<Text fontSize={11} color="$color10" fontFamily="monospace">
|
||||||
PDA: {truncPubkey(pda)}
|
PDA: {truncAddr(pda)}
|
||||||
</Text>
|
</Text>
|
||||||
</YStack>
|
</YStack>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,13 +1,36 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useMemo } from 'react'
|
import { useState } from 'react'
|
||||||
import { YStack, XStack, Text, Button, Spinner, Separator } from 'tamagui'
|
import { YStack, XStack, Text, Button, Spinner, Separator } from 'tamagui'
|
||||||
import { PublicKey, Transaction, Connection } from '@solana/web3.js'
|
import {
|
||||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
createSolanaRpc,
|
||||||
import { useTransactionSigner, useDisconnectWallet, useCluster } from '@solana/connector/react'
|
createSolanaRpcSubscriptions,
|
||||||
import { useWalletAdapterCompat } from '@solana/connector/compat'
|
pipe,
|
||||||
|
createTransactionMessage,
|
||||||
|
setTransactionMessageFeePayerSigner,
|
||||||
|
setTransactionMessageLifetimeUsingBlockhash,
|
||||||
|
appendTransactionMessageInstructions,
|
||||||
|
signTransactionMessageWithSigners,
|
||||||
|
sendAndConfirmTransactionFactory,
|
||||||
|
assertIsTransactionWithBlockhashLifetime,
|
||||||
|
getSignatureFromTransaction,
|
||||||
|
isSome,
|
||||||
|
} from '@solana/kit'
|
||||||
|
import { useKitTransactionSigner, useWallet, useCluster } from '@solana/connector/react'
|
||||||
import { StatusBadge } from './StatusBadge'
|
import { StatusBadge } from './StatusBadge'
|
||||||
import { EscrowClient, isAwaitingDeposit, isActive, isDisputed } from '@descro/sdk'
|
import {
|
||||||
|
isAwaitingDeposit,
|
||||||
|
isActive,
|
||||||
|
isDisputed,
|
||||||
|
getDepositInstructionAsync,
|
||||||
|
getCancelInstruction,
|
||||||
|
getCompleteInstructionAsync,
|
||||||
|
getDisputeInstruction,
|
||||||
|
getResolveInstructionAsync,
|
||||||
|
findResolverEntryPda,
|
||||||
|
DESCRO_EXT_RESOLVERS_PROGRAM_ADDRESS,
|
||||||
|
Winner,
|
||||||
|
} from '@descro/sdk'
|
||||||
import type { EscrowAccountWithPda } from '@descro/sdk'
|
import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||||
import { useEscrowDetail } from '@/hooks/useEscrowDetail'
|
import { useEscrowDetail } from '@/hooks/useEscrowDetail'
|
||||||
|
|
||||||
@@ -16,41 +39,28 @@ interface EscrowDetailProps {
|
|||||||
onAction?: () => void
|
onAction?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function fullPubkey(pk: PublicKey): string {
|
|
||||||
return pk.toBase58()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
||||||
const { pda } = item
|
const { pda } = item
|
||||||
const { account, loading: detailLoading } = useEscrowDetail(pda)
|
const { account, loading: detailLoading } = useEscrowDetail(pda)
|
||||||
const escrow = account ?? item.account
|
const escrow = account ?? item.account
|
||||||
|
|
||||||
const { signer } = useTransactionSigner()
|
const { signer } = useKitTransactionSigner()
|
||||||
const { disconnect } = useDisconnectWallet()
|
const { account: address } = useWallet()
|
||||||
const { cluster } = useCluster()
|
const { cluster } = useCluster()
|
||||||
const walletAdapter = useWalletAdapterCompat(signer, disconnect)
|
const rpcUrl = cluster?.url ?? null
|
||||||
|
|
||||||
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 [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const [success, setSuccess] = useState<string | null>(null)
|
const [success, setSuccess] = useState<string | null>(null)
|
||||||
|
|
||||||
const solAmount = (Number(escrow.amount.toString()) / 1e9).toFixed(4)
|
const solAmount = (Number(escrow.amount) / 1e9).toFixed(4)
|
||||||
const isSeller = publicKey?.equals(escrow.seller) ?? false
|
const isSeller = address != null && address === escrow.seller
|
||||||
const isBuyer = publicKey?.equals(escrow.buyer) ?? false
|
const isBuyer = address != null && address === escrow.buyer
|
||||||
const isResolver =
|
const resolverAddr = isSome(escrow.disputeResolver) ? escrow.disputeResolver.value : null
|
||||||
escrow.disputeResolver != null && publicKey?.equals(escrow.disputeResolver) === true
|
const isResolver = address != null && resolverAddr != null && address === resolverAddr
|
||||||
|
|
||||||
async function runTx(
|
async function runTx(buildIx: () => Promise<unknown>) {
|
||||||
buildFn: (client: EscrowClient) => Promise<import('@solana/web3.js').TransactionInstruction>,
|
if (!signer || !address || !rpcUrl) {
|
||||||
) {
|
|
||||||
if (!publicKey || !connection || !walletAdapter.signTransaction) {
|
|
||||||
setError('Connect your wallet first.')
|
setError('Connect your wallet first.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -58,17 +68,20 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
|||||||
setError(null)
|
setError(null)
|
||||||
setSuccess(null)
|
setSuccess(null)
|
||||||
try {
|
try {
|
||||||
const provider = new AnchorProvider(
|
const rpc = createSolanaRpc(rpcUrl)
|
||||||
connection,
|
const rpcSubscriptions = createSolanaRpcSubscriptions(rpcUrl.replace('http', 'ws'))
|
||||||
{ publicKey, signTransaction: walletAdapter.signTransaction.bind(walletAdapter) } as never,
|
const ix = await buildIx()
|
||||||
{},
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send()
|
||||||
|
const txMsg = pipe(
|
||||||
|
createTransactionMessage({ version: 0 }),
|
||||||
|
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
|
||||||
|
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
||||||
|
(tx) => appendTransactionMessageInstructions([ix as never], tx),
|
||||||
)
|
)
|
||||||
const client = new EscrowClient(provider)
|
const signed = await signTransactionMessageWithSigners(txMsg)
|
||||||
const ix = await buildFn(client)
|
assertIsTransactionWithBlockhashLifetime(signed)
|
||||||
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signed, { commitment: 'confirmed' })
|
||||||
const tx = new Transaction({ blockhash, lastValidBlockHeight, feePayer: publicKey }).add(ix)
|
const sig = getSignatureFromTransaction(signed)
|
||||||
const sig = await walletAdapter.sendTransaction(tx, connection)
|
|
||||||
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, 'confirmed')
|
|
||||||
setSuccess(`Done! Tx: ${sig.slice(0, 20)}…`)
|
setSuccess(`Done! Tx: ${sig.slice(0, 20)}…`)
|
||||||
onAction?.()
|
onAction?.()
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
@@ -79,31 +92,33 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleDeposit = () =>
|
const handleDeposit = () =>
|
||||||
runTx((client) => client.buildDeposit({ buyer: publicKey!, escrowPda: pda }))
|
runTx(() => getDepositInstructionAsync({ buyer: signer!, escrowAccount: pda }))
|
||||||
|
|
||||||
const handleCancel = () =>
|
const handleCancel = () =>
|
||||||
runTx((client) => client.buildCancel({ seller: publicKey!, escrowPda: pda }))
|
runTx(async () => getCancelInstruction({ seller: signer!, escrowAccount: pda }))
|
||||||
|
|
||||||
const handleComplete = () =>
|
const handleComplete = () =>
|
||||||
runTx((client) =>
|
runTx(() =>
|
||||||
client.buildComplete({ buyer: publicKey!, seller: escrow.seller, escrowPda: pda }),
|
getCompleteInstructionAsync({ buyer: signer!, seller: escrow.seller, escrowAccount: pda }),
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleDispute = () =>
|
const handleDispute = () =>
|
||||||
runTx((client) => client.buildDispute({ initiator: publicKey!, escrowPda: pda }))
|
runTx(async () => getDisputeInstruction({ initiator: signer!, escrowAccount: pda }))
|
||||||
|
|
||||||
const handleResolve = (winner: 'buyer' | 'seller') => {
|
const handleResolve = (winner: 'buyer' | 'seller') => {
|
||||||
const winnerPubkey = winner === 'buyer' ? escrow.buyer : escrow.seller
|
if (!resolverAddr) return
|
||||||
runTx((client) =>
|
runTx(async () => {
|
||||||
client.buildResolve({
|
const [resolverEntryAddr] = await findResolverEntryPda({ authority: resolverAddr })
|
||||||
resolver: publicKey!,
|
return getResolveInstructionAsync({
|
||||||
winner,
|
resolver: signer!,
|
||||||
winnerPubkey,
|
winner: winner === 'buyer' ? escrow.buyer : escrow.seller,
|
||||||
seller: escrow.seller,
|
seller: escrow.seller,
|
||||||
escrowPda: pda,
|
escrowAccount: pda,
|
||||||
disputeResolver: escrow.disputeResolver,
|
resolverEntry: resolverEntryAddr,
|
||||||
}),
|
registryProgram: DESCRO_EXT_RESOLVERS_PROGRAM_ADDRESS,
|
||||||
)
|
winnerArg: winner === 'buyer' ? Winner.Buyer : Winner.Seller,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
void detailLoading
|
void detailLoading
|
||||||
@@ -121,14 +136,14 @@ export function EscrowDetail({ item, onAction }: EscrowDetailProps) {
|
|||||||
|
|
||||||
<YStack gap="$2">
|
<YStack gap="$2">
|
||||||
<Row label="Amount" value={`${solAmount} SOL`} />
|
<Row label="Amount" value={`${solAmount} SOL`} />
|
||||||
<Row label="Seller" value={fullPubkey(escrow.seller)} mono />
|
<Row label="Seller" value={escrow.seller} mono />
|
||||||
<Row label="Buyer" value={fullPubkey(escrow.buyer)} mono />
|
<Row label="Buyer" value={escrow.buyer} mono />
|
||||||
<Row
|
<Row
|
||||||
label="Resolver"
|
label="Resolver"
|
||||||
value={escrow.disputeResolver ? fullPubkey(escrow.disputeResolver) : 'None'}
|
value={resolverAddr ?? 'None'}
|
||||||
mono={!!escrow.disputeResolver}
|
mono={!!resolverAddr}
|
||||||
/>
|
/>
|
||||||
<Row label="PDA" value={fullPubkey(pda)} mono />
|
<Row label="PDA" value={pda} mono />
|
||||||
</YStack>
|
</YStack>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|||||||
@@ -1,39 +1,47 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useState, useEffect, useMemo } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { YStack, XStack, Text, Button, Input, Spinner, Separator } from 'tamagui'
|
import { YStack, XStack, Text, Button, Input, Spinner, Separator } from 'tamagui'
|
||||||
import { PublicKey, Transaction, Connection } from '@solana/web3.js'
|
import {
|
||||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
createSolanaRpc,
|
||||||
import { useTransactionSigner, useDisconnectWallet, useCluster } from '@solana/connector/react'
|
createSolanaRpcSubscriptions,
|
||||||
import { useWalletAdapterCompat } from '@solana/connector/compat'
|
pipe,
|
||||||
import { RegistryClient, resolverTypeLabel } from '@descro/sdk'
|
createTransactionMessage,
|
||||||
import type { ResolverEntryWithPda, ResolverType } from '@descro/sdk'
|
setTransactionMessageFeePayerSigner,
|
||||||
|
setTransactionMessageLifetimeUsingBlockhash,
|
||||||
|
appendTransactionMessageInstructions,
|
||||||
|
signTransactionMessageWithSigners,
|
||||||
|
sendAndConfirmTransactionFactory,
|
||||||
|
assertIsTransactionWithBlockhashLifetime,
|
||||||
|
getSignatureFromTransaction,
|
||||||
|
address as kitAddress,
|
||||||
|
} from '@solana/kit'
|
||||||
|
import { useKitTransactionSigner, useWallet, useCluster } from '@solana/connector/react'
|
||||||
|
import {
|
||||||
|
fetchAllResolvers,
|
||||||
|
getRegisterResolverInstructionAsync,
|
||||||
|
resolverTypeLabel,
|
||||||
|
ResolverType,
|
||||||
|
} from '@descro/sdk'
|
||||||
|
import type { ResolverEntryWithPda } from '@descro/sdk'
|
||||||
|
|
||||||
const RESOLVER_TYPE_OPTIONS: { label: string; value: ResolverType }[] = [
|
const RESOLVER_TYPE_OPTIONS: { label: string; value: ResolverType }[] = [
|
||||||
{ label: 'CentralAuthority', value: { centralAuthority: {} } },
|
{ label: 'CentralAuthority', value: ResolverType.CentralAuthority },
|
||||||
{ label: 'JuryDAO', value: { juryDAO: {} } },
|
{ label: 'JuryDAO', value: ResolverType.JuryDAO },
|
||||||
{ label: 'MAD', value: { mad: {} } },
|
{ label: 'MAD', value: ResolverType.MAD },
|
||||||
{ label: 'Algorithmic', value: { algorithmic: {} } },
|
{ label: 'Algorithmic', value: ResolverType.Algorithmic },
|
||||||
{ label: 'Multisig', value: { multisig: {} } },
|
{ label: 'Multisig', value: ResolverType.Multisig },
|
||||||
]
|
]
|
||||||
|
|
||||||
function truncPk(pk: PublicKey): string {
|
function truncAddr(addr: string): string {
|
||||||
const s = pk.toBase58()
|
return `${addr.slice(0, 4)}…${addr.slice(-4)}`
|
||||||
return `${s.slice(0, 4)}…${s.slice(-4)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ResolverPanel() {
|
export function ResolverPanel() {
|
||||||
const { signer } = useTransactionSigner()
|
const { signer } = useKitTransactionSigner()
|
||||||
const { disconnect } = useDisconnectWallet()
|
const { account: address } = useWallet()
|
||||||
const { cluster } = useCluster()
|
const { cluster } = useCluster()
|
||||||
const walletAdapter = useWalletAdapterCompat(signer, disconnect)
|
const rpcUrl = cluster?.url ?? null
|
||||||
|
|
||||||
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 [resolvers, setResolvers] = useState<ResolverEntryWithPda[]>([])
|
||||||
const [resolversLoading, setResolversLoading] = useState(false)
|
const [resolversLoading, setResolversLoading] = useState(false)
|
||||||
@@ -50,12 +58,11 @@ export function ResolverPanel() {
|
|||||||
const [success, setSuccess] = useState<string | null>(null)
|
const [success, setSuccess] = useState<string | null>(null)
|
||||||
|
|
||||||
async function loadResolvers() {
|
async function loadResolvers() {
|
||||||
if (!connection) return
|
if (!rpcUrl) return
|
||||||
setResolversLoading(true)
|
setResolversLoading(true)
|
||||||
try {
|
try {
|
||||||
const provider = new AnchorProvider(connection, {} as never, {})
|
const rpc = createSolanaRpc(rpcUrl)
|
||||||
const client = new RegistryClient(provider)
|
const all = await fetchAllResolvers(rpc)
|
||||||
const all = await client.fetchAllResolvers()
|
|
||||||
setResolvers(all)
|
setResolvers(all)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load resolvers', err)
|
console.error('Failed to load resolvers', err)
|
||||||
@@ -67,22 +74,22 @@ export function ResolverPanel() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadResolvers()
|
loadResolvers()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [connection])
|
}, [rpcUrl])
|
||||||
|
|
||||||
async function handleRegister(e: React.FormEvent) {
|
async function handleRegister(e: React.FormEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setError(null)
|
setError(null)
|
||||||
setSuccess(null)
|
setSuccess(null)
|
||||||
|
|
||||||
if (!publicKey || !connection || !walletAdapter.signTransaction) {
|
if (!signer || !address || !rpcUrl) {
|
||||||
setError('Connect your wallet first.')
|
setError('Connect your wallet first.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let recipientPk: PublicKey
|
let recipientAddr
|
||||||
const recipientStr = feeRecipient.trim() || publicKey.toBase58()
|
const recipientStr = feeRecipient.trim() || address
|
||||||
try {
|
try {
|
||||||
recipientPk = new PublicKey(recipientStr)
|
recipientAddr = kitAddress(recipientStr)
|
||||||
} catch {
|
} catch {
|
||||||
setError('Invalid fee recipient public key.')
|
setError('Invalid fee recipient public key.')
|
||||||
return
|
return
|
||||||
@@ -101,28 +108,31 @@ export function ResolverPanel() {
|
|||||||
|
|
||||||
setFormLoading(true)
|
setFormLoading(true)
|
||||||
try {
|
try {
|
||||||
const provider = new AnchorProvider(
|
const rpc = createSolanaRpc(rpcUrl)
|
||||||
connection,
|
const rpcSubscriptions = createSolanaRpcSubscriptions(rpcUrl.replace('http', 'ws'))
|
||||||
{ publicKey, signTransaction: walletAdapter.signTransaction.bind(walletAdapter) } as never,
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
const client = new RegistryClient(provider)
|
|
||||||
const resolverType = RESOLVER_TYPE_OPTIONS[resolverTypeIdx].value
|
const resolverType = RESOLVER_TYPE_OPTIONS[resolverTypeIdx].value
|
||||||
|
|
||||||
const ix = await client.buildRegisterResolver({
|
const ix = await getRegisterResolverInstructionAsync({
|
||||||
authority: publicKey,
|
authority: signer,
|
||||||
resolverType,
|
resolverType,
|
||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
description: description.trim(),
|
description: description.trim(),
|
||||||
feeBps: fee,
|
feeBps: fee,
|
||||||
feeRecipient: recipientPk,
|
feeRecipient: recipientAddr,
|
||||||
metadataUri: metadataUri.trim(),
|
metadataUri: metadataUri.trim(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send()
|
||||||
const tx = new Transaction({ blockhash, lastValidBlockHeight, feePayer: publicKey }).add(ix)
|
const txMsg = pipe(
|
||||||
const sig = await walletAdapter.sendTransaction(tx, connection)
|
createTransactionMessage({ version: 0 }),
|
||||||
await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, 'confirmed')
|
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
|
||||||
|
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
||||||
|
(tx) => appendTransactionMessageInstructions([ix as never], tx),
|
||||||
|
)
|
||||||
|
const signed = await signTransactionMessageWithSigners(txMsg)
|
||||||
|
assertIsTransactionWithBlockhashLifetime(signed)
|
||||||
|
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signed, { commitment: 'confirmed' })
|
||||||
|
const sig = getSignatureFromTransaction(signed)
|
||||||
|
|
||||||
setSuccess(`Registered! Tx: ${sig.slice(0, 20)}…`)
|
setSuccess(`Registered! Tx: ${sig.slice(0, 20)}…`)
|
||||||
setName('')
|
setName('')
|
||||||
@@ -156,7 +166,7 @@ export function ResolverPanel() {
|
|||||||
|
|
||||||
{resolvers.map(({ pda, account }) => (
|
{resolvers.map(({ pda, account }) => (
|
||||||
<YStack
|
<YStack
|
||||||
key={pda.toBase58()}
|
key={pda}
|
||||||
padding="$3"
|
padding="$3"
|
||||||
borderRadius="$3"
|
borderRadius="$3"
|
||||||
borderWidth={1}
|
borderWidth={1}
|
||||||
@@ -177,7 +187,7 @@ export function ResolverPanel() {
|
|||||||
<Text fontSize={12} color="$orange9">S: {account.ruledForSeller.toString()}</Text>
|
<Text fontSize={12} color="$orange9">S: {account.ruledForSeller.toString()}</Text>
|
||||||
</XStack>
|
</XStack>
|
||||||
<Text fontSize={11} color="$color10" style={{ fontFamily: 'monospace' }}>
|
<Text fontSize={11} color="$color10" style={{ fontFamily: 'monospace' }}>
|
||||||
{truncPk(account.authority)}
|
{truncAddr(account.authority)}
|
||||||
</Text>
|
</Text>
|
||||||
</YStack>
|
</YStack>
|
||||||
))}
|
))}
|
||||||
@@ -241,7 +251,7 @@ export function ResolverPanel() {
|
|||||||
<Button
|
<Button
|
||||||
theme="active"
|
theme="active"
|
||||||
onPress={handleRegister as unknown as () => void}
|
onPress={handleRegister as unknown as () => void}
|
||||||
disabled={formLoading || !publicKey}
|
disabled={formLoading || !address}
|
||||||
icon={formLoading ? <Spinner /> : undefined}
|
icon={formLoading ? <Spinner /> : undefined}
|
||||||
>
|
>
|
||||||
{formLoading ? 'Registering…' : 'Register'}
|
{formLoading ? 'Registering…' : 'Register'}
|
||||||
|
|||||||
@@ -1,40 +1,33 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect, useRef, useMemo } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { PublicKey, Connection } from '@solana/web3.js'
|
import { createSolanaRpc, createSolanaRpcSubscriptions } from '@solana/kit'
|
||||||
import { useCluster } from '@solana/connector/react'
|
import { useCluster } from '@solana/connector/react'
|
||||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
import { fetchMaybeEscrowAccount, subscribeEscrow } from '@descro/sdk'
|
||||||
import { EscrowClient, subscribeEscrow } from '@descro/sdk'
|
import type { Address, EscrowAccount } from '@descro/sdk'
|
||||||
import type { EscrowAccount } from '@descro/sdk'
|
|
||||||
|
|
||||||
export function useEscrowDetail(pda: PublicKey | null) {
|
export function useEscrowDetail(pda: Address | null) {
|
||||||
const { cluster } = useCluster()
|
const { cluster } = useCluster()
|
||||||
const connection = useMemo(
|
const rpcUrl = cluster?.url ?? null
|
||||||
() => (cluster?.url ? new Connection(cluster.url) : null),
|
|
||||||
[cluster?.url],
|
|
||||||
)
|
|
||||||
|
|
||||||
const [account, setAccount] = useState<EscrowAccount | null>(null)
|
const [account, setAccount] = useState<EscrowAccount | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const unsubRef = useRef<(() => void) | null>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!pda || !connection) {
|
if (!pda || !rpcUrl) {
|
||||||
setAccount(null)
|
setAccount(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const provider = new AnchorProvider(connection, {} as never, {})
|
const rpc = createSolanaRpc(rpcUrl)
|
||||||
const client = new EscrowClient(provider)
|
|
||||||
|
|
||||||
client
|
fetchMaybeEscrowAccount(rpc, pda)
|
||||||
.fetchEscrow(pda)
|
.then((maybeAcc) => {
|
||||||
.then((acc) => {
|
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setAccount(acc)
|
setAccount(maybeAcc.exists ? maybeAcc.data : null)
|
||||||
setError(null)
|
setError(null)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -45,17 +38,17 @@ export function useEscrowDetail(pda: PublicKey | null) {
|
|||||||
if (!cancelled) setLoading(false)
|
if (!cancelled) setLoading(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
const unsub = subscribeEscrow(connection, pda, (updated) => {
|
const wsUrl = rpcUrl.replace('http', 'ws')
|
||||||
|
const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrl)
|
||||||
|
const unsub = subscribeEscrow(rpcSubscriptions, pda, (updated) => {
|
||||||
if (!cancelled) setAccount(updated)
|
if (!cancelled) setAccount(updated)
|
||||||
})
|
})
|
||||||
unsubRef.current = unsub
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
unsub()
|
unsub()
|
||||||
unsubRef.current = null
|
|
||||||
}
|
}
|
||||||
}, [pda?.toBase58(), connection])
|
}, [pda, rpcUrl])
|
||||||
|
|
||||||
return { account, loading, error }
|
return { account, loading, error }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
|
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||||
import { Connection, PublicKey } from '@solana/web3.js'
|
import { createSolanaRpc } from '@solana/kit'
|
||||||
import { useTransactionSigner, useCluster } from '@solana/connector/react'
|
import { useWallet, useCluster } from '@solana/connector/react'
|
||||||
import { AnchorProvider } from '@coral-xyz/anchor'
|
import { fetchEscrowsForWallet } from '@descro/sdk'
|
||||||
import { EscrowClient } from '@descro/sdk'
|
|
||||||
import type { EscrowAccountWithPda } from '@descro/sdk'
|
import type { EscrowAccountWithPda } from '@descro/sdk'
|
||||||
|
|
||||||
const POLL_INTERVAL_MS = 5000
|
const POLL_INTERVAL_MS = 5000
|
||||||
|
|
||||||
export function useEscrows() {
|
export function useEscrows() {
|
||||||
const { address } = useTransactionSigner()
|
const { account: address } = useWallet()
|
||||||
const { cluster } = useCluster()
|
const { cluster } = useCluster()
|
||||||
|
const rpcUrl = cluster?.url ?? null
|
||||||
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 [escrows, setEscrows] = useState<EscrowAccountWithPda[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@@ -25,23 +19,20 @@ export function useEscrows() {
|
|||||||
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||||
|
|
||||||
const fetchEscrows = useCallback(async () => {
|
const fetchEscrows = useCallback(async () => {
|
||||||
if (!publicKey || !connection) {
|
if (!address || !rpcUrl) {
|
||||||
setEscrows([])
|
setEscrows([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const provider = new AnchorProvider(connection, {} as never, {})
|
const rpc = createSolanaRpc(rpcUrl)
|
||||||
const client = new EscrowClient(provider)
|
const results = await fetchEscrowsForWallet(rpc, address)
|
||||||
const results = await client.fetchEscrowsForWallet(publicKey)
|
results.sort((a, b) => Number(b.account.escrowId - a.account.escrowId))
|
||||||
results.sort(
|
|
||||||
(a, b) => Number(b.account.escrowId.toString()) - Number(a.account.escrowId.toString()),
|
|
||||||
)
|
|
||||||
setEscrows(results)
|
setEscrows(results)
|
||||||
setError(null)
|
setError(null)
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
setError(err instanceof Error ? err.message : String(err))
|
setError(err instanceof Error ? err.message : String(err))
|
||||||
}
|
}
|
||||||
}, [publicKey, connection])
|
}, [address, rpcUrl])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
"target": "ES2020",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
|
|||||||
@@ -2,4 +2,45 @@ export * from "./types";
|
|||||||
export * from "./pda";
|
export * from "./pda";
|
||||||
export * from "./escrow";
|
export * from "./escrow";
|
||||||
export * from "./registry";
|
export * from "./registry";
|
||||||
export * from "./listener";
|
export * from "./listener";
|
||||||
|
|
||||||
|
export {
|
||||||
|
getCreateEscrowInstructionAsync,
|
||||||
|
getCreateEscrowInstruction,
|
||||||
|
type CreateEscrowAsyncInput,
|
||||||
|
type CreateEscrowInput,
|
||||||
|
} from "./generated/descro/src/generated/instructions/createEscrow";
|
||||||
|
export {
|
||||||
|
getDepositInstructionAsync,
|
||||||
|
getDepositInstruction,
|
||||||
|
type DepositAsyncInput,
|
||||||
|
type DepositInput,
|
||||||
|
} from "./generated/descro/src/generated/instructions/deposit";
|
||||||
|
export {
|
||||||
|
getCompleteInstructionAsync,
|
||||||
|
getCompleteInstruction,
|
||||||
|
type CompleteAsyncInput,
|
||||||
|
type CompleteInput,
|
||||||
|
} from "./generated/descro/src/generated/instructions/complete";
|
||||||
|
export {
|
||||||
|
getCancelInstruction,
|
||||||
|
type CancelInput,
|
||||||
|
} from "./generated/descro/src/generated/instructions/cancel";
|
||||||
|
export {
|
||||||
|
getDisputeInstruction,
|
||||||
|
type DisputeInput,
|
||||||
|
} from "./generated/descro/src/generated/instructions/dispute";
|
||||||
|
export {
|
||||||
|
getResolveInstructionAsync,
|
||||||
|
getResolveInstruction,
|
||||||
|
type ResolveAsyncInput,
|
||||||
|
type ResolveInput,
|
||||||
|
} from "./generated/descro/src/generated/instructions/resolve";
|
||||||
|
export {
|
||||||
|
getRegisterResolverInstructionAsync,
|
||||||
|
getRegisterResolverInstruction,
|
||||||
|
type RegisterResolverAsyncInput,
|
||||||
|
type RegisterResolverInput,
|
||||||
|
} from "./generated/descro_ext_resolvers/src/generated/instructions/registerResolver";
|
||||||
|
export { Winner } from "./generated/descro/src/generated/types/winner";
|
||||||
|
export { ResolverType } from "./generated/descro_ext_resolvers/src/generated/types/resolverType";
|
||||||
@@ -1 +1,49 @@
|
|||||||
export * from "./generated/descro_ext_resolvers/src/generated/accounts";
|
import type { GetProgramAccountsApi, Lamports, Rpc } from "@solana/kit";
|
||||||
|
import {
|
||||||
|
fetchMaybeResolverEntry,
|
||||||
|
fetchResolverEntry,
|
||||||
|
decodeResolverEntry,
|
||||||
|
getResolverEntryDiscriminatorBytes,
|
||||||
|
} from "./generated/descro_ext_resolvers/src/generated/accounts/resolverEntry";
|
||||||
|
import { DESCRO_EXT_RESOLVERS_PROGRAM_ADDRESS } from "./generated/descro_ext_resolvers/src/generated/programs/descroExtResolvers";
|
||||||
|
import type { ResolverEntryWithPda } from "./types";
|
||||||
|
|
||||||
|
export { fetchResolverEntry, fetchMaybeResolverEntry, decodeResolverEntry };
|
||||||
|
|
||||||
|
export async function fetchAllResolvers(
|
||||||
|
rpc: Rpc<GetProgramAccountsApi>,
|
||||||
|
): Promise<ResolverEntryWithPda[]> {
|
||||||
|
const discriminatorBase64 = btoa(
|
||||||
|
String.fromCharCode(...getResolverEntryDiscriminatorBytes()),
|
||||||
|
) as never;
|
||||||
|
|
||||||
|
const accounts = await rpc
|
||||||
|
.getProgramAccounts(DESCRO_EXT_RESOLVERS_PROGRAM_ADDRESS, {
|
||||||
|
filters: [
|
||||||
|
{ memcmp: { offset: 0n, bytes: discriminatorBase64, encoding: "base64" } },
|
||||||
|
],
|
||||||
|
encoding: "base64",
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
|
||||||
|
const results: ResolverEntryWithPda[] = [];
|
||||||
|
|
||||||
|
for (const item of accounts) {
|
||||||
|
const pda = item.pubkey;
|
||||||
|
const [base64Data] = item.account.data;
|
||||||
|
const rawBytes = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0));
|
||||||
|
|
||||||
|
const decoded = decodeResolverEntry({
|
||||||
|
address: pda,
|
||||||
|
data: rawBytes,
|
||||||
|
executable: item.account.executable,
|
||||||
|
lamports: item.account.lamports as Lamports,
|
||||||
|
programAddress: item.account.owner,
|
||||||
|
space: item.account.space,
|
||||||
|
exists: true,
|
||||||
|
});
|
||||||
|
if (decoded.exists) results.push({ pda, account: decoded.data });
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|||||||
48
yarn.lock
48
yarn.lock
@@ -394,46 +394,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@coral-xyz/anchor-errors@npm:^0.31.1":
|
|
||||||
version: 0.31.1
|
|
||||||
resolution: "@coral-xyz/anchor-errors@npm:0.31.1"
|
|
||||||
checksum: 10c0/d359d6244a89bcfb606e6d31d545c3be8e32dccd47b78dac6ec0091a51b2eb6c78aad9601f39db2659d8e66db8fffc36477247d58d570bc9d866e7ec3e40b5b2
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@coral-xyz/anchor@npm:^0.32.1":
|
|
||||||
version: 0.32.1
|
|
||||||
resolution: "@coral-xyz/anchor@npm:0.32.1"
|
|
||||||
dependencies:
|
|
||||||
"@coral-xyz/anchor-errors": "npm:^0.31.1"
|
|
||||||
"@coral-xyz/borsh": "npm:^0.31.1"
|
|
||||||
"@noble/hashes": "npm:^1.3.1"
|
|
||||||
"@solana/web3.js": "npm:^1.69.0"
|
|
||||||
bn.js: "npm:^5.1.2"
|
|
||||||
bs58: "npm:^4.0.1"
|
|
||||||
buffer-layout: "npm:^1.2.2"
|
|
||||||
camelcase: "npm:^6.3.0"
|
|
||||||
cross-fetch: "npm:^3.1.5"
|
|
||||||
eventemitter3: "npm:^4.0.7"
|
|
||||||
pako: "npm:^2.0.3"
|
|
||||||
superstruct: "npm:^0.15.4"
|
|
||||||
toml: "npm:^3.0.0"
|
|
||||||
checksum: 10c0/669b6f54efb0a5a96dad9e055d30b3b803f691b1b058379efefb87c11f97a9d369d513b2f200082c0b55b4cb31540dc5404e244c6f23f0a14b6d2ad2e18f08b7
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@coral-xyz/borsh@npm:^0.31.1":
|
|
||||||
version: 0.31.1
|
|
||||||
resolution: "@coral-xyz/borsh@npm:0.31.1"
|
|
||||||
dependencies:
|
|
||||||
bn.js: "npm:^5.1.2"
|
|
||||||
buffer-layout: "npm:^1.2.0"
|
|
||||||
peerDependencies:
|
|
||||||
"@solana/web3.js": ^1.69.0
|
|
||||||
checksum: 10c0/3d6d40a1476df5eb0635d8687a640de89fd7e7ffd135f733907f13ff2569a2a07f65f64768d632fb45ed55006d9c9ee94e40fd775813a9cf1211fefa567f98dd
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@descro/sdk@workspace:*, @descro/sdk@workspace:sdk":
|
"@descro/sdk@workspace:*, @descro/sdk@workspace:sdk":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@descro/sdk@workspace:sdk"
|
resolution: "@descro/sdk@workspace:sdk"
|
||||||
@@ -441,6 +401,7 @@ __metadata:
|
|||||||
"@codama/nodes-from-anchor": "npm:^1.4.1"
|
"@codama/nodes-from-anchor": "npm:^1.4.1"
|
||||||
"@codama/renderers-js": "npm:^2.2.0"
|
"@codama/renderers-js": "npm:^2.2.0"
|
||||||
"@solana/kit": "npm:^6.0.0"
|
"@solana/kit": "npm:^6.0.0"
|
||||||
|
"@solana/program-client-core": "npm:^6.4.0"
|
||||||
codama: "npm:^1.6.0"
|
codama: "npm:^1.6.0"
|
||||||
typescript: "npm:^6.0.3"
|
typescript: "npm:^6.0.3"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
@@ -2136,7 +2097,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@solana/program-client-core@npm:6.9.0":
|
"@solana/program-client-core@npm:6.9.0, @solana/program-client-core@npm:^6.4.0":
|
||||||
version: 6.9.0
|
version: 6.9.0
|
||||||
resolution: "@solana/program-client-core@npm:6.9.0"
|
resolution: "@solana/program-client-core@npm:6.9.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2927,7 +2888,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@solana/web3.js@npm:^1.69.0, @solana/web3.js@npm:^1.98.4":
|
"@solana/web3.js@npm:^1.69.0":
|
||||||
version: 1.98.4
|
version: 1.98.4
|
||||||
resolution: "@solana/web3.js@npm:1.98.4"
|
resolution: "@solana/web3.js@npm:1.98.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5844,10 +5805,9 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "descro-app@workspace:app"
|
resolution: "descro-app@workspace:app"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@coral-xyz/anchor": "npm:^0.32.1"
|
|
||||||
"@descro/sdk": "workspace:*"
|
"@descro/sdk": "workspace:*"
|
||||||
"@solana/connector": "npm:^0.2.4"
|
"@solana/connector": "npm:^0.2.4"
|
||||||
"@solana/web3.js": "npm:^1.98.4"
|
"@solana/kit": "npm:^6.0.0"
|
||||||
"@tamagui/cli": "npm:^2.0.0-rc.42"
|
"@tamagui/cli": "npm:^2.0.0-rc.42"
|
||||||
"@tamagui/config": "npm:^2.0.0-rc.42"
|
"@tamagui/config": "npm:^2.0.0-rc.42"
|
||||||
"@tamagui/next-theme": "npm:^2.0.0-rc.42"
|
"@tamagui/next-theme": "npm:^2.0.0-rc.42"
|
||||||
|
|||||||
Reference in New Issue
Block a user