Skip to content

Commit

Permalink
more cleanup and move to enums for flashbots
Browse files Browse the repository at this point in the history
  • Loading branch information
walmat committed Oct 1, 2024
1 parent e633236 commit fac310e
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 206 deletions.
7 changes: 3 additions & 4 deletions src/components/coin-row/FastTransactionCoinRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native';
import { ButtonPressAnimation } from '../animations';
import FastTransactionStatusBadge from './FastTransactionStatusBadge';
import { Bleed, Box, Inline, Text, globalColors, useForegroundColor } from '@/design-system';
import { NativeCurrencyKey, RainbowTransaction, TransactionType } from '@/entities';
import { NativeCurrencyKey, RainbowTransaction, TransactionStatus, TransactionType } from '@/entities';
import { ThemeContextProps } from '@/theme';
import { useNavigation } from '@/navigation';
import Routes from '@rainbow-me/routes';
Expand Down Expand Up @@ -53,7 +53,7 @@ const swapTypeValues = (changes: RainbowTransaction['changes'], status: RainbowT

// NOTE: For pending txns let's use the change values instead of
// the transaction balance change since that hasn't happened yet
if (status === 'pending') {
if (status === TransactionStatus.pending) {
const valueOut = `${handleSignificantDecimals(convertRawAmountToDecimalFormat(tokenOut?.value?.toString() || '0', tokenOut?.asset.decimals || 18), tokenOut?.asset.decimals || 18)} ${tokenOut?.asset.symbol}`;
const valueIn = `+${handleSignificantDecimals(convertRawAmountToDecimalFormat(tokenIn?.value?.toString() || '0', tokenIn?.asset.decimals || 18), tokenIn?.asset.decimals || 18)} ${tokenIn?.asset.symbol}`;

Expand Down Expand Up @@ -142,8 +142,7 @@ export const ActivityTypeIcon = ({
transaction: Pick<RainbowTransaction, 'status' | 'type'>;
color: string;
}) => {
// if (status === 'pending') return null;
if (status === 'pending') {
if (status === TransactionStatus.pending) {
return <Spinner color={color} size={11} style={{ marginTop: -1, paddingRight: 2 }} />;
}

Expand Down
17 changes: 12 additions & 5 deletions src/components/coin-row/FastTransactionStatusBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import React, { useCallback } from 'react';
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
import { Text, useForegroundColor } from '@/design-system';
import { RainbowTransaction } from '@/entities';
import { RainbowTransaction, TransactionStatus } from '@/entities';
import { ThemeContextProps } from '@/theme';
import * as lang from '@/languages';
import { ActivityTypeIcon } from './FastTransactionCoinRow';
Expand All @@ -25,20 +25,27 @@ export default React.memo(function FastTransactionStatusBadge({
style?: StyleProp<ViewStyle>;
}) {
let statusColor = useForegroundColor('labelTertiary');
if (transaction?.status === 'pending') {
if (transaction?.status === TransactionStatus.pending) {
statusColor = colors.appleBlue;
} else if (transaction?.status === 'failed') {
statusColor = colors.red;
}

const getStatusTitle = useCallback((status: TransactionStatus, title: string) => {
const transactionType = lang.l.transactions.type[title as keyof typeof lang.l.transactions.type];
if (typeof transactionType === 'string') {
return transactionType;
}
return transactionType[status as keyof typeof transactionType] || '';
}, []);

return (
<View style={[sx.row, style]}>
<View style={sx.icon}>
<ActivityTypeIcon transaction={transaction} color={statusColor} />
</View>
<Text color={{ custom: statusColor }} size="14px / 19px (Deprecated)" weight="semibold">
{/* @ts-ignore */}
{lang.t(lang.l.transactions.type[transaction?.title])}
{getStatusTitle(transaction?.status, transaction?.title)}
</Text>
</View>
);
Expand Down
13 changes: 11 additions & 2 deletions src/entities/transactions/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ChainId, Network } from '@/chains/types';
import { TransactionResponse } from '@ethersproject/providers';

import { LegacyTransactionGasParams, TransactionGasParams } from '@/entities/gas';
import { Address } from 'viem';

export enum TransactionDirection {
IN = 'in',
Expand Down Expand Up @@ -53,7 +54,7 @@ export enum TransactionStatus {
}

export interface RainbowTransaction {
address?: string;
address?: Address;
asset?:
| (ParsedAsset & {
asset_contract?: {
Expand Down Expand Up @@ -125,9 +126,17 @@ export interface RainbowTransaction {
explorerUrl?: string;
}

export enum FlashbotsStatus {
PENDING = 'PENDING',
INCLUDED = 'INCLUDED',
FAILED = 'FAILED',
CANCELLED = 'CANCELLED',
UNKNOWN = 'UNKNOWN',
}

export type MinedTransaction = RainbowTransaction & {
status: TransactionStatus.confirmed | TransactionStatus.failed;
flashbotsStatus?: 'CANCELLED' | 'FAILED' | 'INCLUDED';
flashbotsStatus?: FlashbotsStatus;
blockNumber: number;
minedAt: number;
confirmations: number;
Expand Down
10 changes: 2 additions & 8 deletions src/entities/transactions/zerionTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ProtocolType } from '../protocolTypes';
import { ZerionAsset } from '../tokens';
import { TransactionDirection, TransactionType } from '@/entities';
import { TransactionDirection, TransactionStatus, TransactionType } from '@/entities';

interface ZerionTransactionFee {
price: number;
Expand All @@ -13,12 +13,6 @@ interface ZerionTransactionMeta {
asset?: ZerionAsset;
}

enum ZerionTransactionStatus {
confirmed = 'confirmed',
failed = 'failed',
pending = 'pending',
}

export interface ZerionTransactionChange {
address_from: string | null;
address_to: string | null;
Expand All @@ -42,6 +36,6 @@ export interface ZerionTransaction {
mined_at: number;
nonce: number | null;
protocol: ProtocolType;
status: ZerionTransactionStatus;
status: TransactionStatus.pending | TransactionStatus.confirmed | TransactionStatus.failed;
type: TransactionType;
}
137 changes: 14 additions & 123 deletions src/handlers/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,16 @@
import { Contract } from '@ethersproject/contracts';
import { isEmpty } from 'lodash';
import { web3Provider } from './web3';
import { metadataClient } from '@/graphql';
import { RainbowTransaction, TransactionStatus, ZerionTransaction } from '@/entities';
import store from '@/redux/store';
import { transactionSignaturesDataAddNewSignature } from '@/redux/transactionSignatures';
import { SIGNATURE_REGISTRY_ADDRESS, signatureRegistryABI } from '@/references';
import { ethereumUtils } from '@/utils';
import { fetchWalletENSAvatars, fetchWalletNames } from '@/redux/wallets';
import { FlashbotsStatus, RainbowTransaction, TransactionStatus } from '@/entities';
import { RainbowFetchClient } from '@/rainbow-fetch';
import { IS_TEST } from '@/env';
import { API_BASE_URL } from '@rainbow-me/swaps';
import { logger, RainbowError } from '@/logger';

const flashbotsApi = new RainbowFetchClient({
baseURL: 'https://protect.flashbots.net',
});

const rainbowSwapsApi = new RainbowFetchClient({
baseURL: API_BASE_URL,
});

const parseSignatureToTitle = (signature: string) => {
const rawName = signature.match(/^([^)(]*)\((.*)\)([^)(]*)$/u);
let parsedName = '';

if (rawName) {
parsedName =
rawName[1].charAt(0).toUpperCase() +
rawName[1]
.slice(1)
.split(/(?=[A-Z])/u)
.join(' ');
}
return parsedName;
};

const timeoutPromise = new Promise((_, reject) => {
setTimeout(reject, 800);
});

export const getTransactionMethodName = async (transaction: ZerionTransaction) => {
try {
const { signatures } = store.getState().transactionSignatures;
// only being used on mainnet transactions, so we can use the default web3 provider
const txn = await web3Provider.getTransaction(transaction.hash);
const bytes = txn?.data?.substring(0, 10) || '';
let signature = signatures[bytes] || '';
if (signature) return signature;
try {
const data = await metadataClient.getContractFunction({
chainID: 1,
hex: bytes,
});

if (data.contractFunction && !isEmpty(data?.contractFunction?.text)) {
signature = data.contractFunction.text;
}
// eslint-disable-next-line no-empty
} catch (e) {}
if (!signature) {
try {
const contract = new Contract(SIGNATURE_REGISTRY_ADDRESS, signatureRegistryABI, web3Provider!);
signature = await Promise.race([contract.entries(bytes), timeoutPromise]);
// eslint-disable-next-line no-empty
} catch (e) {}
}
const parsedSignature = parseSignatureToTitle(signature);
store.dispatch(transactionSignaturesDataAddNewSignature(parsedSignature, bytes));
return parsedSignature;
} catch (e) {
return '';
}
};

type FlashbotsStatus = 'PENDING' | 'INCLUDED' | 'FAILED' | 'CANCELLED' | 'UNKNOWN';

export const getTransactionFlashbotStatus = async (
transaction: RainbowTransaction,
txHash: string
): Promise<{
flashbotsStatus: 'FAILED' | 'CANCELLED' | 'UNKNOWN';
flashbotsStatus: FlashbotsStatus;
status: 'failed';
minedAt: number;
title: string;
Expand All @@ -89,61 +19,22 @@ export const getTransactionFlashbotStatus = async (
const fbStatus = await flashbotsApi.get<{ status: FlashbotsStatus }>(`/tx/${txHash}`);
const flashbotsStatus = fbStatus.data.status;
// Make sure it wasn't dropped after 25 blocks or never made it
if (flashbotsStatus === 'FAILED' || flashbotsStatus === 'CANCELLED' || flashbotsStatus === 'UNKNOWN') {
const status = 'failed';
if (
flashbotsStatus === FlashbotsStatus.FAILED ||
flashbotsStatus === FlashbotsStatus.CANCELLED ||
flashbotsStatus === FlashbotsStatus.UNKNOWN
) {
const status = TransactionStatus.failed;
const minedAt = Math.floor(Date.now() / 1000);
const title = `${transaction.type}.failed`;
const title = `${transaction.type}.${status}`;
return { flashbotsStatus, status, minedAt, title };
}
} catch (e) {
//
}
return null;
};
export const getTransactionSocketStatus = async (pendingTransaction: RainbowTransaction) => {
const { swap } = pendingTransaction;
const txHash = ethereumUtils.getHash(pendingTransaction);
let pending = true;
const minedAt: number | null = Math.floor(Date.now() / 1000);
let status = swap?.isBridge ? TransactionStatus.bridging : TransactionStatus.swapping;
try {
const socketStatus = await rainbowSwapsApi.get('/v1/bridge-status', {
params: {
txHash: txHash || '',
fromChainId: String(swap?.fromChainId),
toChainId: String(swap?.toChainId),
},
logger.error(new RainbowError('[getTransactionFlashbotStatus]: Failed to get flashbots status'), {
error: e,
transaction,
txHash,
});
const socketResponse = socketStatus.data;
if (socketResponse.success) {
if (socketResponse?.result?.sourceTxStatus === 'COMPLETED') {
status = swap?.isBridge ? TransactionStatus.bridging : TransactionStatus.swapping;
}
if (socketResponse?.result?.DestinationTxStatus === 'COMPLETED') {
status = swap?.isBridge ? TransactionStatus.bridged : TransactionStatus.swapped;
pending = false;
}
if (socketResponse?.result?.DestinationTxStatus === 'FAILED' || socketResponse?.result?.sourceTxStatus === 'FAILED') {
status = TransactionStatus.failed;
pending = false;
}
} else if (socketResponse.error) {
logger.warn('[getTransactionSocketStatus]: transaction check failed', socketResponse.error);
status = TransactionStatus.failed;
pending = false;
}
} catch (e) {
logger.error(new RainbowError('[getTransactionSocketStatus]: transaction check caught'));
if (IS_TEST) {
status = swap?.isBridge ? TransactionStatus.bridged : TransactionStatus.swapped;
pending = false;
}
}

return { status, minedAt, pending };
};

export const fetchWalletENSDataAfterRegistration = async () => {
await store.dispatch(fetchWalletENSAvatars());
store.dispatch(fetchWalletNames());
return null;
};
2 changes: 1 addition & 1 deletion src/helpers/buildTransactionsSectionsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type RainbowTransactionWithContact = RainbowTransaction & {

// bad news
const groupTransactionByDate = ({ status, minedAt }: { status: TransactionStatus; minedAt: string }) => {
if (status === 'pending') {
if (status === TransactionStatus.pending) {
return i18n.t(i18n.l.transactions.pending_title);
}

Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useWatchPendingTxs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const useWatchPendingTransactions = ({ address }: { address: string }) =>
});
}

if (updatedTransaction?.status !== 'pending') {
if (updatedTransaction?.status !== TransactionStatus.pending) {
refreshAssets(tx);
}
return updatedTransaction;
Expand Down
4 changes: 2 additions & 2 deletions src/languages/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2127,9 +2127,9 @@
"failed": "Send Failed"
},
"receive": {
"pending": "",
"pending": "Receiving",
"confirmed": "Received",
"failed": ""
"failed": "Receive Failed"
},
"withdraw": {
"pending": "Withdrawing",
Expand Down
4 changes: 2 additions & 2 deletions src/raps/actions/claimBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export async function claimBridge({ parameters, wallet, baseNonce }: ActionProps
value: bridgeQuote.buyAmount.toString(),
},
],
from: bridgeQuote.from as Address,
from: bridgeQuote.from,
to: bridgeQuote.to as Address,
hash: swap.hash as TxHash,
network: chainsName[parameters.chainId],
Expand All @@ -207,7 +207,7 @@ export async function claimBridge({ parameters, wallet, baseNonce }: ActionProps
} satisfies NewTransaction;

addNewTransaction({
address: bridgeQuote.from as Address,
address: bridgeQuote.from,
chainId: parameters.chainId,
transaction,
});
Expand Down
Loading

0 comments on commit fac310e

Please sign in to comment.