import { describe, it, expect, vi, beforeEach } from 'vitest' import { SignatureService } from './signature-service' import type { EthereumProvider } from '../eip6963-discovery' function makeProvider(signature = '0xmocksig'): EthereumProvider { return { request: vi.fn().mockResolvedValue(signature) } as unknown as EthereumProvider } const ADDRESS = '0xUserAddress' const USDC_TYPED_DATA = { domain: { name: 'USD Coin', version: '2', chainId: 1, verifyingContract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' }, types: { TransferWithAuthorization: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'validAfter', type: 'uint256' }, { name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' } ] }, primaryType: 'TransferWithAuthorization', message: { from: '0xabc', to: '0xdef', value: '1000000', validAfter: 0, validBefore: 9999999999, nonce: '0xdeadbeef' } } describe('SignatureService (injected-connector)', () => { let service: SignatureService beforeEach(() => { service = new SignatureService() }) describe('signEthereumTypedData', () => { it('calls eth_signTypedData_v4 with address and JSON payload', async () => { const provider = makeProvider() await service.signEthereumTypedData(USDC_TYPED_DATA, ADDRESS, provider) expect(provider.request).toHaveBeenCalledWith({ method: 'eth_signTypedData_v4', params: [ADDRESS, expect.any(String)] }) }) it('injects EIP712Domain into wire payload for mobile wallet compat', async () => { const provider = makeProvider() await service.signEthereumTypedData(USDC_TYPED_DATA, ADDRESS, provider) const [, jsonArg] = (provider.request as ReturnType).mock .calls[0][0].params const sent = JSON.parse(jsonArg) expect(sent.types.EIP712Domain).toEqual([ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' } ]) }) it('does not duplicate EIP712Domain when caller already includes it', async () => { const provider = makeProvider() const dataWithDomain = { ...USDC_TYPED_DATA, types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'chainId', type: 'uint256' } ], ...USDC_TYPED_DATA.types } } await service.signEthereumTypedData(dataWithDomain, ADDRESS, provider) const [, jsonArg] = (provider.request as ReturnType).mock .calls[0][0].params const sent = JSON.parse(jsonArg) // Caller's EIP712Domain is preserved as-is (only 2 fields, not 4) expect(sent.types.EIP712Domain).toEqual([ { name: 'name', type: 'string' }, { name: 'chainId', type: 'uint256' } ]) }) it('omits version from EIP712Domain when domain has no version (UNI-style)', async () => { const provider = makeProvider() const uniData = { ...USDC_TYPED_DATA, domain: { name: 'Uniswap V2', chainId: 1, verifyingContract: '0x...' } } await service.signEthereumTypedData(uniData, ADDRESS, provider) const [, jsonArg] = (provider.request as ReturnType).mock .calls[0][0].params const sent = JSON.parse(jsonArg) expect(sent.types.EIP712Domain).toEqual([ { name: 'name', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' } ]) }) it('does not mutate the original typedData object', async () => { const provider = makeProvider() const original = JSON.parse(JSON.stringify(USDC_TYPED_DATA)) await service.signEthereumTypedData(USDC_TYPED_DATA, ADDRESS, provider) expect(USDC_TYPED_DATA.types).toEqual(original.types) expect( (USDC_TYPED_DATA.types as Record).EIP712Domain ).toBeUndefined() }) it('returns the signature from the provider', async () => { const provider = makeProvider('0xexpectedsig') const result = await service.signEthereumTypedData( USDC_TYPED_DATA, ADDRESS, provider ) expect(result).toBe('0xexpectedsig') }) it('throws when provider is missing', async () => { await expect( service.signEthereumTypedData( USDC_TYPED_DATA, ADDRESS, undefined as unknown as EthereumProvider ) ).rejects.toThrow('Provider is required') }) it('throws when address is missing', async () => { const provider = makeProvider() await expect( service.signEthereumTypedData(USDC_TYPED_DATA, '', provider) ).rejects.toThrow('Address is required') }) }) })