Skip to content

Commit

Permalink
feat(network): add new network config builder (#316)
Browse files Browse the repository at this point in the history
* feat(network): add network config builder for custom configurations

Introduce `NetworkConfigBuilder` to enable customizable network configurations
and support for custom networks. This change provides a more flexible way to
configure network settings, including CAIP-2 chain IDs, and allows applications to
define their own networks beyond the default mainnet/testnet options.

- Add `NetworkConfigBuilder` for creating and customizing network configurations
- Update all wallet implementations to use new network config system
- Add support for CAIP-2 chain IDs in network configurations
- Add `isTestnet` flag to network configurations
- Update all tests to use string literals for network names
- Add `network` property to wallet provider constructor calls
- Update `WalletManager` tests to use new network configuration approach
- Add support for custom networks via `NetworkConfigBuilder`
- Update network-related test assertions
- Add proper genesis hash/ID handling from network configs
- Maintain `NetworkId` enum for defaultNetwork configuration
- Rename config property `network` to `defaultNetwork`
- Rename config property `algod` to `networks`

BREAKING CHANGE: Network configuration now requires using
`NetworkConfigBuilder` instead of the `NetworkId`-keyed mapped object from v3.
While `NetworkId` is still exported for use with the `defaultNetwork` property,
network configurations must be created using the new builder pattern.

* refactor(store): rename defaultState constant to DEFAULT_STATE

* fix(network): update framework adapters for new network config structure

Update framework adapters to handle the new nested algod configuration
structure and `defaultNetwork` property name change. This includes:

- Fix destructuring of algod config in React, Vue, and Solid adapters
- Update all example apps to use `defaultNetwork` instead of network
- Update test files to include networks in wallet constructor params
- Export `DEFAULT_NETWORKS` from core package
- Fix `NetworkId` type in `setActiveNetwork` method signatures

BREAKING CHANGE: The `network` property in `WalletManagerConfig`
has been renamed to `defaultNetwork` to better reflect its purpose.

* refactor(network): improve network config type safety and validation

Enhance the `NetworkConfigBuilder` and network config validation:
- Update builder methods to accept full network config instead of just algod config
- Add `DefaultNetworkConfig` type for partial network configs
- Add robust token validation for all possible algosdk token types
- Improve type guard to validate all network config properties
- Allow full network config customization for localnet

* fix(network): update test cases to reflect new network config structure

* fix(solid): revert to inline default state in wallet tests

Replace imported `DEFAULT_STATE` with inline state definition to prevent test
state pollution. This reverts a change made in 2bb91e4.

- Remove `DEFAULT_STATE` import
- Define default state directly in `beforeEach` block
- Ensure clean store initialization between tests
- Fix regression in 'updates wallets when store state changes' test

* refactor(adapters): change setActiveNetwork to accept string network ID

- Replace `NetworkId` type with string for `networkId` parameter
- Add network existence validation in `setActiveNetwork`
- Add tests for invalid network and custom network scenarios

* chore(test): update test scripts to use vitest run

- Change test script from "vitest" to "vitest run" in all packages
- Ensures tests run once and exit instead of watching by default

* refactor(wallets): move networkConfig getter to BaseWallet class

- Move `networkConfig` getter from `WalletConnect` to `BaseWallet`
- Make networks accessible for testing in all wallet implementations

* fix(test): use DEFAULT_NETWORKS in CustomWallet test mocks

- Replace empty networks object with `DEFAULT_NETWORKS` in Custom wallet tests
- Ensure consistent network configuration across all wallet tests

* style(imports): reorder imports for consistency

- Move DEFAULT_NETWORKS import to top of `LuteWallet` test file
- Group related imports together
- Sort type imports alphabetically
- Follow project import ordering conventions
  • Loading branch information
drichar authored Nov 25, 2024
1 parent b14d1de commit 9fc4365
Show file tree
Hide file tree
Showing 51 changed files with 899 additions and 448 deletions.
2 changes: 1 addition & 1 deletion examples/nextjs/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const walletManager = new WalletManager({
options: { apiKey: 'pk_live_D17FD8D89621B5F3' }
}
],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})

export function Providers({ children }: { children: React.ReactNode }) {
Expand Down
2 changes: 1 addition & 1 deletion examples/nuxt/plugins/walletManager.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ export default defineNuxtPlugin((nuxtApp) => {
options: { apiKey: 'pk_live_D17FD8D89621B5F3' }
}
],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})
})
2 changes: 1 addition & 1 deletion examples/react-ts/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const walletManager = new WalletManager({
options: { apiKey: 'pk_live_D17FD8D89621B5F3' }
}
],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})

function App() {
Expand Down
2 changes: 1 addition & 1 deletion examples/solid-ts/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const walletManager = new WalletManager({
options: { apiKey: 'pk_live_D17FD8D89621B5F3' }
}
],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})

function App() {
Expand Down
2 changes: 1 addition & 1 deletion examples/vanilla-ts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const walletManager = new WalletManager({
options: { apiKey: 'pk_live_D17FD8D89621B5F3' }
}
],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})

const appDiv = document.querySelector<HTMLDivElement>('#app')
Expand Down
2 changes: 1 addition & 1 deletion examples/vue-ts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ app.use(WalletManagerPlugin, {
options: { apiKey: 'pk_live_D17FD8D89621B5F3' }
}
],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})

app.mount('#app')
2 changes: 1 addition & 1 deletion packages/use-wallet-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"scripts": {
"build": "tsup",
"start": "tsup src/index.tsx --watch",
"test": "vitest",
"test": "vitest run",
"test:watch": "vitest --watch",
"lint": "eslint -c \"../../.eslintrc.json\" \"**/*.{js,ts}\"",
"typecheck": "tsc --noEmit"
Expand Down
69 changes: 52 additions & 17 deletions packages/use-wallet-react/src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
NetworkId,
WalletManager,
WalletId,
defaultState,
DEFAULT_NETWORKS,
DEFAULT_STATE,
type State,
type WalletAccount
} from '@txnlab/use-wallet'
Expand Down Expand Up @@ -52,14 +53,15 @@ vi.mock('@txnlab/use-wallet', async (importOriginal) => {
}
})

const mockStore = new Store<State>(defaultState)
const mockStore = new Store<State>(DEFAULT_STATE)

const mockDeflyWallet = new DeflyWallet({
id: WalletId.DEFLY,
metadata: { name: 'Defly', icon: 'icon' },
getAlgodClient: () => ({}) as any,
store: mockStore,
subscribe: vi.fn()
subscribe: vi.fn(),
networks: DEFAULT_NETWORKS
})

const mockMagicAuth = new MagicAuth({
Expand All @@ -70,7 +72,8 @@ const mockMagicAuth = new MagicAuth({
metadata: { name: 'Magic', icon: 'icon' },
getAlgodClient: () => ({}) as any,
store: mockStore,
subscribe: vi.fn()
subscribe: vi.fn(),
networks: DEFAULT_NETWORKS
})

describe('WalletProvider', () => {
Expand Down Expand Up @@ -148,7 +151,7 @@ describe('WalletProvider', () => {

const walletManager = new WalletManager({
wallets: [WalletId.DEFLY],
network: NetworkId.TESTNET
defaultNetwork: NetworkId.TESTNET
})

const TestComponent = () => {
Expand All @@ -167,7 +170,8 @@ describe('WalletProvider', () => {
})

expect(walletManager.store.state.activeNetwork).toBe(newNetwork)
const { token, baseServer, port, headers } = walletManager.networkConfig[newNetwork]
const { algod } = walletManager.networkConfig[newNetwork]
const { token, baseServer, port, headers } = algod
expect(walletManager.algodClient).toEqual(new algosdk.Algodv2(token, baseServer, port, headers))
})
})
Expand All @@ -180,7 +184,7 @@ describe('useWallet', () => {
beforeEach(() => {
vi.clearAllMocks()

mockStore.setState(() => defaultState)
mockStore.setState(() => DEFAULT_STATE)

mockWalletManager = new WalletManager()
mockWallets = [
Expand Down Expand Up @@ -460,18 +464,49 @@ describe('useWallet', () => {
expect(result.current.algodClient).toBe(newAlgodClient)
})

it('calls setActiveNetwork correctly and updates algodClient', async () => {
const newNetwork = NetworkId.MAINNET
const { result } = renderHook(() => useWallet(), { wrapper })
describe('setActiveNetwork', () => {
it('calls setActiveNetwork correctly and updates algodClient', async () => {
const { result } = renderHook(() => useWallet(), { wrapper })
const newNetwork = NetworkId.MAINNET

await act(async () => {
await result.current.setActiveNetwork(newNetwork)
await act(async () => {
await result.current.setActiveNetwork(newNetwork)
})

expect(result.current.activeNetwork).toBe(newNetwork)
const { algod } = mockWalletManager.networkConfig[newNetwork]
const { token, baseServer, port, headers } = algod
expect(result.current.algodClient).toEqual(
new algosdk.Algodv2(token, baseServer, port, headers)
)
})

expect(result.current.activeNetwork).toBe(newNetwork)
const { token, baseServer, port, headers } = mockWalletManager.networkConfig[newNetwork]
expect(result.current.algodClient).toEqual(
new algosdk.Algodv2(token, baseServer, port, headers)
)
it('throws error for invalid network', async () => {
const { result } = renderHook(() => useWallet(), { wrapper })

await expect(result.current.setActiveNetwork('invalid-network')).rejects.toThrow(
'Network "invalid-network" not found in network configuration'
)
})

it('allows setting custom network that exists in config', async () => {
const customNetwork = {
name: 'Custom Network',
algod: {
token: '',
baseServer: 'https://custom.network',
headers: {}
}
}

mockWalletManager.networkConfig['custom-net'] = customNetwork
const { result } = renderHook(() => useWallet(), { wrapper })

await act(async () => {
await result.current.setActiveNetwork('custom-net')
})

expect(result.current.activeNetwork).toBe('custom-net')
})
})
})
11 changes: 8 additions & 3 deletions packages/use-wallet-react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useStore } from '@tanstack/react-store'
import { NetworkId, WalletAccount, WalletManager, WalletMetadata } from '@txnlab/use-wallet'
import { WalletAccount, WalletManager, WalletMetadata } from '@txnlab/use-wallet'
import algosdk from 'algosdk'
import * as React from 'react'

Expand Down Expand Up @@ -37,14 +37,19 @@ export const useWallet = () => {

const activeNetwork = useStore(manager.store, (state) => state.activeNetwork)

const setActiveNetwork = async (networkId: NetworkId): Promise<void> => {
const setActiveNetwork = async (networkId: string): Promise<void> => {
if (networkId === activeNetwork) {
return
}

if (!manager.networkConfig[networkId]) {
throw new Error(`Network "${networkId}" not found in network configuration`)
}

console.info(`[React] Creating Algodv2 client for ${networkId}...`)

const { token = '', baseServer, port = '', headers = {} } = manager.networkConfig[networkId]
const { algod } = manager.networkConfig[networkId]
const { token = '', baseServer, port = '', headers = {} } = algod
const newClient = new algosdk.Algodv2(token, baseServer, port, headers)
setAlgodClient(newClient)

Expand Down
2 changes: 1 addition & 1 deletion packages/use-wallet-solid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"scripts": {
"build": "tsup",
"start": "tsup src/index.tsx --watch",
"test": "vitest",
"test": "vitest run",
"test:watch": "vitest --watch",
"lint": "eslint -c \"../../.eslintrc.json\" \"**/*.{js,ts}\"",
"typecheck": "tsc --noEmit"
Expand Down
78 changes: 74 additions & 4 deletions packages/use-wallet-solid/src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
NetworkId,
WalletManager,
WalletId,
DEFAULT_NETWORKS,
type State,
type WalletAccount
} from '@txnlab/use-wallet'
import { For, Show, createSignal } from 'solid-js'
import { For, Show, createEffect, createSignal } from 'solid-js'
import { Wallet, WalletProvider, useWallet, useWalletManager } from '../index'
import algosdk from 'algosdk'

Expand Down Expand Up @@ -201,15 +202,17 @@ describe('useWallet', () => {
metadata: { name: 'Defly', icon: 'icon' },
getAlgodClient: () => ({}) as any,
store: mockStore,
subscribe: vi.fn()
subscribe: vi.fn(),
networks: DEFAULT_NETWORKS
})

mockMagicAuth = new MagicAuth({
id: WalletId.MAGIC,
metadata: { name: 'Magic', icon: 'icon' },
getAlgodClient: () => ({}) as any,
store: mockStore,
subscribe: vi.fn()
subscribe: vi.fn(),
networks: DEFAULT_NETWORKS
})

mockWalletManager = new WalletManager()
Expand Down Expand Up @@ -400,7 +403,7 @@ describe('useWallet', () => {

const newAlgodClient = new algosdk.Algodv2('', 'https://mainnet-api.4160.nodely.dev/', '')

mockWalletManager.setActiveNetwork = async (networkId: NetworkId) => {
mockWalletManager.setActiveNetwork = async (networkId: string) => {
mockSetAlgodClient(newAlgodClient)
mockWalletManager.store.setState((state) => ({
...state,
Expand Down Expand Up @@ -532,6 +535,73 @@ describe('useWallet', () => {

expect(screen.getByTestId('active-network')).toHaveTextContent(NetworkId.MAINNET)
})

describe('setActiveNetwork', () => {
it('throws error for invalid network', async () => {
let error: Error | undefined

const TestComponent = () => {
const { setActiveNetwork } = useWallet()
return (
<button
onClick={async () => {
try {
await setActiveNetwork('invalid-network')
} catch (e) {
error = e as Error
}
}}
>
Test
</button>
)
}

render(() => (
<WalletProvider manager={mockWalletManager}>
<TestComponent />
</WalletProvider>
))

const button = screen.getByText('Test')
fireEvent.click(button)

await waitFor(() => {
expect(error?.message).toBe('Network "invalid-network" not found in network configuration')
})
})

it('allows setting custom network that exists in config', async () => {
const customNetwork = {
name: 'Custom Network',
algod: {
token: '',
baseServer: 'https://custom.network',
headers: {}
}
}

mockWalletManager.networkConfig['custom-net'] = customNetwork

const TestComponent = () => {
const { setActiveNetwork, activeNetwork } = useWallet()
createEffect(() => {
setActiveNetwork('custom-net')
})
return <div data-testid="active-network">{activeNetwork()}</div>
}

render(() => (
<WalletProvider manager={mockWalletManager}>
<TestComponent />
</WalletProvider>
))

await waitFor(() => {
expect(screen.getByTestId('active-network').textContent).toBe('custom-net')
})
})
})
})

describe('WalletProvider', () => {
Expand Down
12 changes: 8 additions & 4 deletions packages/use-wallet-solid/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useStore } from '@tanstack/solid-store'
import algosdk from 'algosdk'
import { JSX, createContext, createMemo, onMount, useContext } from 'solid-js'
import type {
NetworkId,
WalletAccount,
WalletId,
WalletManager,
Expand Down Expand Up @@ -84,14 +83,19 @@ export function useWallet() {

const activeNetwork = useStore(manager().store, (state) => state.activeNetwork)

const setActiveNetwork = async (networkId: NetworkId): Promise<void> => {
if (activeNetwork() === networkId) {
const setActiveNetwork = async (networkId: string): Promise<void> => {
if (networkId === activeNetwork()) {
return
}

if (!manager().networkConfig[networkId]) {
throw new Error(`Network "${networkId}" not found in network configuration`)
}

console.info(`[Solid] Creating Algodv2 client for ${networkId}...`)

const { token, baseServer, port, headers } = manager().networkConfig[networkId]
const { algod } = manager().networkConfig[networkId]
const { token, baseServer, port, headers } = algod
const newClient = new algosdk.Algodv2(token, baseServer, port, headers)

manager().store.setState((state) => ({
Expand Down
2 changes: 1 addition & 1 deletion packages/use-wallet-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"scripts": {
"build": "tsup",
"start": "tsup src/index.ts --watch",
"test": "vitest",
"test": "vitest run",
"test:watch": "vitest --watch",
"lint": "eslint -c \"../../.eslintrc.json\" \"**/*.{js,ts}\"",
"typecheck": "tsc --noEmit"
Expand Down
Loading

0 comments on commit 9fc4365

Please sign in to comment.