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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion apps/dave/src/components/send/SendContexts.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";
import type { Application } from "@cartesi/viem";
import { createContext, type ActionDispatch } from "react";
import type { DbSpecification } from "../specification/types";

// Actions
type CloseModal = { type: "close_modal" };
Expand All @@ -15,10 +16,14 @@ export type DepositType =
export type TransactionType = DepositType | InputType;

type Deposit = { type: DepositType; payload: { application: Application } };
type GenericInput = { type: InputType; payload: { application: Application } };
type GenericInput = {
type: InputType;
payload: { application: Application; specifications: DbSpecification[] };
};

type SendState = {
application: Application;
specifications: DbSpecification[];
transactionType: TransactionType;
timestamp: number;
} | null;
Expand Down
14 changes: 11 additions & 3 deletions apps/dave/src/components/send/SendMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ import type { Application } from "@cartesi/viem";
import { Button, Group, Menu, Text } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { useChainModal, useConnectModal } from "@rainbow-me/rainbowkit";
import { isNil } from "ramda";
import { filter, isNil } from "ramda";
import type { FC } from "react";
import { TbCoins, TbCurrencyEthereum, TbInbox, TbSend } from "react-icons/tb";
import { useAccount } from "wagmi";
import { useSelectedNodeConnection } from "../connection/hooks";
import { useSpecification } from "../specification/hooks/useSpecification";
import type { DbSpecification } from "../specification/types";
import { useSendAction } from "./hooks";

type SendMenuProps = { application: Application };

const jsonAbiOnly = (spec: DbSpecification) => spec.mode === "json_abi";

const SendMenu: FC<SendMenuProps> = ({ application }) => {
const selectedConnection = useSelectedNodeConnection();
const { listSpecifications } = useSpecification();
const [opened, handlers] = useDisclosure(false);
const actions = useSendAction();
const account = useAccount();
Expand Down Expand Up @@ -54,11 +59,14 @@ const SendMenu: FC<SendMenuProps> = ({ application }) => {

<Menu.Dropdown>
<Menu.Item
disabled
leftSection={<TbInbox size={24} />}
onClick={(evt) => {
evt.stopPropagation();
actions.sendGenericInput(application);
const specifications = filter(
jsonAbiOnly,
listSpecifications() ?? [],
);
actions.sendGenericInput(application, specifications);
handlers.close();
}}
>
Expand Down
12 changes: 12 additions & 0 deletions apps/dave/src/components/send/SendModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import { ERC1155DepositForm } from "../transactions/ERC1155DepositForm";
import { ERC20DepositForm } from "../transactions/ERC20DepositForm";
import { ERC721DepositForm } from "../transactions/ERC721DepositForm";
import { EtherDepositForm } from "../transactions/EtherDepositForm";
import {
GenericInputForm,
type GenericInputFormSpecification,
} from "../transactions/GenericInputForm";
import { useSendAction, useSendState } from "./hooks";
import type { TransactionType } from "./SendContexts";

Expand Down Expand Up @@ -117,6 +121,14 @@ const SendModal: FC = () => {
application={state.application}
onSuccess={onSuccess}
/>
) : state.transactionType === "generic_input" ? (
<GenericInputForm
application={state.application}
specifications={
state.specifications as GenericInputFormSpecification[]
}
onSuccess={onSuccess}
/>
) : null}
</Modal>
);
Expand Down
7 changes: 7 additions & 0 deletions apps/dave/src/components/send/SendProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ const sendReducer: SendReducer = (state, action) => {
case "deposit_erc721":
case "deposit_erc1155Single":
case "deposit_erc1155Batch":
return {
transactionType: action.type,
application: action.payload.application,
specifications: [],
timestamp: Date.now(),
};
case "generic_input":
return {
transactionType: action.type,
application: action.payload.application,
specifications: action.payload.specifications,
timestamp: Date.now(),
};
case "close_modal":
Expand Down
11 changes: 9 additions & 2 deletions apps/dave/src/components/send/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";
import type { Application } from "@cartesi/viem";
import { useContext } from "react";
import type { DbSpecification } from "../specification/types";
import { SendActionContext, SendStateContext } from "./SendContexts";

export const useSendState = () => {
Expand All @@ -27,8 +28,14 @@ export const useSendAction = () => {
type: "deposit_erc1155Batch",
payload: { application },
}),
sendGenericInput: (application: Application) =>
dispatch({ type: "generic_input", payload: { application } }),
sendGenericInput: (
application: Application,
specifications: DbSpecification[],
) =>
dispatch({
type: "generic_input",
payload: { application, specifications },
}),
closeModal: () => dispatch({ type: "close_modal" }),
};
};
163 changes: 163 additions & 0 deletions apps/dave/src/components/transactions/GenericInputForm/AbiFields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"use client";
import {
Flex,
Group,
SegmentedControl,
Select,
Text,
Textarea,
Tooltip,
} from "@mantine/core";
import type { FC } from "react";
import { TbHelp } from "react-icons/tb";
import { AbiFunctionName } from "./AbiFunctionName";
import { AbiFunctionParams } from "./AbiFunctionParams";
import { AbiParameter } from "./AbiParameter";
import { useFormContext } from "./context";
import type {
FormAbiMethod,
FormSpecification,
SpecificationMode,
} from "./types";
import { resetAbiFunctionParams } from "./utils";

export interface AbiFieldsProps {
specifications: FormSpecification[];
}

export const AbiFields: FC<AbiFieldsProps> = ({ specifications }) => {
const form = useFormContext();
const { abiMethod, specificationMode } = form.getTransformedValues();
const specificationOptions = specifications.map((s) => ({
value: s.id,
label: s.name,
}));
const hasSpecifications = specifications.length > 0;

return (
<>
<Select
label="ABI method"
description="Select how to attach an ABI"
data-testid="abi-method-select"
allowDeselect={false}
withAsterisk
data={[
{
value: "existing",
label: "ABI from an existing JSON_ABI specification",
disabled: !hasSpecifications,
},
{ value: "new", label: "New ABI" },
]}
{...form.getInputProps("abiMethod")}
onChange={(nextValue) => {
form.setFieldValue("abiMethod", nextValue as FormAbiMethod);
form.setFieldValue("specificationId", "");
form.setFieldValue("abiFunctionName", "");
form.setFieldValue("humanAbi", "");
form.setFieldValue("abiParam", "");
form.setFieldValue("savedAbiParam", "");
form.setFieldValue("specificationMode", "json_abi");
resetAbiFunctionParams(form, []);
}}
/>

{abiMethod === "new" ? (
<>
<SegmentedControl
aria-label="Specification Mode"
data={[
{
label: "JSON ABI",
value: "json_abi",
},
{
label: "ABI Parameters",
value: "abi_params",
},
]}
{...form.getInputProps("specificationMode")}
onChange={(nextValue) => {
form.setFieldValue(
"specificationMode",
nextValue as SpecificationMode,
);

form.setFieldValue("specificationId", "");
form.setFieldValue("abiFunctionName", "");
form.setFieldValue("humanAbi", "");
form.setFieldValue("abiParam", "");
form.setFieldValue("savedAbiParam", "");
resetAbiFunctionParams(form, []);
}}
/>

{specificationMode === "json_abi" ? (
<Textarea
data-testid="json-abi-textarea"
resize="vertical"
description="Define signatures in Human readable format"
placeholder="function balanceOf(address owner) view returns (uint256) event Transfer(address indexed from, address indexed to, uint256 amount)"
rows={5}
label={
<Group justify="flex-start" gap="3">
<Text size="sm">ABI</Text>
<Tooltip
multiline
label="Define the signature without wrapping it on quotes nor adding comma at the end to separate. Just hit enter and keep defining your signatures"
withArrow
w="300"
>
<Flex direction="column-reverse">
<TbHelp />
</Flex>
</Tooltip>
</Group>
}
{...form.getInputProps("humanAbi")}
onChange={(event) => {
const nextValue = event.target.value;
form.setFieldValue("humanAbi", nextValue);
form.validateField("humanAbi");
form.setFieldValue("abiFunctionName", "");
}}
/>
) : (
<AbiParameter />
)}
</>
) : (
<Select
label="Specifications"
description="Available JSON_ABI specifications"
placeholder="Select specification"
data={specificationOptions}
allowDeselect={false}
withAsterisk
{...form.getInputProps("specificationId")}
onChange={(nextValue) => {
form.setFieldValue(
"specificationId",
nextValue as string,
);
form.setFieldValue("abiFunctionName", "");
resetAbiFunctionParams(form, []);
}}
/>
)}

<AbiFunctionName />
<AbiFunctionParams />

<Textarea
label="Hex value"
description="Encoded hex value for the application"
readOnly
value={
form.isValid() ? form.getInputProps("rawInput").value : "0x"
}
/>
</>
);
};
Loading
Loading