@reown/appkit-controllers
Version:
#### 🔗 [Website](https://reown.com/appkit)
313 lines • 14.7 kB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ConstantsUtil, SafeLocalStorageKeys } from '@reown/appkit-common';
import { SafeLocalStorage } from '@reown/appkit-common';
import { AccountController, CoreHelperUtil, ModalController } from '../../exports/index.js';
import { ChainController } from '../../src/controllers/ChainController.js';
import {} from '../../src/controllers/ConnectionController.js';
import { getActiveNetworkTokenAddress } from '../../src/utils/ChainControllerUtil.js';
// -- Setup --------------------------------------------------------------------
const chainNamespace = 'eip155';
const caipAddress = 'eip155:1:0x123';
const approvedCaipNetworkIds = ['eip155:1', 'eip155:4'];
const requestedCaipNetworks = [
{
id: 1,
caipNetworkId: 'eip155:1',
name: 'Ethereum',
chainNamespace: ConstantsUtil.CHAIN.EVM,
nativeCurrency: {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18
},
rpcUrls: {
default: { http: ['https://rpc.infura.com/v1/'] }
},
blockExplorers: {
default: { name: 'Etherscan', url: 'https://etherscan.io' }
}
},
{
id: 42161,
caipNetworkId: 'eip155:42161',
name: 'Arbitrum One',
chainNamespace: ConstantsUtil.CHAIN.EVM,
nativeCurrency: {
name: 'Arbitrum',
symbol: 'ARB',
decimals: 18
},
rpcUrls: {
default: { http: ['https://rpc.infura.com/v1/'] }
},
blockExplorers: {
default: { name: 'Arbitrum Explorer', url: 'https://explorer.arbitrum.io' }
}
},
{
id: 43114,
caipNetworkId: 'eip155:43114',
name: 'Avalanche C-Chain',
chainNamespace: ConstantsUtil.CHAIN.EVM,
nativeCurrency: {
name: 'Avalanche',
symbol: 'AVAX',
decimals: 18
},
rpcUrls: {
default: { http: ['https://rpc.infura.com/v1/'] }
},
blockExplorers: {
default: { name: 'Avalanche C-Chain', url: 'https://cchain.explorer.avax.network' }
}
}
];
const mainnetCaipNetwork = {
id: 1,
name: 'Ethereum',
chainNamespace: ConstantsUtil.CHAIN.EVM,
caipNetworkId: 'eip155:1',
nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc.infura.com/v1/'] } },
blockExplorers: { default: { name: 'Etherscan', url: 'https://etherscan.io' } }
};
const solanaCaipNetwork = {
id: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
caipNetworkId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
name: 'Solana',
chainNamespace: ConstantsUtil.CHAIN.SOLANA,
nativeCurrency: { name: 'Solana', symbol: 'SOL', decimals: 9 },
rpcUrls: { default: { http: ['https://rpc.infura.com/v1/'] } },
blockExplorers: { default: { name: 'Solscan', url: 'https://solscan.io' } }
};
const connectionControllerClient = {
connectWalletConnect: async () => Promise.resolve(),
disconnect: async () => Promise.resolve(),
estimateGas: async () => Promise.resolve(BigInt(0)),
signMessage: async (message) => Promise.resolve(message),
parseUnits: value => BigInt(value),
formatUnits: value => value.toString(),
sendTransaction: () => Promise.resolve('0x'),
writeContract: () => Promise.resolve('0x'),
getEnsAddress: async (value) => Promise.resolve(value),
getEnsAvatar: async (value) => Promise.resolve(value),
getCapabilities: async () => Promise.resolve(''),
grantPermissions: async () => Promise.resolve(''),
revokePermissions: async () => Promise.resolve('0x'),
walletGetAssets: async () => Promise.resolve({}),
updateBalance: () => Promise.resolve()
};
const networkControllerClient = {
switchCaipNetwork: async (_caipNetwork) => Promise.resolve(),
getApprovedCaipNetworksData: async () => Promise.resolve({ approvedCaipNetworkIds: [], supportsAllNetworks: false })
};
const evmAdapter = {
namespace: ConstantsUtil.CHAIN.EVM,
connectionControllerClient,
networkControllerClient,
caipNetworks: [mainnetCaipNetwork]
};
const solanaAdapter = {
namespace: ConstantsUtil.CHAIN.SOLANA,
connectionControllerClient,
networkControllerClient,
caipNetworks: [solanaCaipNetwork]
};
// -- Tests --------------------------------------------------------------------
describe('ChainController', () => {
beforeEach(() => {
vi.restoreAllMocks();
vi.spyOn(CoreHelperUtil, 'isMobile').mockReturnValue(false);
ChainController.state.noAdapters = false;
ChainController.initialize([evmAdapter], requestedCaipNetworks, {
connectionControllerClient,
networkControllerClient
});
});
it('should be initialized as expected', () => {
expect(ChainController.state.activeChain).toEqual(ConstantsUtil.CHAIN.EVM);
expect(ChainController.getConnectionControllerClient()).toEqual(connectionControllerClient);
expect(ChainController.getNetworkControllerClient()).toEqual(networkControllerClient);
});
it('should update account state as expected', () => {
ChainController.setAccountProp('caipAddress', caipAddress, chainNamespace);
expect(ChainController.getAccountProp('caipAddress')).toEqual(caipAddress);
});
it('should update network state as expected', () => {
ChainController.setAdapterNetworkState(ConstantsUtil.CHAIN.EVM, {
approvedCaipNetworkIds
});
expect(ChainController.getNetworkProp('approvedCaipNetworkIds', ConstantsUtil.CHAIN.EVM)).toEqual(approvedCaipNetworkIds);
});
it('should update state correctly on setApprovedCaipNetworkIds()', async () => {
const networkController = { ...networkControllerClient };
const networkControllerSpy = vi
.spyOn(networkController, 'getApprovedCaipNetworksData')
.mockResolvedValueOnce({
approvedCaipNetworkIds,
supportsAllNetworks: false
});
const evmAdapter = {
namespace: chainNamespace,
connectionControllerClient,
networkControllerClient: networkController,
caipNetworks: []
};
// Need to re-initialize to set the spy properly
ChainController.initialize([evmAdapter], requestedCaipNetworks, {
connectionControllerClient,
networkControllerClient: networkController
});
await ChainController.setApprovedCaipNetworksData(chainNamespace);
expect(ChainController.getApprovedCaipNetworkIds(chainNamespace)).toEqual(approvedCaipNetworkIds);
expect(networkControllerSpy).toHaveBeenCalled();
});
it('should update state correctly on setCaipNetwork()', () => {
ChainController.setActiveCaipNetwork({
id: 1,
chainNamespace: ConstantsUtil.CHAIN.EVM,
caipNetworkId: 'eip155:1',
name: 'Ethereum',
nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc.infura.com/v1/'] } },
blockExplorers: { default: { name: 'Etherscan', url: 'https://etherscan.io' } }
});
expect(ChainController.state.activeCaipNetwork).toEqual(mainnetCaipNetwork);
});
it('should check correctly if smart accounts are enabled on the network', () => {
ChainController.setActiveCaipNetwork(mainnetCaipNetwork);
ChainController.setSmartAccountEnabledNetworks([1], chainNamespace);
expect(ChainController.checkIfSmartAccountEnabled()).toEqual(true);
ChainController.setSmartAccountEnabledNetworks([], chainNamespace);
expect(ChainController.checkIfSmartAccountEnabled()).toEqual(false);
ChainController.setSmartAccountEnabledNetworks([2], chainNamespace);
expect(ChainController.checkIfSmartAccountEnabled()).toEqual(false);
ChainController.setActiveCaipNetwork({
id: 2,
chainNamespace: ConstantsUtil.CHAIN.EVM,
caipNetworkId: 'eip155:2',
name: 'Ethereum',
nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc.infura.com/v1/'] } },
blockExplorers: { default: { name: 'Etherscan', url: 'https://etherscan.io' } }
});
});
it('should check if network supports names feature', () => {
ChainController.setActiveCaipNetwork(mainnetCaipNetwork);
expect(ChainController.checkIfNamesSupported()).toEqual(true);
ChainController.setActiveCaipNetwork(solanaCaipNetwork);
expect(ChainController.checkIfNamesSupported()).toEqual(false);
});
it('should get correct active network token address', () => {
let mock = vi
.spyOn(ChainController.state, 'activeCaipNetwork', 'get')
.mockReturnValue(undefined);
expect(getActiveNetworkTokenAddress()).toEqual('eip155:1:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee');
mock.mockReturnValue(mainnetCaipNetwork);
expect(getActiveNetworkTokenAddress()).toEqual('eip155:1:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee');
mock.mockReturnValue(solanaCaipNetwork);
expect(getActiveNetworkTokenAddress()).toEqual('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:So11111111111111111111111111111111111111111');
mock.mockClear();
});
it('should update state correctly on setRequestedCaipNetworks()', () => {
ChainController.setRequestedCaipNetworks(requestedCaipNetworks, chainNamespace);
const requestedNetworks = ChainController.getRequestedCaipNetworks(chainNamespace);
expect(requestedNetworks).toEqual(requestedCaipNetworks);
});
it('should reset state correctly on resetNetwork()', () => {
const namespace = 'eip155';
ChainController.resetNetwork(namespace);
const requestedCaipNetworks = ChainController.getRequestedCaipNetworks(namespace);
const approvedCaipNetworkIds = ChainController.getApprovedCaipNetworkIds(namespace);
const smartAccountEnabledNetworks = ChainController.getNetworkProp('smartAccountEnabledNetworks', chainNamespace);
expect(approvedCaipNetworkIds).toEqual([]);
expect(requestedCaipNetworks).toEqual(requestedCaipNetworks);
expect(smartAccountEnabledNetworks).toEqual([]);
});
it('should reset account as expected', () => {
ChainController.resetAccount(chainNamespace);
expect(AccountController.state.smartAccountDeployed).toEqual(false);
expect(AccountController.state.currentTab).toEqual(0);
expect(AccountController.state.caipAddress).toEqual(undefined);
expect(AccountController.state.address).toEqual(undefined);
expect(AccountController.state.balance).toEqual(undefined);
expect(AccountController.state.balanceSymbol).toEqual(undefined);
expect(AccountController.state.profileName).toEqual(undefined);
expect(AccountController.state.profileImage).toEqual(undefined);
expect(AccountController.state.addressExplorerUrl).toEqual(undefined);
expect(AccountController.state.tokenBalance).toEqual([]);
expect(AccountController.state.connectedWalletInfo).toEqual(undefined);
expect(AccountController.state.preferredAccountTypes).toEqual(undefined);
expect(AccountController.state.status).toEqual('disconnected');
expect(AccountController.state.socialProvider).toEqual(undefined);
expect(AccountController.state.socialWindow).toEqual(undefined);
});
it('Expect modal to close after switching from unsupported network to supported network', async () => {
// Mock ModalController.close
const modalCloseSpy = vi.spyOn(ModalController, 'close');
// Setup adapter with limited network support
const limitedEvmAdapter = {
namespace: ConstantsUtil.CHAIN.EVM,
connectionControllerClient,
networkControllerClient,
caipNetworks: [
{
id: 1,
caipNetworkId: 'eip155:1',
name: 'Ethereum',
chainNamespace: ConstantsUtil.CHAIN.EVM
}
]
};
ChainController.state.activeCaipNetwork = {
id: 42161,
caipNetworkId: 'eip155:42161',
name: 'Arbitrum One',
chainNamespace: ConstantsUtil.CHAIN.EVM,
nativeCurrency: {
name: 'Arbitrum',
symbol: 'ARB',
decimals: 18
}
};
ChainController.state.chains.set(ConstantsUtil.CHAIN.EVM, limitedEvmAdapter);
await ChainController.switchActiveNetwork(mainnetCaipNetwork);
expect(modalCloseSpy).toHaveBeenCalled();
modalCloseSpy.mockRestore();
});
it('should initialize with active network from local storage', () => {
const getItemSpy = vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('eip155');
ChainController.initialize([evmAdapter], requestedCaipNetworks, {
connectionControllerClient,
networkControllerClient
});
expect(getItemSpy).toHaveBeenCalledWith(SafeLocalStorageKeys.ACTIVE_NAMESPACE);
expect(ChainController.state.activeChain).toEqual(ConstantsUtil.CHAIN.EVM);
getItemSpy.mockRestore();
});
it('should initialize with first adapter when stored network not found', () => {
const getItemSpy = vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('solana');
ChainController.initialize([solanaAdapter, evmAdapter], requestedCaipNetworks, {
connectionControllerClient,
networkControllerClient
});
expect(getItemSpy).toHaveBeenCalledWith(SafeLocalStorageKeys.ACTIVE_NAMESPACE);
expect(ChainController.state.activeChain).toEqual(ConstantsUtil.CHAIN.SOLANA);
getItemSpy.mockRestore();
});
it('should set noAdapters flag when no adapters provided', () => {
ChainController.initialize([], requestedCaipNetworks, {
connectionControllerClient,
networkControllerClient
});
expect(ChainController.state.noAdapters).toBe(true);
});
it('should set noAdapters flag when no adapter provided', () => {
ChainController.initialize([], requestedCaipNetworks, {
connectionControllerClient,
networkControllerClient
});
expect(ChainController.state.noAdapters).toBe(true);
});
});
//# sourceMappingURL=ChainController.test.js.map