Skip to content

Commit

Permalink
fix: fix handling of reverting transactions with EIP5792
Browse files Browse the repository at this point in the history
  • Loading branch information
rkalis committed Jan 13, 2025
1 parent 444be6f commit 9e9c6f6
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 21 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ Adding a new network is relatively straightforward as you only need to change th

#### Prerequisites

To add a new network, one of the following needs to be available:
To add a new network, **one** of the following needs to be available:

- A (public or private) RPC endpoint that supports `eth_getLogs` requests for the entire history of the network.
- Support in [CovalentHQ](https://www.covalenthq.com/) for the network.
- A block explorer with an exposed API that is compatible with Etherscan's API (such as Blockscout).
- Or: Support in [CovalentHQ](https://www.covalenthq.com/) for the network.
- Or: A block explorer with an exposed API that is compatible with Etherscan's API (such as Blockscout).

Also make sure that your network is listed in [ethereum-lists/chains](https://github.com/ethereum-lists/chains) (and that it has subsequently been included in [@revoke.cash/chains](https://github.com/RevokeCash/chains)). Besides the earlier requirements, we also require a publicly available RPC endpoint with rate limits that are not too restrictive. It is also helpful if your network is listed (with TVL and volume stats) on DeFiLlama, but this is not required.

Expand Down
6 changes: 3 additions & 3 deletions lib/api/logs/EtherscanEventGetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ const formatEtherscanEvent = (etherscanLog: any) => ({
topics: etherscanLog.topics.filter((topic: string) => !isNullish(topic)),
data: etherscanLog.data,
transactionHash: etherscanLog.transactionHash,
blockNumber: Number.parseInt(etherscanLog.blockNumber, 16),
transactionIndex: Number.parseInt(etherscanLog.transactionIndex, 16),
logIndex: Number.parseInt(etherscanLog.logIndex, 16),
blockNumber: Number.parseInt(etherscanLog.blockNumber, 16) || 0,
transactionIndex: Number.parseInt(etherscanLog.transactionIndex, 16) || 0,
logIndex: Number.parseInt(etherscanLog.logIndex, 16) || 0,
timestamp: Number.parseInt(etherscanLog.timeStamp, 16),
});

Expand Down
37 changes: 31 additions & 6 deletions lib/hooks/ethereum/useRevokeBatchEip5792.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { throwIfExcessiveGas } from 'lib/utils';
import { type OnUpdate, type TokenAllowanceData, prepareRevokeAllowance, wrapRevoke } from 'lib/utils/allowances';
import {
type Eip5792Call,
Expand All @@ -9,6 +10,7 @@ import {
pollForCallsReceipts,
} from 'lib/utils/eip5792';
import type PQueue from 'p-queue';
import type { EstimateContractGasParameters } from 'viem';
import { eip5792Actions } from 'viem/experimental';
import { useWalletClient } from 'wagmi';
import { useTransactionStore } from '../../stores/transaction-store';
Expand All @@ -29,15 +31,23 @@ export const useRevokeBatchEip5792 = (allowances: TokenAllowanceData[], onUpdate

const extendedWalletClient = walletClient.extend(eip5792Actions());

// TODO: What if estimategas fails (should skip 1 and revoke others)
const calls: Eip5792Call[] = await Promise.all(
allowances.map(async (allowance) => {
const callsSettled = await Promise.allSettled(
allowances.map(async (allowance): Promise<Eip5792Call> => {
const transactionRequest = await prepareRevokeAllowance(walletClient, allowance);
if (!transactionRequest) return;

const publicClient = allowance.contract.publicClient;
const estimatedGas =
transactionRequest.gas ??
(await publicClient.estimateContractGas(transactionRequest as EstimateContractGasParameters));

throwIfExcessiveGas(selectedChainId, allowance.owner, estimatedGas);

return mapContractTransactionRequestToEip5792Call(transactionRequest);
}),
);

const calls = callsSettled.filter((call) => call.status === 'fulfilled').map((call) => call.value);

if (tipAmount && Number(tipAmount) > 0) {
const donateTransaction = await prepareDonate(tipAmount);
calls.push(mapTransactionRequestToEip5792Call(donateTransaction));
Expand All @@ -58,18 +68,24 @@ export const useRevokeBatchEip5792 = (allowances: TokenAllowanceData[], onUpdate
const revoke = wrapRevoke(
allowance,
async () => {
// Check whether the revoke failed *before* even making it into wallet_sendCalls
const settlement = callsSettled[index];
if (settlement.status === 'rejected') throw settlement.reason;

const id = await batchPromise;
const { receipts } = await pollForCallsReceipts(id, extendedWalletClient);

if (receipts?.length === 1) {
return mapWalletCallReceiptToTransactionSubmitted(allowance, receipts[0], onUpdate);
}

if (!receipts?.[index]) {
const callIndex = getCallIndex(index, callsSettled);

if (!receipts?.[callIndex]) {
throw new Error('An error occurred related to EIP5792 batch calls');
}

return mapWalletCallReceiptToTransactionSubmitted(allowance, receipts[index], onUpdate);
return mapWalletCallReceiptToTransactionSubmitted(allowance, receipts[callIndex], onUpdate);
},
updateTransaction,
);
Expand All @@ -83,3 +99,12 @@ export const useRevokeBatchEip5792 = (allowances: TokenAllowanceData[], onUpdate

return revoke;
};

const getCallIndex = (index: number, callsSettled: PromiseSettledResult<Eip5792Call>[]): number => {
const numberOfFailedCallsBeforeIndex = callsSettled
.slice(0, index)
.filter((call) => call.status === 'rejected').length;

const adjustedIndex = index - numberOfFailedCallsBeforeIndex;
return adjustedIndex;
};
16 changes: 7 additions & 9 deletions lib/utils/eip5792.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import type { TransactionSubmitted } from 'lib/interfaces';
import type { SendTransactionParameters, WalletCallReceipt, WalletClient, WriteContractParameters } from 'viem';
import {
type Eip5792Actions,
type GetCallsStatusReturnType,
type SendCallsParameters,
eip5792Actions,
} from 'viem/experimental';
import type { Call } from 'viem/_types/types/calls';
import { type Eip5792Actions, type GetCallsStatusReturnType, eip5792Actions } from 'viem/experimental';
import type { OnUpdate } from './allowances';
import type { TokenAllowanceData } from './allowances';

export type Eip5792Call = SendCallsParameters['calls'][number];
export type Eip5792Call = Call;

export const walletSupportsEip5792 = async (walletClient: WalletClient) => {
try {
Expand Down Expand Up @@ -37,7 +33,9 @@ export const pollForCallsReceipts = async (id: string, walletClient: WalletClien
});
};

export const mapContractTransactionRequestToEip5792Call = (transactionRequest: WriteContractParameters) => {
export const mapContractTransactionRequestToEip5792Call = (
transactionRequest: WriteContractParameters,
): Eip5792Call => {
return {
to: transactionRequest.address,
abi: transactionRequest.abi,
Expand All @@ -49,7 +47,7 @@ export const mapContractTransactionRequestToEip5792Call = (transactionRequest: W

export const mapTransactionRequestToEip5792Call = (transactionRequest: SendTransactionParameters): Eip5792Call => {
return {
to: transactionRequest.to,
to: transactionRequest.to!,
data: transactionRequest.data,
value: transactionRequest.value,
};
Expand Down

0 comments on commit 9e9c6f6

Please sign in to comment.