2

Our dapp is using wagmi 1.4 / viem 1.15 and a plain httpProvider. I'm connecting it to a Safe (1/1) using WalletConnect v2. When I dispatch a contract transaction, viem yields a transaction response like { hash: 0x...}. In contrast to transactions dispatched via an injected wallet / provider this hash is not a transaction hash but represents the Safe's safeTx (that will be signed, relayed and finally executed on the Safe's side). We can resolve the execution result eg by using API Kit (like in https://docs.safe.global/reference/api-kit#gettransaction) or requesting the Transaction Service API directly.

From a DX/UX perspective there's a problem: our frontend (or wagmi for that matter) doesn't know that it's connected to a Safe/AA wallet, so we can't trivially determine whether we must resolve the safetx on our side. The ideal DX would be to extend a viem wallet provider so that it transparently checks and resolves the transaction response as if it were issued by an EOA. Clearly, AA transactions can take very long so that response should still carry the safeTx, or even its current execution state.

I'm wondering if there's any integration available for viem/wagmi already.

To illustrate the case I've assembled a quite unclean and naive demo that checks whether the connected account is a contract (surely not every contract is a Safe ;) ) and issues queries to the Safe Transaction API until it finds the actual transaction hash: https://github.com/moleculeprotocol/test-wagmi-safe-privy (demo: https://moleculeprotocol.github.io/test-wagmi-safe-privy/ should work with injected providers & Safe)

1 Answer 1

2

While this problem must be addressed on the connected library's side (eg inside a viem walletClient and transparently passed through to the public client's interface), we decided to build a package that solves that issue in a pragmatic way:

It offers a dedicated wagmi hook (useSafeWaitForTransaction) that can be used as a dropin replacement for wagmi's useWaitForTransaction and a safeWaitForTransactionReceipt function that can be used in a plain viem context.

//somewhere on your setup:
export const publicClient = publicClient({ chainId: 1 });

//... in your code
const publicClient = usePublicClient();

//somehow get the user account that filed the transaction:
const { data: walletClient } = useWalletClient(); 

const hash = await walletClient.writeContract(request);
// if the account is a Safe, this will resolve hash as `safeTx` under the hood:
const receipt = await safeWaitForTransactionReceipt(publicClient, {
  hash,
  address: walletClient.account,
});

The library first checks whether the wallet that has sent the transaction originated from a Safe (we're only detecting Safes deployed as proxies yet) and then transparently queries the pending safeTx from the respective Safe API service. Once resolved we invoke the "real" hook and yield its results, so this stays downward compatible.


Note, that this ideally must become part of the library's client layer; extending a given publicClient won't work, since it can't know the user's address. The hard part is to identify whether a transaction hash yielded by some write operation actually has to be resolved on the AA wallet's infrastructure first, and how to query from that infrastructure (Safe only being the most used AA implementation right now).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.