Skip to content

Commit

Permalink
fix: types
Browse files Browse the repository at this point in the history
  • Loading branch information
Argeare5 committed Nov 15, 2023
1 parent cebc2e0 commit df2a47e
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 238 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@bgd-labs/frontend-web3-utils",
"description": "Frontend utilities common to multiple Web3 projects",
"version": "0.4.65",
"version": "0.4.66",
"author": "BGD labs",
"license": "MIT",
"private": false,
Expand Down
57 changes: 34 additions & 23 deletions src/hooks/useLastTxLocalStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { Hex } from 'viem';

import { isGelatoBaseTx } from '../web3/adapters/GelatoAdapter';
import { selectLastTxByTypeAndPayload } from '../web3/store/transactionsSelectors';
Expand All @@ -10,7 +11,7 @@ import {

interface LastTxStatusesParams<T extends BaseTx> {
state: ITransactionsState<T>;
activeAddress: string;
activeAddress: Hex;
type: T['type'];
payload: T['payload'];
}
Expand All @@ -28,28 +29,35 @@ export const useLastTxLocalStatus = <T extends BaseTx>({
}: LastTxStatusesParams<T>) => {
const tx = selectLastTxByTypeAndPayload(state, activeAddress, type, payload);

const [fullTxErrorMessage, setFullTxErrorMessage] = useState<string | Error>(
'',
);
const [error, setError] = useState<string | Error>('');
const [fullTxErrorMessage, setFullTxErrorMessage] = useState<string>('');
const [error, setError] = useState<string>('');
const [loading, setLoading] = useState(false);
const [isTxStart, setIsTxStart] = useState(false);

const txHash = tx && tx.hash;
const txPending = tx && tx.pending;
const isError =
tx && isGelatoBaseTx(tx)
? !tx.pending && (tx.status !== TransactionStatus.Success || !!error)
: (tx &&
!tx.pending &&
tx.status !== TransactionStatus.Success &&
tx.status !== TransactionStatus.Replaced) ||
!!error;
const txSuccess = tx && tx.status === TransactionStatus.Success && !isError;
const txChainId = tx && tx.chainId;
const txWalletType = tx && tx.walletType;
const isTxReplaced = tx && tx.status === TransactionStatus.Replaced;
const replacedTxHash = tx && tx.replacedTxHash;
const txHash = tx?.hash;
let txPending = tx?.pending;

let isError: boolean = false;
if (tx) {
if (isGelatoBaseTx(tx)) {
isError =
!tx.pending && (tx.status !== TransactionStatus.Success || !!error);
} else if (
!tx.pending &&
tx.status !== TransactionStatus.Success &&
tx.status !== TransactionStatus.Replaced
) {
isError = true;
} else {
isError = !!error;
}
}

const txSuccess = tx?.status === TransactionStatus.Success && !isError;
const txChainId = tx?.chainId;
const txWalletType = tx?.walletType;
const isTxReplaced = tx?.status === TransactionStatus.Replaced;
const replacedTxHash = tx?.replacedTxHash;

useEffect(() => {
return () => {
Expand Down Expand Up @@ -80,10 +88,13 @@ export const useLastTxLocalStatus = <T extends BaseTx>({
await callbackFunction();
} catch (e) {
if (e instanceof Error) {
console.error('TX error: ', e);
setFullTxErrorMessage(!!e?.message ? e.message : e);
setError(!!errorMessage ? errorMessage : !!e?.message ? e.message : e);
setFullTxErrorMessage(e.message);
setError(!!errorMessage ? errorMessage : e.message);
} else if (typeof e === 'string') {
setFullTxErrorMessage(e);
setError(!!errorMessage ? errorMessage : e);
}
console.error('TX error: ', e);
}
setLoading(false);
}
Expand Down
113 changes: 62 additions & 51 deletions src/web3/adapters/EthereumAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,78 @@
import { PublicClient } from '@wagmi/core';
import { produce } from 'immer';
import { GetTransactionReturnType, Hex } from 'viem';
import { Hex, isHex } from 'viem';

import { setLocalStorageTxPool } from '../../utils/localStorage';
import {
BaseTx,
EthBaseTx,
InitialEthTx,
ITransactionsSlice,
NewTx,
PoolEthTx,
InitialTx,
isEthPoolTx,
ITransactionsSliceWithWallet,
TransactionStatus,
} from '../store/transactionsSlice';
import { Wallet } from '../store/walletSlice';
import { AdapterInterface } from './interface';

export class EthereumAdapter<T extends BaseTx> implements AdapterInterface<T> {
get: () => ITransactionsSlice<T>;
set: (fn: (state: ITransactionsSlice<T>) => ITransactionsSlice<T>) => void;
get: () => ITransactionsSliceWithWallet<T>;
set: (
fn: (
state: ITransactionsSliceWithWallet<T>,
) => ITransactionsSliceWithWallet<T>,
) => void;
transactionsIntervalsMap: Record<string, number | undefined> = {};

constructor(
get: () => ITransactionsSlice<T>,
set: (fn: (state: ITransactionsSlice<T>) => ITransactionsSlice<T>) => void,
get: () => ITransactionsSliceWithWallet<T>,
set: (
fn: (
state: ITransactionsSliceWithWallet<T>,
) => ITransactionsSliceWithWallet<T>,
) => void,
) {
this.get = get;
this.set = set;
}

executeTx = async (params: {
tx: NewTx;
tx: InitialTx;
activeWallet: Wallet;
payload: object | undefined;
chainId: number;
type: T['type'];
}): Promise<T & { status?: TransactionStatus; pending: boolean }> => {
const { activeWallet, chainId, type } = params;
const tx = params.tx as InitialEthTx;
}) => {
const { tx, activeWallet, chainId, type, payload } = params;

const from = activeWallet.address;
const transaction = {
chainId,
hash: tx.hash,
type,
payload: params.payload,
from,
} as EthBaseTx;
const txPool = this.get().addTXToPool(transaction, activeWallet.walletType);
this.waitForTxReceipt(transaction, tx.hash);
return txPool[tx.hash];
if (isHex(tx)) {
const txParams = {
chainId,
hash: tx,
type,
payload: payload,
from,
};
const txPool = this.get().addTXToPool(txParams, activeWallet.walletType);

this.waitForTxReceipt(txParams.hash);
return txPool[txParams.hash];
} else {
return undefined;
}
};
startTxTracking = async (txKey: string) => {
const retryCount = 5;
const txData = this.get().transactionsPool[txKey];
// check if tx is in local storage
if (txData) {
const client = this.get().clients[txData.chainId] as PublicClient;
const client = this.get().clients[txData.chainId];
if (txData.hash) {
// Find the transaction in the waiting pool
for (let i = 0; i < retryCount; i++) {
try {
const tx = await client.getTransaction({ hash: txData.hash });

// If the transaction is found, wait for the receipt
await this.waitForTxReceipt(tx, txData.hash);
await this.waitForTxReceipt(txData.hash, tx.nonce);
return; // Exit the function if successful
} catch (e) {
if (i === retryCount - 1) {
Expand All @@ -83,18 +93,15 @@ export class EthereumAdapter<T extends BaseTx> implements AdapterInterface<T> {
}
};

private waitForTxReceipt = async (
tx: GetTransactionReturnType | EthBaseTx,
txHash: Hex,
) => {
const chainId = tx.chainId || this.get().transactionsPool[txHash].chainId;
private waitForTxReceipt = async (txHash: Hex, txNonce?: number) => {
const chainId = this.get().transactionsPool[txHash].chainId;
const client = this.get().clients[chainId];
let txWasReplaced = false;

try {
const txn = await client.waitForTransactionReceipt({
pollingInterval: 8_000,
hash: tx.hash,
hash: txHash,
onReplaced: (replacement) => {
this.updateTXStatus({
hash: txHash,
Expand All @@ -115,7 +122,7 @@ export class EthereumAdapter<T extends BaseTx> implements AdapterInterface<T> {
? TransactionStatus.Success
: TransactionStatus.Reverted,
to: txn.to as Hex,
nonce: tx.nonce,
nonce: txNonce,
});

const updatedTX = this.get().transactionsPool[txHash];
Expand All @@ -130,7 +137,7 @@ export class EthereumAdapter<T extends BaseTx> implements AdapterInterface<T> {
hash: txHash,
status: TransactionStatus.Failed,
});
console.error(e);
console.error('Error when check tx receipt', e);
}
};

Expand All @@ -141,30 +148,34 @@ export class EthereumAdapter<T extends BaseTx> implements AdapterInterface<T> {
to,
nonce,
}: {
hash: string;
hash: Hex;
status?: TransactionStatus;
replacedHash?: string;
replacedHash?: Hex;
to?: Hex;
nonce?: number;
}) => {
this.set((state) =>
produce(state, (draft) => {
const tx = draft.transactionsPool[hash] as PoolEthTx;
const tx = draft.transactionsPool[hash];

tx.pending = false;
tx.status =
status !== TransactionStatus.Reverted
? status
: TransactionStatus.Reverted;
if (isEthPoolTx(tx)) {
tx.pending = false;
tx.status =
status !== TransactionStatus.Reverted
? status
: TransactionStatus.Reverted;

if (to) {
tx.to = to;
}
if (nonce) {
tx.nonce = nonce;
}
if (replacedHash) {
tx.replacedTxHash = replacedHash;
if (to) {
tx.to = to;
}

if (nonce) {
tx.nonce = nonce;
}

if (replacedHash) {
tx.replacedTxHash = replacedHash;
}
}
}),
);
Expand Down
Loading

0 comments on commit df2a47e

Please sign in to comment.