To use paymaster transactions on ZKsync, the Viem implementation requires you to import {eip712WalletActions} from 'viem/zksync'and extend a WalletClient with it.
However, this modifies the WalletClient.SendTransaction() function by replacing the eth_sendTransaction JSON RPC API method by the eth_sendRawTransaction method, which is not supported on several mobile wallets.
The current work-around for making paymaster transactions work with all wallets is to sign the transaction using a WalletClient, and then send the signed transaction using a PublicClient supporting the eth_sendRawTransaction. The simplest way is to use a public JSON RPC endpoint, such as https://mainnet.era.zksync.io.
Classic Paymaster implementation (not compatible with all wallets):
// Paymaster Data received from Zyfi's APIconstpmData=awaitsubmitTxDataToAPI(API_URL, dataTxRequest)// Extend wallet client to support paymaster transactionsconstwClient=walletClient.extend(eip712WalletActions())// Prepare transaction requestconsttxReq=awaitwClient.prepareTransactionRequest({ account: address, to: toAddress, value:ETH_AMOUNT, chain: zkSync, gas:BigInt(pmData.gasLimit), gasPerPubdata:BigInt(pmData.txData.customData.gasPerPubdata), maxFeePerGas:BigInt(pmData.txData.maxFeePerGas), maxPriorityFeePerGas:0n, data:pmData.txData.data, paymaster:pmData.txData.customData.paymasterParams.paymaster, paymasterInput:pmData.txData.customData.paymasterParams.paymasterInput, })// Signs and sends to an endpoint that might be incompatible with eth_sendRawTransaction consthash=awaitwClient.sendTransaction(txReq)
Work-around Paymaster Implementation (Works with all wallets)
// Paymaster Data received from Zyfi's APIconstpmData=awaitsubmitTxDataToAPI(API_URL, dataTxRequest)// Extend wallet client to support paymaster transactionsconstwClient=walletClient.extend(eip712WalletActions())// Create custom public client with an endpoint that suports eth_sendRawTransactionconstpublicClient=createPublicClient({ chain: zkSync, transport:http('https://mainnet.era.zksync.io') })consttxReq=awaitwClient.prepareTransactionRequest({ account: address, to: toAddress, value:ETH_AMOUNT, chain: zkSync, gas:BigInt(pmData.gasLimit), gasPerPubdata:BigInt(pmData.txData.customData.gasPerPubdata), maxFeePerGas:BigInt(pmData.txData.maxFeePerGas), maxPriorityFeePerGas:0n, data:pmData.txData.data, paymaster:pmData.txData.customData.paymasterParams.paymaster, paymasterInput:pmData.txData.customData.paymasterParams.paymasterInput, })// Sign with Wallet and send signed transaction directly to a compatible endpointconstsignature=awaitwClient.signTransaction(txReq)consthash=awaitpublicClient.sendRawTransaction({ serializedTransaction: signature })
It's a workaround that works perfectly for Dapps wanting to support paymaster functionality on mobile within ZKsync chains.