Value
@@ -66,21 +83,113 @@ import {
AccordionTrigger,
AccordionContent,
} from 'radix-vue';
-import { Hex } from 'viem';
-import { computed } from 'vue';
+import { Address, Hex } from 'viem';
+import { computed, ref, watch } from 'vue';
import IconChevronDown from '@/components/IconChevronDown.vue';
-import { Chain } from '@/utils/chains';
+import useLabels from '@/composables/useLabels';
+import FourByteService from '@/services/4byte';
+import { Chain, getExplorerUrl } from '@/utils/chains';
import { decodeCallData, toAction, Call } from '@/utils/entryPoint';
+import IconArrowTopRight from './IconArrowTopRight.vue';
+
const props = defineProps<{
chain: Chain;
value: Hex;
}>();
+const { requestLabels, getLabelText } = useLabels();
+
+const fourByteService = new FourByteService();
+
const calls = computed
(() => {
return decodeCallData(props.value);
});
+
+const addresses = computed(() => {
+ return calls.value.map((call) => call.to.toLowerCase() as Address);
+});
+
+watch(
+ addresses,
+ () => {
+ requestLabels(props.chain, addresses.value);
+ },
+ {
+ immediate: true,
+ },
+);
+
+function isFunctionSelector(value: string): value is Hex {
+ const selectorRegex = /^0x[a-fA-F0-9]{8}$/;
+ return selectorRegex.test(value);
+}
+
+const functions = computed(() => {
+ return calls.value
+ .map((call) => {
+ const action = toAction(props.chain, call);
+ return action
+ .map((part) => part.value)
+ .filter((value) => isFunctionSelector(value));
+ })
+ .flat();
+});
+const functionSignatures = ref>({});
+
+watch(
+ functions,
+ () => {
+ fetchFunctions();
+ },
+ {
+ immediate: true,
+ },
+);
+
+async function fetchFunctions(): Promise {
+ for (const func of functions.value) {
+ const signature = await fourByteService.getSignature(func as Hex);
+ functionSignatures.value[func as Hex] = signature;
+ }
+}
+
+function getAddressLabel(address: Address, format: boolean): string {
+ const labelText = getLabelText(props.chain, address.toLowerCase() as Address);
+ if (labelText) {
+ return labelText;
+ }
+ if (!format) {
+ return address;
+ }
+ return formatAddress(address);
+}
+
+function getValueLabel(value: string): string {
+ if (!isFunctionSelector(value)) {
+ return value;
+ }
+ const signature = functionSignatures.value[value];
+ if (signature) {
+ const parts = signature.split('(');
+ const firstPart = parts[0];
+ if (!firstPart) {
+ return value;
+ }
+ return firstPart;
+ }
+ return value;
+}
+
+function formatAddress(address: Address): string {
+ return address.slice(0, 8) + '…' + address.slice(-6);
+}
+
+function getAddressExplorerUrl(chain: Chain, address: Address): string {
+ const explorerUrl = getExplorerUrl(chain);
+ return `${explorerUrl}/address/${address}`;
+}