Skip to content
Open
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
16 changes: 16 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,19 @@ VITE_DISPENSER_AUTH0_CLIENT_ID=pSffrx40HwIdWAJFeGCsy5FZn2NzYIvO
VITE_DISPENSER_AUTH0_AUDIENCE=api-staging-dispenser-user
VITE_TESTNET_DISPENSER_API_URL=https://api.dispenser-dev.algorandfoundation.tools
VITE_TESTNET_DISPENSER_ADDRESS=A3Q4VQFQ5DLEIWCMUX6V7YFZ7BUPWUXWY4YVNJQODUODSQ7KVEFOZUNFUQ

# Custom Network Configuration (optional)
# All CUSTOM_NETWORK_* variables must be set together to enable custom network
# The URLs should include the full URL with protocol and port (e.g., https://example.com:443)
#VITE_CUSTOM_NETWORK_NAME=My Custom Network
#VITE_CUSTOM_NETWORK_ID=customnet
#VITE_CUSTOM_NETWORK_FEE_SINK_ADDRESS=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ
#VITE_CUSTOM_NETWORK_ALGOD_URL=https://algod.example.com:443
#VITE_CUSTOM_NETWORK_ALGOD_TOKEN=your-algod-token
#VITE_CUSTOM_NETWORK_INDEXER_URL=https://indexer.example.com:443
#VITE_CUSTOM_NETWORK_INDEXER_TOKEN=your-indexer-token

# Default Network Selection (optional)
# Set to 'mainnet', 'testnet', 'localnet', 'betanet', 'fnet', or your custom network ID
# If not set, defaults to 'mainnet'
#VITE_DEFAULT_NETWORK_ID=mainnet
8 changes: 8 additions & 0 deletions src/features/accounts/data/funded-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@ import {
MAINNET_FEE_SINK_ADDRESS,
selectedNetworkAtomId,
TESTNET_FEE_SINK_ADDRESS,
CUSTOM_NETWORK_FEE_SINK_ADDRESS,
} from '@/features/network/data'
import { settingsStore } from '@/features/settings/data'
import { atom } from 'jotai'
import { Address } from './types'

export const fundedAccountAtom = atom<Promise<Address | undefined>>(async () => {
const selectedNetworkId = settingsStore.get(selectedNetworkAtomId)
const customNetworkId = import.meta.env.VITE_CUSTOM_NETWORK_ID

// Check custom network first
if (customNetworkId && selectedNetworkId === customNetworkId && CUSTOM_NETWORK_FEE_SINK_ADDRESS) {
return CUSTOM_NETWORK_FEE_SINK_ADDRESS
}

if (selectedNetworkId === mainnetId) {
return MAINNET_FEE_SINK_ADDRESS
}
Expand Down
58 changes: 57 additions & 1 deletion src/features/network/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,63 @@ export const allWalletProviderNames: Record<WalletId, string> = {
biatec: 'Biatec',
}

// URL parsing utility for custom network configuration
function parseServiceUrl(url: string): { server: string; port: number } {
const urlObj = new URL(url)
const port = urlObj.port ? parseInt(urlObj.port, 10) : urlObj.protocol === 'https:' ? 443 : 80

// Construct server with protocol, hostname, and pathname (preserving any path)
let server = `${urlObj.protocol}//${urlObj.hostname}${urlObj.pathname}`

// Ensure trailing slash (matches existing network config format)
if (!server.endsWith('/')) {
server += '/'
}

return { server, port }
}

// Build custom network configuration from environment variables
function buildCustomNetworkConfig(): { id: string; config: NetworkConfig } | null {
const name = import.meta.env.VITE_CUSTOM_NETWORK_NAME
const id = import.meta.env.VITE_CUSTOM_NETWORK_ID
const algodUrl = import.meta.env.VITE_CUSTOM_NETWORK_ALGOD_URL
const algodToken = import.meta.env.VITE_CUSTOM_NETWORK_ALGOD_TOKEN
const indexerUrl = import.meta.env.VITE_CUSTOM_NETWORK_INDEXER_URL
const indexerToken = import.meta.env.VITE_CUSTOM_NETWORK_INDEXER_TOKEN

// All required vars must be present
if (!name || !id || !algodUrl || !indexerUrl) {
return null
}

const algod = parseServiceUrl(algodUrl)
const indexer = parseServiceUrl(indexerUrl)

return {
id,
config: {
name,
algod: {
...algod,
token: algodToken,
},
indexer: {
...indexer,
token: indexerToken,
},
walletIds: [WalletId.LUTE, WalletId.MNEMONIC],
},
}
}

export const MAINNET_FEE_SINK_ADDRESS = 'Y76M3MSY6DKBRHBL7C3NNDXGS5IIMQVQVUAB6MP4XEMMGVF2QWNPL226CA'
export const TESTNET_FEE_SINK_ADDRESS = 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE'
export const BETANET_FEE_SINK_ADDRESS = 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE'
export const FNET_FEE_SINK_ADDRESS = 'FEESINK7OJKODDB5ZB4W2SRYPUSTOTK65UDCUYZ5DB4BW3VOHDHGO6JUNE'
export const CUSTOM_NETWORK_FEE_SINK_ADDRESS = import.meta.env.VITE_CUSTOM_NETWORK_FEE_SINK_ADDRESS

const customNetwork = buildCustomNetworkConfig()

export const defaultNetworkConfigs: Record<NetworkId, NetworkConfig> = {
[localnetId]: {
Expand Down Expand Up @@ -118,6 +171,7 @@ export const defaultNetworkConfigs: Record<NetworkId, NetworkConfig> = {
walletIds: nonLocalnetWalletIds,
nfdApiUrl: 'https://api.nf.domains',
},
...(customNetwork ? { [customNetwork.id]: customNetwork.config } : {}),
}

export const temporaryLocalNetSearchParams = {
Expand Down Expand Up @@ -249,7 +303,9 @@ export const useDeleteCustomNetworkConfig = () => {
)
}

export const storedSelectedNetworkIdAtom = atomWithStorage('network', mainnetId, createAtomStorageWithoutSubscription(), {
const defaultNetworkId = import.meta.env.VITE_DEFAULT_NETWORK_ID || mainnetId

export const storedSelectedNetworkIdAtom = atomWithStorage('network', defaultNetworkId, createAtomStorageWithoutSubscription(), {
getOnInit: true,
})
export const selectedNetworkAtomId = atomWithRefresh((get) => {
Expand Down
11 changes: 11 additions & 0 deletions src/features/transaction-wizard/utils/resolve-sender-address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
networkConfigAtom,
BETANET_FEE_SINK_ADDRESS,
FNET_FEE_SINK_ADDRESS,
CUSTOM_NETWORK_FEE_SINK_ADDRESS,
} from '@/features/network/data'
import { TransactionSender } from '../models'
import { settingsStore } from '@/features/settings/data'
Expand All @@ -19,6 +20,16 @@ export async function resolveTransactionSender(data: { value?: string; resolvedA
}

const { id: networkId } = settingsStore.get(networkConfigAtom)
const customNetworkId = import.meta.env.VITE_CUSTOM_NETWORK_ID

// Check custom network first
if (customNetworkId && networkId === customNetworkId && CUSTOM_NETWORK_FEE_SINK_ADDRESS) {
return {
value: CUSTOM_NETWORK_FEE_SINK_ADDRESS,
resolvedAddress: CUSTOM_NETWORK_FEE_SINK_ADDRESS,
autoPopulated: true,
}
}

if (networkId === mainnetId) {
return { value: MAINNET_FEE_SINK_ADDRESS, resolvedAddress: MAINNET_FEE_SINK_ADDRESS, autoPopulated: true }
Expand Down
8 changes: 8 additions & 0 deletions src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ interface ImportMetaEnv {
readonly VITE_DISPENSER_AUTH0_AUDIENCE: string
readonly VITE_TESTNET_DISPENSER_API_URL: string
readonly VITE_TESTNET_DISPENSER_ADDRESS: string
readonly VITE_CUSTOM_NETWORK_NAME?: string
readonly VITE_CUSTOM_NETWORK_ID?: string
readonly VITE_CUSTOM_NETWORK_FEE_SINK_ADDRESS?: string
readonly VITE_CUSTOM_NETWORK_ALGOD_URL?: string
readonly VITE_CUSTOM_NETWORK_ALGOD_TOKEN?: string
readonly VITE_CUSTOM_NETWORK_INDEXER_URL?: string
readonly VITE_CUSTOM_NETWORK_INDEXER_TOKEN?: string
readonly VITE_DEFAULT_NETWORK_ID?: string
}

interface ImportMeta {
Expand Down
Loading