This commit is contained in:
SaraJane
2025-12-30 13:07:28 +00:00
commit 93e3f09667
95 changed files with 20233 additions and 0 deletions

View File

@ -0,0 +1,56 @@
import { SupportedWallet, WalletId, WalletManager, WalletProvider } from '@txnlab/use-wallet-react'
import { SnackbarProvider } from 'notistack'
import Home from './Home'
import { getAlgodConfigFromViteEnvironment, getKmdConfigFromViteEnvironment } from './utils/network/getAlgoClientConfigs'
let supportedWallets: SupportedWallet[]
if (import.meta.env.VITE_ALGOD_NETWORK === 'localnet') {
const kmdConfig = getKmdConfigFromViteEnvironment()
supportedWallets = [
{
id: WalletId.KMD,
options: {
baseServer: kmdConfig.server,
token: String(kmdConfig.token),
port: String(kmdConfig.port),
},
},
]
} else {
supportedWallets = [
{ id: WalletId.DEFLY },
{ id: WalletId.PERA },
{ id: WalletId.EXODUS },
// If you are interested in WalletConnect v2 provider
// refer to https://github.com/TxnLab/use-wallet for detailed integration instructions
]
}
export default function App() {
const algodConfig = getAlgodConfigFromViteEnvironment()
const walletManager = new WalletManager({
wallets: supportedWallets,
defaultNetwork: algodConfig.network,
networks: {
[algodConfig.network]: {
algod: {
baseServer: algodConfig.server,
port: algodConfig.port,
token: String(algodConfig.token),
},
},
},
options: {
resetNetwork: true,
},
})
return (
<SnackbarProvider maxSnack={3}>
<WalletProvider manager={walletManager}>
<Home />
</WalletProvider>
</SnackbarProvider>
)
}

View File

@ -0,0 +1,76 @@
// src/components/Home.tsx
import { useWallet } from '@txnlab/use-wallet-react'
import React, { useState } from 'react'
import ConnectWallet from './components/ConnectWallet'
import Transact from './components/Transact'
import AppCalls from './components/AppCalls'
interface HomeProps {}
const Home: React.FC<HomeProps> = () => {
const [openWalletModal, setOpenWalletModal] = useState<boolean>(false)
const [openDemoModal, setOpenDemoModal] = useState<boolean>(false)
const [appCallsDemoModal, setAppCallsDemoModal] = useState<boolean>(false)
const { activeAddress } = useWallet()
const toggleWalletModal = () => {
setOpenWalletModal(!openWalletModal)
}
const toggleDemoModal = () => {
setOpenDemoModal(!openDemoModal)
}
const toggleAppCallsModal = () => {
setAppCallsDemoModal(!appCallsDemoModal)
}
return (
<div className="hero min-h-screen bg-teal-400">
<div className="hero-content text-center rounded-lg p-6 max-w-md bg-white mx-auto">
<div className="max-w-md">
<h1 className="text-4xl">
Welcome to <div className="font-bold">AlgoKit 🙂</div>
</h1>
<p className="py-6">
This starter has been generated using official AlgoKit React template. Refer to the resource below for next steps.
</p>
<div className="grid">
<a
data-test-id="getting-started"
className="btn btn-primary m-2"
target="_blank"
href="https://github.com/algorandfoundation/algokit-cli"
>
Getting started
</a>
<div className="divider" />
<button data-test-id="connect-wallet" className="btn m-2" onClick={toggleWalletModal}>
Wallet Connection
</button>
{activeAddress && (
<button data-test-id="transactions-demo" className="btn m-2" onClick={toggleDemoModal}>
Transactions Demo
</button>
)}
{activeAddress && (
<button data-test-id="appcalls-demo" className="btn m-2" onClick={toggleAppCallsModal}>
Contract Interactions Demo
</button>
)}
</div>
<ConnectWallet openModal={openWalletModal} closeModal={toggleWalletModal} />
<Transact openModal={openDemoModal} setModalState={setOpenDemoModal} />
<AppCalls openModal={appCallsDemoModal} setModalState={setAppCallsDemoModal} />
</div>
</div>
</div>
)
}
export default Home

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,24 @@
import { useWallet } from '@txnlab/use-wallet-react'
import { useMemo } from 'react'
import { ellipseAddress } from '../utils/ellipseAddress'
import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
const Account = () => {
const { activeAddress } = useWallet()
const algoConfig = getAlgodConfigFromViteEnvironment()
const networkName = useMemo(() => {
return algoConfig.network === '' ? 'localnet' : algoConfig.network.toLocaleLowerCase()
}, [algoConfig.network])
return (
<div>
<a className="text-xl" target="_blank" href={`https://lora.algokit.io/${networkName}/account/${activeAddress}/`}>
Address: {ellipseAddress(activeAddress)}
</a>
<div className="text-xl">Network: {networkName}</div>
</div>
)
}
export default Account

View File

@ -0,0 +1,98 @@
import { useWallet } from '@txnlab/use-wallet-react'
import { useSnackbar } from 'notistack'
import { useState } from 'react'
import { HelloWorldFactory } from '../contracts/HelloWorld'
import { OnSchemaBreak, OnUpdate } from '@algorandfoundation/algokit-utils/types/app'
import { getAlgodConfigFromViteEnvironment, getIndexerConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
import { AlgorandClient } from '@algorandfoundation/algokit-utils'
interface AppCallsInterface {
openModal: boolean
setModalState: (value: boolean) => void
}
const AppCalls = ({ openModal, setModalState }: AppCallsInterface) => {
const [loading, setLoading] = useState<boolean>(false)
const [contractInput, setContractInput] = useState<string>('')
const { enqueueSnackbar } = useSnackbar()
const { transactionSigner, activeAddress } = useWallet()
const algodConfig = getAlgodConfigFromViteEnvironment()
const indexerConfig = getIndexerConfigFromViteEnvironment()
const algorand = AlgorandClient.fromConfig({
algodConfig,
indexerConfig,
})
algorand.setDefaultSigner(transactionSigner)
const sendAppCall = async () => {
setLoading(true)
// Please note, in typical production scenarios,
// you wouldn't want to use deploy directly from your frontend.
// Instead, you would deploy your contract on your backend and reference it by id.
// Given the simplicity of the starter contract, we are deploying it on the frontend
// for demonstration purposes.
const factory = new HelloWorldFactory({
defaultSender: activeAddress ?? undefined,
algorand,
})
const deployResult = await factory
.deploy({
onSchemaBreak: OnSchemaBreak.AppendApp,
onUpdate: OnUpdate.AppendApp,
})
.catch((e: Error) => {
enqueueSnackbar(`Error deploying the contract: ${e.message}`, { variant: 'error' })
setLoading(false)
return undefined
})
if (!deployResult) {
return
}
const { appClient } = deployResult
const response = await appClient.send.hello({ args: { name: contractInput } }).catch((e: Error) => {
enqueueSnackbar(`Error calling the contract: ${e.message}`, { variant: 'error' })
setLoading(false)
return undefined
})
if (!response) {
return
}
enqueueSnackbar(`Response from the contract: ${response.return}`, { variant: 'success' })
setLoading(false)
}
return (
<dialog id="appcalls_modal" className={`modal ${openModal ? 'modal-open' : ''} bg-slate-200`}>
<form method="dialog" className="modal-box">
<h3 className="font-bold text-lg">Say hello to your Algorand smart contract</h3>
<br />
<input
type="text"
placeholder="Provide input to hello function"
className="input input-bordered w-full"
value={contractInput}
onChange={(e) => {
setContractInput(e.target.value)
}}
/>
<div className="modal-action ">
<button className="btn" onClick={() => setModalState(!openModal)}>
Close
</button>
<button className={`btn`} onClick={sendAppCall}>
{loading ? <span className="loading loading-spinner" /> : 'Send application call'}
</button>
</div>
</form>
</dialog>
)
}
export default AppCalls

View File

@ -0,0 +1,86 @@
import { useWallet, Wallet, WalletId } from '@txnlab/use-wallet-react'
import Account from './Account'
interface ConnectWalletInterface {
openModal: boolean
closeModal: () => void
}
const ConnectWallet = ({ openModal, closeModal }: ConnectWalletInterface) => {
const { wallets, activeAddress } = useWallet()
const isKmd = (wallet: Wallet) => wallet.id === WalletId.KMD
return (
<dialog id="connect_wallet_modal" className={`modal ${openModal ? 'modal-open' : ''}`}>
<form method="dialog" className="modal-box">
<h3 className="font-bold text-2xl">Select wallet provider</h3>
<div className="grid m-2 pt-5">
{activeAddress && (
<>
<Account />
<div className="divider" />
</>
)}
{!activeAddress &&
wallets?.map((wallet) => (
<button
data-test-id={`${wallet.id}-connect`}
className="btn border-teal-800 border-1 m-2"
key={`provider-${wallet.id}`}
onClick={() => {
return wallet.connect()
}}
>
{!isKmd(wallet) && (
<img
alt={`wallet_icon_${wallet.id}`}
src={wallet.metadata.icon}
style={{ objectFit: 'contain', width: '30px', height: 'auto' }}
/>
)}
<span>{isKmd(wallet) ? 'LocalNet Wallet' : wallet.metadata.name}</span>
</button>
))}
</div>
<div className="modal-action ">
<button
data-test-id="close-wallet-modal"
className="btn"
onClick={() => {
closeModal()
}}
>
Close
</button>
{activeAddress && (
<button
className="btn btn-warning"
data-test-id="logout"
onClick={async () => {
if (wallets) {
const activeWallet = wallets.find((w) => w.isActive)
if (activeWallet) {
await activeWallet.disconnect()
} else {
// Required for logout/cleanup of inactive providers
// For instance, when you login to localnet wallet and switch network
// to testnet/mainnet or vice verse.
localStorage.removeItem('@txnlab/use-wallet:v3')
window.location.reload()
}
}
}}
>
Logout
</button>
)}
</div>
</form>
</dialog>
)
}
export default ConnectWallet

View File

@ -0,0 +1,46 @@
import React, { ReactNode } from 'react'
interface ErrorBoundaryProps {
children: ReactNode
}
interface ErrorBoundaryState {
hasError: boolean
error: Error | null
}
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error }
}
render(): ReactNode {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<div className="hero min-h-screen bg-teal-400">
<div className="hero-content text-center rounded-lg p-6 max-w-md bg-white mx-auto">
<div className="max-w-md">
<h1 className="text-4xl">Error occured</h1>
<p className="py-6">
{this.state.error?.message.includes('Attempt to get default algod configuration')
? 'Please make sure to set up your environment variables correctly. Create a .env file based on .env.template and fill in the required values. This controls the network and credentials for connections with Algod and Indexer.'
: this.state.error?.message}
</p>
</div>
</div>
</div>
)
}
return this.props.children
}
}
export default ErrorBoundary

View File

@ -0,0 +1,80 @@
import { algo, AlgorandClient } from '@algorandfoundation/algokit-utils'
import { useWallet } from '@txnlab/use-wallet-react'
import { useSnackbar } from 'notistack'
import { useState } from 'react'
import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
interface TransactInterface {
openModal: boolean
setModalState: (value: boolean) => void
}
const Transact = ({ openModal, setModalState }: TransactInterface) => {
const [loading, setLoading] = useState<boolean>(false)
const [receiverAddress, setReceiverAddress] = useState<string>('')
const algodConfig = getAlgodConfigFromViteEnvironment()
const algorand = AlgorandClient.fromConfig({ algodConfig })
const { enqueueSnackbar } = useSnackbar()
const { transactionSigner, activeAddress } = useWallet()
const handleSubmitAlgo = async () => {
setLoading(true)
if (!transactionSigner || !activeAddress) {
enqueueSnackbar('Please connect wallet first', { variant: 'warning' })
return
}
try {
enqueueSnackbar('Sending transaction...', { variant: 'info' })
const result = await algorand.send.payment({
signer: transactionSigner,
sender: activeAddress,
receiver: receiverAddress,
amount: algo(1),
})
enqueueSnackbar(`Transaction sent: ${result.txIds[0]}`, { variant: 'success' })
setReceiverAddress('')
} catch (e) {
enqueueSnackbar('Failed to send transaction', { variant: 'error' })
}
setLoading(false)
}
return (
<dialog id="transact_modal" className={`modal ${openModal ? 'modal-open' : ''} bg-slate-200`}>
<form method="dialog" className="modal-box">
<h3 className="font-bold text-lg">Send payment transaction</h3>
<br />
<input
type="text"
data-test-id="receiver-address"
placeholder="Provide wallet address"
className="input input-bordered w-full"
value={receiverAddress}
onChange={(e) => {
setReceiverAddress(e.target.value)
}}
/>
<div className="modal-action ">
<button className="btn" onClick={() => setModalState(!openModal)}>
Close
</button>
<button
data-test-id="send-algo"
className={`btn ${receiverAddress.length === 58 ? '' : 'btn-disabled'} lo`}
onClick={handleSubmitAlgo}
>
{loading ? <span className="loading loading-spinner" /> : 'Send 1 Algo'}
</button>
</div>
</form>
</dialog>
)
}
export default Transact

View File

@ -0,0 +1,14 @@
## How to connect my web app with Algorand smart contracts?
The following folder is reserved for the Algorand Application Clients. The clients are used to interact with instances of Algorand Smart Contracts (ASC1s) deployed on-chain.
To integrate this react frontend template with your smart contracts codebase, perform the following steps:
1. Generate the typed client using `algokit generate client -l typescript -o {path/to/this/folder}` or using the dedicated `link` command `algokit project link` (ensure to invoke it from the root of this react project). Using the `link` command is especially useful within workspaces that have multiple contract projects.
2. The generated typescript client should be ready to be imported and used in this react frontend template, making it a full fledged dApp.
> Please note, by default this template defines `"generate:app-clients": "algokit project link --all"` which is a shortcut to automatically link TEAL code from all `contract` projects in the workspace as typed clients into the `frontend` project that is invoking the `link` command. Refer to [documentation](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/features/project/link.md) to read more about `link` command.
## **How to interact with the smart contract?**
The generated client provides a set of functions that can be used to interact with the ABI (Application Binary Interface) compliant Algorand smart contract. For example, if the smart contract has a function called `hello`, the generated client will have a function called `hello` that can be used to interact with the smart contract. Refer to a [full-stack end-to-end starter template](https://github.com/algorandfoundation/algokit-fullstack-template) for a reference example on invoking and interacting with typescript typed clients generated.

View File

@ -0,0 +1,26 @@
import { AlgoClientConfig } from '@algorandfoundation/algokit-utils/types/network-client'
import type { TokenHeader } from 'algosdk/dist/types/client/urlTokenBaseHTTPClient'
export interface AlgoViteClientConfig extends AlgoClientConfig {
/** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */
server: string
/** The port to use e.g. 4001, 443, etc. */
port: string | number
/** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */
token: string | TokenHeader
/** String representing current Algorand Network type (testnet/mainnet and etc) */
network: string
}
export interface AlgoViteKMDConfig extends AlgoClientConfig {
/** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */
server: string
/** The port to use e.g. 4001, 443, etc. */
port: string | number
/** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */
token: string | TokenHeader
/** KMD wallet name */
wallet: string
/** KMD wallet password */
password: string
}

View File

@ -0,0 +1,13 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './styles/main.css'
import ErrorBoundary from './components/ErrorBoundary'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</React.StrictMode>,
)

View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -0,0 +1,15 @@
import { ellipseAddress } from './ellipseAddress'
describe('ellipseAddress', () => {
it('should return ellipsed address with specified width', () => {
const address = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
const result = ellipseAddress(address, 4)
expect(result).toBe('aaaa...aaaa')
})
it('should return empty string when address is empty', () => {
const address = ''
const result = ellipseAddress(address)
expect(result).toBe('')
})
})

View File

@ -0,0 +1,3 @@
export function ellipseAddress(address: string | null, width = 6): string {
return address ? `${address.slice(0, width)}...${address.slice(-width)}` : (address ?? '')
}

View File

@ -0,0 +1,41 @@
import { AlgoViteClientConfig, AlgoViteKMDConfig } from '../../interfaces/network'
export function getAlgodConfigFromViteEnvironment(): AlgoViteClientConfig {
if (!import.meta.env.VITE_ALGOD_SERVER) {
throw new Error('Attempt to get default algod configuration without specifying VITE_ALGOD_SERVER in the environment variables')
}
return {
server: import.meta.env.VITE_ALGOD_SERVER,
port: import.meta.env.VITE_ALGOD_PORT,
token: import.meta.env.VITE_ALGOD_TOKEN,
network: import.meta.env.VITE_ALGOD_NETWORK,
}
}
export function getIndexerConfigFromViteEnvironment(): AlgoViteClientConfig {
if (!import.meta.env.VITE_INDEXER_SERVER) {
throw new Error('Attempt to get default algod configuration without specifying VITE_INDEXER_SERVER in the environment variables')
}
return {
server: import.meta.env.VITE_INDEXER_SERVER,
port: import.meta.env.VITE_INDEXER_PORT,
token: import.meta.env.VITE_INDEXER_TOKEN,
network: import.meta.env.VITE_ALGOD_NETWORK,
}
}
export function getKmdConfigFromViteEnvironment(): AlgoViteKMDConfig {
if (!import.meta.env.VITE_KMD_SERVER) {
throw new Error('Attempt to get default kmd configuration without specifying VITE_KMD_SERVER in the environment variables')
}
return {
server: import.meta.env.VITE_KMD_SERVER,
port: import.meta.env.VITE_KMD_PORT,
token: import.meta.env.VITE_KMD_TOKEN,
wallet: import.meta.env.VITE_KMD_WALLET,
password: import.meta.env.VITE_KMD_PASSWORD,
}
}

View File

@ -0,0 +1,24 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_ENVIRONMENT: string
readonly VITE_ALGOD_TOKEN: string
readonly VITE_ALGOD_SERVER: string
readonly VITE_ALGOD_PORT: string
readonly VITE_ALGOD_NETWORK: string
readonly VITE_INDEXER_TOKEN: string
readonly VITE_INDEXER_SERVER: string
readonly VITE_INDEXER_PORT: string
readonly VITE_KMD_TOKEN: string
readonly VITE_KMD_SERVER: string
readonly VITE_KMD_PORT: string
readonly VITE_KMD_PASSWORD: string
readonly VITE_KMD_WALLET: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}