Skip to content

Commit

Permalink
Merge pull request #223 from lc-labs/feat/mixpanel-base
Browse files Browse the repository at this point in the history
chore: add more mixpanel tracking for zapping errors
  • Loading branch information
larrythecucumber321 authored Oct 18, 2023
2 parents 3db75ce + bd70aca commit c2c27f9
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 37 deletions.
9 changes: 8 additions & 1 deletion src/components/chain-selector/ChainSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Button from 'components/button'
import Base from 'components/icons/logos/Base'
import Ethereum from 'components/icons/logos/Ethereum'
import Popup from 'components/popup'
import mixpanel from 'mixpanel-browser'
import { transition } from 'theme'
import { useAtom, useAtomValue } from 'jotai'
import { useState } from 'react'
Expand Down Expand Up @@ -80,7 +81,13 @@ const ChainList = ({ onSelect }: { onSelect(chain: number): void }) => {
)
})}
<Box p={3} sx={{ borderTop: '1px solid', borderColor: 'darkBorder' }}>
<Button variant="muted" onClick={() => navigate(ROUTES.BRIDGE)}>
<Button
variant="muted"
onClick={() => {
mixpanel.track('Clicked Bridge', {})
navigate(ROUTES.BRIDGE)
}}
>
Bridge assets
</Button>
</Box>
Expand Down
12 changes: 10 additions & 2 deletions src/views/bridge/components/ConfirmBridge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TransactionButton from 'components/button/TransactionButton'
import useContractWrite from 'hooks/useContractWrite'
import useHasAllowance from 'hooks/useHasAllowance'
import useWatchTransaction from 'hooks/useWatchTransaction'
import { atom, useAtomValue, useSetAtom } from 'jotai'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useCallback, useEffect, useState } from 'react'
import { safeParseEther } from 'utils'
import { Address } from 'viem'
Expand All @@ -16,6 +16,7 @@ import {
} from '../atoms'
import { Modal } from 'components'
import { Box, Divider, Text } from 'theme-ui'
import mixpanel from 'mixpanel-browser'

const btnLabelAtom = atom((get) => {
const token = get(selectedTokenAtom)
Expand Down Expand Up @@ -78,7 +79,9 @@ const ApproveBtn = () => {

const ConfirmBridgeBtn = ({ onSuccess }: { onSuccess(): void }) => {
const bridgeTransaction = useAtomValue(bridgeTxAtom)
const setAmount = useSetAtom(bridgeAmountAtom)
const bridgeToken = useAtomValue(selectedTokenAtom)
const isWrapping = useAtomValue(isBridgeWrappingAtom)
const [amount, setAmount] = useAtom(bridgeAmountAtom)
const {
isReady,
gas,
Expand All @@ -96,6 +99,11 @@ const ConfirmBridgeBtn = ({ onSuccess }: { onSuccess(): void }) => {

useEffect(() => {
if (status === 'success') {
mixpanel.track('Bridge Success', {
Token: bridgeToken.symbol,
Amount: amount,
Destination: isWrapping ? 'Base' : 'Ethereum',
})
setAmount('')
reset()
onSuccess()
Expand Down
14 changes: 11 additions & 3 deletions src/views/issuance/components/zap/state/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import { resolvedZapState, zappableTokens } from './zapper'
import { type SearcherResult } from '@reserve-protocol/token-zapper/types/searcher/SearcherResult'
import { type ZapTransaction } from '@reserve-protocol/token-zapper/types/searcher/ZapTransaction'
import mixpanel from 'mixpanel-browser'

/**
* I've tried to keep react effects to a minimum so most async code is triggered via some signal
Expand All @@ -44,10 +45,10 @@ import { type ZapTransaction } from '@reserve-protocol/token-zapper/types/search
*/

export const previousZapTransaction = atom<{
result: SearcherResult,
transaction: ZapTransaction,
result: SearcherResult
transaction: ZapTransaction
permit2?: {
permit: PermitTransferFrom,
permit: PermitTransferFrom
signature: string
}
} | null>(null)
Expand Down Expand Up @@ -331,6 +332,8 @@ export const resolvedZapTransaction = simplifyLoadable(zapTransaction)

export const zapTransactionGasEstimateUnits = loadable(
onlyNonNullAtom(async (get) => {
const selectedZapToken = get(selectedZapTokenAtom)
const rToken = get(rTokenAtom)
const tx = get(resolvedZapTransaction)
const needsApproval = get(resolvedApprovalNeeded)
if (
Expand All @@ -357,6 +360,11 @@ export const zapTransactionGasEstimateUnits = loadable(
continue
}
}

mixpanel.track('Failed to estimate gas for zapping', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: selectedZapToken.symbol,
})
throw new Error('Failed to estimate gas')
})
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {
createOneInchDexAggregator,
DexAggregator,
Universe
Universe,
} from '@reserve-protocol/token-zapper'
import mixpanel from 'mixpanel-browser'

/**
* Creates a 1inch aggregator that will use a list of proxies to make requests
Expand Down Expand Up @@ -42,7 +43,7 @@ export const createProxiedOneInchAggregator = (
const instances = [...aggregatorInstances]
for (let i = instances.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[instances[i], instances[j]] = [instances[j], instances[i]]
;[instances[i], instances[j]] = [instances[j], instances[i]]
}
return instances
}
Expand All @@ -64,6 +65,10 @@ export const createProxiedOneInchAggregator = (
continue
}
}
mixpanel.track('All 1inch aggregators failed', {
RToken: output.address.toString().toLowerCase() ?? '',
inputToken: input.token.symbol,
})
throw new Error('All aggregators failed')
}
)
Expand Down
76 changes: 59 additions & 17 deletions src/views/issuance/components/zap/state/ui-atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { Token, TokenQuantity } from '@reserve-protocol/token-zapper'
import { atom, Getter, SetStateAction, Setter } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
import { Atom } from 'jotai/vanilla'
import { ethPriceAtom, gasFeeAtom, gasPriceAtom, isWalletModalVisibleAtom, rTokenAtom } from 'state/atoms'
import {
ethPriceAtom,
gasFeeAtom,
gasPriceAtom,
isWalletModalVisibleAtom,
rTokenAtom,
} from 'state/atoms'
import { onlyNonNullAtom } from 'utils/atoms/utils'

import { notifyError, notifySuccess } from 'hooks/useNotification'
Expand Down Expand Up @@ -99,7 +105,11 @@ export const zapTxFeeAtom = atom((get) => {
const gasPrice = get(gasFeeAtom)
const gasUsdPrice = get(ethPriceAtom)
return tx?.transaction?.gasEstimate
? Number(tx.result.universe.nativeToken.from(tx.transaction.feeEstimate(gasPrice ?? 1n)).format()) * gasUsdPrice
? Number(
tx.result.universe.nativeToken
.from(tx.transaction.feeEstimate(gasPrice ?? 1n))
.format()
) * gasUsdPrice
: 0
})

Expand Down Expand Up @@ -134,7 +144,9 @@ export const zapDust = atom((get) => {

const rTokenOut = quote.outputToken

const dust = quote.swaps.outputs.filter(i => i.token !== rTokenOut && i.amount !== 0n)
const dust = quote.swaps.outputs.filter(
(i) => i.token !== rTokenOut && i.amount !== 0n
)
return dust
})

Expand All @@ -148,18 +160,20 @@ export const zapDustValue = atom(async (get) => {
if (quote == null) {
return null
}
const dustUSD = await Promise.all(dust.map(async d => ({
dustQuantity: d,
usdValueOfDust: await quote.universe.fairPrice(d)
})))
const dustUSD = await Promise.all(
dust.map(async (d) => ({
dustQuantity: d,
usdValueOfDust: await quote.universe.fairPrice(d),
}))
)

let total = 0n
for (const d of dustUSD) {
total += d.usdValueOfDust?.amount ?? 0n
}
return {
dust: dustUSD,
total: quote.universe.usd.from(total)
total: quote.universe.usd.from(total),
}
})

Expand All @@ -172,7 +186,6 @@ export const zapOutputValue = onlyNonNullAtom((get) => {
return qty.format()
}, '0')


const state = atom((get) => {
const quotePromise = get(zapQuotePromise)
const approvePromise = get(approvalNeededAtom)
Expand Down Expand Up @@ -243,7 +256,10 @@ const loadingStates = new Set<UIState>([
// 'approval_sent_loading',
'signature_loading',
])
const buttonEnabled = atom((get) => buttonEnabledStates.has(get(state)) || get(previousZapTransaction) != null)
const buttonEnabled = atom(
(get) =>
buttonEnabledStates.has(get(state)) || get(previousZapTransaction) != null
)
const buttonIsLoading = atom((get) => loadingStates.has(get(state)))
const buttonLabel = atom((get) => {
if (get(zapSender) == null) {
Expand Down Expand Up @@ -300,7 +316,7 @@ const zapEnabledForRTokens = new Set<string>([
'0x9b451beb49a03586e6995e5a93b9c745d068581e',
'0xfc0b1eef20e4c68b3dcf36c4537cfa7ce46ca70b',
'0x50249c768a6d3cb4b6565c0a2bfbdb62be94915c',
'0xcc7ff230365bd730ee4b352cc2492cedac49383e'
'0xcc7ff230365bd730ee4b352cc2492cedac49383e',
])

export const zapEnabledAtom = atomWithStorage('zap-enabled', false)
Expand Down Expand Up @@ -375,7 +391,7 @@ export const ui = {
button: atom(
(get) => ({
loadingLabel: get(buttonLoadingLabel),
enabled: (get(zapSender) == null || get(buttonEnabled)),
enabled: get(zapSender) == null || get(buttonEnabled),
loading: get(previousZapTransaction) == null && get(buttonIsLoading),
label: get(buttonLabel),
}),
Expand All @@ -391,10 +407,9 @@ export const ui = {
}
if (flowState === 'tx_loading') {
errors = 0
}
else if (flowState === 'tx_error') {
} else if (flowState === 'tx_error') {
if (errors < 5) {
console.log("Requoting..")
console.log('Requoting..')
set(redoQuote, Math.random())
errors += 1
}
Expand Down Expand Up @@ -487,7 +502,7 @@ const resetTxAtoms = (set: Setter) => {
const signAndSendTx: ZapperAction = async (
get,
set,
{ signer, provider, rToken, quote }
{ signer, provider, rToken, quote, inputToken }
) => {
try {
const permit = get(permit2ToSignAtom)
Expand Down Expand Up @@ -526,10 +541,23 @@ const signAndSendTx: ZapperAction = async (
set(zapTxHash, resp.hash)
set(zapInputString, '')
set(addTransactionAtom, [resp.hash, `Easy mint ${rToken.symbol}`])
mixpanel.track('Zap Success', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: inputToken.symbol,
})
} catch (e: any) {
if (e.code === 'ACTION_REJECTED') {
mixpanel.track('User Rejected Zap', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: inputToken.symbol,
})
notifyError('Zap failed', 'User rejected signature request')
} else {
mixpanel.track('Zap Execution Error', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: inputToken.symbol,
error: e,
})
notifyError('Zap failed', 'Unknown error ' + e.code)
}
} finally {
Expand All @@ -539,7 +567,7 @@ const signAndSendTx: ZapperAction = async (
const sendTx: ZapperAction = async (
get,
set,
{ inputQuantity, rToken, signer }
{ inputToken, rToken, signer }
) => {
const zapTx = get(resolvedZapTransaction)
const gasLimit = get(resolvedZapTransactionGasEstimateUnits)
Expand All @@ -558,10 +586,24 @@ const sendTx: ZapperAction = async (
set(permitSignature, null)
set(zapInputString, '')
set(addTransactionAtom, [resp.hash, `Easy mint ${rToken.symbol}`])

mixpanel.track('Zap Success', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: inputToken.symbol,
})
} catch (e: any) {
if (e.code === 'ACTION_REJECTED') {
mixpanel.track('User Rejected Zap', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: inputToken.symbol,
})
notifyError('Zap failed', 'User rejected')
} else {
mixpanel.track('Zap Execution Error', {
RToken: rToken.address.toString().toLowerCase() ?? '',
inputToken: inputToken.symbol,
error: e,
})
notifyError('Zap failed', 'Unknown error ' + e.code)
}
} finally {
Expand Down
Loading

0 comments on commit c2c27f9

Please sign in to comment.