@reown/appkit-utils
Version:
The full stack toolkit to build onchain app UX.
314 lines • 12.8 kB
JavaScript
import { PublicKey, Transaction } from '@solana/web3.js';
import bs58 from 'bs58';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { createNamespaces } from '../src/WCNamespaceUtil.js';
import { SolConstantsUtil } from '../src/solana/SolanaConstantsUtil.js';
import { SolanaWalletConnectStandardWallet } from '../src/wallet-standard/SolanaWalletConnectStandardWallet.js';
const SOLANA_CHAINS = ['solana:mainnet', 'solana:devnet', 'solana:testnet', 'solana:localnet'];
vi.mock('@walletconnect/universal-provider');
vi.mock('@wallet-standard/wallet');
const mockAddress = 'HPAccp9wmUAP4kxATmf1CjARHfPzB1HXKWoEaNiZqvUQ';
const mockPublicKey = bs58.decode(mockAddress);
const mockTransactionBytes = Uint8Array.from([
0x01,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00
]);
describe('SolanaWalletConnectStandardWallet', () => {
let mockProvider;
let wallet;
beforeEach(() => {
vi.clearAllMocks();
mockProvider = {
connect: vi.fn(),
disconnect: vi.fn(),
request: vi.fn(),
on: vi.fn(),
session: vi.fn()
};
SolanaWalletConnectStandardWallet.register(mockProvider);
wallet = new SolanaWalletConnectStandardWallet(mockProvider);
});
describe('constructor and initialization', () => {
it('should initialize with correct properties', () => {
expect(wallet.name).toBe('WalletConnect');
expect(wallet.version).toBe('1.0.0');
expect(wallet.chains).toEqual(SOLANA_CHAINS);
});
it('should set up event listeners on provider', () => {
expect(mockProvider.on).toHaveBeenCalledWith('connect', expect.any(Function));
expect(mockProvider.on).toHaveBeenCalledWith('disconnect', expect.any(Function));
expect(mockProvider.on).toHaveBeenCalledWith('accountsChanged', expect.any(Function));
expect(mockProvider.on).toHaveBeenCalledWith('chainChanged', expect.any(Function));
});
});
describe('connect', () => {
beforeEach(() => {
vi.spyOn(mockProvider, 'connect').mockResolvedValue({
namespaces: {
solana: {
accounts: [`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${mockAddress}`],
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
methods: [
'solana:signAndSendTransaction',
'solana:signTransaction',
'solana:signMessage'
],
events: ['change']
}
}
});
});
it('should connect successfully', async () => {
const connectResult = await wallet.features['standard:connect'].connect();
expect(connectResult.accounts).toBeDefined();
expect(mockProvider.connect).toHaveBeenCalledWith({
namespaces: createNamespaces([SolConstantsUtil.DEFAULT_CHAIN])
});
});
});
describe('disconnect', () => {
beforeEach(() => {
vi.spyOn(mockProvider, 'session', 'get').mockReturnValue({
namespaces: {
solana: {
accounts: [`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${mockAddress}`],
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
methods: [
'solana:signAndSendTransaction',
'solana:signTransaction',
'solana:signMessage'
],
events: ['change']
}
}
});
});
afterEach(() => {
vi.clearAllMocks();
});
it('should disconnect successfully', async () => {
await wallet.features['standard:disconnect'].disconnect();
expect(mockProvider.disconnect).toHaveBeenCalled();
});
});
describe('signAndSendTransaction', () => {
const mockAccount = {
address: mockAddress,
publicKey: new PublicKey(mockPublicKey).toBytes(),
chains: ['solana:mainnet'],
features: ['solana:signAndSendTransaction']
};
beforeEach(async () => {
vi.restoreAllMocks();
vi.spyOn(mockProvider, 'session', 'get').mockReturnValue({
namespaces: {
solana: {
accounts: [`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${mockAddress}`],
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
methods: ['solana:signAndSendTransaction'],
events: ['change']
}
}
});
});
afterEach(() => {
vi.clearAllMocks();
});
it('should throw error when not connected', async () => {
const mockTransactionBytes = new Uint8Array([1, 2, 3, 4, 5]);
await expect(wallet.features['solana:signAndSendTransaction'].signAndSendTransaction({
transaction: mockTransactionBytes,
account: mockAccount,
chain: 'solana:mainnet'
})).rejects.toThrow('not connected');
});
it('should sign and send transaction successfully', async () => {
await wallet.features['standard:connect'].connect();
const mockSignature = 'mockSignature';
vi.spyOn(mockProvider, 'request').mockResolvedValueOnce({
signature: mockSignature
});
const result = await wallet.features['solana:signAndSendTransaction'].signAndSendTransaction({
transaction: mockTransactionBytes,
account: mockAccount,
chain: 'solana:mainnet'
});
expect(result?.[0]?.signature).toBeDefined();
expect(mockProvider.request).toHaveBeenCalledWith({
method: 'solana_signAndSendTransaction',
params: expect.any(Object)
});
});
});
describe('signTransaction', () => {
const mockAccount = {
address: mockAddress,
publicKey: new PublicKey(mockPublicKey).toBytes(),
chains: ['solana:mainnet'],
features: ['solana:signTransaction']
};
beforeEach(async () => {
vi.restoreAllMocks();
vi.spyOn(mockProvider, 'session', 'get').mockReturnValue({
namespaces: {
solana: {
accounts: [`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${mockAddress}`],
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
methods: ['solana:signTransaction'],
events: ['change']
}
}
});
});
afterEach(() => {
vi.clearAllMocks();
});
it('should throw error when not connected', async () => {
await expect(wallet.features['solana:signTransaction'].signTransaction({
transaction: mockTransactionBytes,
account: mockAccount,
chain: 'solana:mainnet'
})).rejects.toThrow('not connected');
});
it('should sign transaction successfully', async () => {
vi.spyOn(Transaction, 'from').mockImplementation(buffer => {
return {
serialize: vi.fn().mockReturnValue(new Uint8Array(buffer)),
recentBlockhash: 'mockBlockhash',
feePayer: new PublicKey(mockPublicKey)
};
});
await wallet.features['standard:connect'].connect();
const mockSignedTx = 'mockSignedTransaction';
vi.spyOn(mockProvider, 'request').mockResolvedValueOnce({
transaction: mockSignedTx
});
const result = await wallet.features['solana:signTransaction'].signTransaction({
transaction: mockTransactionBytes,
account: mockAccount,
chain: 'solana:mainnet'
});
expect(result?.[0]?.signedTransaction).toBeDefined();
expect(mockProvider.request).toHaveBeenCalledWith({
method: 'solana_signTransaction',
params: expect.any(Object)
});
});
});
describe('signMessage', () => {
const mockAccount = {
address: mockAddress,
publicKey: new PublicKey(mockPublicKey).toBytes(),
chains: ['solana:mainnet'],
features: ['solana:signMessage']
};
beforeEach(() => {
vi.restoreAllMocks();
vi.spyOn(mockProvider, 'session', 'get').mockReturnValue({
namespaces: {
solana: {
accounts: [`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${mockAddress}`],
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
methods: ['solana:signMessage'],
events: ['change']
}
}
});
});
afterEach(() => {
vi.clearAllMocks();
});
it('should throw error when not connected', async () => {
vi.spyOn(mockProvider, 'session', 'get').mockReturnValue(undefined);
const mockMessage = new Uint8Array([1, 2, 3]);
await expect(wallet.features['solana:signMessage'].signMessage({
message: mockMessage,
account: mockAccount
})).rejects.toThrow('not connected');
});
it('should sign message successfully', async () => {
await wallet.features['standard:connect'].connect();
const mockMessage = new Uint8Array([1, 2, 3]);
const mockSignature = 'mockSignature';
vi.spyOn(mockProvider, 'request').mockResolvedValueOnce({
signature: mockSignature
});
const result = await wallet.features['solana:signMessage'].signMessage({
message: mockMessage,
account: mockAccount
});
expect(result?.[0]?.signature).toBeDefined();
expect(result?.[0]?.signedMessage).toEqual(mockMessage);
expect(mockProvider.request).toHaveBeenCalledWith({
method: 'solana_signMessage',
params: expect.any(Object)
});
});
});
describe('events', () => {
let connectHandler;
let disconnectHandler;
beforeEach(() => {
vi.restoreAllMocks();
vi.spyOn(mockProvider, 'session', 'get').mockReturnValue({
namespaces: {
solana: {
accounts: [`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${mockAddress}`],
chains: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'],
methods: [
'solana:signAndSendTransaction',
'solana:signTransaction',
'solana:signMessage'
],
events: ['change']
}
}
});
vi.spyOn(mockProvider, 'on').mockImplementation((event, handler) => {
if (event === 'connect') {
connectHandler = handler;
}
else if (event === 'disconnect') {
disconnectHandler = handler;
}
});
wallet.setProvider(mockProvider);
});
afterEach(() => {
vi.clearAllMocks();
});
it('should handle connect event', () => {
const mockListener = vi.fn();
wallet.features['standard:events'].on('change', mockListener);
if (connectHandler) {
connectHandler();
}
expect(mockListener).toHaveBeenCalled();
});
it('should handle disconnect event', () => {
const mockListener = vi.fn();
wallet.features['standard:events'].on('change', mockListener);
if (disconnectHandler) {
disconnectHandler();
}
expect(mockListener).toHaveBeenCalled();
});
});
});
//# sourceMappingURL=WalletConnectWalletStandard.test.js.map