Skip to content
Open
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
14 changes: 13 additions & 1 deletion frontend/src/components/Simulator/ConstructorParameters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ArrowUpTrayIcon } from '@heroicons/vue/16/solid';
import ContractParams from './ContractParams.vue';
import { type ArgData, unfoldArgsData } from './ContractParams';
import type { ExecutionMode } from '@/types';
import GenVMErrorDisplay from '@/components/Simulator/GenVMErrorDisplay.vue';

const props = defineProps<{
executionMode: ExecutionMode;
Expand All @@ -15,12 +16,18 @@ const props = defineProps<{
const { contract, contractSchemaQuery, deployContract, isDeploying } =
useContractQueries();

const { data, isPending, isRefetching, isError } = contractSchemaQuery;
const { data, isPending, isRefetching, isError, error: schemaError } = contractSchemaQuery;

const calldataArguments = ref<ArgData>({ args: [], kwargs: {} });

const ctorMethod = computed(() => data.value.ctor);

const schemaErrorMessage = computed(() => {
const err = schemaError.value as any;
if (!err) return '';
return err.rawGenvmError || err.message || String(err);
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const emit = defineEmits(['deployed-contract']);

const handleDeployContract = async () => {
Expand All @@ -45,6 +52,10 @@ const handleDeployContract = async () => {

<ContentLoader v-if="isPending" />

<GenVMErrorDisplay
v-else-if="isError && schemaErrorMessage"
:raw-error="schemaErrorMessage"
/>
<Alert v-else-if="isError" error> Could not load contract schema. </Alert>

<template v-else-if="data">
Expand All @@ -69,3 +80,4 @@ const handleDeployContract = async () => {
</template>
</PageSection>
</template>

123 changes: 123 additions & 0 deletions frontend/src/components/Simulator/GenVMErrorDisplay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import { parseGenvmError, type FriendlyError } from '@/utils/genvmErrors';
import { useClipboard } from '@vueuse/core';
import {
AlertTriangle,
ChevronDown,
ChevronUp,
Clipboard,
ClipboardCheck,
Lightbulb,
Wrench,
} from 'lucide-vue-next';

const props = defineProps<{
/** The raw error text from GenVM / the backend */
rawError: string;
}>();

const friendlyError = computed<FriendlyError>(() =>
parseGenvmError(props.rawError),
);

const showRawDetails = ref(false);

const {
copy: copyRaw,
copied: rawCopied,
isSupported: clipboardSupported,
} = useClipboard({ source: computed(() => props.rawError) });
</script>

<template>
<div
class="genvm-error-display rounded-lg border border-red-200 bg-red-50 dark:border-red-800/60 dark:bg-red-950/30"
data-testid="genvm-error-display"
>
<!-- Error title + icon -->
<div class="flex items-start gap-2.5 p-3 pb-2">
<AlertTriangle
class="mt-0.5 h-4 w-4 shrink-0 text-red-500 dark:text-red-400"
/>
<div class="min-w-0 flex-1">
<div
class="text-sm font-semibold text-red-700 dark:text-red-300"
data-testid="genvm-error-title"
>
{{ friendlyError.title }}
</div>
<p
class="mt-1 text-xs leading-relaxed text-red-600/90 dark:text-red-300/80"
>
{{ friendlyError.reason }}
</p>
</div>
</div>

<!-- Fix suggestion -->
<div
class="mx-3 mb-2 flex items-start gap-2 rounded-md border border-amber-200/80 bg-amber-50 p-2.5 dark:border-amber-700/40 dark:bg-amber-950/20"
>
<Lightbulb
class="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600 dark:text-amber-400"
/>
<div class="min-w-0 flex-1">
<div
class="mb-0.5 text-[10px] font-semibold uppercase tracking-wider text-amber-700 dark:text-amber-400"
>
How to fix
</div>
<pre
class="whitespace-pre-wrap break-words text-xs leading-relaxed text-amber-800/90 dark:text-amber-300/80"
data-testid="genvm-error-fix"
>{{ friendlyError.fix }}</pre
>
</div>
</div>

<!-- Toggle raw details -->
<div class="border-t border-red-200/60 dark:border-red-800/40">
<button
class="flex w-full items-center gap-1.5 px-3 py-2 text-[11px] font-medium text-red-500/80 transition-colors hover:text-red-600 dark:text-red-400/70 dark:hover:text-red-300"
data-testid="genvm-error-toggle-details"
@click="showRawDetails = !showRawDetails"
>
<Wrench class="h-3 w-3" />
Technical Details
<ChevronDown v-if="!showRawDetails" class="h-3 w-3" />
<ChevronUp v-else class="h-3 w-3" />
</button>

<div v-if="showRawDetails" class="px-3 pb-3">
<div class="flex items-center justify-between pb-1">
<span
class="text-[10px] font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500"
>
Raw Error
</span>
<button
v-if="clipboardSupported"
@click.stop="copyRaw(rawError)"
class="flex items-center gap-1 text-[10px] text-gray-400 transition-colors hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
data-testid="genvm-error-copy-raw"
>
<template v-if="rawCopied">
<ClipboardCheck class="h-3 w-3" />
Copied!
</template>
<template v-else>
<Clipboard class="h-3 w-3" />
Copy
</template>
</button>
</div>
<pre
class="max-h-[200px] overflow-auto whitespace-pre-wrap break-all rounded bg-gray-100 p-2 text-[10px] text-gray-600 dark:bg-zinc-900 dark:text-gray-400"
data-testid="genvm-error-raw"
>{{ rawError }}</pre
>
</div>
</div>
</div>
</template>
34 changes: 12 additions & 22 deletions frontend/src/components/Simulator/TransactionItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
getRuntimeConfigNumber,
} from '@/utils/runtimeConfig';
import { getExplorerUrl } from '@/utils/explorerUrl';
import GenVMErrorDisplay from '@/components/Simulator/GenVMErrorDisplay.vue';

const explorerUrl = computed(() => getExplorerUrl());

Expand Down Expand Up @@ -455,15 +456,10 @@ const badgeColorClass = computed(() => {
class="overflow-x-auto whitespace-pre rounded bg-gray-200 p-1 text-xs text-gray-600 dark:bg-zinc-800 dark:text-gray-300"
>{{ leaderReceipt?.result?.payload?.readable || 'None' }}</pre
>
<div
<GenVMErrorDisplay
v-if="leaderErrorDetail"
class="rounded border border-red-300 bg-red-50 p-2 text-xs text-red-700 dark:border-red-700 dark:bg-red-900/20 dark:text-red-300"
>
<div class="mb-1 font-medium">Error Detail</div>
<pre class="whitespace-pre-wrap break-all">{{
leaderErrorDetail
}}</pre>
</div>
:raw-error="leaderErrorDetail"
/>
</div>
</ModalSection>

Expand Down Expand Up @@ -616,14 +612,11 @@ const badgeColorClass = computed(() => {
</div>
</div>
</div>
<div
<GenVMErrorDisplay
v-if="extractErrorText(history.leader_result[0])"
class="ml-5 mt-1 rounded bg-red-50 px-2 py-1 text-[10px] text-red-600 dark:bg-red-900/20 dark:text-red-300"
>
<pre class="whitespace-pre-wrap break-all">{{
extractErrorText(history.leader_result[0])
}}</pre>
</div>
:raw-error="extractErrorText(history.leader_result[0])!"
class="ml-5 mt-1"
/>
</div>

<!-- Validator rows -->
Expand Down Expand Up @@ -684,14 +677,11 @@ const badgeColorClass = computed(() => {
</div>
</div>
</div>
<div
<GenVMErrorDisplay
v-if="extractErrorText(validator)"
class="ml-5 mt-1 rounded bg-red-50 px-2 py-1 text-[10px] text-red-600 dark:bg-red-900/20 dark:text-red-300"
>
<pre class="whitespace-pre-wrap break-all">{{
extractErrorText(validator)
}}</pre>
</div>
:raw-error="extractErrorText(validator)!"
class="ml-5 mt-1"
/>
</div>
</template>
</div>
Expand Down
26 changes: 20 additions & 6 deletions frontend/src/hooks/useContractQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
useWallet,
useChainEnforcer,
} from '@/hooks';
import { parseGenvmError } from '@/utils/genvmErrors';
import type {
Address,
TransactionHash,
Expand Down Expand Up @@ -113,7 +114,11 @@ export function useContractQueries() {
schema.value = result;
return schema.value;
} catch (error: any) {
throw new Error(error.details || error.message);
const rawMsg = error.details || error.message || String(error);
const friendly = parseGenvmError(rawMsg);
const err = new Error(friendly.title + ': ' + friendly.reason);
(err as any).rawGenvmError = rawMsg;
throw err;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

Expand Down Expand Up @@ -174,14 +179,19 @@ export function useContractQueries() {
transactionsStore.addTransaction(tx);
contractsStore.removeDeployedContract(contract.value?.id ?? '');
return tx;
} catch (error) {
} catch (error: any) {
isDeploying.value = false;
const rawMsg = error?.details || error?.message || String(error);
const friendly = parseGenvmError(rawMsg);
notify({
type: 'error',
title: 'Error deploying contract',
title: friendly.title,
text: friendly.reason,
});
console.error('Error Deploying the contract', error);
throw new Error('Error Deploying the contract');
const err = new Error(friendly.title + ': ' + friendly.reason);
(err as any).rawGenvmError = rawMsg;
throw err;
}
}

Expand Down Expand Up @@ -289,9 +299,13 @@ export function useContractQueries() {
},
});
return true;
} catch (error) {
} catch (error: any) {
const rawMsg = error?.details || error?.message || String(error);
const friendly = parseGenvmError(rawMsg);
console.error(error);
throw new Error('Error writing to contract');
const err = new Error(friendly.title + ': ' + friendly.reason);
(err as any).rawGenvmError = rawMsg;
throw err;
}
}

Expand Down
Loading