import type { EthereumProvider } from '../eip6963-discovery' import type { TronProvider } from '../tron-discovery' import bs58 from 'bs58' import { parseError } from '../utils/error-utils' import type { SolanaAdapter } from '../wallet-standard-discovery' import type { EIP712TypedData } from '@meshconnect/uwc-types' import { withEIP712Domain } from '@meshconnect/uwc-types' /** * Service for handling message signing operations for injected wallets */ export class SignatureService { /** * Sign EIP-712 typed structured data with an Ethereum wallet (eth_signTypedData_v4). * Used by ERC-3009 (Transfer With Authorization) and EIP-2612 (Permit) relay flows. */ async signEthereumTypedData( typedData: EIP712TypedData, address: string, provider: EthereumProvider ): Promise { if (!provider) { throw new Error('Provider is required for typed data signing') } if (!address) { throw new Error('Address is required for typed data signing') } try { // Inject EIP712Domain into the types before signing — mobile wallets // (Trust Wallet, OKX mobile, etc.) require it explicitly in the payload. const wireData = withEIP712Domain(typedData) const signature = await provider.request({ method: 'eth_signTypedData_v4', params: [address, JSON.stringify(wireData)] }) return signature as string } catch (error) { parseError(error) } } /** * Sign a message with an Ethereum wallet */ async signEthereumMessage( message: string, address: string, provider: EthereumProvider ): Promise { if (!provider) { throw new Error('Provider is required for Ethereum message signing') } if (!message) { throw new Error('Message is required for signing') } if (!address) { throw new Error('Address is required for signing') } try { // Use personal_sign for Ethereum const signature = await provider.request({ method: 'personal_sign', params: [message, address] }) return signature } catch (error) { parseError(error) } } /** * Sign a message with a Solana wallet */ async signSolanaMessage( message: string, adapter: SolanaAdapter ): Promise { if (!message) { throw new Error('Message is required for signing') } try { // Convert message to Uint8Array for Solana const encoder = new TextEncoder() const messageBytes = encoder.encode(message) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const signedMessage = await adapter.signMessage(messageBytes) // Encode signature using base58 for Solana (standard encoding for Solana) // The signature is returned as a Uint8Array, encode it to base58 string const signatureBase58 = bs58.encode(signedMessage) return signatureBase58 } catch (error) { parseError(error) } } /** * Sign a message with a Tron wallet */ async signTronMessage( message: string, provider: TronProvider ): Promise { if (!message) { throw new Error('Message is required for signing') } if (!provider) { throw new Error('Provider is required for Tron message signing') } try { const signature = await provider.tronWeb.trx.signMessageV2(message) return signature } catch (error) { parseError(error) } } }