Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/solana-logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Escrow Program</title>
</head>
Expand Down
13 changes: 13 additions & 0 deletions apps/web/public/solana-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 3 additions & 9 deletions apps/web/src/components/TxResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ import { Badge, Button } from '@solana/design-system';

import { useClusterConfig } from '@/hooks/use-cluster-config';
import { getClusterFromClusterId, getSolanaExplorerUrl } from '@/lib/explorer';
import { formatTransactionErrorWithLogs } from '@/lib/transactionErrors';

interface TxResultProps {
error: unknown;
signature: string | null | undefined;
}

function getErrorMessage(error: unknown): string | null {
if (!error) return null;
if (error instanceof Error) return error.message;
if (typeof error === 'string') return error;
return 'Transaction failed';
}

export function TxResult({ signature, error }: TxResultProps) {
const { id } = useClusterConfig();
const errorMessage = getErrorMessage(error);
const errorMessage = error ? formatTransactionErrorWithLogs(error) : null;

if (!signature && !errorMessage) return null;

Expand All @@ -27,7 +21,7 @@ export function TxResult({ signature, error }: TxResultProps) {
return (
<div className="mt-4 flex items-start gap-2 rounded-lg border border-destructive/20 bg-card px-3 py-2 text-sm text-destructive">
<Badge variant="danger">Failed</Badge>
<span className="break-all">{errorMessage}</span>
<span className="whitespace-pre-wrap break-words">{errorMessage}</span>
</div>
);
}
Expand Down
17 changes: 13 additions & 4 deletions apps/web/src/components/instructions/AllowMint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import { useState } from 'react';
import { useSavedValues } from '@/contexts/SavedValuesContext';
import { useEscrowMutations } from '@/hooks/use-escrow-mutations';
import { useTokenFormDefaults } from '@/hooks/use-token-form-defaults';
import { TxResult } from '@/components/TxResult';
import { firstValidationError, validateAddress } from '@/lib/validation';
import { firstValidationError, validateAddress, validateOptionalAddress } from '@/lib/validation';
import { FormField, SendButton } from './shared';

interface AllowMintProps {
Expand All @@ -25,7 +26,7 @@ export function AllowMint({
const { allowMint } = useEscrowMutations();
const { defaultEscrow, defaultMint, rememberEscrow, rememberMint } = useSavedValues();
const [escrow, setEscrow] = useState(initialEscrow);
const [mint, setMint] = useState(initialMint);
const { clusterMint, mint, setMint, setTokenProgram, tokenProgram } = useTokenFormDefaults(initialMint);
const [formError, setFormError] = useState<string | null>(null);

const handleSubmit = async (e: React.FormEvent) => {
Expand All @@ -36,13 +37,14 @@ export function AllowMint({
const validationError = firstValidationError(
validateAddress(escrow, 'Escrow address'),
validateAddress(mint, 'Mint address'),
validateOptionalAddress(tokenProgram, 'Token program'),
);
if (validationError) {
setFormError(validationError);
return;
}

const result = await allowMint.mutateAsync({ escrow, mint }).catch(() => null);
const result = await allowMint.mutateAsync({ escrow, mint, tokenProgram }).catch(() => null);
if (!result) return;

rememberEscrow(escrow);
Expand Down Expand Up @@ -72,13 +74,20 @@ export function AllowMint({
label="Mint Address"
value={mint}
onChange={setMint}
autoFillValue={defaultMint}
autoFillValue={defaultMint || clusterMint}
onAutoFill={setMint}
placeholder="SPL token mint to allow"
required
/>
</>
)}
<FormField
label="Token Program"
value={tokenProgram}
onChange={setTokenProgram}
placeholder="Token program address"
hint="Use Token-2022 program address for Token-2022 mints"
/>
<SendButton sending={allowMint.isPending} label={submitLabel} />
<TxResult signature={allowMint.data?.signature} error={formError ?? allowMint.error} />
</form>
Expand Down
15 changes: 12 additions & 3 deletions apps/web/src/components/instructions/BlockMint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState } from 'react';
import { useSavedValues } from '@/contexts/SavedValuesContext';
import { useWallet } from '@/contexts/WalletContext';
import { useEscrowMutations } from '@/hooks/use-escrow-mutations';
import { useTokenFormDefaults } from '@/hooks/use-token-form-defaults';
import { TxResult } from '@/components/TxResult';
import { firstValidationError, validateAddress, validateOptionalAddress } from '@/lib/validation';
import { FormField, SendButton } from './shared';
Expand All @@ -29,7 +30,7 @@ export function BlockMint({
const { blockMint } = useEscrowMutations();
const { defaultEscrow, defaultMint, rememberEscrow, rememberMint } = useSavedValues();
const [escrow, setEscrow] = useState(initialEscrow);
const [mint, setMint] = useState(initialMint);
const { clusterMint, mint, setMint, setTokenProgram, tokenProgram } = useTokenFormDefaults(initialMint);
const [rentRecipient, setRentRecipient] = useState(initialRentRecipient);
const [formError, setFormError] = useState<string | null>(null);

Expand All @@ -42,13 +43,14 @@ export function BlockMint({
validateAddress(escrow, 'Escrow address'),
validateAddress(mint, 'Mint address'),
validateOptionalAddress(rentRecipient, 'Rent recipient'),
validateOptionalAddress(tokenProgram, 'Token program'),
);
if (validationError) {
setFormError(validationError);
return;
}

const result = await blockMint.mutateAsync({ escrow, mint, rentRecipient }).catch(() => null);
const result = await blockMint.mutateAsync({ escrow, mint, rentRecipient, tokenProgram }).catch(() => null);
if (!result) return;

rememberEscrow(escrow);
Expand Down Expand Up @@ -78,7 +80,7 @@ export function BlockMint({
label="Mint Address"
value={mint}
onChange={setMint}
autoFillValue={defaultMint}
autoFillValue={defaultMint || clusterMint}
onAutoFill={setMint}
placeholder="SPL token mint to block"
required
Expand All @@ -92,6 +94,13 @@ export function BlockMint({
placeholder={account?.address ?? 'Defaults to connected wallet'}
hint="Address that receives rent from the closed allowed-mint account"
/>
<FormField
label="Token Program"
value={tokenProgram}
onChange={setTokenProgram}
placeholder="Token program address"
hint="Use Token-2022 program address for Token-2022 mints"
/>
<SendButton sending={blockMint.isPending} label={submitLabel} />
<TxResult signature={blockMint.data?.signature} error={formError ?? blockMint.error} />
</form>
Expand Down
21 changes: 18 additions & 3 deletions apps/web/src/components/instructions/Deposit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
import { useState } from 'react';
import { useSavedValues } from '@/contexts/SavedValuesContext';
import { useEscrowMutations } from '@/hooks/use-escrow-mutations';
import { useTokenFormDefaults } from '@/hooks/use-token-form-defaults';
import { TxResult } from '@/components/TxResult';
import { firstValidationError, validateAddress, validatePositiveInteger } from '@/lib/validation';
import {
firstValidationError,
validateAddress,
validateOptionalAddress,
validatePositiveInteger,
} from '@/lib/validation';
import { FormField, SendButton } from './shared';

interface DepositProps {
Expand All @@ -27,7 +33,7 @@ export function Deposit({
const { deposit } = useEscrowMutations();
const { defaultEscrow, defaultMint, rememberEscrow, rememberMint, rememberReceipt } = useSavedValues();
const [escrow, setEscrow] = useState(initialEscrow);
const [mint, setMint] = useState(initialMint);
const { clusterMint, mint, setMint, setTokenProgram, tokenProgram } = useTokenFormDefaults(initialMint);
const [amount, setAmount] = useState(initialAmount);
const [generatedSeed, setGeneratedSeed] = useState('');
const [generatedReceipt, setGeneratedReceipt] = useState('');
Expand All @@ -42,6 +48,7 @@ export function Deposit({
validateAddress(escrow, 'Escrow address'),
validateAddress(mint, 'Mint address'),
validatePositiveInteger(amount, 'Amount'),
validateOptionalAddress(tokenProgram, 'Token program'),
);
if (validationError) {
setFormError(validationError);
Expand All @@ -53,6 +60,7 @@ export function Deposit({
amount: BigInt(amount),
escrow,
mint,
tokenProgram,
})
.catch(() => null);
if (!result) return;
Expand Down Expand Up @@ -87,7 +95,7 @@ export function Deposit({
label="Mint Address"
value={mint}
onChange={setMint}
autoFillValue={defaultMint}
autoFillValue={defaultMint || clusterMint}
onAutoFill={setMint}
placeholder="SPL token mint address"
required
Expand All @@ -103,6 +111,13 @@ export function Deposit({
hint="Amount in smallest token units (no decimals)"
required
/>
<FormField
label="Token Program"
value={tokenProgram}
onChange={setTokenProgram}
placeholder="Token program address"
hint="Use Token-2022 program address for Token-2022 mints"
/>
{generatedSeed && (
<FormField
label="Generated Receipt Seed"
Expand Down
14 changes: 12 additions & 2 deletions apps/web/src/components/instructions/Withdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState } from 'react';
import { useSavedValues } from '@/contexts/SavedValuesContext';
import { useWallet } from '@/contexts/WalletContext';
import { useEscrowMutations } from '@/hooks/use-escrow-mutations';
import { useTokenFormDefaults } from '@/hooks/use-token-form-defaults';
import { TxResult } from '@/components/TxResult';
import { firstValidationError, validateAddress, validateOptionalAddress } from '@/lib/validation';
import { FormField, SendButton } from './shared';
Expand Down Expand Up @@ -32,7 +33,7 @@ export function Withdraw({
const { defaultEscrow, defaultMint, defaultReceipt, rememberEscrow, rememberMint, rememberReceipt } =
useSavedValues();
const [escrow, setEscrow] = useState(initialEscrow);
const [mint, setMint] = useState(initialMint);
const { clusterMint, mint, setMint, setTokenProgram, tokenProgram } = useTokenFormDefaults(initialMint);
const [receipt, setReceipt] = useState(initialReceipt);
const [rentRecipient, setRentRecipient] = useState(initialRentRecipient);
const [formError, setFormError] = useState<string | null>(null);
Expand All @@ -47,6 +48,7 @@ export function Withdraw({
validateAddress(mint, 'Mint address'),
validateAddress(receipt, 'Receipt address'),
validateOptionalAddress(rentRecipient, 'Rent recipient'),
validateOptionalAddress(tokenProgram, 'Token program'),
);
if (validationError) {
setFormError(validationError);
Expand All @@ -59,6 +61,7 @@ export function Withdraw({
mint,
receipt,
rentRecipient,
tokenProgram,
})
.catch(() => null);
if (!result) return;
Expand Down Expand Up @@ -91,7 +94,7 @@ export function Withdraw({
label="Mint Address"
value={mint}
onChange={setMint}
autoFillValue={defaultMint}
autoFillValue={defaultMint || clusterMint}
onAutoFill={setMint}
placeholder="SPL token mint address"
required
Expand All @@ -115,6 +118,13 @@ export function Withdraw({
placeholder={account?.address ?? 'Defaults to connected wallet'}
hint="Address that receives rent from the closed receipt account"
/>
<FormField
label="Token Program"
value={tokenProgram}
onChange={setTokenProgram}
placeholder="Token program address"
hint="Use Token-2022 program address for Token-2022 mints"
/>
<SendButton sending={withdraw.isPending} label={submitLabel} />
<TxResult signature={withdraw.data?.signature} error={formError ?? withdraw.error} />
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export function useWalletTransactionSignAndSend() {

await sendAndConfirm(signedTx, {
commitment: 'confirmed',
skipPreflight: true,
});

return signature;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/use-transaction-toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getClusterFromClusterId, getSolanaExplorerUrl } from '@/lib/explorer';
import { formatTransactionError } from '@/lib/transactionErrors';

function errorMessage(error: unknown): string {
return error instanceof Error ? error.message : formatTransactionError(error);
return formatTransactionError(error);
}

export function useTransactionToast() {
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/contexts/RecentTransactionsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface RecentTransactionValues {
lockDuration?: string;
hookProgram?: string;
rentRecipient?: string;
tokenProgram?: string;
}

export interface RecentTransaction {
Expand Down
Loading