diff --git a/src/lib/components/elements/contract.svelte b/src/lib/components/elements/contract.svelte index 49f6ccb5..940e8161 100644 --- a/src/lib/components/elements/contract.svelte +++ b/src/lib/components/elements/contract.svelte @@ -10,16 +10,20 @@ children?: Snippet; name?: Name | string; action?: Name | string; + struct?: Name; class?: string; } - let { name, action, children, ...props }: Props = $props(); + let { name, action, struct, children, ...props }: Props = $props(); let href = $derived.by(() => { const base = `/${network}/contract/${String(name)}`; if (action) { return base + `/actions/${action}`; } + if (struct) { + return base + `/structs/${struct}`; + } return base; }); </script> diff --git a/src/routes/[network]/(explorer)/contract/[contract]/+layout.svelte b/src/routes/[network]/(explorer)/contract/[contract]/+layout.svelte index eb3a9d98..6787c2b5 100644 --- a/src/routes/[network]/(explorer)/contract/[contract]/+layout.svelte +++ b/src/routes/[network]/(explorer)/contract/[contract]/+layout.svelte @@ -43,6 +43,6 @@ <!-- {/if} --> <!-- </Stack> --> -<PillGroup {options} class="mb-6" /> +<PillGroup {options} /> {@render children()} diff --git a/src/routes/[network]/(explorer)/contract/[contract]/actions/+page.svelte b/src/routes/[network]/(explorer)/contract/[contract]/actions/+page.svelte index e37c7791..e88e060d 100644 --- a/src/routes/[network]/(explorer)/contract/[contract]/actions/+page.svelte +++ b/src/routes/[network]/(explorer)/contract/[contract]/actions/+page.svelte @@ -1,13 +1,15 @@ <script lang="ts"> import Stack from '$lib/components/layout/stack.svelte'; + import Action from './action.svelte'; const { data } = $props(); </script> <Stack> - {#each data.abi.actions as action} - <a href="/{data.network}/contract/{data.contract}/actions/{action.name}"> - {action.name} - </a> - {/each} + <p>The actions for this contract with their input parameters and potential response data.</p> + <ul class="grid grid-cols-[auto_1fr] gap-2 overflow-x-auto"> + {#each data.abi.actions as action} + <Action abi={data.abi} contract={data.contract} network={data.network} {action} /> + {/each} + </ul> </Stack> diff --git a/src/routes/[network]/(explorer)/contract/[contract]/actions/action.svelte b/src/routes/[network]/(explorer)/contract/[contract]/actions/action.svelte new file mode 100644 index 00000000..50b5de97 --- /dev/null +++ b/src/routes/[network]/(explorer)/contract/[contract]/actions/action.svelte @@ -0,0 +1,121 @@ +<script lang="ts"> + import Key from '$lib/components/elements/key.svelte'; + import CopyButton from '$lib/components/button/copy.svelte'; + import * as m from '$lib/paraglide/messages'; + import Code from '$lib/components/code.svelte'; + import type { UnicoveContext } from '$lib/state/client.svelte'; + import { getContext } from 'svelte'; + import Contract from '$lib/components/elements/contract.svelte'; + + const context = getContext<UnicoveContext>('state'); + + interface Props {} + let { abi, action, contract, network, ...props }: Props = $props(); + + const ricardian = $derived.by(() => { + try { + console.log(action.ricardian_contract); + } catch (e) { + console.error(e); + } + return { + summary: 'Error parsing Ricardian contract' + }; + }); + const struct = $derived(abi.structs.find((s) => s.name === action.type)); + const response = $derived.by(() => { + const resultType = abi.action_results.find((s) => s.name === action.name); + if (resultType) { + const resultStruct = abi.structs.find((s) => s.name === resultType.result_type); + if (resultStruct) { + return resultStruct.fields; + } + const resultArray = abi.structs.find((s) => s.name === parseType(resultType.result_type)); + if (resultArray) { + return [ + { + name: resultType.result_type, + type: resultArray.name + } + ]; + } + return resultType; + } + return undefined; + }); + + function parseType(type: string) { + // Remove suffix syntax (e.g. [], ?, !, $) + return type.replace(/\[|\]|\?|\!|\$/g, ''); + } +</script> + +<li class="relative col-span-full grid grid-cols-subgrid bg-shark-950"> + <dl + class="z-20 col-span-full space-y-1 rounded-t-lg bg-mineShaft-950 px-4 py-3 md:col-span-1 md:rounded-l-lg" + > + <div> + <div class="text-lg font-bold">Action</div> + <dd class="font-semibold"> + <Contract name={contract} action={action.name}> + {action.name} + </Contract> + </dd> + </div> + </dl> + + <div class="rounded-b-lg bg-mineShaft-950/50 px-4 py-3 md:rounded-r-lg"> + <div class="grid grid-cols-2 gap-4"> + <div> + <div class="text-lg font-bold">Parameters</div> + {#each struct.fields as field} + {@const fieldType = abi.structs.find((s) => s.name === parseType(field.type))} + <div> + <span class="text-white"> + {field.name} + </span> + <span class="text-muted"> + {#if fieldType} + <Contract name={contract} struct={fieldType.name}> + {field.type} + </Contract> + {:else} + {field.type} + {/if} + </span> + </div> + {:else} + <span class="text-muted"> No action parameters. </span> + {/each} + {#if context.settings.data.debugMode} + <Code>{JSON.stringify(struct, null, 2)}</Code> + {/if} + </div> + <div> + <div class="text-lg font-bold">Response Data</div> + {#each response as field} + {@const fieldType = abi.structs.find((s) => s.name === parseType(field.type))} + <div> + <span class="text-white"> + {field.name} + </span> + <span class="text-muted"> + {#if fieldType} + <Contract name={contract} struct={fieldType.name}> + {field.type} + </Contract> + {:else} + {field.type} + {/if} + </span> + </div> + {:else} + <p>No response data.</p> + {/each} + {#if context.settings.data.debugMode} + <Code>{JSON.stringify(response, null, 2)}</Code> + {/if} + </div> + </div> + </div> +</li>