From 9d94da022064ba9ffa9a0f0a36c5fadb13f6a736 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sat, 9 Sep 2023 12:07:49 +0200
Subject: [PATCH 01/37] open transaction in interact screen (first pass)

---
 .../TransactionDetailsTabs.tsx                |  7 +--
 .../TransactionSource.module.scss             | 14 ++++++
 .../TransactionSource/TransactionSource.tsx   | 49 ++++++++++++++++---
 3 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx b/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
index d21832ac..bd8c9532 100644
--- a/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
+++ b/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
@@ -50,12 +50,7 @@ export function TransactionDetailsTabs(
     tabs.push({
       id: "script",
       label: "Script",
-      content: (
-        <TransactionSource
-          code={transaction.script}
-          arguments={transaction.arguments}
-        />
-      ),
+      content: <TransactionSource transaction={transaction} />,
     });
   }
 
diff --git a/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss b/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss
index 550929f2..7023a9b3 100644
--- a/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss
+++ b/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss
@@ -5,6 +5,20 @@
 .root {
   display: flex;
   column-gap: $spacing-xl;
+  position: relative;
+
+  .interactLink {
+    position: absolute;
+    top: 0;
+    right: 0;
+    display: flex;
+    align-items: center;
+    column-gap: $spacing-s;
+
+    svg * {
+      fill: $blue;
+    }
+  }
 
   .left {
     flex: 1;
diff --git a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
index c748804d..dcea995c 100644
--- a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
+++ b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
@@ -1,28 +1,30 @@
 import React, { FC } from "react";
 import Card from "../../../components/card/Card";
 import classes from "./TransactionSource.module.scss";
-import { TransactionArgument } from "@flowser/shared";
+import { Transaction } from "@flowser/shared";
 import { CadenceEditor } from "../../../components/cadence-editor/CadenceEditor";
 import { ParamBuilder } from "../../interactions/components/ParamBuilder/ParamBuilder";
 import { SizedBox } from "../../../components/sized-box/SizedBox";
+import { ProjectLink } from "../../../components/links/ProjectLink";
+import { FlowserIcon } from "../../../components/icons/Icons";
+import { useInteractionRegistry } from "../../interactions/contexts/interaction-registry.context";
 
 type TransactionSourceProps = {
-  code: string;
-  arguments: TransactionArgument[];
+  transaction: Transaction;
 };
 
 export const TransactionSource: FC<TransactionSourceProps> = ({
-  code,
-  arguments: args,
+  transaction,
 }) => {
+  const { setFocused, create } = useInteractionRegistry();
   return (
     <Card className={classes.root}>
-      {args.length > 0 && (
+      {transaction.arguments.length > 0 && (
         <div className={classes.left}>
           <h3>Arguments</h3>
           <SizedBox height={20} />
           <div className={classes.argumentsWrapper}>
-            {args.map((arg) => (
+            {transaction.arguments.map((arg) => (
               <ParamBuilder
                 key={arg.identifier}
                 disabled
@@ -35,8 +37,39 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
         </div>
       )}
       <div className={classes.right}>
-        <CadenceEditor value={code} editable={false} />
+        <CadenceEditor value={transaction.script} editable={false} />
       </div>
+      <ProjectLink
+        className={classes.interactLink}
+        to="/interactions"
+        onClick={() => {
+          create({
+            code: transaction.script,
+            fclValuesByIdentifier: new Map(
+              transaction.arguments.map((arg) => [
+                arg.identifier,
+                JSON.parse(arg.valueAsJson),
+              ])
+            ),
+            id: transaction.id,
+            initialOutcome: {
+              transaction: {
+                transactionId: transaction.id,
+                error: transaction.status?.errorMessage,
+              },
+            },
+            name: "Test",
+            transactionOptions: {
+              authorizerAddresses: transaction.authorizers,
+              payerAddress: transaction.payer,
+              proposerAddress: transaction.proposalKey!.address,
+            },
+          });
+          setFocused(transaction.id);
+        }}
+      >
+        <FlowserIcon.CursorClick /> Interact
+      </ProjectLink>
     </Card>
   );
 };

From 59a6cc3348bb67b1e0572f52c2379480ee4add81 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sat, 9 Sep 2023 12:08:35 +0200
Subject: [PATCH 02/37] remove predefined interactions

---
 .../contexts/interaction-registry.context.tsx | 54 -------------------
 1 file changed, 54 deletions(-)

diff --git a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
index 38cd4103..964d494d 100644
--- a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
+++ b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
@@ -74,24 +74,6 @@ export type FlowInteractionOutcome = {
 
 const Context = createContext<InteractionsRegistry>(undefined as never);
 
-const helloWorldScript = `pub fun main(): String {
-  return "Hello World"
-}`;
-
-const helloWorldScriptWithArguments = `pub fun main(a: String, b: String): String {
-  return a.concat(" ").concat(b)
-}`;
-
-const helloWorldTransaction = `transaction() {
-  prepare(signer: AuthAccount) {
-    log("Preparing")
-  }
-  execute {
-    log("Executing")    
-  }
-}
-`;
-
 export function InteractionRegistryProvider(props: {
   children: React.ReactNode;
 }): ReactElement {
@@ -112,44 +94,8 @@ export function InteractionRegistryProvider(props: {
   const [customTemplates, setRawTemplates] = useLocalStorage<
     RawInteractionDefinitionTemplate[]
   >("interactions", []);
-  const predefinedTemplates = useMemo<
-    (CoreInteractionDefinition & Partial<InteractionDefinitionTemplate>)[]
-  >(
-    () => [
-      {
-        id: "hello-world-script",
-        name: "Hello World",
-        code: helloWorldScript,
-      },
-      {
-        id: "script-with-arguments",
-        name: "Arguments example",
-        code: helloWorldScriptWithArguments,
-        fclValuesByIdentifier: new Map([
-          ["a", "Hello"],
-          ["b", "World"],
-        ]),
-      },
-      {
-        id: "hello-world-transaction",
-        name: "Hello World",
-        code: helloWorldTransaction,
-      },
-    ],
-    []
-  );
   const templates = useMemo<InteractionDefinitionTemplate[]>(
     () => [
-      ...predefinedTemplates.map(
-        (template): InteractionDefinitionTemplate => ({
-          createdDate: new Date(),
-          updatedDate: new Date(),
-          fclValuesByIdentifier: new Map(),
-          transactionOptions: undefined,
-          isMutable: false,
-          ...template,
-        })
-      ),
       ...customTemplates.map(
         (template): InteractionDefinitionTemplate => ({
           ...template,

From 476f92790395bce457a2f17e17888b998623ee1d Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sat, 9 Sep 2023 12:16:56 +0200
Subject: [PATCH 03/37] fix execution settings width

---
 frontend/src/components/links/ExternalLink.module.scss        | 4 +---
 .../ExecutionSettings/ExecutionSettings.module.scss           | 1 -
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/frontend/src/components/links/ExternalLink.module.scss b/frontend/src/components/links/ExternalLink.module.scss
index 28151c98..92ad47b3 100644
--- a/frontend/src/components/links/ExternalLink.module.scss
+++ b/frontend/src/components/links/ExternalLink.module.scss
@@ -13,8 +13,6 @@
     fill: $blue;
   }
   .url {
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
+    word-break: break-all;
   }
 }
diff --git a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss
index 7876a8cd..04c56bdc 100644
--- a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss
+++ b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss
@@ -7,7 +7,6 @@
   justify-content: space-between;
   flex-direction: column;
   padding: $spacing-base;
-  min-width: 300px;
   row-gap: $spacing-base;
 
   .top {

From 5c622f67a8d9e9cced6d5dc3e358809b0bd8805b Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sat, 9 Sep 2023 12:24:29 +0200
Subject: [PATCH 04/37] consolidate section spacings

---
 frontend/src/modules/interactions/InteractionsPage.module.scss | 1 +
 frontend/src/modules/interactions/InteractionsPage.tsx         | 2 --
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/frontend/src/modules/interactions/InteractionsPage.module.scss b/frontend/src/modules/interactions/InteractionsPage.module.scss
index 418c5e3f..afe7e174 100644
--- a/frontend/src/modules/interactions/InteractionsPage.module.scss
+++ b/frontend/src/modules/interactions/InteractionsPage.module.scss
@@ -4,6 +4,7 @@
 .pageRoot {
   height: 100%;
   display: flex;
+  column-gap: $spacing-base;
   flex: 1;
   background: #1C2128;
   .leftSideMenu {
diff --git a/frontend/src/modules/interactions/InteractionsPage.tsx b/frontend/src/modules/interactions/InteractionsPage.tsx
index 9550ea9f..fdc00a57 100644
--- a/frontend/src/modules/interactions/InteractionsPage.tsx
+++ b/frontend/src/modules/interactions/InteractionsPage.tsx
@@ -58,7 +58,6 @@ export function InteractionsPage(): ReactElement {
         onChangeTab={(tab) => setCurrentSideMenuTabId(tab.id)}
         tabs={sideMenuTabs}
       />
-      <SizedBox width={20} />
       <Tabs
         className={classes.mainContent}
         tabWrapperClassName={classes.interactionsTabWrapper}
@@ -97,7 +96,6 @@ function InteractionBody(): ReactElement {
           <InteractionDetails />
         </div>
       </div>
-      <LineSeparator vertical />
       <ExecutionSettings />
     </div>
   );

From 0fc26b291d5358548062c30c6456e9e95b73a1da Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sat, 9 Sep 2023 12:26:42 +0200
Subject: [PATCH 05/37] tweak execution settings padding

---
 .../src/modules/interactions/InteractionsPage.module.scss | 8 ++++----
 .../ExecutionSettings/ExecutionSettings.module.scss       | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/frontend/src/modules/interactions/InteractionsPage.module.scss b/frontend/src/modules/interactions/InteractionsPage.module.scss
index afe7e174..f7ec3dd8 100644
--- a/frontend/src/modules/interactions/InteractionsPage.module.scss
+++ b/frontend/src/modules/interactions/InteractionsPage.module.scss
@@ -6,9 +6,9 @@
   display: flex;
   column-gap: $spacing-base;
   flex: 1;
-  background: #1C2128;
+  background: $gray-110;
   .leftSideMenu {
-    background: #272B32;
+    background: $gray-100;
     flex: 1;
     .content {
       margin: $spacing-base;
@@ -38,11 +38,11 @@
         overflow: hidden;
         .code {
           height: 60%;
-          background: #272B32;
+          background: $gray-100;
         }
         .details {
           height: 40%;
-          background: #272B32;
+          background: $gray-100;
           .error {
             padding: $spacing-base;
           }
diff --git a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss
index 04c56bdc..e180ed9d 100644
--- a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss
+++ b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.module.scss
@@ -6,7 +6,7 @@
   display: flex;
   justify-content: space-between;
   flex-direction: column;
-  padding: $spacing-base;
+  padding: $spacing-l;
   row-gap: $spacing-base;
 
   .top {

From 387612daf21cb9c9d1efda230e59efcb5b9a4a0c Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sat, 9 Sep 2023 12:31:32 +0200
Subject: [PATCH 06/37] fix opened logs positioning

---
 frontend/src/modules/logs/Logs.module.scss | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/src/modules/logs/Logs.module.scss b/frontend/src/modules/logs/Logs.module.scss
index e6dadcd8..906bbd9f 100644
--- a/frontend/src/modules/logs/Logs.module.scss
+++ b/frontend/src/modules/logs/Logs.module.scss
@@ -5,7 +5,7 @@
 
 @mixin overlay {
   z-index: 99;
-  position: absolute;
+  position: absolute !important;
   bottom: 0;
   left: 0;
   right: 0;

From ff134ea3b6d55c6192330206b8b4631ec35fdd94 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 18:39:23 +0200
Subject: [PATCH 07/37] move templates logic to a separate provider

---
 frontend/src/App.tsx                          |  25 ++--
 .../InteractionTemplates.tsx                  |  10 +-
 .../contexts/interaction-registry.context.tsx |  90 +------------
 .../contexts/templates.context.tsx            | 126 ++++++++++++++++++
 .../hooks/use-transaction-name.ts             |   4 +-
 5 files changed, 152 insertions(+), 103 deletions(-)
 create mode 100644 frontend/src/modules/interactions/contexts/templates.context.tsx

diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 8edc2495..80d84353 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -49,6 +49,9 @@ import { ContractsTable } from "./modules/contracts/ContractsTable";
 import { ContractDetails } from "./modules/contracts/ContractDetails/ContractDetails";
 import { EventsTable } from "./modules/events/EventsTable/EventsTable";
 import { createCrumbHandle } from "./components/breadcrumbs/Breadcrumbs";
+import {
+  TemplatesRegistryProvider
+} from './modules/interactions/contexts/templates.context';
 
 const BrowserRouterEvents = (props: { children: ReactNode }): ReactElement => {
   const location = useLocation();
@@ -85,16 +88,18 @@ export const FlowserClientApp = ({
         <ConfirmDialogProvider>
           <PlatformAdapterProvider {...platformAdapter}>
             <InteractionRegistryProvider>
-              <ConsentAnalytics />
-              <ProjectRequirements />
-              <RouterProvider
-                router={useHashRouter ? hashRouter : browserRouter}
-              />
-              <Toaster
-                position="bottom-center"
-                gutter={8}
-                toastOptions={toastOptions}
-              />
+              <TemplatesRegistryProvider>
+                <ConsentAnalytics />
+                <ProjectRequirements />
+                <RouterProvider
+                  router={useHashRouter ? hashRouter : browserRouter}
+                />
+                <Toaster
+                  position="bottom-center"
+                  gutter={8}
+                  toastOptions={toastOptions}
+                />
+              </TemplatesRegistryProvider>
             </InteractionRegistryProvider>
           </PlatformAdapterProvider>
         </ConfirmDialogProvider>
diff --git a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
index 0f58b484..293fa019 100644
--- a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
+++ b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
@@ -8,6 +8,7 @@ import { SearchInput } from "../../../../components/inputs/search-input/SearchIn
 import { useConfirmDialog } from "../../../../contexts/confirm-dialog.context";
 import classNames from "classnames";
 import { InteractionLabel } from "../InteractionLabel/InteractionLabel";
+import { useTemplatesRegistry } from "../../contexts/templates.context";
 
 export function InteractionTemplates(): ReactElement {
   return (
@@ -21,8 +22,8 @@ export function InteractionTemplates(): ReactElement {
 function StoredTemplates() {
   const { showDialog } = useConfirmDialog();
   const [searchTerm, setSearchTerm] = useState("");
-  const { templates, forkTemplate, removeTemplate, focusedDefinition } =
-    useInteractionRegistry();
+  const { forkTemplate, focusedDefinition } = useInteractionRegistry();
+  const { templates, removeTemplate } = useTemplatesRegistry();
   const filteredTemplates = useMemo(() => {
     if (searchTerm === "") {
       return templates;
@@ -84,7 +85,8 @@ function StoredTemplates() {
 }
 
 function FocusedDefinitionSettings() {
-  const { focusedDefinition, update, persist } = useInteractionRegistry();
+  const { focusedDefinition, update } = useInteractionRegistry();
+  const { saveTemplate } = useTemplatesRegistry();
 
   if (!focusedDefinition) {
     return null;
@@ -97,7 +99,7 @@ function FocusedDefinitionSettings() {
         value={focusedDefinition.name}
         onChange={(e) => update({ ...focusedDefinition, name: e.target.value })}
       />
-      <PrimaryButton onClick={() => persist(focusedDefinition.id)}>
+      <PrimaryButton onClick={() => saveTemplate(focusedDefinition)}>
         Save
       </PrimaryButton>
     </div>
diff --git a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
index 964d494d..a9c98166 100644
--- a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
+++ b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
@@ -7,21 +7,16 @@ import React, {
   useState,
 } from "react";
 import { FclValueLookupByIdentifier } from "./definition.context";
-import { useLocalStorage } from "usehooks-ts";
-import { FclValue, InteractionTemplate } from "@flowser/shared";
-import { useGetPollingFlowInteractionTemplates } from "../../../hooks/use-api";
+import { InteractionTemplate } from "@flowser/shared";
 
 type InteractionsRegistry = {
-  templates: InteractionDefinitionTemplate[];
   definitions: InteractionDefinition[];
   focusedDefinition: InteractionDefinition | undefined;
   update: (interaction: InteractionDefinition) => void;
   create: (interaction: InteractionDefinition) => void;
   remove: (interactionId: string) => void;
   setFocused: (interactionId: string) => void;
-  persist: (interactionId: string) => void;
   forkTemplate: (template: InteractionDefinitionTemplate) => void;
-  removeTemplate: (template: InteractionDefinitionTemplate) => void;
 };
 
 export type CoreInteractionDefinition = Omit<
@@ -43,14 +38,6 @@ export type InteractionDefinitionTemplate = CoreInteractionDefinition & {
   isMutable: boolean;
 };
 
-// Internal structure that's persisted in local storage.
-type RawInteractionDefinitionTemplate = CoreInteractionDefinition & {
-  fclValuesByIdentifier: Record<string, FclValue>;
-  transactionOptions: TransactionOptions | undefined;
-  createdDate: string;
-  updatedDate: string;
-};
-
 export type TransactionOptions = {
   authorizerAddresses: string[];
   proposerAddress: string;
@@ -89,37 +76,6 @@ export function InteractionRegistryProvider(props: {
       payerAddress: "0xf8d6e0586b0a20c7",
     },
   };
-  const { data: projectTemplatesData } =
-    useGetPollingFlowInteractionTemplates();
-  const [customTemplates, setRawTemplates] = useLocalStorage<
-    RawInteractionDefinitionTemplate[]
-  >("interactions", []);
-  const templates = useMemo<InteractionDefinitionTemplate[]>(
-    () => [
-      ...customTemplates.map(
-        (template): InteractionDefinitionTemplate => ({
-          ...template,
-          createdDate: new Date(template.createdDate),
-          updatedDate: new Date(template.updatedDate),
-          fclValuesByIdentifier: new Map(
-            Object.entries(template.fclValuesByIdentifier)
-          ),
-          isMutable: true,
-        })
-      ),
-      ...(projectTemplatesData?.templates?.map(
-        (template): InteractionDefinitionTemplate => ({
-          ...template,
-          isMutable: false,
-          transactionOptions: undefined,
-          fclValuesByIdentifier: new Map(),
-          createdDate: new Date(template.createdDate),
-          updatedDate: new Date(template.updatedDate),
-        })
-      ) ?? []),
-    ],
-    [customTemplates, projectTemplatesData]
-  );
   const [definitions, setDefinitions] = useState<InteractionDefinition[]>([
     defaultInteraction,
   ]);
@@ -141,29 +97,6 @@ export function InteractionRegistryProvider(props: {
     }
   }, [definitions]);
 
-  function persist(interactionId: string) {
-    const interaction = getById(interactionId);
-
-    const newTemplate: RawInteractionDefinitionTemplate = {
-      id: interaction.name,
-      name: interaction.name,
-      code: interaction.code,
-      fclValuesByIdentifier: Object.fromEntries(
-        interaction.fclValuesByIdentifier
-      ),
-      transactionOptions: interaction.transactionOptions,
-      createdDate: new Date().toISOString(),
-      updatedDate: new Date().toISOString(),
-    };
-
-    setRawTemplates([
-      ...customTemplates.filter(
-        (template) => template.name !== newTemplate.name
-      ),
-      newTemplate,
-    ]);
-  }
-
   function forkTemplate(template: InteractionDefinitionTemplate) {
     const definition: InteractionDefinition = {
       id: template.id,
@@ -182,14 +115,6 @@ export function InteractionRegistryProvider(props: {
     }
   }
 
-  function removeTemplate(template: InteractionDefinitionTemplate) {
-    setRawTemplates((rawTemplates) =>
-      rawTemplates.filter(
-        (existingTemplate) => existingTemplate.name !== template.name
-      )
-    );
-  }
-
   function update(updatedInteraction: InteractionDefinition) {
     setDefinitions((interactions) =>
       interactions.map((existingInteraction) => {
@@ -221,27 +146,16 @@ export function InteractionRegistryProvider(props: {
     }
   }
 
-  function getById(id: string) {
-    const definition = definitions.find((definition) => definition.id === id);
-    if (!definition) {
-      throw new Error(`Definition not found by id: ${id}`);
-    }
-    return definition;
-  }
-
   return (
     <Context.Provider
       value={{
-        templates,
         definitions,
         focusedDefinition,
         setFocused: setFocusedInteractionId,
         forkTemplate,
-        removeTemplate,
         remove,
         create,
         update,
-        persist,
       }}
     >
       {props.children}
@@ -253,7 +167,7 @@ export function useInteractionRegistry(): InteractionsRegistry {
   const context = useContext(Context);
 
   if (context === undefined) {
-    throw new Error("Interaction definitions manager provider not found");
+    throw new Error("Interaction definitions registry provider not found");
   }
 
   return context;
diff --git a/frontend/src/modules/interactions/contexts/templates.context.tsx b/frontend/src/modules/interactions/contexts/templates.context.tsx
new file mode 100644
index 00000000..bb7c2105
--- /dev/null
+++ b/frontend/src/modules/interactions/contexts/templates.context.tsx
@@ -0,0 +1,126 @@
+import React, { createContext, ReactElement, useContext, useMemo } from "react";
+import { FclValueLookupByIdentifier } from "./definition.context";
+import { useLocalStorage } from "usehooks-ts";
+import { FclValue } from "@flowser/shared";
+import { useGetPollingFlowInteractionTemplates } from "../../../hooks/use-api";
+import {
+  CoreInteractionDefinition,
+  InteractionDefinition,
+} from "./interaction-registry.context";
+
+type InteractionTemplatesRegistry = {
+  templates: InteractionDefinitionTemplate[];
+  saveTemplate: (definition: InteractionDefinition) => void;
+  removeTemplate: (template: InteractionDefinitionTemplate) => void;
+};
+
+export type InteractionDefinitionTemplate = CoreInteractionDefinition & {
+  fclValuesByIdentifier: FclValueLookupByIdentifier;
+  transactionOptions: TransactionOptions | undefined;
+  createdDate: Date;
+  updatedDate: Date;
+  isMutable: boolean;
+};
+
+// Internal structure that's persisted in local storage.
+type RawInteractionDefinitionTemplate = CoreInteractionDefinition & {
+  fclValuesByIdentifier: Record<string, FclValue>;
+  transactionOptions: TransactionOptions | undefined;
+  createdDate: string;
+  updatedDate: string;
+};
+
+export type TransactionOptions = {
+  authorizerAddresses: string[];
+  proposerAddress: string;
+  payerAddress: string;
+};
+
+const Context = createContext<InteractionTemplatesRegistry>(undefined as never);
+
+export function TemplatesRegistryProvider(props: {
+  children: React.ReactNode;
+}): ReactElement {
+  const { data: projectTemplatesData } =
+    useGetPollingFlowInteractionTemplates();
+  const [customTemplates, setRawTemplates] = useLocalStorage<
+    RawInteractionDefinitionTemplate[]
+  >("interactions", []);
+  const templates = useMemo<InteractionDefinitionTemplate[]>(
+    () => [
+      ...customTemplates.map(
+        (template): InteractionDefinitionTemplate => ({
+          ...template,
+          createdDate: new Date(template.createdDate),
+          updatedDate: new Date(template.updatedDate),
+          fclValuesByIdentifier: new Map(
+            Object.entries(template.fclValuesByIdentifier)
+          ),
+          isMutable: true,
+        })
+      ),
+      ...(projectTemplatesData?.templates?.map(
+        (template): InteractionDefinitionTemplate => ({
+          ...template,
+          isMutable: false,
+          transactionOptions: undefined,
+          fclValuesByIdentifier: new Map(),
+          createdDate: new Date(template.createdDate),
+          updatedDate: new Date(template.updatedDate),
+        })
+      ) ?? []),
+    ],
+    [customTemplates, projectTemplatesData]
+  );
+
+  function saveTemplate(interaction: InteractionDefinition) {
+    const newTemplate: RawInteractionDefinitionTemplate = {
+      id: interaction.name,
+      name: interaction.name,
+      code: interaction.code,
+      fclValuesByIdentifier: Object.fromEntries(
+        interaction.fclValuesByIdentifier
+      ),
+      transactionOptions: interaction.transactionOptions,
+      createdDate: new Date().toISOString(),
+      updatedDate: new Date().toISOString(),
+    };
+
+    setRawTemplates([
+      ...customTemplates.filter(
+        (template) => template.name !== newTemplate.name
+      ),
+      newTemplate,
+    ]);
+  }
+
+  function removeTemplate(template: InteractionDefinitionTemplate) {
+    setRawTemplates((rawTemplates) =>
+      rawTemplates.filter(
+        (existingTemplate) => existingTemplate.name !== template.name
+      )
+    );
+  }
+
+  return (
+    <Context.Provider
+      value={{
+        templates,
+        removeTemplate,
+        saveTemplate,
+      }}
+    >
+      {props.children}
+    </Context.Provider>
+  );
+}
+
+export function useTemplatesRegistry(): InteractionTemplatesRegistry {
+  const context = useContext(Context);
+
+  if (context === undefined) {
+    throw new Error("Interaction templates registry provider not found");
+  }
+
+  return context;
+}
diff --git a/frontend/src/modules/interactions/hooks/use-transaction-name.ts b/frontend/src/modules/interactions/hooks/use-transaction-name.ts
index b437dbb3..b8f502a9 100644
--- a/frontend/src/modules/interactions/hooks/use-transaction-name.ts
+++ b/frontend/src/modules/interactions/hooks/use-transaction-name.ts
@@ -1,6 +1,7 @@
 import { useInteractionRegistry } from "../contexts/interaction-registry.context";
 import { useMemo } from "react";
 import { Transaction } from "@flowser/shared";
+import { useTemplatesRegistry } from "../contexts/templates.context";
 
 type UseInteractionNameProps = {
   transaction: Transaction | undefined;
@@ -59,7 +60,8 @@ export function useTransactionName(
   props: UseInteractionNameProps
 ): string | undefined {
   const { transaction } = props;
-  const { templates, definitions } = useInteractionRegistry();
+  const { templates } = useTemplatesRegistry();
+  const { definitions } = useInteractionRegistry();
 
   return useMemo(() => {
     if (!transaction?.script) {

From 4995ec56f15e2fa4b1d698a81dbb47d99faf616b Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 20:32:39 +0200
Subject: [PATCH 08/37] simplify/refactor interaction structures and logic

---
 frontend/src/hooks/use-api.ts                 | 18 ++--
 .../modules/interactions/InteractionsPage.tsx | 16 +--
 .../ExecutionSettings/ExecutionSettings.tsx   |  2 +-
 .../InteractionHistory/InteractionHistory.tsx |  1 -
 .../InteractionLabel/InteractionLabel.tsx     |  9 +-
 .../InteractionOutcomeDisplay.module.scss}    |  0
 .../InteractionOutcomeDisplay.tsx}            | 17 ++--
 .../InteractionTemplates.tsx                  |  7 +-
 .../contexts/definition.context.tsx           | 11 +--
 .../contexts/interaction-registry.context.tsx | 98 ++++++-------------
 .../interactions/contexts/outcome.context.tsx |  9 +-
 .../contexts/templates.context.tsx            | 30 +++---
 .../modules/interactions/core/core-types.ts   | 35 +++++++
 .../modules/interactions/core/core-utils.ts   | 31 ++++++
 .../hooks/use-transaction-name.ts             | 28 +++---
 .../TransactionSource/TransactionSource.tsx   |  5 +-
 16 files changed, 156 insertions(+), 161 deletions(-)
 rename frontend/src/modules/interactions/components/{InteractionOutcome/InteractionOutcome.module.scss => InteractionOutcomeDisplay/InteractionOutcomeDisplay.module.scss} (100%)
 rename frontend/src/modules/interactions/components/{InteractionOutcome/InteractionOutcome.tsx => InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx} (90%)
 create mode 100644 frontend/src/modules/interactions/core/core-types.ts
 create mode 100644 frontend/src/modules/interactions/core/core-utils.ts

diff --git a/frontend/src/hooks/use-api.ts b/frontend/src/hooks/use-api.ts
index f895718a..0ea622ec 100644
--- a/frontend/src/hooks/use-api.ts
+++ b/frontend/src/hooks/use-api.ts
@@ -47,6 +47,7 @@ import {
 } from "contexts/timeout-polling.context";
 import { useEffect, useState } from "react";
 import { useCurrentProjectId } from "./use-current-project-id";
+import { InteractionDefinition } from "../modules/interactions/core/core-types";
 
 const {
   projectsService,
@@ -349,26 +350,21 @@ export function useGetFlowserVersion() {
   );
 }
 
-type UseGetParsedInteractionRequest = {
-  // Used as a cache key.
-  id: string;
-  sourceCode: string;
-};
-
-export function useGetParsedInteraction(
-  request: UseGetParsedInteractionRequest
-) {
+export function useGetParsedInteraction(request: InteractionDefinition) {
   // We are not using `sourceCode` as the cache key,
   // to avoid the flickering UI effect that's caused
   // by undefined parsed interaction every time the source code changes.
   const queryState = useQuery<GetParsedInteractionResponse>(
     `/go-bindings/get-parsed-interaction/${request.id}`,
-    () => goBindingsService.getParsedInteraction(request)
+    () =>
+      goBindingsService.getParsedInteraction({
+        sourceCode: request.code,
+      })
   );
 
   useEffect(() => {
     queryState.refetch();
-  }, [request.sourceCode]);
+  }, [request.code]);
 
   return queryState;
 }
diff --git a/frontend/src/modules/interactions/InteractionsPage.tsx b/frontend/src/modules/interactions/InteractionsPage.tsx
index fdc00a57..83a40a66 100644
--- a/frontend/src/modules/interactions/InteractionsPage.tsx
+++ b/frontend/src/modules/interactions/InteractionsPage.tsx
@@ -9,16 +9,14 @@ import {
   useInteractionDefinitionManager,
 } from "./contexts/definition.context";
 import { InteractionTemplates } from "./components/InteractionTemplates/InteractionTemplates";
-import { SizedBox } from "../../components/sized-box/SizedBox";
-import { LineSeparator } from "../../components/line-separator/LineSeparator";
 import { ExecutionSettings } from "./components/ExecutionSettings/ExecutionSettings";
 import { CadenceEditor } from "../../components/cadence-editor/CadenceEditor";
-import { InteractionOutcome } from "./components/InteractionOutcome/InteractionOutcome";
+import { InteractionOutcomeDisplay } from "./components/InteractionOutcomeDisplay/InteractionOutcomeDisplay";
 import { SpinnerWithLabel } from "../../components/spinner/SpinnerWithLabel";
 import { InteractionLabel } from "./components/InteractionLabel/InteractionLabel";
 
 export function InteractionsPage(): ReactElement {
-  const { definitions, focusedDefinition, remove, setFocused, forkTemplate } =
+  const { definitions, focusedDefinition, remove, setFocused, create } =
     useInteractionRegistry();
 
   const sideMenuTabs: TabItem[] = [
@@ -68,16 +66,12 @@ export function InteractionsPage(): ReactElement {
         tabs={openEditorTabs}
         onClose={(tab) => remove(tab.id)}
         onAddNew={() =>
-          forkTemplate({
-            id: crypto.randomUUID(),
+          create({
             name: "New interaction",
             code: "",
             fclValuesByIdentifier: new Map(),
             transactionOptions: undefined,
-            createdDate: new Date(),
-            updatedDate: new Date(),
-            // TODO(feature-interact-screen): This should be defined in the implemented function
-            isMutable: true,
+            initialOutcome: undefined,
           })
         }
       />
@@ -122,5 +116,5 @@ function InteractionDetails() {
     return <pre className={classes.error}>{parseError}</pre>;
   }
 
-  return <InteractionOutcome />;
+  return <InteractionOutcomeDisplay />;
 }
diff --git a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx
index ea6b4966..8b492967 100644
--- a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx
+++ b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx
@@ -12,9 +12,9 @@ import classes from "./ExecutionSettings.module.scss";
 import { LoaderButton } from "../../../../components/buttons/loader-button/LoaderButton";
 import { useInteractionOutcomeManager } from "../../contexts/outcome.context";
 import { useInteractionDefinitionManager } from "../../contexts/definition.context";
-import { TransactionOptions } from "../../contexts/interaction-registry.context";
 import { Callout } from "../../../../components/callout/Callout";
 import { ExternalLink } from "../../../../components/links/ExternalLink";
+import { TransactionOptions } from "modules/interactions/core/core-types";
 
 export function ExecutionSettings(): ReactElement {
   return (
diff --git a/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx b/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
index 647b40c1..203ef3a7 100644
--- a/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
+++ b/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
@@ -61,7 +61,6 @@ function BlockItem(props: BlockItemProps) {
       return;
     }
     create({
-      id: block.id,
       name: transactionName ?? `Tx from block #${block.height}`,
       code: firstTransaction.script,
       fclValuesByIdentifier: new Map(
diff --git a/frontend/src/modules/interactions/components/InteractionLabel/InteractionLabel.tsx b/frontend/src/modules/interactions/components/InteractionLabel/InteractionLabel.tsx
index 47e355e6..ff07e0fa 100644
--- a/frontend/src/modules/interactions/components/InteractionLabel/InteractionLabel.tsx
+++ b/frontend/src/modules/interactions/components/InteractionLabel/InteractionLabel.tsx
@@ -1,23 +1,20 @@
 import React, { ReactElement } from "react";
 import { InteractionIcon } from "../InteractionIcon/InteractionIcon";
 import { SizedBox } from "../../../../components/sized-box/SizedBox";
-import { CoreInteractionDefinition } from "../../contexts/interaction-registry.context";
 import { useGetParsedInteraction } from "../../../../hooks/use-api";
 import { Spinner } from "../../../../components/spinner/Spinner";
 import classes from "./InteractionLabel.module.scss";
 import { InteractionKind } from "@flowser/shared";
+import { InteractionDefinition } from "modules/interactions/core/core-types";
 
 type InteractionLabelProps = {
-  interaction: CoreInteractionDefinition;
+  interaction: InteractionDefinition;
 };
 
 export function InteractionLabel(props: InteractionLabelProps): ReactElement {
   const { interaction } = props;
 
-  const { data } = useGetParsedInteraction({
-    id: interaction.id,
-    sourceCode: interaction.code,
-  });
+  const { data } = useGetParsedInteraction(interaction);
 
   return (
     <div className={classes.root}>
diff --git a/frontend/src/modules/interactions/components/InteractionOutcome/InteractionOutcome.module.scss b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.module.scss
similarity index 100%
rename from frontend/src/modules/interactions/components/InteractionOutcome/InteractionOutcome.module.scss
rename to frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.module.scss
diff --git a/frontend/src/modules/interactions/components/InteractionOutcome/InteractionOutcome.tsx b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
similarity index 90%
rename from frontend/src/modules/interactions/components/InteractionOutcome/InteractionOutcome.tsx
rename to frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
index 3b771b0c..079e5347 100644
--- a/frontend/src/modules/interactions/components/InteractionOutcome/InteractionOutcome.tsx
+++ b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
@@ -3,11 +3,7 @@ import { useInteractionOutcomeManager } from "../../contexts/outcome.context";
 import { ScriptError } from "../../../../components/status/ErrorMessage";
 import { JsonView } from "../../../../components/json-view/JsonView";
 import { useGetTransaction } from "../../../../hooks/use-api";
-import classes from "./InteractionOutcome.module.scss";
-import {
-  FlowScriptOutcome,
-  FlowTransactionOutcome,
-} from "modules/interactions/contexts/interaction-registry.context";
+import classes from "./InteractionOutcomeDisplay.module.scss";
 import { TabItem } from "../../../../components/tabs/Tabs";
 import { Callout } from "../../../../components/callout/Callout";
 import { useInteractionDefinitionManager } from "../../contexts/definition.context";
@@ -17,14 +13,15 @@ import { LineSeparator } from "../../../../components/line-separator/LineSeparat
 import { SpinnerWithLabel } from "../../../../components/spinner/SpinnerWithLabel";
 import { StyledTabs } from "../../../../components/tabs/StyledTabs";
 import { TransactionDetailsTabs } from "../../../transactions/TransactionDetailsTabs/TransactionDetailsTabs";
+import { ScriptOutcome, TransactionOutcome } from "../../core/core-types";
 
-export function InteractionOutcome(): ReactElement {
+export function InteractionOutcomeDisplay(): ReactElement {
   const { outcome } = useInteractionOutcomeManager();
   return (
     <div className={classes.root}>
-      {outcome?.script && <ScriptOutcome outcome={outcome.script} />}
+      {outcome?.script && <ScriptOutcomeDisplay outcome={outcome.script} />}
       {outcome?.transaction && (
-        <TransactionOutcome outcome={outcome.transaction} />
+        <TransactionOutcomeDisplay outcome={outcome.transaction} />
       )}
       {!outcome && (
         <div className={classes.emptyStateWrapper}>
@@ -97,7 +94,7 @@ function EmptyState() {
   }
 }
 
-function TransactionOutcome(props: { outcome: FlowTransactionOutcome }) {
+function TransactionOutcomeDisplay(props: { outcome: TransactionOutcome }) {
   const { outcome } = props;
   const { data } = useGetTransaction(outcome.transactionId);
 
@@ -119,7 +116,7 @@ function TransactionOutcome(props: { outcome: FlowTransactionOutcome }) {
   );
 }
 
-function ScriptOutcome(props: { outcome: FlowScriptOutcome }) {
+function ScriptOutcomeDisplay(props: { outcome: ScriptOutcome }) {
   const { result, error } = props.outcome;
   const resultTabId = "result";
   const errorTabId = "result";
diff --git a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
index 293fa019..b8080857 100644
--- a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
+++ b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
@@ -22,7 +22,7 @@ export function InteractionTemplates(): ReactElement {
 function StoredTemplates() {
   const { showDialog } = useConfirmDialog();
   const [searchTerm, setSearchTerm] = useState("");
-  const { forkTemplate, focusedDefinition } = useInteractionRegistry();
+  const { create, focusedDefinition, setFocused } = useInteractionRegistry();
   const { templates, removeTemplate } = useTemplatesRegistry();
   const filteredTemplates = useMemo(() => {
     if (searchTerm === "") {
@@ -51,7 +51,10 @@ function StoredTemplates() {
         {filteredAndSortedTemplates.map((template) => (
           <div
             key={template.id}
-            onClick={() => forkTemplate(template)}
+            onClick={() => {
+              const createdInteraction = create(template);
+              setFocused(createdInteraction.id);
+            }}
             className={classNames(classes.item, {
               [classes.focusedItem]: focusedDefinition?.id === template.id,
             })}
diff --git a/frontend/src/modules/interactions/contexts/definition.context.tsx b/frontend/src/modules/interactions/contexts/definition.context.tsx
index 638a71d8..792de358 100644
--- a/frontend/src/modules/interactions/contexts/definition.context.tsx
+++ b/frontend/src/modules/interactions/contexts/definition.context.tsx
@@ -4,12 +4,10 @@ import React, {
   ReactNode,
   useContext,
 } from "react";
-import {
-  InteractionDefinition,
-  useInteractionRegistry,
-} from "./interaction-registry.context";
+import { useInteractionRegistry } from "./interaction-registry.context";
 import { useGetParsedInteraction } from "../../../hooks/use-api";
 import { FclValue, Interaction } from "@flowser/shared";
+import { InteractionDefinition } from "../core/core-types";
 
 type InteractionDefinitionManager = InteractionParameterBuilder & {
   isParsing: boolean;
@@ -34,10 +32,7 @@ export function InteractionDefinitionManagerProvider(props: {
 }): ReactElement {
   const { definition } = props;
   const { update } = useInteractionRegistry();
-  const { data, isLoading } = useGetParsedInteraction({
-    id: definition.id,
-    sourceCode: definition.code,
-  });
+  const { data, isLoading } = useGetParsedInteraction(definition);
   const fclValuesByIdentifier = definition.fclValuesByIdentifier;
 
   function partialUpdate(newDefinition: Partial<InteractionDefinition>) {
diff --git a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
index a9c98166..d2ded59e 100644
--- a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
+++ b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
@@ -6,57 +6,21 @@ import React, {
   useMemo,
   useState,
 } from "react";
-import { FclValueLookupByIdentifier } from "./definition.context";
-import { InteractionTemplate } from "@flowser/shared";
+import { InteractionDefinition } from "../core/core-types";
+import { InteractionUtils } from "../core/core-utils";
+
+type CreateInteractionDefinition = Omit<
+  InteractionDefinition,
+  "id" | "createdDate" | "updatedDate"
+>;
 
 type InteractionsRegistry = {
   definitions: InteractionDefinition[];
   focusedDefinition: InteractionDefinition | undefined;
+  create: (interaction: CreateInteractionDefinition) => InteractionDefinition;
   update: (interaction: InteractionDefinition) => void;
-  create: (interaction: InteractionDefinition) => void;
   remove: (interactionId: string) => void;
   setFocused: (interactionId: string) => void;
-  forkTemplate: (template: InteractionDefinitionTemplate) => void;
-};
-
-export type CoreInteractionDefinition = Omit<
-  InteractionTemplate,
-  "source" | "createdDate" | "updatedDate"
->;
-
-export type InteractionDefinition = CoreInteractionDefinition & {
-  fclValuesByIdentifier: FclValueLookupByIdentifier;
-  initialOutcome: FlowInteractionOutcome | undefined;
-  transactionOptions: TransactionOptions | undefined;
-};
-
-export type InteractionDefinitionTemplate = CoreInteractionDefinition & {
-  fclValuesByIdentifier: FclValueLookupByIdentifier;
-  transactionOptions: TransactionOptions | undefined;
-  createdDate: Date;
-  updatedDate: Date;
-  isMutable: boolean;
-};
-
-export type TransactionOptions = {
-  authorizerAddresses: string[];
-  proposerAddress: string;
-  payerAddress: string;
-};
-
-export type FlowTransactionOutcome = {
-  transactionId?: string;
-  error?: string;
-};
-
-export type FlowScriptOutcome = {
-  result?: unknown;
-  error?: string;
-};
-
-export type FlowInteractionOutcome = {
-  transaction?: FlowTransactionOutcome;
-  script?: FlowScriptOutcome;
 };
 
 const Context = createContext<InteractionsRegistry>(undefined as never);
@@ -65,8 +29,8 @@ export function InteractionRegistryProvider(props: {
   children: React.ReactNode;
 }): ReactElement {
   const defaultInteraction: InteractionDefinition = {
-    name: "Your first interaction",
     id: crypto.randomUUID(),
+    name: "Your first interaction",
     code: "",
     fclValuesByIdentifier: new Map(),
     initialOutcome: undefined,
@@ -75,13 +39,15 @@ export function InteractionRegistryProvider(props: {
       proposerAddress: "0xf8d6e0586b0a20c7",
       payerAddress: "0xf8d6e0586b0a20c7",
     },
+    createdDate: new Date(),
+    updatedDate: new Date(),
   };
   const [definitions, setDefinitions] = useState<InteractionDefinition[]>([
     defaultInteraction,
   ]);
   const [focusedInteractionId, setFocusedInteractionId] = useState<
     string | undefined
-  >(definitions?.[0]?.id);
+  >();
   const focusedDefinition = useMemo(
     () =>
       definitions.find((definition) => definition.id === focusedInteractionId),
@@ -97,24 +63,6 @@ export function InteractionRegistryProvider(props: {
     }
   }, [definitions]);
 
-  function forkTemplate(template: InteractionDefinitionTemplate) {
-    const definition: InteractionDefinition = {
-      id: template.id,
-      name: template.name,
-      code: template.code,
-      fclValuesByIdentifier: template.fclValuesByIdentifier,
-      transactionOptions: template.transactionOptions,
-      initialOutcome: undefined,
-    };
-    const isAlreadyOpen = definitions.some(
-      (definition) => definition.id === template.id
-    );
-    if (!isAlreadyOpen) {
-      setDefinitions([...definitions, definition]);
-      setFocusedInteractionId(definition.id);
-    }
-  }
-
   function update(updatedInteraction: InteractionDefinition) {
     setDefinitions((interactions) =>
       interactions.map((existingInteraction) => {
@@ -137,12 +85,23 @@ export function InteractionRegistryProvider(props: {
     }
   }
 
-  function create(newInteraction: InteractionDefinition) {
-    const isExisting = definitions.some(
-      (definition) => definition.id === newInteraction.id
+  function create(
+    newPartialInteraction: CreateInteractionDefinition
+  ): InteractionDefinition {
+    const existingInteraction = definitions.find((definition) =>
+      InteractionUtils.areEqual(newPartialInteraction, definition)
     );
-    if (!isExisting) {
-      setDefinitions([...definitions, newInteraction]);
+    if (existingInteraction) {
+      return existingInteraction;
+    } else {
+      const newInteractionDefinition: InteractionDefinition = {
+        id: crypto.randomUUID(),
+        ...newPartialInteraction,
+        createdDate: new Date(),
+        updatedDate: new Date(),
+      };
+      setDefinitions([...definitions, newInteractionDefinition]);
+      return newInteractionDefinition;
     }
   }
 
@@ -152,7 +111,6 @@ export function InteractionRegistryProvider(props: {
         definitions,
         focusedDefinition,
         setFocused: setFocusedInteractionId,
-        forkTemplate,
         remove,
         create,
         update,
diff --git a/frontend/src/modules/interactions/contexts/outcome.context.tsx b/frontend/src/modules/interactions/contexts/outcome.context.tsx
index 9b97c0eb..dbfad4ee 100644
--- a/frontend/src/modules/interactions/contexts/outcome.context.tsx
+++ b/frontend/src/modules/interactions/contexts/outcome.context.tsx
@@ -1,8 +1,4 @@
 import { ServiceRegistry } from "../../../services/service-registry";
-import {
-  FlowInteractionOutcome,
-  InteractionDefinition,
-} from "./interaction-registry.context";
 import React, {
   createContext,
   ReactElement,
@@ -23,9 +19,10 @@ import {
 import { useInteractionDefinitionManager } from "./definition.context";
 import toast from "react-hot-toast";
 import { useQuery } from "react-query";
+import { InteractionDefinition, InteractionOutcome } from "../core/core-types";
 
 type InteractionOutcomeManager = {
-  outcome: FlowInteractionOutcome | undefined;
+  outcome: InteractionOutcome | undefined;
   execute: () => Promise<void>;
 };
 
@@ -76,7 +73,7 @@ export function InteractionOutcomeManagerProvider(props: {
 
   async function executeTransaction(
     definition: InteractionDefinition
-  ): Promise<FlowInteractionOutcome | undefined> {
+  ): Promise<InteractionOutcome | undefined> {
     const { transactionOptions } = definition;
     if (!transactionOptions) {
       throw new Error("Transaction options must be set");
diff --git a/frontend/src/modules/interactions/contexts/templates.context.tsx b/frontend/src/modules/interactions/contexts/templates.context.tsx
index bb7c2105..d6024193 100644
--- a/frontend/src/modules/interactions/contexts/templates.context.tsx
+++ b/frontend/src/modules/interactions/contexts/templates.context.tsx
@@ -1,12 +1,8 @@
 import React, { createContext, ReactElement, useContext, useMemo } from "react";
-import { FclValueLookupByIdentifier } from "./definition.context";
 import { useLocalStorage } from "usehooks-ts";
 import { FclValue } from "@flowser/shared";
 import { useGetPollingFlowInteractionTemplates } from "../../../hooks/use-api";
-import {
-  CoreInteractionDefinition,
-  InteractionDefinition,
-} from "./interaction-registry.context";
+import { InteractionDefinition } from "../core/core-types";
 
 type InteractionTemplatesRegistry = {
   templates: InteractionDefinitionTemplate[];
@@ -14,16 +10,14 @@ type InteractionTemplatesRegistry = {
   removeTemplate: (template: InteractionDefinitionTemplate) => void;
 };
 
-export type InteractionDefinitionTemplate = CoreInteractionDefinition & {
-  fclValuesByIdentifier: FclValueLookupByIdentifier;
-  transactionOptions: TransactionOptions | undefined;
-  createdDate: Date;
-  updatedDate: Date;
+export type InteractionDefinitionTemplate = InteractionDefinition & {
   isMutable: boolean;
 };
 
 // Internal structure that's persisted in local storage.
-type RawInteractionDefinitionTemplate = CoreInteractionDefinition & {
+type RawInteractionDefinitionTemplate = {
+  name: string;
+  code: string;
   fclValuesByIdentifier: Record<string, FclValue>;
   transactionOptions: TransactionOptions | undefined;
   createdDate: string;
@@ -50,7 +44,11 @@ export function TemplatesRegistryProvider(props: {
     () => [
       ...customTemplates.map(
         (template): InteractionDefinitionTemplate => ({
-          ...template,
+          id: crypto.randomUUID(),
+          name: template.name,
+          code: template.code,
+          transactionOptions: undefined,
+          initialOutcome: undefined,
           createdDate: new Date(template.createdDate),
           updatedDate: new Date(template.updatedDate),
           fclValuesByIdentifier: new Map(
@@ -61,12 +59,15 @@ export function TemplatesRegistryProvider(props: {
       ),
       ...(projectTemplatesData?.templates?.map(
         (template): InteractionDefinitionTemplate => ({
-          ...template,
-          isMutable: false,
+          id: crypto.randomUUID(),
+          name: template.name,
+          code: template.code,
           transactionOptions: undefined,
+          initialOutcome: undefined,
           fclValuesByIdentifier: new Map(),
           createdDate: new Date(template.createdDate),
           updatedDate: new Date(template.updatedDate),
+          isMutable: false,
         })
       ) ?? []),
     ],
@@ -75,7 +76,6 @@ export function TemplatesRegistryProvider(props: {
 
   function saveTemplate(interaction: InteractionDefinition) {
     const newTemplate: RawInteractionDefinitionTemplate = {
-      id: interaction.name,
       name: interaction.name,
       code: interaction.code,
       fclValuesByIdentifier: Object.fromEntries(
diff --git a/frontend/src/modules/interactions/core/core-types.ts b/frontend/src/modules/interactions/core/core-types.ts
new file mode 100644
index 00000000..55761aad
--- /dev/null
+++ b/frontend/src/modules/interactions/core/core-types.ts
@@ -0,0 +1,35 @@
+import { FclValue } from "@flowser/shared";
+
+export type InteractionDefinition = {
+  id: string;
+  name: string;
+  code: string;
+  fclValuesByIdentifier: FclValueLookupByIdentifier;
+  initialOutcome: InteractionOutcome | undefined;
+  transactionOptions: TransactionOptions | undefined;
+  createdDate: Date;
+  updatedDate: Date;
+};
+
+export type FclValueLookupByIdentifier = Map<string, FclValue>;
+
+export type TransactionOptions = {
+  authorizerAddresses: string[];
+  proposerAddress: string;
+  payerAddress: string;
+};
+
+export type InteractionOutcome = {
+  transaction?: TransactionOutcome;
+  script?: ScriptOutcome;
+};
+
+export type TransactionOutcome = {
+  transactionId?: string;
+  error?: string;
+};
+
+export type ScriptOutcome = {
+  result?: unknown;
+  error?: string;
+};
diff --git a/frontend/src/modules/interactions/core/core-utils.ts b/frontend/src/modules/interactions/core/core-utils.ts
new file mode 100644
index 00000000..fa9e3179
--- /dev/null
+++ b/frontend/src/modules/interactions/core/core-utils.ts
@@ -0,0 +1,31 @@
+import { InteractionDefinition } from "./core-types";
+
+type IdentifiableInteractionDefinition = Pick<InteractionDefinition, "code">;
+
+export class InteractionUtils {
+  // Interaction structures that represent the same logical interaction,
+  // should be treated as the same entity.
+  // See how this is handled within FLIX standard:
+  // https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#data-structure-serialization--identifier-generation
+  static areEqual(
+    a: IdentifiableInteractionDefinition,
+    b: IdentifiableInteractionDefinition
+  ): boolean {
+    return (
+      this.normalizeCadenceCode(a.code) === this.normalizeCadenceCode(b.code)
+    );
+  }
+
+  static normalizeCadenceCode(code: string): string {
+    // Ignore imports for comparison,
+    // since those can differ due to address replacement.
+    // See: https://developers.flow.com/tooling/fcl-js/api#address-replacement
+    const strippedImports = code
+      .split("\n")
+      .filter((line) => !line.startsWith("import"))
+      .join("\n");
+
+    // Replace all whitespace and newlines
+    return strippedImports.replaceAll(/[\n\t ]/g, "");
+  }
+}
diff --git a/frontend/src/modules/interactions/hooks/use-transaction-name.ts b/frontend/src/modules/interactions/hooks/use-transaction-name.ts
index b8f502a9..36b16132 100644
--- a/frontend/src/modules/interactions/hooks/use-transaction-name.ts
+++ b/frontend/src/modules/interactions/hooks/use-transaction-name.ts
@@ -2,6 +2,7 @@ import { useInteractionRegistry } from "../contexts/interaction-registry.context
 import { useMemo } from "react";
 import { Transaction } from "@flowser/shared";
 import { useTemplatesRegistry } from "../contexts/templates.context";
+import { InteractionUtils } from "../core/core-utils";
 
 type UseInteractionNameProps = {
   transaction: Transaction | undefined;
@@ -53,7 +54,10 @@ const hardcodedTemplates: [string, TransactionKind][] = [
 ];
 
 const transactionKindBySource = new Map<string, TransactionKind>(
-  hardcodedTemplates.map((entry) => [sanitizeCadenceSource(entry[0]), entry[1]])
+  hardcodedTemplates.map((entry) => [
+    InteractionUtils.normalizeCadenceCode(entry[0]),
+    entry[1],
+  ])
 );
 
 export function useTransactionName(
@@ -68,11 +72,14 @@ export function useTransactionName(
       return undefined;
     }
 
-    const sanitizedTargetCode = sanitizeCadenceSource(transaction.script);
+    const sanitizedTargetCode = InteractionUtils.normalizeCadenceCode(
+      transaction.script
+    );
     const matchingTemplateName = [...templates, ...definitions].find(
       (template) =>
         template.code &&
-        sanitizeCadenceSource(template.code) === sanitizedTargetCode
+        InteractionUtils.normalizeCadenceCode(template.code) ===
+          sanitizedTargetCode
     )?.name;
 
     return matchingTemplateName ?? getDynamicName(transaction);
@@ -81,7 +88,7 @@ export function useTransactionName(
 
 function getDynamicName(transaction: Transaction) {
   const kind = transactionKindBySource.get(
-    sanitizeCadenceSource(transaction.script)
+    InteractionUtils.normalizeCadenceCode(transaction.script)
   );
 
   switch (kind) {
@@ -106,16 +113,3 @@ function getArgumentValueById(transaction: Transaction, id: string) {
       ?.valueAsJson ?? ""
   );
 }
-
-function sanitizeCadenceSource(code: string) {
-  // Ignore imports for comparison,
-  // since those can differ due to address replacement.
-  // See: https://developers.flow.com/tooling/fcl-js/api#address-replacement
-  const strippedImports = code
-    .split("\n")
-    .filter((line) => !line.startsWith("import"))
-    .join("\n");
-
-  // Replace all whitespace and newlines
-  return strippedImports.replaceAll(/[\n\t ]/g, "");
-}
diff --git a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
index dcea995c..2be3f303 100644
--- a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
+++ b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
@@ -43,7 +43,7 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
         className={classes.interactLink}
         to="/interactions"
         onClick={() => {
-          create({
+          const createdInteraction = create({
             code: transaction.script,
             fclValuesByIdentifier: new Map(
               transaction.arguments.map((arg) => [
@@ -51,7 +51,6 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
                 JSON.parse(arg.valueAsJson),
               ])
             ),
-            id: transaction.id,
             initialOutcome: {
               transaction: {
                 transactionId: transaction.id,
@@ -65,7 +64,7 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
               proposerAddress: transaction.proposalKey!.address,
             },
           });
-          setFocused(transaction.id);
+          setFocused(createdInteraction.id);
         }}
       >
         <FlowserIcon.CursorClick /> Interact

From 9970cf3bd5c50169a4664e823fc5ca57aa79eeeb Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 20:42:48 +0200
Subject: [PATCH 09/37] fix empty interaction creation

---
 .../modules/interactions/InteractionsPage.tsx | 24 ++++++++++++-------
 .../contexts/interaction-registry.context.tsx | 14 ++++++++---
 2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/frontend/src/modules/interactions/InteractionsPage.tsx b/frontend/src/modules/interactions/InteractionsPage.tsx
index 83a40a66..6eea82e7 100644
--- a/frontend/src/modules/interactions/InteractionsPage.tsx
+++ b/frontend/src/modules/interactions/InteractionsPage.tsx
@@ -65,15 +65,21 @@ export function InteractionsPage(): ReactElement {
         onChangeTab={(tab) => setFocused(tab.id)}
         tabs={openEditorTabs}
         onClose={(tab) => remove(tab.id)}
-        onAddNew={() =>
-          create({
-            name: "New interaction",
-            code: "",
-            fclValuesByIdentifier: new Map(),
-            transactionOptions: undefined,
-            initialOutcome: undefined,
-          })
-        }
+        onAddNew={() => {
+          const createdInteraction = create(
+            {
+              name: "New interaction",
+              code: "",
+              fclValuesByIdentifier: new Map(),
+              transactionOptions: undefined,
+              initialOutcome: undefined,
+            },
+            {
+              allowDuplicates: true,
+            }
+          );
+          setFocused(createdInteraction.id);
+        }}
       />
     </div>
   );
diff --git a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
index d2ded59e..2ce6723f 100644
--- a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
+++ b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
@@ -14,10 +14,17 @@ type CreateInteractionDefinition = Omit<
   "id" | "createdDate" | "updatedDate"
 >;
 
+type CreateInteractionOptions = {
+  allowDuplicates: boolean;
+};
+
 type InteractionsRegistry = {
   definitions: InteractionDefinition[];
   focusedDefinition: InteractionDefinition | undefined;
-  create: (interaction: CreateInteractionDefinition) => InteractionDefinition;
+  create: (
+    interaction: CreateInteractionDefinition,
+    options?: CreateInteractionOptions
+  ) => InteractionDefinition;
   update: (interaction: InteractionDefinition) => void;
   remove: (interactionId: string) => void;
   setFocused: (interactionId: string) => void;
@@ -86,12 +93,13 @@ export function InteractionRegistryProvider(props: {
   }
 
   function create(
-    newPartialInteraction: CreateInteractionDefinition
+    newPartialInteraction: CreateInteractionDefinition,
+    options?: CreateInteractionOptions
   ): InteractionDefinition {
     const existingInteraction = definitions.find((definition) =>
       InteractionUtils.areEqual(newPartialInteraction, definition)
     );
-    if (existingInteraction) {
+    if (existingInteraction && !options?.allowDuplicates) {
       return existingInteraction;
     } else {
       const newInteractionDefinition: InteractionDefinition = {

From 06c50208850a54f33288d449b2f74826e2db758c Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 20:46:41 +0200
Subject: [PATCH 10/37] fix address builder overflow

---
 .../ValueBuilder/AddressBuilder/AddressBuilder.module.scss      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/src/modules/interactions/components/ValueBuilder/AddressBuilder/AddressBuilder.module.scss b/frontend/src/modules/interactions/components/ValueBuilder/AddressBuilder/AddressBuilder.module.scss
index 30a5ab9d..91798587 100644
--- a/frontend/src/modules/interactions/components/ValueBuilder/AddressBuilder/AddressBuilder.module.scss
+++ b/frontend/src/modules/interactions/components/ValueBuilder/AddressBuilder/AddressBuilder.module.scss
@@ -7,7 +7,6 @@ $avatar-size: 30px;
 
 .root {
   width: 100%;
-  overflow-x: scroll;
   background: $gray-80;
   border-radius: $border-radius-input;
 
@@ -15,6 +14,7 @@ $avatar-size: 30px;
 
   .innerWrapper {
     display: flex;
+    flex-wrap: wrap;
     column-gap: $spacing-base;
     row-gap: $spacing-base;
     padding: $spacing-base;

From ca6ec3f10d9510bf80a795419befc6e2a6b19f52 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 20:50:34 +0200
Subject: [PATCH 11/37] temporarily only show settings for mutable templates

---
 .../InteractionTemplates/InteractionTemplates.tsx     | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
index b8080857..c65ff6c6 100644
--- a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
+++ b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
@@ -9,6 +9,7 @@ import { useConfirmDialog } from "../../../../contexts/confirm-dialog.context";
 import classNames from "classnames";
 import { InteractionLabel } from "../InteractionLabel/InteractionLabel";
 import { useTemplatesRegistry } from "../../contexts/templates.context";
+import { InteractionUtils } from "../../core/core-utils";
 
 export function InteractionTemplates(): ReactElement {
   return (
@@ -89,12 +90,20 @@ function StoredTemplates() {
 
 function FocusedDefinitionSettings() {
   const { focusedDefinition, update } = useInteractionRegistry();
-  const { saveTemplate } = useTemplatesRegistry();
+  const { templates, saveTemplate } = useTemplatesRegistry();
 
   if (!focusedDefinition) {
     return null;
   }
 
+  const correspondingTemplate = templates.find((template) =>
+    InteractionUtils.areEqual(template, focusedDefinition)
+  );
+
+  if (!correspondingTemplate?.isMutable) {
+    return null;
+  }
+
   return (
     <div className={classes.focusedTemplate}>
       <Input

From 032ee93f705c73454da2159335901f08ace7bc75 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 20:55:12 +0200
Subject: [PATCH 12/37] fixed interaction tab width

---
 frontend/src/modules/interactions/InteractionsPage.module.scss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/frontend/src/modules/interactions/InteractionsPage.module.scss b/frontend/src/modules/interactions/InteractionsPage.module.scss
index f7ec3dd8..b83a1c87 100644
--- a/frontend/src/modules/interactions/InteractionsPage.module.scss
+++ b/frontend/src/modules/interactions/InteractionsPage.module.scss
@@ -18,6 +18,7 @@
     overflow-x: scroll;
     .interactionTab {
       max-width: 200px;
+      min-width: 200px;
       margin-right: $spacing-s;
       .label {
         text-align: left;

From 291ad00b425d08c934771351330e466ab362ff97 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 21:00:43 +0200
Subject: [PATCH 13/37] fix transaction source overflow

---
 .../transactions/TransactionSource/TransactionSource.module.scss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss b/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss
index 7023a9b3..5271bd33 100644
--- a/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss
+++ b/frontend/src/modules/transactions/TransactionSource/TransactionSource.module.scss
@@ -33,5 +33,6 @@
 
   .right {
     flex: 3;
+    overflow: scroll;
   }
 }

From c759d9918d20dc52866abbc79716736b4bef5952 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 22:59:03 +0200
Subject: [PATCH 14/37] fix open transaction from history

---
 .../components/InteractionHistory/InteractionHistory.tsx      | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx b/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
index 203ef3a7..aa06a377 100644
--- a/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
+++ b/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
@@ -60,7 +60,7 @@ function BlockItem(props: BlockItemProps) {
     if (!firstTransaction?.proposalKey) {
       return;
     }
-    create({
+    const createdInteraction = create({
       name: transactionName ?? `Tx from block #${block.height}`,
       code: firstTransaction.script,
       fclValuesByIdentifier: new Map(
@@ -80,7 +80,7 @@ function BlockItem(props: BlockItemProps) {
         authorizerAddresses: firstTransaction.authorizers,
       },
     });
-    setFocused(block.id);
+    setFocused(createdInteraction.id);
   }
 
   return (

From 59a1c8af158269dd338fc75a859afa87f6c27bf0 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 23:11:48 +0200
Subject: [PATCH 15/37] make transaction error handling more consistent

---
 .../InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx   | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
index 079e5347..aa112c67 100644
--- a/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
+++ b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
@@ -98,10 +98,6 @@ function TransactionOutcomeDisplay(props: { outcome: TransactionOutcome }) {
   const { outcome } = props;
   const { data } = useGetTransaction(outcome.transactionId);
 
-  if (outcome.error) {
-    return <ScriptError errorMessage={outcome.error} />;
-  }
-
   if (!data?.transaction) {
     return <SpinnerWithLabel label="Executing" />;
   }

From b77d6aabd91b161f95a8241a959bd3dc1287567d Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Sun, 10 Sep 2023 23:19:36 +0200
Subject: [PATCH 16/37] fix transaction name when opened from source component

---
 .../transactions/TransactionSource/TransactionSource.tsx     | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
index 2be3f303..9eaa13b5 100644
--- a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
+++ b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
@@ -8,6 +8,7 @@ import { SizedBox } from "../../../components/sized-box/SizedBox";
 import { ProjectLink } from "../../../components/links/ProjectLink";
 import { FlowserIcon } from "../../../components/icons/Icons";
 import { useInteractionRegistry } from "../../interactions/contexts/interaction-registry.context";
+import { useTransactionName } from "../../interactions/hooks/use-transaction-name";
 
 type TransactionSourceProps = {
   transaction: Transaction;
@@ -17,6 +18,8 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
   transaction,
 }) => {
   const { setFocused, create } = useInteractionRegistry();
+  const transactionName = useTransactionName({ transaction });
+
   return (
     <Card className={classes.root}>
       {transaction.arguments.length > 0 && (
@@ -44,6 +47,7 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
         to="/interactions"
         onClick={() => {
           const createdInteraction = create({
+            name: transactionName ?? "Unknown",
             code: transaction.script,
             fclValuesByIdentifier: new Map(
               transaction.arguments.map((arg) => [
@@ -57,7 +61,6 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
                 error: transaction.status?.errorMessage,
               },
             },
-            name: "Test",
             transactionOptions: {
               authorizerAddresses: transaction.authorizers,
               payerAddress: transaction.payer,

From 8ae96fde1b893425da1af85dac51f22f9935ad09 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 08:33:33 +0200
Subject: [PATCH 17/37] fix layout body scrollbars

---
 frontend/src/components/layout/Layout.module.scss | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/frontend/src/components/layout/Layout.module.scss b/frontend/src/components/layout/Layout.module.scss
index c5045443..74e5d1db 100644
--- a/frontend/src/components/layout/Layout.module.scss
+++ b/frontend/src/components/layout/Layout.module.scss
@@ -1,5 +1,6 @@
 @import "styles/spacings";
 @import "styles/colors";
+@import "styles/scrollbars";
 
 $side-nav-width: 80px;
 
@@ -34,6 +35,8 @@ $side-nav-width: 80px;
       display: flex;
       flex-direction: column;
       flex: 1;
+
+      @include hiddenScrollbars();
     }
 
     .bodyWithBorderSpacing {

From 6fbade38f2236569b502e473df9d42a00f010364 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 08:36:39 +0200
Subject: [PATCH 18/37] fix contract details link

---
 .../modules/contracts/ContractDetails/ContractDetails.tsx   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/frontend/src/modules/contracts/ContractDetails/ContractDetails.tsx b/frontend/src/modules/contracts/ContractDetails/ContractDetails.tsx
index 3010d934..7beda825 100644
--- a/frontend/src/modules/contracts/ContractDetails/ContractDetails.tsx
+++ b/frontend/src/modules/contracts/ContractDetails/ContractDetails.tsx
@@ -1,5 +1,4 @@
 import React, { FunctionComponent } from "react";
-import { NavLink } from "react-router-dom";
 import FullScreenLoading from "../../../components/fullscreen-loading/FullScreenLoading";
 import { useGetContract } from "../../../hooks/use-api";
 import classes from "./ContractDetails.module.scss";
@@ -10,6 +9,7 @@ import {
 import { SizedBox } from "../../../components/sized-box/SizedBox";
 import { CadenceEditor } from "../../../components/cadence-editor/CadenceEditor";
 import { DateDisplay } from "../../../components/time/DateDisplay/DateDisplay";
+import { ProjectLink } from "../../../components/links/ProjectLink";
 
 type ContractDetailsProps = {
   contractId: string;
@@ -35,9 +35,9 @@ export const ContractDetails: FunctionComponent<ContractDetailsProps> = (
       {
         label: "Account",
         value: (
-          <NavLink to={`/accounts/details/${contract.accountAddress}`}>
+          <ProjectLink to={`/accounts/${contract.accountAddress}`}>
             {contract.accountAddress}
-          </NavLink>
+          </ProjectLink>
         ),
       },
       {

From 17aab53539e0fe189c651186f43ac23b5508e7c0 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 08:48:31 +0200
Subject: [PATCH 19/37] fix basic layout

---
 frontend/src/App.tsx                          | 17 ++++---
 .../components/breadcrumbs/Breadcrumbs.tsx    |  4 +-
 frontend/src/components/icons/Icons.tsx       |  2 +
 .../BasicLayout/BasicLayout.module.scss       | 10 ++++
 .../layout/BasicLayout/BasicLayout.tsx        | 14 ++++++
 frontend/src/components/layout/Layout.tsx     | 48 -------------------
 .../ProjectLayout.module.scss}                |  0
 .../layout/ProjectLayout/ProjectLayout.tsx    | 32 +++++++++++++
 .../AccountStorage/AccountStorage.tsx         |  2 +-
 .../ExecutionSettings/ExecutionSettings.tsx   |  5 --
 10 files changed, 72 insertions(+), 62 deletions(-)
 create mode 100644 frontend/src/components/layout/BasicLayout/BasicLayout.module.scss
 create mode 100644 frontend/src/components/layout/BasicLayout/BasicLayout.tsx
 delete mode 100644 frontend/src/components/layout/Layout.tsx
 rename frontend/src/components/layout/{Layout.module.scss => ProjectLayout/ProjectLayout.module.scss} (100%)
 create mode 100644 frontend/src/components/layout/ProjectLayout/ProjectLayout.tsx

diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 80d84353..665aa48a 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -10,7 +10,7 @@ import {
   useParams,
 } from "react-router-dom";
 import { Toaster } from "react-hot-toast";
-import { BackButtonLayout, ProjectLayout } from "./components/layout/Layout";
+import { ProjectLayout } from "./components/layout/ProjectLayout/ProjectLayout";
 import "./App.scss";
 import { toastOptions } from "./config/toast";
 
@@ -49,9 +49,8 @@ import { ContractsTable } from "./modules/contracts/ContractsTable";
 import { ContractDetails } from "./modules/contracts/ContractDetails/ContractDetails";
 import { EventsTable } from "./modules/events/EventsTable/EventsTable";
 import { createCrumbHandle } from "./components/breadcrumbs/Breadcrumbs";
-import {
-  TemplatesRegistryProvider
-} from './modules/interactions/contexts/templates.context';
+import { TemplatesRegistryProvider } from "./modules/interactions/contexts/templates.context";
+import { BasicLayout } from "./components/layout/BasicLayout/BasicLayout";
 
 const BrowserRouterEvents = (props: { children: ReactNode }): ReactElement => {
   const location = useLocation();
@@ -122,6 +121,9 @@ const routes: RouteObject[] = [
         </BrowserRouterEvents>
       </ProjectProvider>
     ),
+    handle: createCrumbHandle({
+      crumbName: "Projects",
+    }),
     children: [
       {
         index: true,
@@ -130,10 +132,13 @@ const routes: RouteObject[] = [
       {
         path: "create",
         element: (
-          <BackButtonLayout>
+          <BasicLayout>
             <ProjectSettingsPage />
-          </BackButtonLayout>
+          </BasicLayout>
         ),
+        handle: createCrumbHandle({
+          crumbName: "Create",
+        }),
       },
       {
         path: ":projectId",
diff --git a/frontend/src/components/breadcrumbs/Breadcrumbs.tsx b/frontend/src/components/breadcrumbs/Breadcrumbs.tsx
index 8a5edfb5..1f223406 100644
--- a/frontend/src/components/breadcrumbs/Breadcrumbs.tsx
+++ b/frontend/src/components/breadcrumbs/Breadcrumbs.tsx
@@ -1,8 +1,8 @@
 import React, { ReactElement } from "react";
 import { NavLink, useMatches, useNavigate } from "react-router-dom";
 import classes from "./Breadcrumbs.module.scss";
-import { ReactComponent as IconBackButton } from "../../assets/icons/back-button.svg";
 import classNames from "classnames";
+import { FlowserIcon } from "../icons/Icons";
 
 type BreadcrumbsProps = {
   className?: string;
@@ -54,7 +54,7 @@ export function Breadcrumbs(props: BreadcrumbsProps): ReactElement | null {
     <div className={classNames(classes.root, props.className)}>
       {breadcrumbs.length > 1 && (
         <div className={classes.backButtonWrapper} onClick={() => navigate(-1)}>
-          <IconBackButton className={classes.backButton} />
+          <FlowserIcon.Back className={classes.backButton} />
         </div>
       )}
       <div className={classes.breadcrumbs}>
diff --git a/frontend/src/components/icons/Icons.tsx b/frontend/src/components/icons/Icons.tsx
index 1c6c3e7d..adf3acba 100644
--- a/frontend/src/components/icons/Icons.tsx
+++ b/frontend/src/components/icons/Icons.tsx
@@ -20,8 +20,10 @@ import { ReactComponent as Exit } from "../../assets/icons/exit.svg";
 import { ReactComponent as Restart } from "../../assets/icons/restart.svg";
 import { ReactComponent as Open } from "../../assets/icons/open.svg";
 import { ReactComponent as Share } from "../../assets/icons/share.svg";
+import { ReactComponent as Back } from "../../assets/icons/back-button.svg";
 
 export const FlowserIcon = {
+  Back: Back,
   Account: Account,
   Block: Block,
   Contract: Contract,
diff --git a/frontend/src/components/layout/BasicLayout/BasicLayout.module.scss b/frontend/src/components/layout/BasicLayout/BasicLayout.module.scss
new file mode 100644
index 00000000..c1f53d45
--- /dev/null
+++ b/frontend/src/components/layout/BasicLayout/BasicLayout.module.scss
@@ -0,0 +1,10 @@
+@import "styles/colors";
+
+.root {
+  .header {
+    position: sticky;
+    top: 0;
+    background: $gray-110;
+    z-index: 100;
+  }
+}
diff --git a/frontend/src/components/layout/BasicLayout/BasicLayout.tsx b/frontend/src/components/layout/BasicLayout/BasicLayout.tsx
new file mode 100644
index 00000000..cd0fe4e9
--- /dev/null
+++ b/frontend/src/components/layout/BasicLayout/BasicLayout.tsx
@@ -0,0 +1,14 @@
+import React, { FC, ReactNode } from "react";
+import { Breadcrumbs } from "../../breadcrumbs/Breadcrumbs";
+import classes from "./BasicLayout.module.scss";
+
+export const BasicLayout: FC<{ children: ReactNode }> = (props) => {
+  return (
+    <div className={classes.root}>
+      <div className={classes.header}>
+        <Breadcrumbs className={classes.breadcrumbs} />
+      </div>
+      {props.children}
+    </div>
+  );
+};
diff --git a/frontend/src/components/layout/Layout.tsx b/frontend/src/components/layout/Layout.tsx
deleted file mode 100644
index d9f256aa..00000000
--- a/frontend/src/components/layout/Layout.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import React, { FC, FunctionComponent, ReactNode } from "react";
-import classes from "./Layout.module.scss";
-import { Logs } from "../../modules/logs/Logs";
-import { useLocation, useNavigate } from "react-router-dom";
-import { ReactComponent as IconBackButton } from "../../assets/icons/back-button.svg";
-import classNames from "classnames";
-import { Breadcrumbs } from "../breadcrumbs/Breadcrumbs";
-import { SideNavigation } from "../side-navigation/SideNavigation";
-
-export const BackButtonLayout: FC<{ children: ReactNode }> = (props) => {
-  const navigate = useNavigate();
-  return (
-    <div style={{ height: "100%" }}>
-      <div className={classNames(classes.backButtonWrapper)}>
-        <IconBackButton
-          onClick={() => navigate(-1)}
-          className={classes.backButton}
-        />
-      </div>
-      {props.children}
-    </div>
-  );
-};
-
-export const scrollableElementId = "flowser-scroll";
-
-export const ProjectLayout: FunctionComponent = ({ children }) => {
-  const location = useLocation();
-  const showMargin = !location.pathname.endsWith("interactions");
-
-  return (
-    <div className={classes.root}>
-      <SideNavigation className={classes.sideNavigation} />
-      <div className={classes.mainContent}>
-        <Breadcrumbs className={classes.breadcrumbs} />
-        <div
-          id={scrollableElementId}
-          className={classNames(classes.body, {
-            [classes.bodyWithBorderSpacing]: showMargin,
-          })}
-        >
-          {children}
-        </div>
-        <Logs className={classes.logs} />
-      </div>
-    </div>
-  );
-};
diff --git a/frontend/src/components/layout/Layout.module.scss b/frontend/src/components/layout/ProjectLayout/ProjectLayout.module.scss
similarity index 100%
rename from frontend/src/components/layout/Layout.module.scss
rename to frontend/src/components/layout/ProjectLayout/ProjectLayout.module.scss
diff --git a/frontend/src/components/layout/ProjectLayout/ProjectLayout.tsx b/frontend/src/components/layout/ProjectLayout/ProjectLayout.tsx
new file mode 100644
index 00000000..8039a849
--- /dev/null
+++ b/frontend/src/components/layout/ProjectLayout/ProjectLayout.tsx
@@ -0,0 +1,32 @@
+import React, { FunctionComponent } from "react";
+import classes from "./ProjectLayout.module.scss";
+import { Logs } from "../../../modules/logs/Logs";
+import { useLocation } from "react-router-dom";
+import classNames from "classnames";
+import { Breadcrumbs } from "../../breadcrumbs/Breadcrumbs";
+import { SideNavigation } from "../../side-navigation/SideNavigation";
+
+export const scrollableElementId = "flowser-scroll";
+
+export const ProjectLayout: FunctionComponent = ({ children }) => {
+  const location = useLocation();
+  const showMargin = !location.pathname.endsWith("interactions");
+
+  return (
+    <div className={classes.root}>
+      <SideNavigation className={classes.sideNavigation} />
+      <div className={classes.mainContent}>
+        <Breadcrumbs className={classes.breadcrumbs} />
+        <div
+          id={scrollableElementId}
+          className={classNames(classes.body, {
+            [classes.bodyWithBorderSpacing]: showMargin,
+          })}
+        >
+          {children}
+        </div>
+        <Logs className={classes.logs} />
+      </div>
+    </div>
+  );
+};
diff --git a/frontend/src/modules/accounts/AccountStorage/AccountStorage.tsx b/frontend/src/modules/accounts/AccountStorage/AccountStorage.tsx
index 93363e09..90e7ad86 100644
--- a/frontend/src/modules/accounts/AccountStorage/AccountStorage.tsx
+++ b/frontend/src/modules/accounts/AccountStorage/AccountStorage.tsx
@@ -9,7 +9,7 @@ import { enableDetailsIntroAnimation } from "../../../config/common";
 import { InternalStorageCard } from "../InternalStorageCard/InternalStorageCard";
 import classNames from "classnames";
 import classes from "./AccountStorage.module.scss";
-import { scrollableElementId } from "../../../components/layout/Layout";
+import { scrollableElementId } from "../../../components/layout/ProjectLayout/ProjectLayout";
 
 type AccountStorageProps = {
   account: Account;
diff --git a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx
index 8b492967..d6b143b9 100644
--- a/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx
+++ b/frontend/src/modules/interactions/components/ExecutionSettings/ExecutionSettings.tsx
@@ -112,11 +112,6 @@ function EmptyInteractionHelp() {
             </b>{" "}
             are used for reading existing state from the blockchain.
           </p>
-          <SizedBox height={10} />
-          <p>To learn more about Cadence, check out the resources below.</p>
-          <SizedBox height={10} />
-          <ExternalLink href="https://developers.flow.com/cadence/intro" />
-          <ExternalLink href="https://academy.ecdao.org/en/cadence-by-example" />
         </div>
       }
     />

From ff4638223b4bd027bc1c221b5224888280340569 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 08:50:42 +0200
Subject: [PATCH 20/37] collapse event table data

---
 frontend/src/modules/events/EventsTable/EventsTable.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/src/modules/events/EventsTable/EventsTable.tsx b/frontend/src/modules/events/EventsTable/EventsTable.tsx
index e6a1a616..81ce7d8f 100644
--- a/frontend/src/modules/events/EventsTable/EventsTable.tsx
+++ b/frontend/src/modules/events/EventsTable/EventsTable.tsx
@@ -67,7 +67,7 @@ const columns = [
       <Value>
         <JsonView
           name="data"
-          collapseAtDepth={1}
+          collapseAtDepth={0}
           data={info.getValue() as Record<string, unknown>}
         />
       </Value>

From 6222a24bdcb7ef81c8750d57c9e91b5c48142af7 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 08:52:39 +0200
Subject: [PATCH 21/37] tweak card scrollbars

---
 frontend/src/components/card/Card.module.scss | 3 +++
 frontend/src/contexts/project.context.tsx     | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/frontend/src/components/card/Card.module.scss b/frontend/src/components/card/Card.module.scss
index b2e80712..a8a65c9c 100644
--- a/frontend/src/components/card/Card.module.scss
+++ b/frontend/src/components/card/Card.module.scss
@@ -2,6 +2,7 @@
 @import 'styles/rules';
 @import 'styles/spacings';
 @import 'styles/animations';
+@import 'styles/scrollbars';
 
 .root {
   border-radius: $border-radius-card;
@@ -9,6 +10,8 @@
   border: 1px solid transparent;
   position: relative;
 
+  @include hiddenScrollbars();
+
   &.introAnimation {
     @include addNewContentAnimation(.4s);
   }
diff --git a/frontend/src/contexts/project.context.tsx b/frontend/src/contexts/project.context.tsx
index 761dfc98..60d775ea 100644
--- a/frontend/src/contexts/project.context.tsx
+++ b/frontend/src/contexts/project.context.tsx
@@ -111,7 +111,7 @@ export function ProjectProvider({
       body: <span>Are you sure you want to delete this project?</span>,
       onConfirm: () => confirmProjectRemove(project),
       confirmButtonLabel: "DELETE",
-      cancelButtonLabel: "BACK",
+      cancelButtonLabel: "CANCEL",
     });
   }
 

From 1cce838c83fd3fb20c87640678369c618f8ef4df Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:08:29 +0200
Subject: [PATCH 22/37] fix breadcrumbs filtration

---
 .../src/components/breadcrumbs/Breadcrumbs.tsx    | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/frontend/src/components/breadcrumbs/Breadcrumbs.tsx b/frontend/src/components/breadcrumbs/Breadcrumbs.tsx
index 1f223406..c8e12737 100644
--- a/frontend/src/components/breadcrumbs/Breadcrumbs.tsx
+++ b/frontend/src/components/breadcrumbs/Breadcrumbs.tsx
@@ -3,6 +3,7 @@ import { NavLink, useMatches, useNavigate } from "react-router-dom";
 import classes from "./Breadcrumbs.module.scss";
 import classNames from "classnames";
 import { FlowserIcon } from "../icons/Icons";
+import { useCurrentProjectId } from "../../hooks/use-current-project-id";
 
 type BreadcrumbsProps = {
   className?: string;
@@ -35,10 +36,18 @@ function isMatchWithCrumb(match: Match): match is MatchWithCrumb {
 
 export function Breadcrumbs(props: BreadcrumbsProps): ReactElement | null {
   const navigate = useNavigate();
-  const currentUrl = window.location.pathname;
+  const projectId = useCurrentProjectId();
+
+  function shouldShowMatch(match: Match) {
+    // For project pages, only show matches from project-scoped routes.
+    // This is to avoid showing the "Projects" crumb on every page.
+    return !projectId || match.pathname.startsWith(`/projects/${projectId}`);
+  }
 
   const matches: Match[] = useMatches();
-  const matchesWithCrumbs: MatchWithCrumb[] = matches.filter(isMatchWithCrumb);
+  const matchesWithCrumbs: MatchWithCrumb[] = matches
+    .filter(isMatchWithCrumb)
+    .filter(shouldShowMatch);
   const breadcrumbs = matchesWithCrumbs.map(
     (match): Breadcrumb => ({
       to: match.pathname,
@@ -60,7 +69,7 @@ export function Breadcrumbs(props: BreadcrumbsProps): ReactElement | null {
       <div className={classes.breadcrumbs}>
         {breadcrumbs
           .map<React.ReactNode>((item, key) => (
-            <NavLink key={key} to={item.to || currentUrl}>
+            <NavLink key={key} to={item.to}>
               {item.label}
             </NavLink>
           ))

From 6d922d0c4f6bcd90471400506aec3125f335bf6d Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:16:58 +0200
Subject: [PATCH 23/37] fix project update

---
 backend/src/projects/projects.service.ts | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/backend/src/projects/projects.service.ts b/backend/src/projects/projects.service.ts
index d065fe40..ca63803d 100644
--- a/backend/src/projects/projects.service.ts
+++ b/backend/src/projects/projects.service.ts
@@ -28,7 +28,7 @@ import * as fs from "fs";
 import { CacheRemovalService } from "../core/services/cache-removal.service";
 import { WalletService } from "../wallet/wallet.service";
 import { FlowSnapshotService } from "../flow/services/snapshot.service";
-import { FlowTemplatesService } from '../flow/services/templates.service';
+import { FlowTemplatesService } from "../flow/services/templates.service";
 
 const commandExists = require("command-exists");
 const semver = require("semver");
@@ -58,7 +58,7 @@ export class ProjectsService {
       this.flowSnapshotsService,
       // Wallet service also depends on the gateway service (needs to initialize fcl).
       this.walletService,
-      this.flowTemplatesService
+      this.flowTemplatesService,
     ];
 
   constructor(
@@ -235,13 +235,21 @@ export class ProjectsService {
 
   async update(id: string, updateProjectDto: UpdateProjectDto) {
     const currentProject = this.getCurrentProject();
+    const currentPersistedProject = await this.projectRepository.findOneBy({
+      id,
+    });
+
     const project = ProjectEntity.create(updateProjectDto);
     project.markUpdated();
 
     // Project can be persisted only in-memory
-    if (currentProject?.id === id) {
+    if (currentProject && currentProject.id === id) {
       this.currentProject = project;
-      return this.currentProject;
+
+      // If this is an in-memory project, don't execute update to avoid http error.
+      if (!currentPersistedProject) {
+        return this.currentProject;
+      }
     }
 
     await this.projectRepository.update(
@@ -249,6 +257,7 @@ export class ProjectsService {
       // Prevent overwriting existing created date
       { ...project, createdAt: undefined }
     );
+
     return project;
   }
 

From baa5e7b7fae12623521c0f52b0ad270b406c2b8f Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:22:44 +0200
Subject: [PATCH 24/37] hide root scrollbars

---
 .../components/layout/ProjectLayout/ProjectLayout.module.scss   | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/frontend/src/components/layout/ProjectLayout/ProjectLayout.module.scss b/frontend/src/components/layout/ProjectLayout/ProjectLayout.module.scss
index 74e5d1db..0ca04492 100644
--- a/frontend/src/components/layout/ProjectLayout/ProjectLayout.module.scss
+++ b/frontend/src/components/layout/ProjectLayout/ProjectLayout.module.scss
@@ -21,6 +21,8 @@ $side-nav-width: 80px;
     max-height: 100vh;
     overflow-y: scroll;
 
+    @include hiddenScrollbars();
+
     .breadcrumbs {
       position: sticky;
       top: 0;

From 009f44cd138854d4dec714e81f08ed8b62957a60 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:25:28 +0200
Subject: [PATCH 25/37] move interaction providers lower down the react tree

---
 frontend/src/App.tsx                          | 30 +++++++++----------
 .../src/contexts/platform-adapter.context.tsx |  9 ++++--
 2 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 665aa48a..34ffd789 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -86,20 +86,16 @@ export const FlowserClientApp = ({
       <TimeoutPollingProvider enabled={enableTimeoutPolling}>
         <ConfirmDialogProvider>
           <PlatformAdapterProvider {...platformAdapter}>
-            <InteractionRegistryProvider>
-              <TemplatesRegistryProvider>
-                <ConsentAnalytics />
-                <ProjectRequirements />
-                <RouterProvider
-                  router={useHashRouter ? hashRouter : browserRouter}
-                />
-                <Toaster
-                  position="bottom-center"
-                  gutter={8}
-                  toastOptions={toastOptions}
-                />
-              </TemplatesRegistryProvider>
-            </InteractionRegistryProvider>
+            <ConsentAnalytics />
+            <ProjectRequirements />
+            <RouterProvider
+              router={useHashRouter ? hashRouter : browserRouter}
+            />
+            <Toaster
+              position="bottom-center"
+              gutter={8}
+              toastOptions={toastOptions}
+            />
           </PlatformAdapterProvider>
         </ConfirmDialogProvider>
       </TimeoutPollingProvider>
@@ -144,7 +140,11 @@ const routes: RouteObject[] = [
         path: ":projectId",
         element: (
           <ProjectLayout>
-            <Outlet />
+            <InteractionRegistryProvider>
+              <TemplatesRegistryProvider>
+                <Outlet />
+              </TemplatesRegistryProvider>
+            </InteractionRegistryProvider>
           </ProjectLayout>
         ),
         children: [
diff --git a/frontend/src/contexts/platform-adapter.context.tsx b/frontend/src/contexts/platform-adapter.context.tsx
index f8d51d2a..f112a1cf 100644
--- a/frontend/src/contexts/platform-adapter.context.tsx
+++ b/frontend/src/contexts/platform-adapter.context.tsx
@@ -1,4 +1,9 @@
-import React, { createContext, ReactElement, useContext } from "react";
+import React, {
+  createContext,
+  ReactElement,
+  ReactNode,
+  useContext,
+} from "react";
 import { MonitoringServiceInt } from "../services/monitoring.service";
 
 export type PlatformAdapterState = {
@@ -11,7 +16,7 @@ const PlatformAdapterContext = createContext<PlatformAdapterState>(
 );
 
 export type PlatformAdapterProviderProps = PlatformAdapterState & {
-  children: ReactElement;
+  children: ReactNode;
 };
 
 export function PlatformAdapterProvider({

From 3458dd4b7821de991e66d7ae23dc3ca9794b2c50 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:37:51 +0200
Subject: [PATCH 26/37] fix interaction removal

---
 .../components/InteractionTemplates/InteractionTemplates.tsx  | 2 +-
 .../interactions/contexts/interaction-registry.context.tsx    | 4 ++--
 frontend/src/modules/interactions/core/core-utils.ts          | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
index c65ff6c6..f6b177aa 100644
--- a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
+++ b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
@@ -97,7 +97,7 @@ function FocusedDefinitionSettings() {
   }
 
   const correspondingTemplate = templates.find((template) =>
-    InteractionUtils.areEqual(template, focusedDefinition)
+    InteractionUtils.areLogicallyEqual(template, focusedDefinition)
   );
 
   if (!correspondingTemplate?.isMutable) {
diff --git a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
index 2ce6723f..db203832 100644
--- a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
+++ b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
@@ -86,7 +86,7 @@ export function InteractionRegistryProvider(props: {
       (definition) => definition.id !== interactionId
     );
     setDefinitions(newDefinitions);
-    const lastDefinition = newDefinitions.reverse()[0];
+    const lastDefinition = newDefinitions[newDefinitions.length - 1];
     if (lastDefinition) {
       setFocusedInteractionId(lastDefinition.id);
     }
@@ -97,7 +97,7 @@ export function InteractionRegistryProvider(props: {
     options?: CreateInteractionOptions
   ): InteractionDefinition {
     const existingInteraction = definitions.find((definition) =>
-      InteractionUtils.areEqual(newPartialInteraction, definition)
+      InteractionUtils.areLogicallyEqual(newPartialInteraction, definition)
     );
     if (existingInteraction && !options?.allowDuplicates) {
       return existingInteraction;
diff --git a/frontend/src/modules/interactions/core/core-utils.ts b/frontend/src/modules/interactions/core/core-utils.ts
index fa9e3179..d7d99763 100644
--- a/frontend/src/modules/interactions/core/core-utils.ts
+++ b/frontend/src/modules/interactions/core/core-utils.ts
@@ -7,7 +7,7 @@ export class InteractionUtils {
   // should be treated as the same entity.
   // See how this is handled within FLIX standard:
   // https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#data-structure-serialization--identifier-generation
-  static areEqual(
+  static areLogicallyEqual(
     a: IdentifiableInteractionDefinition,
     b: IdentifiableInteractionDefinition
   ): boolean {

From 071ce5761539057d37144aa4d14ca3d7c0544a66 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:55:01 +0200
Subject: [PATCH 27/37] improve interaction creation logic

---
 .../modules/interactions/InteractionsPage.tsx | 19 +++-----
 .../InteractionTemplates.tsx                  |  6 +--
 .../contexts/interaction-registry.context.tsx | 20 ++++++---
 .../modules/interactions/core/core-utils.ts   |  2 +-
 .../TransactionSource/TransactionSource.tsx   | 43 +++++++++++--------
 5 files changed, 50 insertions(+), 40 deletions(-)

diff --git a/frontend/src/modules/interactions/InteractionsPage.tsx b/frontend/src/modules/interactions/InteractionsPage.tsx
index 6eea82e7..e9220c03 100644
--- a/frontend/src/modules/interactions/InteractionsPage.tsx
+++ b/frontend/src/modules/interactions/InteractionsPage.tsx
@@ -66,18 +66,13 @@ export function InteractionsPage(): ReactElement {
         tabs={openEditorTabs}
         onClose={(tab) => remove(tab.id)}
         onAddNew={() => {
-          const createdInteraction = create(
-            {
-              name: "New interaction",
-              code: "",
-              fclValuesByIdentifier: new Map(),
-              transactionOptions: undefined,
-              initialOutcome: undefined,
-            },
-            {
-              allowDuplicates: true,
-            }
-          );
+          const createdInteraction = create({
+            name: "New interaction",
+            code: "",
+            fclValuesByIdentifier: new Map(),
+            transactionOptions: undefined,
+            initialOutcome: undefined,
+          });
           setFocused(createdInteraction.id);
         }}
       />
diff --git a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
index f6b177aa..5d536b38 100644
--- a/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
+++ b/frontend/src/modules/interactions/components/InteractionTemplates/InteractionTemplates.tsx
@@ -96,11 +96,11 @@ function FocusedDefinitionSettings() {
     return null;
   }
 
-  const correspondingTemplate = templates.find((template) =>
-    InteractionUtils.areLogicallyEqual(template, focusedDefinition)
+  const correspondingTemplate = templates.find(
+    (template) => template.id === focusedDefinition.id
   );
 
-  if (!correspondingTemplate?.isMutable) {
+  if (correspondingTemplate && !correspondingTemplate.isMutable) {
     return null;
   }
 
diff --git a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
index db203832..6aa586fd 100644
--- a/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
+++ b/frontend/src/modules/interactions/contexts/interaction-registry.context.tsx
@@ -12,10 +12,13 @@ import { InteractionUtils } from "../core/core-utils";
 type CreateInteractionDefinition = Omit<
   InteractionDefinition,
   "id" | "createdDate" | "updatedDate"
->;
+> & {
+  // If not provided, a random UUID will be used.
+  id?: string;
+};
 
 type CreateInteractionOptions = {
-  allowDuplicates: boolean;
+  deduplicateBySourceCodeSemantics: boolean;
 };
 
 type InteractionsRegistry = {
@@ -96,15 +99,22 @@ export function InteractionRegistryProvider(props: {
     newPartialInteraction: CreateInteractionDefinition,
     options?: CreateInteractionOptions
   ): InteractionDefinition {
+    const newPartialDefinitionId =
+      newPartialInteraction.id ?? crypto.randomUUID();
     const existingInteraction = definitions.find((definition) =>
-      InteractionUtils.areLogicallyEqual(newPartialInteraction, definition)
+      options?.deduplicateBySourceCodeSemantics
+        ? InteractionUtils.areSemanticallyEquivalent(
+            newPartialInteraction,
+            definition
+          )
+        : definition.id === newPartialDefinitionId
     );
-    if (existingInteraction && !options?.allowDuplicates) {
+    if (existingInteraction) {
       return existingInteraction;
     } else {
       const newInteractionDefinition: InteractionDefinition = {
-        id: crypto.randomUUID(),
         ...newPartialInteraction,
+        id: newPartialDefinitionId,
         createdDate: new Date(),
         updatedDate: new Date(),
       };
diff --git a/frontend/src/modules/interactions/core/core-utils.ts b/frontend/src/modules/interactions/core/core-utils.ts
index d7d99763..1df48139 100644
--- a/frontend/src/modules/interactions/core/core-utils.ts
+++ b/frontend/src/modules/interactions/core/core-utils.ts
@@ -7,7 +7,7 @@ export class InteractionUtils {
   // should be treated as the same entity.
   // See how this is handled within FLIX standard:
   // https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#data-structure-serialization--identifier-generation
-  static areLogicallyEqual(
+  static areSemanticallyEquivalent(
     a: IdentifiableInteractionDefinition,
     b: IdentifiableInteractionDefinition
   ): boolean {
diff --git a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
index 9eaa13b5..487bfd34 100644
--- a/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
+++ b/frontend/src/modules/transactions/TransactionSource/TransactionSource.tsx
@@ -46,27 +46,32 @@ export const TransactionSource: FC<TransactionSourceProps> = ({
         className={classes.interactLink}
         to="/interactions"
         onClick={() => {
-          const createdInteraction = create({
-            name: transactionName ?? "Unknown",
-            code: transaction.script,
-            fclValuesByIdentifier: new Map(
-              transaction.arguments.map((arg) => [
-                arg.identifier,
-                JSON.parse(arg.valueAsJson),
-              ])
-            ),
-            initialOutcome: {
-              transaction: {
-                transactionId: transaction.id,
-                error: transaction.status?.errorMessage,
+          const createdInteraction = create(
+            {
+              name: transactionName ?? "Unknown",
+              code: transaction.script,
+              fclValuesByIdentifier: new Map(
+                transaction.arguments.map((arg) => [
+                  arg.identifier,
+                  JSON.parse(arg.valueAsJson),
+                ])
+              ),
+              initialOutcome: {
+                transaction: {
+                  transactionId: transaction.id,
+                  error: transaction.status?.errorMessage,
+                },
+              },
+              transactionOptions: {
+                authorizerAddresses: transaction.authorizers,
+                payerAddress: transaction.payer,
+                proposerAddress: transaction.proposalKey!.address,
               },
             },
-            transactionOptions: {
-              authorizerAddresses: transaction.authorizers,
-              payerAddress: transaction.payer,
-              proposerAddress: transaction.proposalKey!.address,
-            },
-          });
+            {
+              deduplicateBySourceCodeSemantics: true,
+            }
+          );
           setFocused(createdInteraction.id);
         }}
       >

From 9fe4d085a499de07c8d61d1d7c3d17211322bd47 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Mon, 11 Sep 2023 09:56:40 +0200
Subject: [PATCH 28/37] display tabs in reverse order of stored templates

---
 .../modules/interactions/InteractionsPage.tsx | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/frontend/src/modules/interactions/InteractionsPage.tsx b/frontend/src/modules/interactions/InteractionsPage.tsx
index e9220c03..c7f255ae 100644
--- a/frontend/src/modules/interactions/InteractionsPage.tsx
+++ b/frontend/src/modules/interactions/InteractionsPage.tsx
@@ -35,17 +35,19 @@ export function InteractionsPage(): ReactElement {
     sideMenuTabs[0].id
   );
 
-  const openEditorTabs: TabItem[] = definitions.map((definition) => ({
-    id: definition.id,
-    label: <InteractionLabel interaction={definition} />,
-    content: (
-      <InteractionDefinitionManagerProvider definition={definition}>
-        <InteractionOutcomeManagerProvider>
-          <InteractionBody />
-        </InteractionOutcomeManagerProvider>
-      </InteractionDefinitionManagerProvider>
-    ),
-  }));
+  const openEditorTabs: TabItem[] = [...definitions]
+    .reverse()
+    .map((definition) => ({
+      id: definition.id,
+      label: <InteractionLabel interaction={definition} />,
+      content: (
+        <InteractionDefinitionManagerProvider definition={definition}>
+          <InteractionOutcomeManagerProvider>
+            <InteractionBody />
+          </InteractionOutcomeManagerProvider>
+        </InteractionDefinitionManagerProvider>
+      ),
+    }));
 
   return (
     <div className={classes.pageRoot}>

From 65fa8628017dcb5de5d889a8986815a9109afe13 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Wed, 13 Sep 2023 08:43:52 +0200
Subject: [PATCH 29/37] fix focused transaction tab reset

---
 frontend/src/components/tabs/Tabs.tsx                         | 4 +---
 .../InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx   | 3 +++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/frontend/src/components/tabs/Tabs.tsx b/frontend/src/components/tabs/Tabs.tsx
index 1d266367..c8a15c2a 100644
--- a/frontend/src/components/tabs/Tabs.tsx
+++ b/frontend/src/components/tabs/Tabs.tsx
@@ -36,9 +36,7 @@ export function Tabs(props: TabsProps): ReactElement {
     onAddNew,
   } = props;
 
-  const [fallbackCurrentTabId, setFallbackCurrentTabId] = useState(
-    props.tabs[0]?.id
-  );
+  const [fallbackCurrentTabId, setFallbackCurrentTabId] = useState(tabs[0]?.id);
   const currentTabId = props.currentTabId ?? fallbackCurrentTabId;
 
   function onChangeTab(tab: TabItem) {
diff --git a/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
index aa112c67..e32be9d1 100644
--- a/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
+++ b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
@@ -104,6 +104,9 @@ function TransactionOutcomeDisplay(props: { outcome: TransactionOutcome }) {
 
   return (
     <TransactionDetailsTabs
+      // Re-mount this component when different transaction is used.
+      // This is mainly to reset the initial focused tab.
+      key={data.transaction.id}
       label="Transaction"
       includeOverviewTab={true}
       includeScriptTab={false}

From d507dff9b4d7d6b62a02ff41e1995240b2f42952 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Wed, 13 Sep 2023 11:47:56 +0200
Subject: [PATCH 30/37] add unsupported import syntax notice

---
 .../src/components/status/ErrorMessage.tsx    | 71 +++++++++++++++++--
 .../InteractionOutcomeDisplay.tsx             |  7 +-
 .../TransactionDetailsTabs.tsx                |  8 ++-
 3 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/frontend/src/components/status/ErrorMessage.tsx b/frontend/src/components/status/ErrorMessage.tsx
index 582b3d6f..f9e190bd 100644
--- a/frontend/src/components/status/ErrorMessage.tsx
+++ b/frontend/src/components/status/ErrorMessage.tsx
@@ -2,21 +2,28 @@ import React, { ReactElement } from "react";
 import { FlowUtils } from "../../utils/flow-utils";
 import classes from "./ErrorMessage.module.scss";
 import { JsonView } from "../json-view/JsonView";
+import { ExternalLink } from "../links/ExternalLink";
+import { Callout } from "../callout/Callout";
+import { SizedBox } from "../sized-box/SizedBox";
 
 type ScriptErrorProps = {
+  cadenceSource: string;
   errorMessage: string;
 };
 
-export function ScriptError({ errorMessage }: ScriptErrorProps): ReactElement {
-  const parsedMessage = FlowUtils.parseScriptError(errorMessage);
+export function ScriptError(props: ScriptErrorProps): ReactElement {
+  const parsedMessage = FlowUtils.parseScriptError(props.errorMessage);
 
   if (parsedMessage === undefined) {
-    return <pre className={classes.root}>{errorMessage}</pre>;
+    return <pre className={classes.root}>{props.errorMessage}</pre>;
   }
 
   if (parsedMessage.responseBody?.message) {
     return (
-      <pre className={classes.root}>{parsedMessage.responseBody.message}</pre>
+      <>
+        <UnsupportedImportSyntaxNotice cadenceSource={props.cadenceSource} />
+        <pre className={classes.root}>{parsedMessage.responseBody.message}</pre>
+      </>
     );
   }
 
@@ -26,9 +33,63 @@ export function ScriptError({ errorMessage }: ScriptErrorProps): ReactElement {
 }
 
 type TransactionErrorProps = {
+  cadenceSource: string;
   errorMessage: string;
 };
 
 export function TransactionError(props: TransactionErrorProps): ReactElement {
-  return <pre className={classes.root}>{props.errorMessage}</pre>;
+  return (
+    <>
+      <UnsupportedImportSyntaxNotice cadenceSource={props.cadenceSource} />
+      <pre className={classes.root}>{props.errorMessage}</pre>
+    </>
+  );
+}
+
+type UnsupportedImportSyntaxNoticeProps = {
+  cadenceSource: string;
+};
+
+function UnsupportedImportSyntaxNotice(
+  props: UnsupportedImportSyntaxNoticeProps
+) {
+  const isUnsupportedSyntax = props.cadenceSource
+    .split("\n")
+    .some((sourceCodeLine) => /^import ".*"$/.test(sourceCodeLine.trim()));
+
+  if (!isUnsupportedSyntax) {
+    return null;
+  }
+
+  return (
+    <>
+      <Callout
+        icon="🚨"
+        title="Unsupported import syntax"
+        description={
+          <div>
+            <p>
+              It looks like the executed Cadence code uses{" "}
+              <ExternalLink
+                href="https://developers.flow.com/tools/toolchains/flow-cli/super-commands#import-schema"
+                inline
+              >
+                the latest import syntax
+              </ExternalLink>{" "}
+              {/* eslint-disable-next-line react/no-unescaped-entities */}
+              syntax, which isn't supported within Flowser yet.
+            </p>
+            <p>
+              For more details, see:{" "}
+              <ExternalLink
+                inline
+                href="https://github.com/onflow/fcl-js/issues/1765"
+              />
+            </p>
+          </div>
+        }
+      />
+      <SizedBox height={20} />
+    </>
+  );
 }
diff --git a/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
index e32be9d1..002cf1d4 100644
--- a/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
+++ b/frontend/src/modules/interactions/components/InteractionOutcomeDisplay/InteractionOutcomeDisplay.tsx
@@ -117,8 +117,9 @@ function TransactionOutcomeDisplay(props: { outcome: TransactionOutcome }) {
 
 function ScriptOutcomeDisplay(props: { outcome: ScriptOutcome }) {
   const { result, error } = props.outcome;
+  const { definition } = useInteractionDefinitionManager();
   const resultTabId = "result";
-  const errorTabId = "result";
+  const errorTabId = "error";
   const [currentTabId, setCurrentTabId] = useState(resultTabId);
 
   useEffect(() => {
@@ -133,7 +134,9 @@ function ScriptOutcomeDisplay(props: { outcome: ScriptOutcome }) {
     tabs.push({
       id: errorTabId,
       label: "Error",
-      content: <ScriptError errorMessage={error} />,
+      content: (
+        <ScriptError errorMessage={error} cadenceSource={definition.code} />
+      ),
     });
   } else {
     tabs.push({
diff --git a/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx b/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
index bd8c9532..469953f2 100644
--- a/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
+++ b/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
@@ -11,6 +11,7 @@ import {
 import { Transaction } from "@flowser/shared";
 import { useGetPollingEventsByTransaction } from "../../../hooks/use-api";
 import { TransactionOverview } from "../TransactionOverview/TransactionOverview";
+import { useInteractionDefinitionManager } from "../../interactions/contexts/definition.context";
 
 type TransactionDetailsTabsProps = Omit<StyledTabsProps, "tabs"> & {
   transaction: Transaction;
@@ -24,6 +25,8 @@ export function TransactionDetailsTabs(
   const { transaction, includeOverviewTab, includeScriptTab, ...tabProps } =
     props;
 
+  const { definition } = useInteractionDefinitionManager();
+
   const { data: events } = useGetPollingEventsByTransaction(transaction.id);
 
   const tabs: TabItem[] = [];
@@ -33,7 +36,10 @@ export function TransactionDetailsTabs(
       id: "error",
       label: "Error",
       content: (
-        <TransactionError errorMessage={transaction.status.errorMessage} />
+        <TransactionError
+          errorMessage={transaction.status.errorMessage}
+          cadenceSource={definition.code}
+        />
       ),
     });
   }

From 903b5f41315f0b1ce65120c1a7c136224cc62366 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Wed, 13 Sep 2023 11:57:43 +0200
Subject: [PATCH 31/37] improve error message

---
 .../src/components/status/ErrorMessage.tsx     | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/frontend/src/components/status/ErrorMessage.tsx b/frontend/src/components/status/ErrorMessage.tsx
index f9e190bd..89e33c0e 100644
--- a/frontend/src/components/status/ErrorMessage.tsx
+++ b/frontend/src/components/status/ErrorMessage.tsx
@@ -5,6 +5,8 @@ import { JsonView } from "../json-view/JsonView";
 import { ExternalLink } from "../links/ExternalLink";
 import { Callout } from "../callout/Callout";
 import { SizedBox } from "../sized-box/SizedBox";
+import { CadenceEditor } from "../cadence-editor/CadenceEditor";
+import { LineSeparator } from "../line-separator/LineSeparator";
 
 type ScriptErrorProps = {
   cadenceSource: string;
@@ -75,10 +77,22 @@ function UnsupportedImportSyntaxNotice(
                 inline
               >
                 the latest import syntax
+              </ExternalLink>
+              {/* eslint-disable-next-line react/no-unescaped-entities */},
+              which isn't supported within Flowser yet.
+            </p>
+            <SizedBox height={10} />
+            <p>
+              Try refactoring your code to use the{" "}
+              <ExternalLink
+                inline
+                href="https://developers.flow.com/cadence/language/imports#docusaurus_skipToContent_fallback"
+              >
+                standard Cadence syntax
               </ExternalLink>{" "}
-              {/* eslint-disable-next-line react/no-unescaped-entities */}
-              syntax, which isn't supported within Flowser yet.
+              instead.
             </p>
+            <SizedBox height={10} />
             <p>
               For more details, see:{" "}
               <ExternalLink

From 49d9ac760d201c11edc4fb03b49cb863b6ccaea8 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Wed, 13 Sep 2023 12:10:40 +0200
Subject: [PATCH 32/37] remove bottom border from tabs

---
 frontend/src/components/status/ErrorMessage.tsx     | 2 --
 frontend/src/components/tabs/StyledTabs.module.scss | 1 -
 2 files changed, 3 deletions(-)

diff --git a/frontend/src/components/status/ErrorMessage.tsx b/frontend/src/components/status/ErrorMessage.tsx
index 89e33c0e..f4d349e4 100644
--- a/frontend/src/components/status/ErrorMessage.tsx
+++ b/frontend/src/components/status/ErrorMessage.tsx
@@ -5,8 +5,6 @@ import { JsonView } from "../json-view/JsonView";
 import { ExternalLink } from "../links/ExternalLink";
 import { Callout } from "../callout/Callout";
 import { SizedBox } from "../sized-box/SizedBox";
-import { CadenceEditor } from "../cadence-editor/CadenceEditor";
-import { LineSeparator } from "../line-separator/LineSeparator";
 
 type ScriptErrorProps = {
   cadenceSource: string;
diff --git a/frontend/src/components/tabs/StyledTabs.module.scss b/frontend/src/components/tabs/StyledTabs.module.scss
index 4311153a..33a2e115 100644
--- a/frontend/src/components/tabs/StyledTabs.module.scss
+++ b/frontend/src/components/tabs/StyledTabs.module.scss
@@ -11,7 +11,6 @@
 
 .activeTab {
   background: $gray-100 !important;
-  border-bottom: 2px solid $violet-100 !important;
 }
 
 .tabLabel {

From 0c72c5756aacfabf3f1ccd3b135654f5e82b4cb9 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Wed, 13 Sep 2023 12:15:48 +0200
Subject: [PATCH 33/37] settings screen tweaks

---
 .../ProjectSettings/ProjectSettings.tsx        | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/frontend/src/modules/projects/ProjectSettings/ProjectSettings.tsx b/frontend/src/modules/projects/ProjectSettings/ProjectSettings.tsx
index e6c1c852..e1aa3ae1 100644
--- a/frontend/src/modules/projects/ProjectSettings/ProjectSettings.tsx
+++ b/frontend/src/modules/projects/ProjectSettings/ProjectSettings.tsx
@@ -298,6 +298,18 @@ export const ProjectSettings: FunctionComponent<ProjectSettingsProps> = (
               />
             </Card>
             <Card className={classes.card}>
+              <EmulatorToggleField
+                label="REST API debug"
+                path="enableRestDebug"
+                description="Enable REST API debugging output (not recommended as it slows down the app)"
+                formik={formik}
+              />
+              <EmulatorToggleField
+                label="GRPC API debug"
+                path="enableGrpcDebug"
+                description="enable gRPC server reflection for debugging with grpc_cli"
+                formik={formik}
+              />
               <EmulatorToggleField
                 label="Verbose"
                 path="verboseLogging"
@@ -307,13 +319,13 @@ export const ProjectSettings: FunctionComponent<ProjectSettingsProps> = (
               <EmulatorToggleField
                 label="Persist"
                 path="persist"
-                description="Enable persistence of the state between restarts"
+                description="Persist emaulator blockchain state between restarts"
                 formik={formik}
               />
               <EmulatorToggleField
-                label="Snapshot"
+                label="Snapshots"
                 path="snapshot"
-                description="Enable snapshot support (this option automatically enables persistence)"
+                description="Enable blockchain state snapshots (this option automatically enables persistence)"
                 formik={formik}
               />
               <EmulatorToggleField

From f66033d213c669a5852539821b8b7997240040d7 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Wed, 13 Sep 2023 12:32:20 +0200
Subject: [PATCH 34/37] change block menu item labels

---
 .../components/InteractionHistory/InteractionHistory.tsx      | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx b/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
index aa06a377..bd2e4ad2 100644
--- a/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
+++ b/frontend/src/modules/interactions/components/InteractionHistory/InteractionHistory.tsx
@@ -97,7 +97,7 @@ function BlockItem(props: BlockItemProps) {
       <MenuItem onClick={() => onForkAsTemplate()}>
         <FlowserIcon.Share width={menuIconSize} height={menuIconSize} />
         <SizedBox width={10} />
-        Open
+        View transaction
       </MenuItem>
       <MenuItem onClick={() => checkoutBlock(block.id)}>
         <FlowserIcon.CircleArrowLeft
@@ -105,7 +105,7 @@ function BlockItem(props: BlockItemProps) {
           height={menuIconSize}
         />
         <SizedBox width={10} />
-        Rollback
+        Rollback to block
       </MenuItem>
     </FlowserMenu>
   );

From dc2569f08a3be67e53fbe4be9e7b5c4d754113f0 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Thu, 14 Sep 2023 09:28:43 +0200
Subject: [PATCH 35/37] custom outline color

---
 frontend/src/App.scss | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/frontend/src/App.scss b/frontend/src/App.scss
index fb4157a6..8cd96fa6 100644
--- a/frontend/src/App.scss
+++ b/frontend/src/App.scss
@@ -2,9 +2,14 @@
 @import "styles/typography";
 
 * {
-    margin: 0;
-    padding: 0;
-    box-sizing: content-box;
+  margin: 0;
+  padding: 0;
+  box-sizing: content-box;
+  outline: none;
+
+  &:focus-visible {
+    outline: 2px solid $blue;
+  }
 }
 
 html,
@@ -23,6 +28,7 @@ body {
   height: 100%;
 
   scroll-behavior: smooth;
+
   &::-webkit-scrollbar {
     display: none;
   }

From 4993922d211b29e6fb51dc248d29b8c0ef7955a2 Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Fri, 15 Sep 2023 11:59:09 +0200
Subject: [PATCH 36/37] fix external link overflow

---
 frontend/src/components/links/ExternalLink.module.scss | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/frontend/src/components/links/ExternalLink.module.scss b/frontend/src/components/links/ExternalLink.module.scss
index 92ad47b3..07d8b75b 100644
--- a/frontend/src/components/links/ExternalLink.module.scss
+++ b/frontend/src/components/links/ExternalLink.module.scss
@@ -13,6 +13,8 @@
     fill: $blue;
   }
   .url {
-    word-break: break-all;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
   }
 }

From c87e2999a2393391323144ddd966dec1fd7a17da Mon Sep 17 00:00:00 2001
From: Bart <bartolomej.kozorog@gmail.com>
Date: Fri, 15 Sep 2023 12:08:04 +0200
Subject: [PATCH 37/37] fix missing context

---
 .../modules/transactions/SignaturesTable/SignaturesTable.tsx | 2 +-
 .../TransactionDetailsTabs/TransactionDetailsTabs.tsx        | 5 +----
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/frontend/src/modules/transactions/SignaturesTable/SignaturesTable.tsx b/frontend/src/modules/transactions/SignaturesTable/SignaturesTable.tsx
index a4a689b8..73973ef2 100644
--- a/frontend/src/modules/transactions/SignaturesTable/SignaturesTable.tsx
+++ b/frontend/src/modules/transactions/SignaturesTable/SignaturesTable.tsx
@@ -12,7 +12,7 @@ const columnsHelper = createColumnHelper<SignableObject>();
 
 const columns = [
   columnsHelper.accessor("address", {
-    header: () => <Label variant="medium">ACCOUNT ADDRESS</Label>,
+    header: () => <Label variant="medium">ADDRESS</Label>,
     cell: (info) => (
       <Value>
         <ProjectLink to={`/accounts/${info.getValue()}`}>
diff --git a/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx b/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
index 469953f2..3886040c 100644
--- a/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
+++ b/frontend/src/modules/transactions/TransactionDetailsTabs/TransactionDetailsTabs.tsx
@@ -11,7 +11,6 @@ import {
 import { Transaction } from "@flowser/shared";
 import { useGetPollingEventsByTransaction } from "../../../hooks/use-api";
 import { TransactionOverview } from "../TransactionOverview/TransactionOverview";
-import { useInteractionDefinitionManager } from "../../interactions/contexts/definition.context";
 
 type TransactionDetailsTabsProps = Omit<StyledTabsProps, "tabs"> & {
   transaction: Transaction;
@@ -25,8 +24,6 @@ export function TransactionDetailsTabs(
   const { transaction, includeOverviewTab, includeScriptTab, ...tabProps } =
     props;
 
-  const { definition } = useInteractionDefinitionManager();
-
   const { data: events } = useGetPollingEventsByTransaction(transaction.id);
 
   const tabs: TabItem[] = [];
@@ -38,7 +35,7 @@ export function TransactionDetailsTabs(
       content: (
         <TransactionError
           errorMessage={transaction.status.errorMessage}
-          cadenceSource={definition.code}
+          cadenceSource={transaction.script}
         />
       ),
     });