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
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 74 additions & 8 deletions src/components/GasEstimator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,46 @@ interface GasEstimatorProps {

type GasSpeed = 'slow' | 'standard' | 'fast';

const ETH_PRICE_USD = 2500; // Mock ETH price
type GasConfidence = {
label: "High" | "Medium" | "Low";
variance: string;
};

const getGasConfidence = (
gasPrice?: bigint
): GasConfidence => {
if (!gasPrice) {
return {
label: "Low",
variance: "±20%",
};
}

const gwei = Number(formatUnits(gasPrice, 9));

if (gwei < 30) {
return {
label: "High",
variance: "±5%",
};
}

if (gwei < 80) {
return {
label: "Medium",
variance: "±10%",
};
}

return {
label: "Low",
variance: "±20%",
};
};

const confidence = getGasConfidence(adjustedGasPrice);

const ESTIMATED_ETH_PRICE_USD = 2500; // Mock ETH price

export const GasEstimator: React.FC<GasEstimatorProps> = ({
to,
Expand All @@ -28,7 +67,7 @@ export const GasEstimator: React.FC<GasEstimatorProps> = ({
}) => {
const [selectedSpeed, setSelectedSpeed] = useState<GasSpeed>('standard');
const [estimatedGas, setEstimatedGas] = useState<bigint | null>(null);

const { data: baseGasPrice, isLoading: isGasPriceLoading } = useGasPrice();
const { data: gasEstimate, isLoading: isGasEstimateLoading } = useEstimateGas({
to: to as `0x${string}`,
Expand Down Expand Up @@ -62,7 +101,7 @@ export const GasEstimator: React.FC<GasEstimatorProps> = ({
const totalCostWei = estimatedGas && adjustedGasPrice ? estimatedGas * adjustedGasPrice : null;
const totalCostEth = totalCostWei ? formatUnits(totalCostWei, 18) : null;
const totalCostUsd = totalCostEth ? (parseFloat(totalCostEth) * ETH_PRICE_USD).toFixed(2) : null;

const isHighGas = adjustedGasPrice ? adjustedGasPrice > parseUnits('100', 9) : false;

return (
Expand All @@ -73,9 +112,9 @@ export const GasEstimator: React.FC<GasEstimatorProps> = ({
<Gauge className="h-4 w-4 text-blue-500" />
Gas Fee Estimator
</CardTitle>
<a
href="https://etherscan.io/gastracker"
target="_blank"
<a
href="https://etherscan.io/gastracker"
target="_blank"
rel="noopener noreferrer"
className="text-xs text-blue-500 hover:underline flex items-center gap-1"
>
Expand Down Expand Up @@ -117,8 +156,18 @@ export const GasEstimator: React.FC<GasEstimatorProps> = ({
<div className="flex justify-between items-center">
<span className="text-sm text-muted-foreground font-medium">Estimated Cost</span>
<div className="text-right">
<div className="text-lg font-bold text-gray-900 dark:text-white">
${totalCostUsd || '0.00'}
<div className="text-lg font-bold">
≈ ${totalCostUsd || "0.00"}
</div>

<div className="text-xs text-muted-foreground">
Approximate USD value
</div>

<div className="text-xs text-muted-foreground">
{totalCostEth
? parseFloat(totalCostEth).toFixed(6)
: "0"} ETH
</div>
<div className="text-xs text-muted-foreground">
{totalCostEth ? parseFloat(totalCostEth).toFixed(6) : '0'} ETH
Expand All @@ -136,6 +185,23 @@ export const GasEstimator: React.FC<GasEstimatorProps> = ({
</div>
<div className="flex justify-between items-center text-xs">
<span className="text-muted-foreground">Gas Limit</span>
<div className="flex justify-between items-center text-xs">
<span className="text-muted-foreground">
Confidence
</span>

<Badge
variant={
confidence.label === "High"
? "default"
: confidence.label === "Medium"
? "secondary"
: "destructive"
}
>
{confidence.label} ({confidence.variance})
</Badge>
</div>
<span className="font-mono">{estimatedGas?.toString() || '0'}</span>
</div>
</div>
Expand Down
29 changes: 29 additions & 0 deletions src/components/TransactionHistory/TransactionHistory.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {
TransactionStatus,
TransactionType,
} from "@/store/transactionStore";

export const TRANSACTION_TYPES: TransactionType[] = [
"purchase",
"transfer",
"management",
"other",
];

export const TRANSACTION_STATUSES: TransactionStatus[] = [
"pending",
"processing",
"confirmed",
"failed",
"cancelled",
];

export const isTransactionType = (
value: string
): value is TransactionType =>
TRANSACTION_TYPES.includes(value as TransactionType);

export const isTransactionStatus = (
value: string
): value is TransactionStatus =>
TRANSACTION_STATUSES.includes(value as TransactionStatus);
30 changes: 30 additions & 0 deletions src/components/TransactionHistory/TransactionHistory.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type {
Transaction,
TransactionStatus,
TransactionType,
} from "@/store/transactionStore";

export type SortBy = "timestamp" | "value" | "gasUsed";

export type SortOrder = "asc" | "desc";

export interface DateRange {
from?: Date;
to?: Date;
}

export interface FilterTransactionsParams {
transactions: Transaction[];
typeFilter: TransactionType | "all";
statusFilter: TransactionStatus | "all";
propertyFilter: string;
searchTerm: string;
amountRange: [number, number];
gasPriceRange: [number, number];
dateRange: DateRange;
sortBy: SortBy;
sortOrder: SortOrder;
getTransactionsByType: (
type: TransactionType
) => Transaction[];
}
13 changes: 13 additions & 0 deletions src/components/dialogs/ConfirmDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<AlertDialog>

<AlertDialogTrigger />

<AlertDialogContent>

<AlertDialogHeader />

<AlertDialogFooter />

</AlertDialogContent>

</AlertDialog>
13 changes: 13 additions & 0 deletions src/components/dialogs/ConfirmDialog.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface ConfirmDialogOptions {

title: string;

description?: string;

confirmText?: string;

cancelText?: string;

variant?: "default" | "destructive";

}
51 changes: 34 additions & 17 deletions src/config/chains.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,74 @@
import {mainnet, polygon, bsc} from "wagmi/chains";
import {getRpcUrl} from "./env";
import { mainnet, polygon, bsc } from "wagmi/chains";
import { getRpcUrl } from "./env";

/**
* Supported EVM chain IDs.
*/
export const CHAIN_IDS = {
ETHEREUM: 1,
POLYGON: 137,
BSC: 56,
} as const;

export type ChainId = (typeof CHAIN_IDS)[keyof typeof CHAIN_IDS];

export const SUPPORTED_CHAINS = [mainnet, polygon, bsc];

/**
* Get RPC URL for a chain, falling back to defaults if env var is not set
* Get RPC URL for a chain, falling back to defaults if env var is not set.
*/
const getChainRpcUrl = (chainId: number): string => {
const getChainRpcUrl = (chainId: ChainId): string => {
switch (chainId) {
case 1:
case CHAIN_IDS.ETHEREUM:
return getRpcUrl("ethereum") ?? "https://mainnet.infura.io/v3/";
case 137:

case CHAIN_IDS.POLYGON:
return getRpcUrl("polygon") ?? "https://polygon-rpc.com";
case 56:

case CHAIN_IDS.BSC:
return getRpcUrl("bsc") ?? "https://bsc-dataseed1.binance.org";

default:
return "";
}
};

export const CHAIN_CONFIG = {
1: {
[CHAIN_IDS.ETHEREUM]: {
id: CHAIN_IDS.ETHEREUM,
name: "Ethereum",
symbol: "ETH",
decimals: 18,
rpcUrl: getChainRpcUrl(1),
rpcUrl: getChainRpcUrl(CHAIN_IDS.ETHEREUM),
blockExplorer: "https://etherscan.io",
color: "#627EEA",
},
137: {

[CHAIN_IDS.POLYGON]: {
id: CHAIN_IDS.POLYGON,
name: "Polygon",
symbol: "MATIC",
decimals: 18,
rpcUrl: getChainRpcUrl(137),
rpcUrl: getChainRpcUrl(CHAIN_IDS.POLYGON),
blockExplorer: "https://polygonscan.com",
color: "#8247E5",
},
56: {

[CHAIN_IDS.BSC]: {
id: CHAIN_IDS.BSC,
name: "Binance Smart Chain",
symbol: "BNB",
decimals: 18,
rpcUrl: getChainRpcUrl(56),
rpcUrl: getChainRpcUrl(CHAIN_IDS.BSC),
blockExplorer: "https://bscscan.com",
color: "#F3BA2F",
},
} as const;

export type ChainId = keyof typeof CHAIN_CONFIG;

export const DEFAULT_CHAIN_ID: ChainId = 1;
export const DEFAULT_CHAIN_ID: ChainId = CHAIN_IDS.ETHEREUM;

export const isChainId = (value: number): value is ChainId =>
value in CHAIN_CONFIG;

export const toChainId = (value: number): ChainId | null =>
isChainId(value) ? value : null;
isChainId(value) ? value : null;