Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Safe Transaction Details Parser Feature #33

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ How to use the application:
- Fill the rest of the data according to your selected method.
- Click "Calculate Hashes" to view the results.

## Features

- Safe transaction hash calculation for all networks
- Calculate hashes for transaction execution
- Calculate domain hash and message hash for EIP-712 signature verification
- Direct Manual Input method
- Safe API Integration for transaction retrieval
- **Paste and Parse**: Easily paste transaction details from Safe UI and automatically fill the form fields
- **EIP-3770 Support**: Auto-detection of network from chain shortnames (e.g. "eth:", "gnosis:")
- Zero address expansion: "0x0000...0000" pattern is automatically expanded to the full address

## Learn More

To learn more about the technologies used in this project, check out the following resources:
Expand Down
131 changes: 94 additions & 37 deletions app/components/transaction/DirectInputWizard.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as React from "react";
import { useState, useEffect } from "react";
import { UseFormReturn } from "react-hook-form";
import { Button } from "@/components/ui/button";
Expand All @@ -6,6 +7,9 @@ import { FormData } from "@/types/form-types";
import BasicInfoStep from "@/components/transaction/BasicInfoStep";
import TransactionDetailsStep from "@/components/transaction/TransactionDetailsStep";
import AdvancedParamsStep from "@/components/transaction/AdvancedParamsStep";
import PasteTransactionDetails from "@/components/transaction/PasteTransactionDetails";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";

interface DirectInputWizardProps {
form: UseFormReturn<FormData>;
Expand All @@ -25,6 +29,7 @@ export default function DirectInputWizard({
calculationSubmit
}: DirectInputWizardProps) {
const [submitting, setSubmitting] = useState(false);
const [showPasteOption, setShowPasteOption] = useState(false);
const steps = ["Basic Information", "Transaction Details", "Advanced Parameters"];

useEffect(() => {
Expand All @@ -48,59 +53,111 @@ export default function DirectInputWizard({
}
};

const handlePastedSuccess = () => {
// If we're already at step 2 or 3 no need to advance, otherwise go to step 2
if (step === 1) {
nextStep();
}
};

return (
<div className="space-y-6">
<StepProgressIndicator
steps={steps}
currentStep={visualStep}
/>

{/* Toggle for paste option */}
<div className="flex items-center justify-end space-x-2 mb-2">
<Label htmlFor="paste-mode" className="text-sm cursor-pointer">Paste from Safe</Label>
<Switch
id="paste-mode"
checked={showPasteOption}
onCheckedChange={setShowPasteOption}
/>
</div>

<div className="min-h-[320px]">
{step === 1 && <BasicInfoStep form={form} />}
{step === 2 && <TransactionDetailsStep form={form} />}
{(step === 3 || step === 4) && <AdvancedParamsStep form={form} />}
{showPasteOption ? (
<PasteTransactionDetails form={form} onParsed={handlePastedSuccess} />
) : (
<>
{step === 1 && <BasicInfoStep form={form} />}
{step === 2 && <TransactionDetailsStep form={form} />}
{(step === 3 || step === 4) && <AdvancedParamsStep form={form} />}
</>
)}
</div>

<div className="flex justify-between pt-4">
{step > 1 ? (
{!showPasteOption && (
<div className="flex justify-between pt-4">
{step > 1 ? (
<Button
type="button"
variant="outline"
onClick={handleBack}
className="px-6 rounded-full border-button text-button hover:bg-transparent dark:bg-white dark:hover:bg-gray-100 h-[48px]"
>
Back
</Button>
) : (
<div />
)}

{step < 3 && (
<Button
type="button"
onClick={() => {
nextStep();
}}
className="bg-button hover:bg-button-hover active:bg-button-active text-white rounded-full px-6 h-[48px]"
>
Continue
</Button>
)}

{step >= 3 && (
<Button
type="button"
disabled={isSubmitting}
onClick={async (e) => {
setSubmitting(true);
await calculationSubmit(e);
}}
className="bg-button hover:bg-button-hover active:bg-button-active text-white rounded-full px-6 h-[48px]"
>
{isSubmitting ? "Calculating..." : "Calculate Hash"}
</Button>
)}
</div>
)}

{showPasteOption && (
<div className="flex justify-between pt-4">
<Button
type="button"
variant="outline"
onClick={handleBack}
onClick={() => setShowPasteOption(false)}
className="px-6 rounded-full border-button text-button hover:bg-transparent dark:bg-white dark:hover:bg-gray-100 h-[48px]"
>
Back
Back to Form
</Button>
) : (
<div />
)}

{step < 3 && (
<Button
type="button"
onClick={() => {
nextStep();
}}
className="bg-button hover:bg-button-hover active:bg-button-active text-white rounded-full px-6 h-[48px]"
>
Continue
</Button>
)}

{step >= 3 && (
<Button
type="button"
disabled={isSubmitting}
onClick={async (e) => {
setSubmitting(true);
await calculationSubmit(e);
}}
className="bg-button hover:bg-button-hover active:bg-button-active text-white rounded-full px-6 h-[48px]"
>
{isSubmitting ? "Calculating..." : "Calculate Hash"}
</Button>
)}
</div>

{step >= 3 && (
<Button
type="button"
disabled={isSubmitting}
onClick={async (e) => {
setSubmitting(true);
await calculationSubmit(e);
}}
className="bg-button hover:bg-button-hover active:bg-button-active text-white rounded-full px-6 h-[48px]"
>
{isSubmitting ? "Calculating..." : "Calculate Hash"}
</Button>
)}
</div>
)}
</div>
);
}
Loading