Skip to content

New pcaversaccio features #39

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
18 changes: 18 additions & 0 deletions app/components/result/trusted-addresses.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// https://github.com/safe-global/safe-deployments/blob/4e25b09f62a4acec92b4ebe6b8ae496b3852d440/src/assets
export const trustedAddresses = [
"0x8D29bE29923b68abfDD21e541b9374737B49cdAD", // MultiSend `v1.1.1` (canonical).
"0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761", // MultiSend `v1.3.0` (canonical).
"0x998739BFdAAdde7C933B942a68053933098f9EDa", // MultiSend `v1.3.0` (eip155).
"0x0dFcccB95225ffB03c6FBB2559B530C2B7C8A912", // MultiSend `v1.3.0` (zksync).
"0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526", // MultiSend `v1.4.1` (canonical).
"0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", // MultiSendCallOnly `v1.3.0` (canonical).
"0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B", // MultiSendCallOnly `v1.3.0` (eip155).
"0xf220D3b4DFb23C4ade8C88E526C1353AbAcbC38F", // MultiSendCallOnly `v1.3.0` (zksync).
"0x9641d764fc13c8B624c04430C7356C1C7C8102e2", // MultiSendCallOnly `v1.4.1` (canonical).
"0x526643F69b81B008F46d95CD5ced5eC0edFFDaC6", // SafeMigration `v1.4.1` (canonical).
"0xfF83F6335d8930cBad1c0D439A841f01888D9f69", // SafeToL2Migration `v1.4.1` (canonical).
"0xA65387F16B013cf2Af4605Ad8aA5ec25a2cbA3a2", // SignMessageLib `v1.3.0` (canonical).
"0x98FFBBF51bb33A056B08ddf711f289936AafF717", // SignMessageLib `v1.3.0` (eip155).
"0x357147caf9C0cCa67DfA0CF5369318d8193c8407", // SignMessageLib `v1.3.0` (zksync).
"0xd53cd0aB83D845Ac265BE939c57F53AD838012c9" // SignMessageLib `v1.4.1` (canonical).
];
51 changes: 50 additions & 1 deletion app/components/transaction/AdvancedParamsStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@radix-ui/react-tooltip";
import { HelpCircle } from "lucide-react";
import { AlertTriangle, HelpCircle } from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import PixelAvatar from "@/components/pixel-avatar";

interface AdvancedParamsStepProps {
Expand All @@ -18,10 +19,22 @@ interface AdvancedParamsStepProps {

export default function AdvancedParamsStep({ form }: AdvancedParamsStepProps) {
const [activeTooltip, setActiveTooltip] = useState<string | null>(null);
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";

// Watch for changes in these fields
const gasToken = form.watch("gasToken");
const refundReceiver = form.watch("refundReceiver");
const gasPrice = form.watch("gasPrice");

const handleTooltipToggle = (id: string) => {
setActiveTooltip(activeTooltip === id ? null : id);
};

const hasBothCustom = gasToken && gasToken !== ZERO_ADDRESS && refundReceiver && refundReceiver !== ZERO_ADDRESS;
const hasCustomGasToken = gasToken && gasToken !== ZERO_ADDRESS;
const hasCustomRefundReceiver = refundReceiver && refundReceiver !== ZERO_ADDRESS;
const hasNonZeroGasPrice = gasPrice && gasPrice !== "0";
const showWarning = hasCustomGasToken || hasCustomRefundReceiver;

return (
<TooltipProvider>
Expand Down Expand Up @@ -108,6 +121,42 @@ export default function AdvancedParamsStep({ form }: AdvancedParamsStepProps) {
</FormItem>
)}
/>

{/* Gas token warning */}
{showWarning && (
<Alert variant="warning" className="flex items-center gap-4">
<div className="flex-shrink-0">
<AlertTriangle className="h-6 w-6" />
</div>
<div>
<AlertDescription className="text-md">
{hasBothCustom ? (
<>
<span className="font-medium block">
This transaction uses a custom gas token AND a custom refund receiver.
</span>
<span className="block mt-1">
This combination can be used to hide a rerouting of funds through gas refunds.
</span>
{hasNonZeroGasPrice && (
<span className="block mt-1">
Furthermore, the gas price is non-zero, which increases the potential for hidden value transfers.
</span>
)}
</>
) : hasCustomGasToken ? (
<span>
This transaction uses a custom gas token. Please verify that this is intended.
</span>
) : (
<span>
This transaction uses a custom refund receiver. Please verify that this is intended.
</span>
)}
</AlertDescription>
</div>
</Alert>
)}
</div>
</TooltipProvider>
);
Expand Down
72 changes: 72 additions & 0 deletions app/components/transaction/ApiInputFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface ApiInputFieldsProps {

export default function ApiInputFields({ form }: ApiInputFieldsProps) {
const [activeTooltip, setActiveTooltip] = useState<string | null>(null);
const nestedSafeEnabled = form.watch("nestedSafeEnabled");

const handleTooltipToggle = (id: string) => {
setActiveTooltip(activeTooltip === id ? null : id);
Expand Down Expand Up @@ -210,6 +211,77 @@ export default function ApiInputFields({ form }: ApiInputFieldsProps) {
</FormItem>
)}
/>

<FormField
control={form.control}
name="nestedSafeEnabled"
render={({ field }) => {
const { value, ...inputProps } = field;
return (
<FormItem className="flex items-center space-x-2">
<input
type="checkbox"
{...inputProps}
checked={!!value}
onChange={(e) => field.onChange(e.target.checked)}
className="h-4 w-4"
/>
<FormLabel>Use Nested Safe</FormLabel>
</FormItem>
);
}}
/>

{nestedSafeEnabled && (
<>
<FormField
control={form.control}
name="nestedSafeAddress"
render={({ field }) => (
<FormItem>
<FormLabel>Nested Safe Address</FormLabel>
<FormControl>
<Input
placeholder="Enter nested safe address (0x...)"
{...field}
value={field.value || ''}
onChange={(e) => {
if (e.target.value === '') {
field.onChange('');
} else {
const address = e.target.value.match(/0x[a-fA-F0-9]{40}/)?.[0];
if (address) {
field.onChange(address);
} else {
field.onChange(e.target.value);
}
}
}}
/>
</FormControl>
</FormItem>
)}
/>

<FormField
control={form.control}
name="nestedSafeNonce"
render={({ field }) => (
<FormItem>
<FormLabel>Nested Safe Nonce</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Enter nested safe nonce"
{...field}
value={field.value || ''}
/>
</FormControl>
</FormItem>
)}
/>
</>
)}
</div>
);
}
113 changes: 112 additions & 1 deletion app/components/transaction/BasicInfoStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { UseFormReturn } from "react-hook-form";
import { FormData } from "@/types/form-types";
import { NETWORKS } from "@/app/constants";
Expand Down Expand Up @@ -26,6 +26,15 @@ interface BasicInfoStepProps {

export default function BasicInfoStep({ form }: BasicInfoStepProps) {
const [activeTooltip, setActiveTooltip] = useState<string | null>(null);
const nestedSafeEnabled = form.watch("nestedSafeEnabled");
const mainSafeVersion = form.watch("version");

// Set nested safe version to match main safe version when enabled
useEffect(() => {
if (nestedSafeEnabled && mainSafeVersion && !form.getValues("nestedSafeVersion")) {
form.setValue("nestedSafeVersion", mainSafeVersion);
}
}, [nestedSafeEnabled, mainSafeVersion, form]);

const handleTooltipToggle = (id: string) => {
setActiveTooltip(activeTooltip === id ? null : id);
Expand Down Expand Up @@ -254,6 +263,108 @@ export default function BasicInfoStep({ form }: BasicInfoStepProps) {
</FormItem>
)}
/>

<FormField
control={form.control}
name="nestedSafeEnabled"
render={({ field }) => {
const { value, ...inputProps } = field;
return (
<FormItem className="flex items-center space-x-2">
<input
type="checkbox"
{...inputProps}
checked={!!value}
onChange={(e) => field.onChange(e.target.checked)}
className="h-4 w-4"
/>
<FormLabel className="!mt-0.5">Use Nested Safe</FormLabel>
</FormItem>
);
}}
/>

{nestedSafeEnabled && (
<>
<FormField
control={form.control}
name="nestedSafeAddress"
render={({ field }) => (
<FormItem>
<FormLabel>Nested Safe Address</FormLabel>
<FormControl>
<Input
placeholder="Enter nested safe address (0x...)"
leftIcon={<PixelAvatar address={field.value || ''} />}
{...field}
value={field.value || ''}
onChange={(e) => {
if (e.target.value === '') {
field.onChange('');
} else {
const address = e.target.value.match(/0x[a-fA-F0-9]{40}/)?.[0];
if (address) {
field.onChange(address);
} else {
field.onChange(e.target.value);
}
}
}}
/>
</FormControl>
</FormItem>
)}
/>

<FormField
control={form.control}
name="nestedSafeNonce"
render={({ field }) => (
<FormItem>
<FormLabel>Nested Safe Nonce</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Enter nested safe nonce"
{...field}
value={field.value || ''}
/>
</FormControl>
</FormItem>
)}
/>

<FormField
control={form.control}
name="nestedSafeVersion"
render={({ field }) => (
<FormItem>
<FormLabel>Nested Safe Version</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value || ''}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select nested Safe version" />
</SelectTrigger>
</FormControl>
<SelectContent>
{["0.0.1", "0.1.0", "1.0.0", "1.1.0", "1.1.1", "1.2.0", "1.3.0", "1.4.1"].map((version) => (
<SelectItem key={version} value={version}>
{version}
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground mt-1">
Defaults to same version as main Safe but can be changed if needed.
</p>
</FormItem>
)}
/>
</>
)}
</div>
</TooltipProvider>
);
Expand Down
Loading