Skip to content

Commit

Permalink
feat: indexer and explorer env. var.
Browse files Browse the repository at this point in the history
- use environment variables to provide indexer and explorer urls
- add indexer and explorer urls to custom endpoint form
  • Loading branch information
fmorency committed Dec 2, 2024
1 parent 60f31fd commit b0099f4
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 19 deletions.
6 changes: 5 additions & 1 deletion components/bank/components/historyBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
useTokenFactoryDenomsMetadata,
} from '@/hooks';
import { ReceiveIcon, SendIcon } from '@/components/icons';
import { useEndpointStore } from '@/store/endpointStore';

interface Transaction {
tx_type: HistoryTxType;
Expand Down Expand Up @@ -58,12 +59,15 @@ export function HistoryBox({
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 10;

const { selectedEndpoint } = useEndpointStore();
const indexerUrl = selectedEndpoint?.indexer || '';

const {
sendTxs,
totalPages,
isLoading: txLoading,
isError,
} = useGetFilteredTxAndSuccessfulProposals(address, currentPage, pageSize);
} = useGetFilteredTxAndSuccessfulProposals(indexerUrl, address, currentPage, pageSize);

const isLoading = initialLoading || txLoading;

Expand Down
41 changes: 35 additions & 6 deletions components/bank/modals/txInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TruncatedAddressWithCopy } from '@/components/react/addressCopy';
import { formatDenom, TransactionGroup } from '@/components';
import { FaExternalLinkAlt } from 'react-icons/fa';
import { shiftDigits } from '@/utils';
import { useEndpointStore } from '@/store/endpointStore';

interface TxInfoModalProps {
tx: TransactionGroup;
Expand All @@ -11,6 +12,9 @@ interface TxInfoModalProps {
}

export default function TxInfoModal({ tx, modalId }: TxInfoModalProps) {
const { selectedEndpoint } = useEndpointStore();
const explorerUrl = selectedEndpoint?.explorer || '';

function formatDate(dateString: string): string {
const date = new Date(dateString);
return date.toLocaleString('en-US', {
Expand Down Expand Up @@ -39,13 +43,36 @@ export default function TxInfoModal({ tx, modalId }: TxInfoModalProps) {
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<InfoItem label="TRANSACTION HASH" value={tx?.tx_hash} isAddress={true} />
<InfoItem label="BLOCK" value={tx?.block_number?.toString()} />
<InfoItem label="TIMESTAMP" value={formatDate(tx?.formatted_date)} />
<InfoItem
label="TRANSACTION HASH"
explorerUrl={explorerUrl}
value={tx?.tx_hash}
isAddress={true}
/>
<InfoItem
label="BLOCK"
explorerUrl={explorerUrl}
value={tx?.block_number?.toString()}
/>
<InfoItem
label="TIMESTAMP"
explorerUrl={explorerUrl}
value={formatDate(tx?.formatted_date)}
/>
</div>
<div>
<InfoItem label="FROM" value={tx?.data?.from_address} isAddress={true} />
<InfoItem label="TO" value={tx?.data?.to_address} isAddress={true} />
<InfoItem
label="FROM"
explorerUrl={explorerUrl}
value={tx?.data?.from_address}
isAddress={true}
/>
<InfoItem
label="TO"
explorerUrl={explorerUrl}
value={tx?.data?.to_address}
isAddress={true}
/>
<div>
<p className="text-sm font-semibold text-[#00000099] dark:text-[#FFFFFF99] mb-2">
VALUE
Expand Down Expand Up @@ -74,10 +101,12 @@ export default function TxInfoModal({ tx, modalId }: TxInfoModalProps) {
function InfoItem({
label,
value,
explorerUrl,
isAddress = false,
}: {
label: string;
value: string;
explorerUrl: string;
isAddress?: boolean;
}) {
return (
Expand All @@ -88,7 +117,7 @@ function InfoItem({
<div className="flex items-center">
<TruncatedAddressWithCopy address={value} slice={8} />
<a
href={`${process.env.NEXT_PUBLIC_TESTNET_EXPLORER_URL}/${label === 'TRANSACTION HASH' ? 'transaction' : 'account'}/${label?.includes('TRANSACTION') ? value?.toUpperCase() : value}`}
href={`${explorerUrl}/${label === 'TRANSACTION HASH' ? 'transaction' : 'account'}/${label?.includes('TRANSACTION') ? value?.toUpperCase() : value}`}
target="_blank"
rel="noopener noreferrer"
className="ml-2 text-primary hover:text-primary/50"
Expand Down
23 changes: 20 additions & 3 deletions components/factory/modals/denomInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { TruncatedAddressWithCopy } from '@/components/react/addressCopy';
import { FaExternalLinkAlt } from 'react-icons/fa';
import { MetadataSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank';
import { useEndpointStore } from '@/store/endpointStore';

export const DenomInfoModal: React.FC<{
denom: MetadataSDKType | null;
Expand All @@ -12,6 +13,9 @@ export const DenomInfoModal: React.FC<{
nameIsAddress = true;
}

const { selectedEndpoint } = useEndpointStore();
const explorerUrl = selectedEndpoint?.explorer || '';

return (
<dialog id={modalId} className="modal" aria-labelledby="denom-info-title" aria-modal="true">
<div className="modal-box max-w-4xl mx-auto rounded-[24px] bg-[#F4F4FF] dark:bg-[#1D192D] shadow-lg">
Expand All @@ -28,12 +32,18 @@ export const DenomInfoModal: React.FC<{
<InfoItem
label="Name"
value={denom?.name ?? 'No name available'}
explorerUrl={explorerUrl}
isAddress={nameIsAddress}
/>
<InfoItem label="Ticker" value={denom?.display?.toUpperCase() ?? 'No ticker available'} />
<InfoItem
label="Ticker"
value={denom?.display?.toUpperCase() ?? 'No ticker available'}
explorerUrl={explorerUrl}
/>
<InfoItem
label="Description"
value={denom?.description ?? 'No description available'}
explorerUrl={explorerUrl}
className="col-span-2 row-span-2"
/>
</div>
Expand All @@ -55,9 +65,14 @@ export const DenomInfoModal: React.FC<{
})()
: ''
}
explorerUrl={explorerUrl}
isAddress={true}
/>
<InfoItem label="DISPLAY" value={denom?.display ?? 'No display available'} />
<InfoItem
label="DISPLAY"
value={denom?.display ?? 'No display available'}
explorerUrl={explorerUrl}
/>
</div>
</div>
<form method="dialog" className="modal-backdrop">
Expand All @@ -70,11 +85,13 @@ export const DenomInfoModal: React.FC<{
function InfoItem({
label,
value,
explorerUrl,
isAddress = false,
className = '',
}: {
label: string;
value: string;
explorerUrl: string;
isAddress?: boolean;
className?: string;
}) {
Expand All @@ -86,7 +103,7 @@ function InfoItem({
<div className="flex items-center">
<TruncatedAddressWithCopy address={value} slice={17} />
<a
href={`${process.env.NEXT_PUBLIC_TESTNET_EXPLORER_URL}/account/${value}`}
href={`${explorerUrl}/account/${value}`}
target="_blank"
aria-label={`View ${value} on block explorer (opens in new tab)`}
rel="noopener noreferrer"
Expand Down
118 changes: 114 additions & 4 deletions components/react/endpointSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,57 @@ const validateAPIEndpoint = async (url: string) => {
}
};

const validateIndexerEndpoint = async (url: string) => {
console.log('Validating Indexer endpoint:', url);
if (!url) return false;
try {
const endpoint = url.startsWith('http') ? url : `https://${url}`;
const baseUrl = endpoint.endsWith('/') ? endpoint.slice(0, -1) : endpoint;
const indexerUrl = `${baseUrl}`;

console.log('Making Indexer request to:', indexerUrl);

const response = await fetch(indexerUrl);
console.log('Indexer Response status:', response.status);

if (!response.ok) {
console.log('Indexer Response not ok');
return false;
}

return response.ok;
} catch (error) {
console.error('Indexer Validation error:', error);
return false;
}
};

const validateExplorerEndpoint = async (url: string) => {
console.log('Validating Explorer endpoint:', url);
if (!url) return false;
try {
// const endpoint = url.startsWith('http') ? url : `https://${url}`;
// const baseUrl = endpoint.endsWith('/') ? endpoint.slice(0, -1) : endpoint;
// const explorerUrl = `${baseUrl}`;

// console.log('Making Explorer request to:', explorerUrl);
//
// const response = await fetch(explorerUrl);
// console.log('Explorer Response status:', response.status);
//
// if (!response.ok) {
// console.log('Explorer Response not ok');
// return false;
// }
//
// return response.ok;
return true;
} catch (error) {
console.error('Explorer Validation error:', error);
return false;
}
};

const EndpointSchema = Yup.object().shape({
rpc: Yup.string().required('RPC endpoint is required').test({
name: 'rpc-validation',
Expand All @@ -89,6 +140,16 @@ const EndpointSchema = Yup.object().shape({
message: 'API endpoint is not responding',
test: validateAPIEndpoint,
}),
indexer: Yup.string().required('Indexer endpoint is required').test({
name: 'indexer-validation',
message: 'Indexer endpoint is not responding',
test: validateIndexerEndpoint,
}),
explorer: Yup.string().required('Explorer endpoint is required').test({
name: 'explorer-validation',
message: 'Explorer endpoint is not responding',
test: validateExplorerEndpoint,
}),
});

function SSREndpointSelector() {
Expand Down Expand Up @@ -119,9 +180,20 @@ function SSREndpointSelector() {
enabled: true,
});

const handleCustomEndpointSubmit = async (values: { rpc: string; api: string }) => {
const handleCustomEndpointSubmit = async (values: {
rpc: string;
api: string;
indexer: string;
explorer: string;
}) => {
const rpcUrl = values.rpc.startsWith('http') ? values.rpc : `https://${values.rpc}`;
const apiUrl = values.api.startsWith('http') ? values.api : `https://${values.api}`;
const indexerUrl = values.indexer.startsWith('http')
? values.indexer
: `https://${values.indexer}`;
const explorerUrl = values.explorer.startsWith('http')
? values.explorer
: `https://${values.explorer}`;

try {
const [isRPCValid, isAPIValid] = await Promise.all([
Expand All @@ -133,7 +205,7 @@ function SSREndpointSelector() {
throw new Error('Endpoint validation failed');
}

await addEndpoint(rpcUrl, apiUrl);
await addEndpoint(rpcUrl, apiUrl, indexerUrl, explorerUrl);
setToastMessage({
type: 'alert-success',
title: 'Custom endpoint added',
Expand All @@ -146,7 +218,7 @@ function SSREndpointSelector() {

if (error instanceof Error) {
if (error.message.includes('Invalid URL')) {
errorMessage = 'Invalid URL format. Please check both RPC and API URLs.';
errorMessage = 'Invalid URL format. Please check all URLs.';
} else if (error.message.includes('Network error')) {
errorMessage = 'Network error. Please check your internet connection and try again.';
} else if (error.message.includes('Timeout')) {
Expand Down Expand Up @@ -305,7 +377,7 @@ function SSREndpointSelector() {

<div className="dark:bg-[#FFFFFF0F] bg-[#FFFFFFCC] p-6 rounded-2xl">
<Formik
initialValues={{ rpc: '', api: '' }}
initialValues={{ rpc: '', api: '', indexer: '', explorer: '' }}
validationSchema={EndpointSchema}
onSubmit={async (values, { setSubmitting, resetForm }) => {
try {
Expand Down Expand Up @@ -359,6 +431,44 @@ function SSREndpointSelector() {
</div>
</div>

<div className="form-control">
<label className="text-sm font-medium mb-2 dark:text-[#FFFFFF99] text-[#000000CC]">
Indexer Endpoint
</label>
<div className="relative">
<TextInput
name="indexer"
placeholder="Enter indexer URL"
className="w-full px-4 py-3 bg-transparent
dark:bg-[#FFFFFF0F] bg-[#FFFFFFCC]
border dark:border-[#FFFFFF20] border-[#00000020]
dark:focus:border-[#FFFFFF40] focus:border-[#00000040]
rounded-xl transition-all duration-200
dark:text-[#FFFFFF99] text-[#000000CC]
placeholder:dark:text-[#FFFFFF60] placeholder:text-[#00000060]"
/>
</div>
</div>

<div className="form-control">
<label className="text-sm font-medium mb-2 dark:text-[#FFFFFF99] text-[#000000CC]">
Explorer Endpoint
</label>
<div className="relative">
<TextInput
name="explorer"
placeholder="Enter explorer URL"
className="w-full px-4 py-3 bg-transparent
dark:bg-[#FFFFFF0F] bg-[#FFFFFFCC]
border dark:border-[#FFFFFF20] border-[#00000020]
dark:focus:border-[#FFFFFF40] focus:border-[#00000040]
rounded-xl transition-all duration-200
dark:text-[#FFFFFF99] text-[#000000CC]
placeholder:dark:text-[#FFFFFF60] placeholder:text-[#00000060]"
/>
</div>
</div>

<div className="modal-action flex gap-3 mt-6">
<button
type="button"
Expand Down
3 changes: 2 additions & 1 deletion hooks/useQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -876,12 +876,13 @@ const transformTransactions = (tx: any, address: string) => {

// Helper function to transform API response to match the component's expected format
export const useGetFilteredTxAndSuccessfulProposals = (
indexerUrl: string,
address: string,
page: number = 1,
pageSize: number = 10
) => {
const fetchTransactions = async () => {
const baseUrl = `https://testnet-indexer.liftedinit.tech/rpc/get_address_filtered_transactions_and_successful_proposals?address=${address}`;
const baseUrl = `${indexerUrl}/rpc/get_address_filtered_transactions_and_successful_proposals?address=${address}`;

// Add pagination parameters
const offset = (page - 1) * pageSize;
Expand Down
8 changes: 6 additions & 2 deletions pages/bank.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import React, { useMemo } from 'react';
import { HistoryBox } from '@/components';
import { BankIcon } from '@/components/icons';
import { CombinedBalanceInfo } from '@/utils/types';
import { MFX_TOKEN_DATA } from '@/utils/constants'; // Import MFX_TOKEN_DATA
import { MFX_TOKEN_DATA } from '@/utils/constants';
import { useEndpointStore } from '@/store/endpointStore'; // Import MFX_TOKEN_DATA

export default function Bank() {
const { address, isWalletConnected } = useChain(chainName);
Expand All @@ -27,6 +28,9 @@ export default function Bank() {
refetchBalances: resolveRefetch,
} = useTokenBalancesResolved(address ?? '');

const { selectedEndpoint } = useEndpointStore();
const indexerUrl = selectedEndpoint?.indexer || '';

const { metadatas, isMetadatasLoading } = useTokenFactoryDenomsMetadata();

const combinedBalances = useMemo(() => {
Expand Down Expand Up @@ -69,7 +73,7 @@ export default function Bank() {

const isLoading = isBalancesLoading || resolvedLoading || isMetadatasLoading;

const { sendTxs, refetch } = useGetFilteredTxAndSuccessfulProposals(address ?? '');
const { sendTxs, refetch } = useGetFilteredTxAndSuccessfulProposals(indexerUrl, address ?? '');

return (
<>
Expand Down
Loading

0 comments on commit b0099f4

Please sign in to comment.