diff --git a/RELEASE b/RELEASE index ec089a30129..42cdb1a4684 100644 --- a/RELEASE +++ b/RELEASE @@ -1,6 +1,6 @@ IPFS hash of the deployment: -- CIDv0: `QmWXkZCZCVea5C5dkKAgH2jR3a8LWCFmJGedqiEaK21wHj` -- CIDv1: `bafybeidzwv7ldnoexuakxu7nxwvkvh7bcku5kxdixe7jdgal36mcur7doi` +- CIDv0: `QmZ2woTrzL5shcw9Ek6jqsLnHAMh1KGWGPkbeoMw3toRfC` +- CIDv1: `bafybeie645wzgpsz5isxptb3bdyo67rhtc4dfxjekenod6jvf6jxi7yqe4` The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). @@ -10,15 +10,10 @@ You can also access the Uniswap Interface from an IPFS gateway. Your Uniswap settings are never remembered across different URLs. IPFS gateways: -- https://bafybeidzwv7ldnoexuakxu7nxwvkvh7bcku5kxdixe7jdgal36mcur7doi.ipfs.dweb.link/ -- https://bafybeidzwv7ldnoexuakxu7nxwvkvh7bcku5kxdixe7jdgal36mcur7doi.ipfs.cf-ipfs.com/ -- [ipfs://QmWXkZCZCVea5C5dkKAgH2jR3a8LWCFmJGedqiEaK21wHj/](ipfs://QmWXkZCZCVea5C5dkKAgH2jR3a8LWCFmJGedqiEaK21wHj/) +- https://bafybeie645wzgpsz5isxptb3bdyo67rhtc4dfxjekenod6jvf6jxi7yqe4.ipfs.dweb.link/ +- https://bafybeie645wzgpsz5isxptb3bdyo67rhtc4dfxjekenod6jvf6jxi7yqe4.ipfs.cf-ipfs.com/ +- [ipfs://QmZ2woTrzL5shcw9Ek6jqsLnHAMh1KGWGPkbeoMw3toRfC/](ipfs://QmZ2woTrzL5shcw9Ek6jqsLnHAMh1KGWGPkbeoMw3toRfC/) -### 5.51.1 (2024-10-10) - - -### Bug Fixes - -* **web:** Cab/web 5130 remove extension launch modal prod (#12891) 07a5a21 +### 5.51.2 (2024-10-10) diff --git a/VERSION b/VERSION index d68bfecb8f9..27cf5ad7f7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -web/5.51.1 \ No newline at end of file +web/5.51.2 \ No newline at end of file diff --git a/apps/web/.env.production b/apps/web/.env.production index 1698bad5002..70977d7d386 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -13,4 +13,3 @@ REACT_APP_SENTRY_ENABLED=true REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.003 REACT_APP_STATSIG_PROXY_URL="https://interface.gateway.uniswap.org/v1/statsig-proxy" REACT_APP_IS_UNISWAP_INTERFACE=true -REACT_APP_TRADING_API_KEY=gcZrVL9FxqnqjVytJd2z3oqImkOKRjZ49sF7WXy9 diff --git a/apps/web/src/state/sagas/transactions/swapSaga.ts b/apps/web/src/state/sagas/transactions/swapSaga.ts index ea509c2f8d3..6c8e49ce32b 100644 --- a/apps/web/src/state/sagas/transactions/swapSaga.ts +++ b/apps/web/src/state/sagas/transactions/swapSaga.ts @@ -11,6 +11,7 @@ import { PopupType, addPopup } from 'state/application/reducer' import { handleUniswapXSignatureStep } from 'state/sagas/transactions/uniswapx' import { HandleOnChainStepParams, + addTransactionBreadcrumb, getSwapTransactionInfo, handleApprovalTransactionStep, handleOnChainStep, @@ -271,8 +272,11 @@ function* uniswapXSwap( } function getDisplayableError(error: Error, step: TransactionStep): TransactionError | undefined { + const userRejected = didUserReject(error) // If the user rejects a request, or it's a known interruption e.g. trade update, we handle gracefully / do not show error UI - if (didUserReject(error) || error instanceof HandledTransactionInterrupt) { + if (userRejected || error instanceof HandledTransactionInterrupt) { + const loggableMessage = userRejected ? 'user rejected request' : error.message // for user rejections, avoid logging redundant/long message + addTransactionBreadcrumb({ step, status: 'interrupted', data: { message: loggableMessage } }) return undefined } else if (error instanceof TransactionError) { return error // If the error was already formatted as a TransactionError, we just propagate diff --git a/apps/web/src/state/sagas/transactions/uniswapx.ts b/apps/web/src/state/sagas/transactions/uniswapx.ts index 96aecaa6aaa..42e1da765a1 100644 --- a/apps/web/src/state/sagas/transactions/uniswapx.ts +++ b/apps/web/src/state/sagas/transactions/uniswapx.ts @@ -1,6 +1,11 @@ import { formatSwapSignedAnalyticsEventProperties } from 'lib/utils/analytics' import { PopupType, addPopup } from 'state/application/reducer' -import { HandleSignatureStepParams, getSwapTransactionInfo, handleSignatureStep } from 'state/sagas/transactions/utils' +import { + HandleSignatureStepParams, + addTransactionBreadcrumb, + getSwapTransactionInfo, + handleSignatureStep, +} from 'state/sagas/transactions/utils' import { addSignature } from 'state/signatures/reducer' import { SignatureType, UnfilledUniswapXOrderDetails } from 'state/signatures/types' import { call, put } from 'typed-redux-saga' @@ -49,6 +54,8 @@ export function* handleUniswapXSignatureStep(params: HandleUniswapXSignatureStep throw new HandledTransactionInterrupt('User signed after deadline') } + addTransactionBreadcrumb({ step, data: { routing, ...signatureDetails.swapInfo }, status: 'in progress' }) + try { yield* call(submitOrder, { signature, quote, routing }) } catch (error) { diff --git a/apps/web/src/state/sagas/transactions/utils.ts b/apps/web/src/state/sagas/transactions/utils.ts index 74ddfdde199..bfa99f62d0b 100644 --- a/apps/web/src/state/sagas/transactions/utils.ts +++ b/apps/web/src/state/sagas/transactions/utils.ts @@ -36,6 +36,7 @@ import { isUniswapX } from 'uniswap/src/features/transactions/swap/utils/routing import { interruptTransactionFlow } from 'uniswap/src/utils/saga' import { isSameAddress } from 'utilities/src/addresses' import { percentFromFloat } from 'utilities/src/format/percent' +import { Sentry } from 'utilities/src/logger/Sentry' import noop from 'utilities/src/react/noop' import { currencyId } from 'utils/currencyId' import { signTypedData } from 'utils/signing' @@ -51,6 +52,15 @@ export function* handleSignatureStep({ setCurrentStep, step, ignoreInterrupt, ac // Add a watcher to check if the transaction flow is interrupted during this step const { throwIfInterrupted } = yield* watchForInterruption(ignoreInterrupt) + addTransactionBreadcrumb({ + step, + data: { + domain: JSON.stringify(step.domain), + values: JSON.stringify(step.values), + types: JSON.stringify(step.types), + }, + }) + // Trigger UI prompting user to accept setCurrentStep({ step, accepted: false }) @@ -59,6 +69,8 @@ export function* handleSignatureStep({ setCurrentStep, step, ignoreInterrupt, ac // If the transaction flow was interrupted, throw an error after the step has completed yield* call(throwIfInterrupted) + addTransactionBreadcrumb({ step, data: { signature }, status: 'complete' }) + return signature } @@ -81,12 +93,16 @@ export function* handleOnChainStep(params: Han const { chainId } = step.txRequest const signer = yield* call(getSigner, account.address) + addTransactionBreadcrumb({ step, data: { ...info } }) + // Avoid sending prompting a transaction if the user already submitted an equivalent tx, e.g. by closing and reopening a transaction flow const duplicativeTx = yield* findDuplicativeTx(info, account, chainId, allowDuplicativeTx) if (duplicativeTx) { if (duplicativeTx.status === TransactionStatus.Confirmed) { + addTransactionBreadcrumb({ step, data: { duplicativeTx: true, hash: duplicativeTx.hash }, status: 'complete' }) return duplicativeTx.hash } else { + addTransactionBreadcrumb({ step, data: { duplicativeTx: true, hash: duplicativeTx.hash }, status: 'in progress' }) setCurrentStep({ step, accepted: true }) return yield* handleOnChainConfirmation(params, duplicativeTx.hash) } @@ -139,6 +155,9 @@ function* handleOnChainConfirmation(params: HandleOnChainStepParams, hash: strin if (interrupt) { throw new HandledTransactionInterrupt('Transaction flow was interrupted') } + + addTransactionBreadcrumb({ step, data: { txHash: hash }, status: 'complete' }) + return hash } @@ -267,3 +286,22 @@ export function getSwapTransactionInfo(trade: ClassicTrade | UniswapXTrade): Swa }), } } + +export function addTransactionBreadcrumb({ + step, + data = {}, + status = 'initiated', +}: { + step: TransactionStep + data?: { + [key: string]: string | number | boolean | undefined + } + status?: 'initiated' | 'complete' | 'in progress' | 'interrupted' +}) { + Sentry.addBreadCrumb({ + level: 'info', + category: 'transaction', + message: `${step.type} ${status}`, + data, + }) +} diff --git a/packages/uniswap/src/features/transactions/errors.tsx b/packages/uniswap/src/features/transactions/errors.tsx index bd755fd5045..11d55f15ed1 100644 --- a/packages/uniswap/src/features/transactions/errors.tsx +++ b/packages/uniswap/src/features/transactions/errors.tsx @@ -21,7 +21,11 @@ export class TransactionStepFailedError extends TransactionError { step: TransactionStep isBackendRejection: boolean originalError?: Error - stringified?: string + + // string fields for Sentry + originalErrorStringified?: string + originalErrorString?: string // originalErrorStringified error may get cut off by sentry size limits; this acts as minimal backup + stepStringified?: string constructor({ message, @@ -41,7 +45,9 @@ export class TransactionStepFailedError extends TransactionError { this.originalError = originalError try { - this.stringified = JSON.stringify(this, null, 2) // provides more insight to sentry logs + this.originalErrorString = originalError?.toString() + this.originalErrorStringified = JSON.stringify(originalError, null, 2) + this.stepStringified = JSON.stringify(step, null, 2) } catch {} } }