Balance not saved in DB: When you are using event souring for your wallet service, then it is expected that you do not save the balance of the wallet in database. This is perfect. The balance should be computed by replaying all the transactions (events)
Duplicate Transactions not processed: This sounds a bit risky to me. If duplicate transaction requests could reach the WalletService, then I believe there could a transaction request that might not reach the Wallet service or may be delayed in reaching the Wallet service. If this is the case, the ordering of the transaction request to wallet service is not guarenteed. The transactions to the wallet should be sequential and strictly ordered. The order of the transactions is very important is deciding if any transaction should succeed or fail.
Client generating the transaction id: The IDs of the transaction in a wallet needs to be unique. This could be easily achieved when the transaction ids are generated by the wallet service. When clients are generating the transaction ids, making sure two clients don't generate the same transaction id would be a bit difficult.
Duplicate Transactions: Duplicate Transaction reaching the wallet service will increase the complexity of the design. If you have decided that duplicate transaction could reach the wallet service, just to over come the reliability of the transport of the transaction request, then I would reconsider the design.
Suggested Design: The wallet service should have end points to post transaction requests. The response should be transaction id and status of the request. There should be a way to query the wallet service for transactions as well. You might want to have a mechanism to acknowledge transactions by the client. This could serve audit-ability needs. We might need to tweak the system a bit if the transaction requests are by messages (RabbitMQ, Kafka or something like that)