@openzeppelin/contracts-ui-builder-adapter-evm
Version:
EVM Adapter for Contracts UI Builder
161 lines (139 loc) • 5 kB
text/typescript
import { useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form';
import { Speed } from '@openzeppelin/relayer-sdk';
import { gweiToWei, weiToGwei } from '../../utils';
import type { EvmRelayerTransactionOptions } from '../relayer';
export interface EvmRelayerFormData {
transactionOptions: EvmRelayerTransactionOptions & {
showGasLimit?: boolean;
};
}
interface UseEvmRelayerOptionsProps {
options: Record<string, unknown>;
onChange: (options: Record<string, unknown>) => void;
}
export const useEvmRelayerOptions = ({ options, onChange }: UseEvmRelayerOptionsProps) => {
// Store latest onChange in a ref to avoid effect re-runs due to changing function identity
const onChangeRef = useRef(onChange);
onChangeRef.current = onChange;
// Initialize form with options from parent (only on mount to prevent loops)
const initialOptions = {
speed: (() => {
const hasCustomSettings = Boolean(
options.gasPrice || options.maxFeePerGas || options.maxPriorityFeePerGas
);
return options.speed || (!hasCustomSettings ? Speed.FAST : undefined);
})() as Speed,
gasPrice: weiToGwei(options.gasPrice as number | undefined),
maxFeePerGas: weiToGwei(options.maxFeePerGas as number | undefined),
maxPriorityFeePerGas: weiToGwei(options.maxPriorityFeePerGas as number | undefined),
gasLimit: options.gasLimit as number | undefined,
showGasLimit: Boolean(options.gasLimit),
};
const { control, setValue, watch } = useForm<EvmRelayerFormData>({
defaultValues: {
transactionOptions: initialOptions,
},
});
const formValues = watch('transactionOptions');
const isInitialMount = useRef(true);
// Determine current mode and gas type
const hasCustomSettings = Boolean(
formValues.gasPrice || formValues.maxFeePerGas || formValues.maxPriorityFeePerGas
);
const configMode = hasCustomSettings ? 'custom' : 'speed';
const isEip1559 = Boolean(formValues.maxFeePerGas || formValues.maxPriorityFeePerGas);
const gasType = isEip1559 ? 'eip1559' : 'legacy';
// Handle initial mount - ensure the default speed value is communicated to parent
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
// Only notify parent if we have a default speed value that wasn't already in options
if (initialOptions.speed && !options.speed) {
const newOptions: EvmRelayerTransactionOptions = {};
if (initialOptions.speed) newOptions.speed = initialOptions.speed;
if (initialOptions.gasLimit) newOptions.gasLimit = initialOptions.gasLimit;
onChange(newOptions as Record<string, unknown>);
}
return;
}
}, []);
// Notify parent of changes after initial mount (watch specific fields to avoid loops)
useEffect(() => {
if (isInitialMount.current) {
return;
}
const timeoutId = setTimeout(() => {
const newOptions: EvmRelayerTransactionOptions = {};
if (formValues.speed) newOptions.speed = formValues.speed;
if (formValues.gasPrice) newOptions.gasPrice = gweiToWei(formValues.gasPrice);
if (formValues.maxFeePerGas) newOptions.maxFeePerGas = gweiToWei(formValues.maxFeePerGas);
if (formValues.maxPriorityFeePerGas) {
newOptions.maxPriorityFeePerGas = gweiToWei(formValues.maxPriorityFeePerGas);
}
if (formValues.gasLimit) newOptions.gasLimit = formValues.gasLimit;
onChangeRef.current(newOptions as Record<string, unknown>);
}, 100);
return () => clearTimeout(timeoutId);
}, [
formValues.speed,
formValues.gasPrice,
formValues.maxFeePerGas,
formValues.maxPriorityFeePerGas,
formValues.gasLimit,
]);
// Event handlers
const handleSpeedChange = (speed: Speed) => {
setValue('transactionOptions', {
...formValues,
speed,
gasPrice: undefined,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
});
};
const handleModeChange = (mode: string) => {
if (mode === 'speed') {
setValue('transactionOptions', {
...formValues,
speed: Speed.FAST,
gasPrice: undefined,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
});
} else {
setValue('transactionOptions', {
...formValues,
speed: undefined,
maxFeePerGas: 30,
maxPriorityFeePerGas: 2,
});
}
};
const handleGasTypeSwitch = (type: string) => {
if (type === 'eip1559') {
setValue('transactionOptions', {
...formValues,
gasPrice: undefined,
maxFeePerGas: 30,
maxPriorityFeePerGas: 2,
});
} else {
setValue('transactionOptions', {
...formValues,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
gasPrice: 20,
});
}
};
return {
control,
formValues,
configMode,
gasType,
handleSpeedChange,
handleModeChange,
handleGasTypeSwitch,
};
};