Commit fb079198 authored by Moody Salem's avatar Moody Salem Committed by GitHub

refactor: transaction info is translated in the appropriate places (#2380)

* refactor: transaction info is translated in the appropriate places

fixes https://github.com/Uniswap/interface/issues/1756

* getting closer

* more work

* finished, finally

* bit more refactoring

* move summary into its own file

* little more cleanup in the transaction summary file

* fix bad copy

* fix the migrate notification

* missing translation

* fix the language for vote and address other pr comments

* fix typo

* - remove old transactions with this update
- change to present tense
- show ens name where appropriate

* add a test that shows we don't clear old ones
parent 9fa3b704
import styled from 'styled-components/macro'
import { CheckCircle, Triangle } from 'react-feather' import { CheckCircle, Triangle } from 'react-feather'
import styled from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink } from '../../theme'
import { useAllTransactions } from '../../state/transactions/hooks' import { useAllTransactions } from '../../state/transactions/hooks'
import { ExternalLink } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { RowFixed } from '../Row'
import Loader from '../Loader' import Loader from '../Loader'
import { RowFixed } from '../Row'
const TransactionWrapper = styled.div`` import { TransactionSummary } from './TransactionSummary'
const TransactionStatusText = styled.div` const TransactionStatusText = styled.div`
margin-right: 0.5rem; margin-right: 0.5rem;
...@@ -40,26 +39,28 @@ export default function Transaction({ hash }: { hash: string }) { ...@@ -40,26 +39,28 @@ export default function Transaction({ hash }: { hash: string }) {
const allTransactions = useAllTransactions() const allTransactions = useAllTransactions()
const tx = allTransactions?.[hash] const tx = allTransactions?.[hash]
const summary = tx?.summary const info = tx?.info
const pending = !tx?.receipt const pending = !tx?.receipt
const success = !pending && tx && (tx.receipt?.status === 1 || typeof tx.receipt?.status === 'undefined') const success = !pending && tx && (tx.receipt?.status === 1 || typeof tx.receipt?.status === 'undefined')
if (!chainId) return null if (!chainId) return null
return ( return (
<TransactionWrapper> <div>
<TransactionState <TransactionState
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)} href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
pending={pending} pending={pending}
success={success} success={success}
> >
<RowFixed> <RowFixed>
<TransactionStatusText>{summary ?? hash}</TransactionStatusText> <TransactionStatusText>
<TransactionSummary info={info} />
</TransactionStatusText>
</RowFixed> </RowFixed>
<IconWrapper pending={pending} success={success}> <IconWrapper pending={pending} success={success}>
{pending ? <Loader /> : success ? <CheckCircle size="16" /> : <Triangle size="16" />} {pending ? <Loader /> : success ? <CheckCircle size="16" /> : <Triangle size="16" />}
</IconWrapper> </IconWrapper>
</TransactionState> </TransactionState>
</TransactionWrapper> </div>
) )
} }
import { Trans } from '@lingui/macro'
import { Fraction, TradeType } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { useCurrency, useToken } from '../../hooks/Tokens'
import useENSName from '../../hooks/useENSName'
import { VoteOption } from '../../state/governance/types'
import {
AddLiquidityV2PoolTransactionInfo,
AddLiquidityV3PoolTransactionInfo,
ApproveTransactionInfo,
ClaimTransactionInfo,
CollectFeesTransactionInfo,
CreateV3PoolTransactionInfo,
DelegateTransactionInfo,
DepositLiquidityStakingTransactionInfo,
ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo,
MigrateV2LiquidityToV3TransactionInfo,
RemoveLiquidityV3TransactionInfo,
SubmitProposalTransactionInfo,
TransactionInfo,
TransactionType,
VoteTransactionInfo,
WithdrawLiquidityStakingTransactionInfo,
WrapTransactionInfo,
} from '../../state/transactions/actions'
function formatAmount(amountRaw: string, decimals: number, sigFigs: number): string {
return new Fraction(amountRaw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))).toSignificant(sigFigs)
}
function FormattedCurrencyAmount({
rawAmount,
symbol,
decimals,
sigFigs,
}: {
rawAmount: string
symbol: string
decimals: number
sigFigs: number
}) {
return (
<>
{formatAmount(rawAmount, decimals, sigFigs)} {symbol}
</>
)
}
function FormattedCurrencyAmountManaged({
rawAmount,
currencyId,
sigFigs = 6,
}: {
rawAmount: string
currencyId: string
sigFigs: number
}) {
const currency = useCurrency(currencyId)
return currency ? (
<FormattedCurrencyAmount
rawAmount={rawAmount}
decimals={currency.decimals}
sigFigs={sigFigs}
symbol={currency.symbol ?? '???'}
/>
) : null
}
function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransactionInfo }) {
const { ENSName } = useENSName()
return typeof uniAmountRaw === 'string' ? (
<Trans>
Claim <FormattedCurrencyAmount rawAmount={uniAmountRaw} symbol={'UNI'} decimals={18} sigFigs={4} /> for{' '}
{ENSName ?? recipient}
</Trans>
) : (
<Trans>Claim UNI reward for {ENSName ?? recipient}</Trans>
)
}
function SubmitProposalTransactionSummary({}: { info: SubmitProposalTransactionInfo }) {
return <Trans>Submit new proposal</Trans>
}
function ApprovalSummary({ info }: { info: ApproveTransactionInfo }) {
const token = useToken(info.tokenAddress)
return <Trans>Approve {token?.symbol}</Trans>
}
function VoteSummary({ info }: { info: VoteTransactionInfo }) {
const proposalKey = `${info.governorAddress}/${info.proposalId}`
if (info.reason && info.reason.trim().length > 0) {
switch (info.decision) {
case VoteOption.For:
return <Trans>Vote for proposal {proposalKey}</Trans>
case VoteOption.Abstain:
return <Trans>Vote to abstain on proposal {proposalKey}</Trans>
case VoteOption.Against:
return <Trans>Vote against proposal {proposalKey}</Trans>
}
} else {
switch (info.decision) {
case VoteOption.For:
return (
<Trans>
Vote for proposal {proposalKey} with reason &quot;{info.reason}&quot;
</Trans>
)
case VoteOption.Abstain:
return (
<Trans>
Vote to abstain on proposal {proposalKey} with reason &quot;{info.reason}&quot;
</Trans>
)
case VoteOption.Against:
return (
<Trans>
Vote against proposal {proposalKey} with reason &quot;{info.reason}&quot;
</Trans>
)
}
}
}
function DelegateSummary({ info: { delegatee } }: { info: DelegateTransactionInfo }) {
const { ENSName } = useENSName(delegatee)
return <Trans>Delegate voting power to {ENSName ?? delegatee}</Trans>
}
function WrapSummary({ info: { currencyAmountRaw, unwrapped } }: { info: WrapTransactionInfo }) {
if (unwrapped) {
return (
<Trans>
Unwrap <FormattedCurrencyAmount rawAmount={currencyAmountRaw} symbol={'WETH'} decimals={18} sigFigs={6} /> to
ETH
</Trans>
)
} else {
return (
<Trans>
Wrap <FormattedCurrencyAmount rawAmount={currencyAmountRaw} symbol={'ETH'} decimals={18} sigFigs={6} /> to WETH
</Trans>
)
}
}
function DepositLiquidityStakingSummary({}: { info: DepositLiquidityStakingTransactionInfo }) {
// not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts
// todo: deprecate and delete the code paths that allow this, show user more information
return <Trans>Deposit liquidity</Trans>
}
function WithdrawLiquidityStakingSummary({}: { info: WithdrawLiquidityStakingTransactionInfo }) {
return <Trans>Withdraw deposited liquidity</Trans>
}
function MigrateLiquidityToV3Summary({
info: { baseCurrencyId, quoteCurrencyId },
}: {
info: MigrateV2LiquidityToV3TransactionInfo
}) {
const baseCurrency = useCurrency(baseCurrencyId)
const quoteCurrency = useCurrency(quoteCurrencyId)
return (
<Trans>
Migrate {baseCurrency?.symbol}/{quoteCurrency?.symbol} liquidity to V3
</Trans>
)
}
function CreateV3PoolSummary({ info: { quoteCurrencyId, baseCurrencyId } }: { info: CreateV3PoolTransactionInfo }) {
const baseCurrency = useCurrency(baseCurrencyId)
const quoteCurrency = useCurrency(quoteCurrencyId)
return (
<Trans>
Create {baseCurrency?.symbol}/{quoteCurrency?.symbol} V3 pool
</Trans>
)
}
function CollectFeesSummary({ info: { currencyId0, currencyId1 } }: { info: CollectFeesTransactionInfo }) {
const currency0 = useCurrency(currencyId0)
const currency1 = useCurrency(currencyId1)
return (
<Trans>
Collect {currency0?.symbol}/{currency1?.symbol} fees
</Trans>
)
}
function RemoveLiquidityV3Summary({
info: { baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw },
}: {
info: RemoveLiquidityV3TransactionInfo
}) {
return (
<Trans>
Remove{' '}
<FormattedCurrencyAmountManaged rawAmount={expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={3} /> and{' '}
<FormattedCurrencyAmountManaged rawAmount={expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={3} />
</Trans>
)
}
function AddLiquidityV3PoolSummary({
info: { createPool, quoteCurrencyId, baseCurrencyId },
}: {
info: AddLiquidityV3PoolTransactionInfo
}) {
const baseCurrency = useCurrency(baseCurrencyId)
const quoteCurrency = useCurrency(quoteCurrencyId)
return createPool ? (
<Trans>
Create pool and add {baseCurrency?.symbol}/{quoteCurrency?.symbol} V3 liquidity
</Trans>
) : (
<Trans>
Add {baseCurrency?.symbol}/{quoteCurrency?.symbol} V3 liquidity
</Trans>
)
}
function AddLiquidityV2PoolSummary({
info: { quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw, baseCurrencyId },
}: {
info: AddLiquidityV2PoolTransactionInfo
}) {
return (
<Trans>
Add <FormattedCurrencyAmountManaged rawAmount={expectedAmountBaseRaw} currencyId={baseCurrencyId} sigFigs={3} />{' '}
and <FormattedCurrencyAmountManaged rawAmount={expectedAmountQuoteRaw} currencyId={quoteCurrencyId} sigFigs={3} />{' '}
to Uniswap V2
</Trans>
)
}
function SwapSummary({ info }: { info: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo }) {
if (info.tradeType === TradeType.EXACT_INPUT) {
return (
<Trans>
Swap exactly{' '}
<FormattedCurrencyAmountManaged
rawAmount={info.inputCurrencyAmountRaw}
currencyId={info.inputCurrencyId}
sigFigs={6}
/>{' '}
for{' '}
<FormattedCurrencyAmountManaged
rawAmount={info.expectedOutputCurrencyAmountRaw}
currencyId={info.outputCurrencyId}
sigFigs={6}
/>
</Trans>
)
} else {
return (
<Trans>
Swap{' '}
<FormattedCurrencyAmountManaged
rawAmount={info.expectedInputCurrencyAmountRaw}
currencyId={info.inputCurrencyId}
sigFigs={6}
/>{' '}
for exactly{' '}
<FormattedCurrencyAmountManaged
rawAmount={info.outputCurrencyAmountRaw}
currencyId={info.outputCurrencyId}
sigFigs={6}
/>
</Trans>
)
}
}
export function TransactionSummary({ info }: { info: TransactionInfo }) {
switch (info.type) {
case TransactionType.ADD_LIQUIDITY_V3_POOL:
return <AddLiquidityV3PoolSummary info={info} />
case TransactionType.ADD_LIQUIDITY_V2_POOL:
return <AddLiquidityV2PoolSummary info={info} />
case TransactionType.CLAIM:
return <ClaimSummary info={info} />
case TransactionType.DEPOSIT_LIQUIDITY_STAKING:
return <DepositLiquidityStakingSummary info={info} />
case TransactionType.WITHDRAW_LIQUIDITY_STAKING:
return <WithdrawLiquidityStakingSummary info={info} />
case TransactionType.SWAP:
return <SwapSummary info={info} />
case TransactionType.APPROVAL:
return <ApprovalSummary info={info} />
case TransactionType.VOTE:
return <VoteSummary info={info} />
case TransactionType.DELEGATE:
return <DelegateSummary info={info} />
case TransactionType.WRAP:
return <WrapSummary info={info} />
case TransactionType.CREATE_V3_POOL:
return <CreateV3PoolSummary info={info} />
case TransactionType.MIGRATE_LIQUIDITY_V3:
return <MigrateLiquidityToV3Summary info={info} />
case TransactionType.COLLECT_FEES:
return <CollectFeesSummary info={info} />
case TransactionType.REMOVE_LIQUIDITY_V3:
return <RemoveLiquidityV3Summary info={info} />
case TransactionType.SUBMIT_PROPOSAL:
return <SubmitProposalTransactionSummary info={info} />
}
}
...@@ -73,9 +73,9 @@ export default function PopupItem({ ...@@ -73,9 +73,9 @@ export default function PopupItem({
let popupContent let popupContent
if ('txn' in content) { if ('txn' in content) {
const { const {
txn: { hash, success, summary }, txn: { hash },
} = content } = content
popupContent = <TransactionPopup hash={hash} success={success} summary={summary} /> popupContent = <TransactionPopup hash={hash} />
} }
const faderStyle = useSpring({ const faderStyle = useSpring({
......
...@@ -2,9 +2,11 @@ import { useContext } from 'react' ...@@ -2,9 +2,11 @@ import { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather' import { AlertCircle, CheckCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro' import styled, { ThemeContext } from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { useTransaction } from '../../state/transactions/hooks'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components' import { ExternalLink } from '../../theme/components'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
import { AutoRow } from '../Row' import { AutoRow } from '../Row'
...@@ -12,26 +14,24 @@ const RowNoFlex = styled(AutoRow)` ...@@ -12,26 +14,24 @@ const RowNoFlex = styled(AutoRow)`
flex-wrap: nowrap; flex-wrap: nowrap;
` `
export default function TransactionPopup({ export default function TransactionPopup({ hash }: { hash: string }) {
hash,
success,
summary,
}: {
hash: string
success?: boolean
summary?: string
}) {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
const tx = useTransaction(hash)
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
if (!tx) return null
const success = Boolean(tx.receipt && tx.receipt.status === 1)
return ( return (
<RowNoFlex> <RowNoFlex>
<div style={{ paddingRight: 16 }}> <div style={{ paddingRight: 16 }}>
{success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />} {success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />}
</div> </div>
<AutoColumn gap="8px"> <AutoColumn gap="8px">
<TYPE.body fontWeight={500}>{summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.body> <TYPE.body fontWeight={500}>
<TransactionSummary info={tx.info} />
</TYPE.body>
{chainId && ( {chainId && (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}> <ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
View on Explorer View on Explorer
......
...@@ -2,6 +2,7 @@ import { Currency } from '@uniswap/sdk-core' ...@@ -2,6 +2,7 @@ import { Currency } from '@uniswap/sdk-core'
import { ReactNode, useContext } from 'react' import { ReactNode, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components/macro' import styled, { ThemeContext } from 'styled-components/macro'
import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink'
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
import Modal from '../Modal' import Modal from '../Modal'
import { ExternalLink } from '../../theme' import { ExternalLink } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
...@@ -273,7 +274,7 @@ function L2Content({ ...@@ -273,7 +274,7 @@ function L2Content({
)} )}
</Text> </Text>
<Text fontWeight={400} fontSize={16} textAlign="center"> <Text fontWeight={400} fontSize={16} textAlign="center">
{transaction?.summary ?? pendingText ?? ''} {transaction ? <TransactionSummary info={transaction.info} /> : pendingText}
</Text> </Text>
{chainId && hash ? ( {chainId && hash ? (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}> <ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
......
...@@ -188,7 +188,7 @@ export default function ClaimModal() { ...@@ -188,7 +188,7 @@ export default function ClaimModal() {
<AutoColumn gap="100px" justify={'center'}> <AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}> <AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader fontWeight={600} color="black"> <TYPE.largeHeader fontWeight={600} color="black">
{claimConfirmed ? 'Claimed!' : 'Claiming'} {claimConfirmed ? <Trans>Claimed!</Trans> : <Trans>Claiming</Trans>}
</TYPE.largeHeader> </TYPE.largeHeader>
{!claimConfirmed && ( {!claimConfirmed && (
<Text fontSize={36} color={'#ff007a'} fontWeight={800}> <Text fontSize={36} color={'#ff007a'} fontWeight={800}>
......
import { TransactionResponse } from '@ethersproject/providers'
import { t, Trans } from '@lingui/macro'
import { useState } from 'react' import { useState } from 'react'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon } from '../../theme'
import { ButtonError } from '../Button'
import { StakingInfo } from '../../state/stake/hooks'
import { useStakingContract } from '../../hooks/useContract' import { useStakingContract } from '../../hooks/useContract'
import { SubmittedView, LoadingView } from '../ModalViews'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { t, Trans } from '@lingui/macro' import { StakingInfo } from '../../state/stake/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { CloseIcon, TYPE } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import Modal from '../Modal'
import { LoadingView, SubmittedView } from '../ModalViews'
import { RowBetween } from '../Row'
const ContentWrapper = styled(AutoColumn)` const ContentWrapper = styled(AutoColumn)`
width: 100%; width: 100%;
...@@ -41,12 +42,15 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta ...@@ -41,12 +42,15 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress) const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onClaimReward() { async function onClaimReward() {
if (stakingContract && stakingInfo?.stakedAmount) { if (stakingContract && stakingInfo?.stakedAmount && account) {
setAttempting(true) setAttempting(true)
await stakingContract await stakingContract
.getReward({ gasLimit: 350000 }) .getReward({ gasLimit: 350000 })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { summary: t`Claim accumulated UNI rewards` }) addTransaction(response, {
type: TransactionType.CLAIM,
recipient: account,
})
setHash(response.hash) setHash(response.hash)
}) })
.catch((error: any) => { .catch((error: any) => {
......
import { useState, useCallback } from 'react' import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useCallback, useState } from 'react'
import styled from 'styled-components/macro'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { usePairContract, useStakingContract, useV2RouterContract } from '../../hooks/useContract'
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit' import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
import useTransactionDeadline from '../../hooks/useTransactionDeadline' import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components/macro'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon } from '../../theme'
import { ButtonConfirmed, ButtonError } from '../Button'
import ProgressCircles from '../ProgressSteps'
import CurrencyInputPanel from '../CurrencyInputPanel'
import { Pair } from '@uniswap/v2-sdk'
import { Token, CurrencyAmount } from '@uniswap/sdk-core'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { usePairContract, useStakingContract, useV2RouterContract } from '../../hooks/useContract'
import { useApproveCallback, ApprovalState } from '../../hooks/useApproveCallback'
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks' import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { CloseIcon, TYPE } from '../../theme'
import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { ButtonConfirmed, ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import CurrencyInputPanel from '../CurrencyInputPanel'
import Modal from '../Modal'
import { LoadingView, SubmittedView } from '../ModalViews' import { LoadingView, SubmittedView } from '../ModalViews'
import { t, Trans } from '@lingui/macro' import ProgressCircles from '../ProgressSteps'
import { RowBetween } from '../Row'
const HypotheticalRewardRate = styled.div<{ dim: boolean }>` const HypotheticalRewardRate = styled.div<{ dim: boolean }>`
display: flex; display: flex;
...@@ -105,7 +106,9 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui ...@@ -105,7 +106,9 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
) )
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: t`Deposit liquidity`, type: TransactionType.DEPOSIT_LIQUIDITY_STAKING,
token0Address: stakingInfo.tokens[0].address,
token1Address: stakingInfo.tokens[1].address,
}) })
setHash(response.hash) setHash(response.hash)
}) })
......
import { TransactionResponse } from '@ethersproject/providers'
import { t, Trans } from '@lingui/macro'
import { useState } from 'react' import { useState } from 'react'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon } from '../../theme'
import { ButtonError } from '../Button'
import { StakingInfo } from '../../state/stake/hooks'
import { useStakingContract } from '../../hooks/useContract' import { useStakingContract } from '../../hooks/useContract'
import { SubmittedView, LoadingView } from '../ModalViews' import { useActiveWeb3React } from '../../hooks/web3'
import { TransactionResponse } from '@ethersproject/providers' import { StakingInfo } from '../../state/stake/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { CloseIcon, TYPE } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import FormattedCurrencyAmount from '../FormattedCurrencyAmount' import FormattedCurrencyAmount from '../FormattedCurrencyAmount'
import { useActiveWeb3React } from '../../hooks/web3' import Modal from '../Modal'
import { t, Trans } from '@lingui/macro' import { LoadingView, SubmittedView } from '../ModalViews'
import { RowBetween } from '../Row'
const ContentWrapper = styled(AutoColumn)` const ContentWrapper = styled(AutoColumn)`
width: 100%; width: 100%;
...@@ -48,7 +49,9 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki ...@@ -48,7 +49,9 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
.exit({ gasLimit: 300000 }) .exit({ gasLimit: 300000 })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: t`Withdraw deposited liquidity`, type: TransactionType.WITHDRAW_LIQUIDITY_STAKING,
token0Address: stakingInfo.tokens[0].address,
token1Address: stakingInfo.tokens[1].address,
}) })
setHash(response.hash) setHash(response.hash)
}) })
......
import { useState, useContext } from 'react' import { useState, useContext } from 'react'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { VoteOption } from '../../state/governance/types'
import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink'
import Modal from '../Modal' import Modal from '../Modal'
...@@ -10,7 +11,7 @@ import { TYPE, CustomLightSpinner } from '../../theme' ...@@ -10,7 +11,7 @@ import { TYPE, CustomLightSpinner } from '../../theme'
import { X, ArrowUpCircle } from 'react-feather' import { X, ArrowUpCircle } from 'react-feather'
import { ButtonPrimary } from '../Button' import { ButtonPrimary } from '../Button'
import Circle from '../../assets/images/blue-loader.svg' import Circle from '../../assets/images/blue-loader.svg'
import { useVoteCallback, useUserVotes, VoteOption } from '../../state/governance/hooks' import { useVoteCallback, useUserVotes } from '../../state/governance/hooks'
import { ExternalLink } from '../../theme/components' import { ExternalLink } from '../../theme/components'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
......
...@@ -5,6 +5,7 @@ import { Trade as V2Trade } from '@uniswap/v2-sdk' ...@@ -5,6 +5,7 @@ import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { SWAP_ROUTER_ADDRESSES, V2_ROUTER_ADDRESS } from '../constants/addresses' import { SWAP_ROUTER_ADDRESSES, V2_ROUTER_ADDRESS } from '../constants/addresses'
import { TransactionType } from '../state/transactions/actions'
import { useTransactionAdder, useHasPendingApproval } from '../state/transactions/hooks' import { useTransactionAdder, useHasPendingApproval } from '../state/transactions/hooks'
import { calculateGasMargin } from '../utils/calculateGasMargin' import { calculateGasMargin } from '../utils/calculateGasMargin'
import { useTokenContract } from './useContract' import { useTokenContract } from './useContract'
...@@ -88,10 +89,7 @@ export function useApproveCallback( ...@@ -88,10 +89,7 @@ export function useApproveCallback(
gasLimit: calculateGasMargin(chainId, estimatedGas), gasLimit: calculateGasMargin(chainId, estimatedGas),
}) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress: token.address, spender })
summary: 'Approve ' + amountToApprove.currency.symbol,
approval: { tokenAddress: token.address, spender },
})
}) })
.catch((error: Error) => { .catch((error: Error) => {
console.debug('Failed to approve token', error) console.debug('Failed to approve token', error)
......
...@@ -5,11 +5,11 @@ import { SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk' ...@@ -5,11 +5,11 @@ import { SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useMemo } from 'react' import { useMemo } from 'react'
import { SWAP_ROUTER_ADDRESSES } from '../constants/addresses' import { SWAP_ROUTER_ADDRESSES } from '../constants/addresses'
import { TransactionType } from '../state/transactions/actions'
import { calculateGasMargin } from '../utils/calculateGasMargin' import { calculateGasMargin } from '../utils/calculateGasMargin'
import approveAmountCalldata from '../utils/approveAmountCalldata' import approveAmountCalldata from '../utils/approveAmountCalldata'
import { getTradeVersion } from '../utils/getTradeVersion'
import { useTransactionAdder } from '../state/transactions/hooks' import { useTransactionAdder } from '../state/transactions/hooks'
import { isAddress, shortenAddress } from '../utils' import { currencyId } from '../utils/currencyId'
import isZero from '../utils/isZero' import isZero from '../utils/isZero'
import { useActiveWeb3React } from './web3' import { useActiveWeb3React } from './web3'
import { useArgentWalletContract } from './useArgentWalletContract' import { useArgentWalletContract } from './useArgentWalletContract'
...@@ -17,7 +17,6 @@ import { useV2RouterContract } from './useContract' ...@@ -17,7 +17,6 @@ import { useV2RouterContract } from './useContract'
import { SignatureData } from './useERC20Permit' import { SignatureData } from './useERC20Permit'
import useTransactionDeadline from './useTransactionDeadline' import useTransactionDeadline from './useTransactionDeadline'
import useENS from './useENS' import useENS from './useENS'
import { Version } from './useToggledVersion'
enum SwapCallbackState { enum SwapCallbackState {
INVALID, INVALID,
...@@ -334,28 +333,28 @@ export function useSwapCallback( ...@@ -334,28 +333,28 @@ export function useSwapCallback(
...(value && !isZero(value) ? { value } : {}), ...(value && !isZero(value) ? { value } : {}),
}) })
.then((response) => { .then((response) => {
const inputSymbol = trade.inputAmount.currency.symbol addTransaction(
const outputSymbol = trade.outputAmount.currency.symbol response,
const inputAmount = trade.inputAmount.toSignificant(4) trade.tradeType === TradeType.EXACT_INPUT
const outputAmount = trade.outputAmount.toSignificant(4) ? {
type: TransactionType.SWAP,
const base = `Swap ${inputAmount} ${inputSymbol} for ${outputAmount} ${outputSymbol}` tradeType: TradeType.EXACT_INPUT,
const withRecipient = inputCurrencyId: currencyId(trade.inputAmount.currency),
recipient === account inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
? base expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
: `${base} to ${ outputCurrencyId: currencyId(trade.outputAmount.currency),
recipientAddressOrName && isAddress(recipientAddressOrName) minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
? shortenAddress(recipientAddressOrName) }
: recipientAddressOrName : {
}` type: TransactionType.SWAP,
tradeType: TradeType.EXACT_OUTPUT,
const tradeVersion = getTradeVersion(trade) inputCurrencyId: currencyId(trade.inputAmount.currency),
maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
const withVersion = tradeVersion === Version.v3 ? withRecipient : `${withRecipient} on ${tradeVersion}` outputCurrencyId: currencyId(trade.outputAmount.currency),
outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
addTransaction(response, { expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
summary: withVersion, }
}) )
return response.hash return response.hash
}) })
...@@ -373,5 +372,5 @@ export function useSwapCallback( ...@@ -373,5 +372,5 @@ export function useSwapCallback(
}, },
error: null, error: null,
} }
}, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, addTransaction]) }, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, addTransaction, allowedSlippage])
} }
...@@ -2,10 +2,11 @@ import { Currency } from '@uniswap/sdk-core' ...@@ -2,10 +2,11 @@ import { Currency } from '@uniswap/sdk-core'
import { useMemo } from 'react' import { useMemo } from 'react'
import { WETH9_EXTENDED } from '../constants/tokens' import { WETH9_EXTENDED } from '../constants/tokens'
import { tryParseAmount } from '../state/swap/hooks' import { tryParseAmount } from '../state/swap/hooks'
import { TransactionType } from '../state/transactions/actions'
import { useTransactionAdder } from '../state/transactions/hooks' import { useTransactionAdder } from '../state/transactions/hooks'
import { useCurrencyBalance } from '../state/wallet/hooks' import { useCurrencyBalance } from '../state/wallet/hooks'
import { useActiveWeb3React } from './web3'
import { useWETHContract } from './useContract' import { useWETHContract } from './useContract'
import { useActiveWeb3React } from './web3'
export enum WrapType { export enum WrapType {
NOT_APPLICABLE, NOT_APPLICABLE,
...@@ -48,7 +49,11 @@ export default function useWrapCallback( ...@@ -48,7 +49,11 @@ export default function useWrapCallback(
? async () => { ? async () => {
try { try {
const txReceipt = await wethContract.deposit({ value: `0x${inputAmount.quotient.toString(16)}` }) const txReceipt = await wethContract.deposit({ value: `0x${inputAmount.quotient.toString(16)}` })
addTransaction(txReceipt, { summary: `Wrap ${inputAmount.toSignificant(6)} ETH to WETH` }) addTransaction(txReceipt, {
type: TransactionType.WRAP,
unwrapped: false,
currencyAmountRaw: inputAmount?.quotient.toString(),
})
} catch (error) { } catch (error) {
console.error('Could not deposit', error) console.error('Could not deposit', error)
} }
...@@ -64,7 +69,11 @@ export default function useWrapCallback( ...@@ -64,7 +69,11 @@ export default function useWrapCallback(
? async () => { ? async () => {
try { try {
const txReceipt = await wethContract.withdraw(`0x${inputAmount.quotient.toString(16)}`) const txReceipt = await wethContract.withdraw(`0x${inputAmount.quotient.toString(16)}`)
addTransaction(txReceipt, { summary: `Unwrap ${inputAmount.toSignificant(6)} WETH to ETH` }) addTransaction(txReceipt, {
type: TransactionType.WRAP,
unwrapped: true,
currencyAmountRaw: inputAmount?.quotient.toString(),
})
} catch (error) { } catch (error) {
console.error('Could not withdraw', error) console.error('Could not withdraw', error)
} }
......
import { useCallback, useContext, useEffect, useState } from 'react' import { Trans, t } from '@lingui/macro'
import {
useV3MintState,
useV3MintActionHandlers,
useRangeHopCallbacks,
useV3DerivedMintInfo,
} from 'state/mint/v3/hooks'
import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { BigNumber } from '@ethersproject/bignumber'
import DowntimeWarning from 'components/DowntimeWarning'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { useCallback, useContext, useEffect, useState } from 'react'
import { AlertTriangle } from 'react-feather' import { AlertTriangle } from 'react-feather'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { ZERO_PERCENT } from '../../constants/misc'
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from '../../constants/addresses'
import { WETH9_EXTENDED } from '../../constants/tokens'
import { useArgentWalletContract } from '../../hooks/useArgentWalletContract'
import { useV3NFTPositionManagerContract } from '../../hooks/useContract'
import { RouteComponentProps } from 'react-router-dom' import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass' import { Text } from 'rebass'
import { ThemeContext } from 'styled-components/macro' import { ThemeContext } from 'styled-components/macro'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonText, ButtonYellow } from '../../components/Button' import { ButtonError, ButtonLight, ButtonPrimary, ButtonText, ButtonYellow } from '../../components/Button'
import { YellowCard, OutlineCard, BlueCard } from '../../components/Card' import { BlueCard, OutlineCard, YellowCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column' import { AutoColumn } from '../../components/Column'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import Row, { RowBetween, RowFixed, AutoRow } from '../../components/Row' import FeeSelector from '../../components/FeeSelector'
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported' import HoverInlineText from '../../components/HoverInlineText'
import { useUSDCValue } from '../../hooks/useUSDCPrice' import LiquidityChartRangeInput from '../../components/LiquidityChartRangeInput'
import approveAmountCalldata from '../../utils/approveAmountCalldata' import { AddRemoveTabs } from '../../components/NavigationTabs'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { PositionPreview } from '../../components/PositionPreview'
import { Review } from './Review' import RangeSelector from '../../components/RangeSelector'
import { useActiveWeb3React } from '../../hooks/web3' import PresetsButtons from '../../components/RangeSelector/PresetsButtons'
import RateToggle from '../../components/RateToggle'
import Row, { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from '../../constants/addresses'
import { CHAIN_INFO, SupportedChainId } from '../../constants/chains'
import { ZERO_PERCENT } from '../../constants/misc'
import { WETH9_EXTENDED } from '../../constants/tokens'
import { useCurrency } from '../../hooks/Tokens' import { useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { useArgentWalletContract } from '../../hooks/useArgentWalletContract'
import { useV3NFTPositionManagerContract } from '../../hooks/useContract'
import { useDerivedPositionInfo } from '../../hooks/useDerivedPositionInfo'
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
import useTransactionDeadline from '../../hooks/useTransactionDeadline' import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { useUSDCValue } from '../../hooks/useUSDCPrice'
import { useV3PositionFromTokenId } from '../../hooks/useV3Positions'
import { useActiveWeb3React } from '../../hooks/web3'
import { useWalletModalToggle } from '../../state/application/hooks' import { useWalletModalToggle } from '../../state/application/hooks'
import { Field, Bound } from '../../state/mint/v3/actions' import { Bound, Field } from '../../state/mint/v3/actions'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks' import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
import { TYPE, ExternalLink } from '../../theme' import { ExternalLink, TYPE } from '../../theme'
import approveAmountCalldata from '../../utils/approveAmountCalldata'
import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { currencyId } from '../../utils/currencyId'
import { maxAmountSpend } from '../../utils/maxAmountSpend' import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { Dots } from '../Pool/styleds' import { Dots } from '../Pool/styleds'
import { currencyId } from '../../utils/currencyId' import { Review } from './Review'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { import {
DynamicSection,
CurrencyDropdown, CurrencyDropdown,
StyledInput, DynamicSection,
Wrapper, HideMedium,
ScrollablePage, MediumOnly,
ResponsiveTwoColumns,
PageWrapper, PageWrapper,
ResponsiveTwoColumns,
RightContainer,
ScrollablePage,
StackedContainer, StackedContainer,
StackedItem, StackedItem,
RightContainer, StyledInput,
MediumOnly, Wrapper,
HideMedium,
} from './styled' } from './styled'
import { Trans, t } from '@lingui/macro'
import {
useV3MintState,
useV3MintActionHandlers,
useRangeHopCallbacks,
useV3DerivedMintInfo,
} from 'state/mint/v3/hooks'
import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { useDerivedPositionInfo } from 'hooks/useDerivedPositionInfo'
import { PositionPreview } from 'components/PositionPreview'
import FeeSelector from 'components/FeeSelector'
import RangeSelector from 'components/RangeSelector'
import PresetsButtons from 'components/RangeSelector/PresetsButtons'
import RateToggle from 'components/RateToggle'
import { BigNumber } from '@ethersproject/bignumber'
import { AddRemoveTabs } from 'components/NavigationTabs'
import HoverInlineText from 'components/HoverInlineText'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import LiquidityChartRangeInput from 'components/LiquidityChartRangeInput'
import { SupportedChainId } from 'constants/chains'
import DowntimeWarning from 'components/DowntimeWarning'
import { CHAIN_INFO } from '../../constants/chains'
const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000) const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
...@@ -208,11 +208,7 @@ export default function AddLiquidity({ ...@@ -208,11 +208,7 @@ export default function AddLiquidity({
async function onCreate() { async function onCreate() {
if (!chainId || !library) return if (!chainId || !library) return
if (!positionManager || !baseCurrency || !quoteCurrency) { if (chainId && library && position && account && deadline && baseCurrency && quoteCurrency && positionManager) {
return
}
if (position && account && deadline) {
const { calldata, value } = NonfungiblePositionManager.createCallParameters(position.pool) const { calldata, value } = NonfungiblePositionManager.createCallParameters(position.pool)
const txn: { to: string; data: string; value: string } = { const txn: { to: string; data: string; value: string } = {
...@@ -237,7 +233,9 @@ export default function AddLiquidity({ ...@@ -237,7 +233,9 @@ export default function AddLiquidity({
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
setAttemptingTxn(false) setAttemptingTxn(false)
addTransaction(response, { addTransaction(response, {
summary: t`Create ${baseCurrency?.symbol}/${quoteCurrency?.symbol} V3 pool`, type: TransactionType.CREATE_V3_POOL,
baseCurrencyId: currencyId(baseCurrency),
quoteCurrencyId: currencyId(quoteCurrency),
}) })
// dont set txn hash as we dont want submitted txn screen for create // dont set txn hash as we dont want submitted txn screen for create
ReactGA.event({ ReactGA.event({
...@@ -332,9 +330,13 @@ export default function AddLiquidity({ ...@@ -332,9 +330,13 @@ export default function AddLiquidity({
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
setAttemptingTxn(false) setAttemptingTxn(false)
addTransaction(response, { addTransaction(response, {
summary: noLiquidity type: TransactionType.ADD_LIQUIDITY_V3_POOL,
? t`Create pool and add ${baseCurrency?.symbol}/${quoteCurrency?.symbol} V3 liquidity` baseCurrencyId: currencyId(baseCurrency),
: t`Add ${baseCurrency?.symbol}/${quoteCurrency?.symbol} V3 liquidity`, quoteCurrencyId: currencyId(quoteCurrency),
createPool: Boolean(noLiquidity),
expectedAmountBaseRaw: parsedAmounts[Field.CURRENCY_A]?.quotient?.toString() ?? '0',
expectedAmountQuoteRaw: parsedAmounts[Field.CURRENCY_B]?.quotient?.toString() ?? '0',
feeAmount: position.pool.fee,
}) })
setTxHash(response.hash) setTxHash(response.hash)
ReactGA.event({ ReactGA.event({
......
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { t, Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import { useCallback, useContext, useState } from 'react' import { useCallback, useContext, useState } from 'react'
import { Plus } from 'react-feather' import { Plus } from 'react-feather'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
...@@ -10,40 +13,38 @@ import { ThemeContext } from 'styled-components/macro' ...@@ -10,40 +13,38 @@ import { ThemeContext } from 'styled-components/macro'
import { ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button' import { ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
import { BlueCard, LightCard } from '../../components/Card' import { BlueCard, LightCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import DoubleCurrencyLogo from '../../components/DoubleLogo' import DoubleCurrencyLogo from '../../components/DoubleLogo'
import { AddRemoveTabs } from '../../components/NavigationTabs' import { AddRemoveTabs } from '../../components/NavigationTabs'
import { MinimalPositionCard } from '../../components/PositionCard' import { MinimalPositionCard } from '../../components/PositionCard'
import Row, { RowBetween, RowFlat } from '../../components/Row' import Row, { RowBetween, RowFlat } from '../../components/Row'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import { ZERO_PERCENT } from '../../constants/misc' import { ZERO_PERCENT } from '../../constants/misc'
import { WETH9_EXTENDED } from '../../constants/tokens' import { WETH9_EXTENDED } from '../../constants/tokens'
import { useV2RouterContract } from '../../hooks/useContract'
import { PairState } from '../../hooks/useV2Pairs'
import { useActiveWeb3React } from '../../hooks/web3'
import { useCurrency } from '../../hooks/Tokens' import { useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { useV2RouterContract } from '../../hooks/useContract'
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported' import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
import useTransactionDeadline from '../../hooks/useTransactionDeadline' import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { PairState } from '../../hooks/useV2Pairs'
import { useActiveWeb3React } from '../../hooks/web3'
import { useWalletModalToggle } from '../../state/application/hooks' import { useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/mint/actions' import { Field } from '../../state/mint/actions'
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks' import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks' import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { calculateSlippageAmount } from '../../utils/calculateSlippageAmount' import { calculateSlippageAmount } from '../../utils/calculateSlippageAmount'
import { currencyId } from '../../utils/currencyId'
import { maxAmountSpend } from '../../utils/maxAmountSpend' import { maxAmountSpend } from '../../utils/maxAmountSpend'
import AppBody from '../AppBody' import AppBody from '../AppBody'
import { Dots, Wrapper } from '../Pool/styleds' import { Dots, Wrapper } from '../Pool/styleds'
import { ConfirmAddModalBottom } from './ConfirmAddModalBottom' import { ConfirmAddModalBottom } from './ConfirmAddModalBottom'
import { currencyId } from '../../utils/currencyId'
import { PoolPriceBar } from './PoolPriceBar' import { PoolPriceBar } from './PoolPriceBar'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import { t, Trans } from '@lingui/macro'
const DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE = new Percent(50, 10_000) const DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
...@@ -189,9 +190,11 @@ export default function AddLiquidity({ ...@@ -189,9 +190,11 @@ export default function AddLiquidity({
setAttemptingTxn(false) setAttemptingTxn(false)
addTransaction(response, { addTransaction(response, {
summary: t`Add ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(3)} ${ type: TransactionType.ADD_LIQUIDITY_V2_POOL,
currencies[Field.CURRENCY_A]?.symbol baseCurrencyId: currencyId(currencyA),
} and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(3)} ${currencies[Field.CURRENCY_B]?.symbol}`, expectedAmountBaseRaw: parsedAmounts[Field.CURRENCY_A]?.quotient.toString() ?? '0',
quoteCurrencyId: currencyId(currencyB),
expectedAmountQuoteRaw: parsedAmounts[Field.CURRENCY_B]?.quotient.toString() ?? '0',
}) })
setTxHash(response.hash) setTxHash(response.hash)
......
import { Contract } from '@ethersproject/contracts'
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk'
import Badge, { BadgeVariant } from 'components/Badge'
import { ButtonConfirmed } from 'components/Button'
import { BlueCard, DarkGreyCard, LightCard, YellowCard } from 'components/Card'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import FeeSelector from 'components/FeeSelector'
import RangeSelector from 'components/RangeSelector'
import RateToggle from 'components/RateToggle'
import SettingsTab from 'components/Settings'
import { Dots } from 'components/swap/styleds'
import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { PoolState, usePool } from 'hooks/usePools'
import useTheme from 'hooks/useTheme'
import useTransactionDeadline from 'hooks/useTransactionDeadline'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { useCallback, useMemo, useState, useEffect, ReactNode } from 'react' import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { Fraction, Percent, Price, Token, CurrencyAmount } from '@uniswap/sdk-core' import { AlertCircle, AlertTriangle, ArrowDown } from 'react-feather'
import ReactGA from 'react-ga'
import { Redirect, RouteComponentProps } from 'react-router' import { Redirect, RouteComponentProps } from 'react-router'
import { Text } from 'rebass' import { Text } from 'rebass'
import { useAppDispatch } from 'state/hooks'
import { Bound, resetMintState } from 'state/mint/v3/actions'
import { useRangeHopCallbacks, useV3DerivedMintInfo, useV3MintActionHandlers } from 'state/mint/v3/hooks'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { unwrappedToken } from 'utils/unwrappedToken'
import { AutoColumn } from '../../components/Column' import { AutoColumn } from '../../components/Column'
import CurrencyLogo from '../../components/CurrencyLogo' import CurrencyLogo from '../../components/CurrencyLogo'
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount' import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
import { AutoRow, RowBetween, RowFixed } from '../../components/Row' import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses' import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
import { WETH9_EXTENDED } from '../../constants/tokens' import { WETH9_EXTENDED } from '../../constants/tokens'
import { useToken } from '../../hooks/Tokens'
import { usePairContract, useV2MigratorContract } from '../../hooks/useContract'
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit' import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
import useIsArgentWallet from '../../hooks/useIsArgentWallet' import useIsArgentWallet from '../../hooks/useIsArgentWallet'
import { useTotalSupply } from '../../hooks/useTotalSupply' import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { useToken } from '../../hooks/Tokens'
import { usePairContract, useV2MigratorContract } from '../../hooks/useContract'
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks' import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTokenBalance } from '../../state/wallet/hooks' import { useTokenBalance } from '../../state/wallet/hooks'
import { BackArrow, ExternalLink, TYPE } from '../../theme' import { BackArrow, ExternalLink, TYPE } from '../../theme'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' import { currencyId } from '../../utils/currencyId'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { BodyWrapper } from '../AppBody' import { BodyWrapper } from '../AppBody'
import { PoolState, usePool } from 'hooks/usePools'
import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk'
import { BlueCard, DarkGreyCard, LightCard, YellowCard } from 'components/Card'
import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
import { Dots } from 'components/swap/styleds'
import { ButtonConfirmed } from 'components/Button'
import useTransactionDeadline from 'hooks/useTransactionDeadline'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import ReactGA from 'react-ga'
import { TransactionResponse } from '@ethersproject/providers'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import { useV3DerivedMintInfo, useRangeHopCallbacks, useV3MintActionHandlers } from 'state/mint/v3/hooks'
import { Bound, resetMintState } from 'state/mint/v3/actions'
import { Trans } from '@lingui/macro'
import { AlertCircle, AlertTriangle, ArrowDown } from 'react-feather'
import FeeSelector from 'components/FeeSelector'
import RangeSelector from 'components/RangeSelector'
import RateToggle from 'components/RateToggle'
import { Contract } from '@ethersproject/contracts'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import useTheme from 'hooks/useTheme'
import { unwrappedToken } from 'utils/unwrappedToken'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import Badge, { BadgeVariant } from 'components/Badge'
import { useAppDispatch } from 'state/hooks'
import SettingsTab from 'components/Settings'
const ZERO = JSBI.BigInt(0) const ZERO = JSBI.BigInt(0)
...@@ -341,7 +343,10 @@ function V2PairMigration({ ...@@ -341,7 +343,10 @@ function V2PairMigration({
}) })
addTransaction(response, { addTransaction(response, {
summary: `Migrate ${currency0.symbol}/${currency1.symbol} liquidity to V3`, type: TransactionType.MIGRATE_LIQUIDITY_V3,
baseCurrencyId: currencyId(currency0),
quoteCurrencyId: currencyId(currency1),
isFork: isNotUniswap,
}) })
setPendingMigrationHash(response.hash) setPendingMigrationHash(response.hash)
}) })
......
import { useCallback, useMemo, useRef, useState } from 'react' import { BigNumber } from '@ethersproject/bignumber'
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk' import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import { ButtonConfirmed, ButtonGray, ButtonPrimary } from 'components/Button'
import { DarkCard, LightCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import Loader from 'components/Loader'
import { RowBetween, RowFixed } from 'components/Row'
import { Dots } from 'components/swap/styleds'
import Toggle from 'components/Toggle'
import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal'
import { SupportedChainId } from 'constants/chains'
import { useToken } from 'hooks/Tokens'
import { useV3NFTPositionManagerContract } from 'hooks/useContract'
import useIsTickAtLimit from 'hooks/useIsTickAtLimit'
import { PoolState, usePool } from 'hooks/usePools' import { PoolState, usePool } from 'hooks/usePools'
import { useToken } from 'hooks/Tokens' import useUSDCPrice from 'hooks/useUSDCPrice'
import { useV3PositionFees } from 'hooks/useV3PositionFees'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions' import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { useActiveWeb3React } from 'hooks/web3'
import { useCallback, useMemo, useRef, useState } from 'react'
import ReactGA from 'react-ga'
import { Link, RouteComponentProps } from 'react-router-dom' import { Link, RouteComponentProps } from 'react-router-dom'
import { unwrappedToken } from 'utils/unwrappedToken' import { Bound } from 'state/mint/v3/actions'
import { usePositionTokenURI } from '../../hooks/usePositionTokenURI' import { useSingleCallResult } from 'state/multicall/hooks'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { AutoColumn } from 'components/Column'
import { RowBetween, RowFixed } from 'components/Row'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import { ExternalLink, HideExtraSmall, TYPE } from 'theme' import { ExternalLink, HideExtraSmall, TYPE } from 'theme'
import Badge from 'components/Badge'
import { ButtonConfirmed, ButtonPrimary, ButtonGray } from 'components/Button'
import { DarkCard, LightCard } from 'components/Card'
import CurrencyLogo from 'components/CurrencyLogo'
import { Trans } from '@lingui/macro'
import { currencyId } from 'utils/currencyId' import { currencyId } from 'utils/currencyId'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { useV3PositionFees } from 'hooks/useV3PositionFees' import { formatTickPrice } from 'utils/formatTickPrice'
import { BigNumber } from '@ethersproject/bignumber' import { unwrappedToken } from 'utils/unwrappedToken'
import { Token, Currency, CurrencyAmount, Percent, Fraction, Price } from '@uniswap/sdk-core' import RangeBadge from '../../components/Badge/RangeBadge'
import { useActiveWeb3React } from 'hooks/web3'
import { useV3NFTPositionManagerContract } from 'hooks/useContract'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import ReactGA from 'react-ga'
import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal'
import { TransactionResponse } from '@ethersproject/providers'
import { Dots } from 'components/swap/styleds'
import { getPriceOrderingFromPositionForUI } from '../../components/PositionListItem' import { getPriceOrderingFromPositionForUI } from '../../components/PositionListItem'
import useTheme from '../../hooks/useTheme'
import RateToggle from '../../components/RateToggle' import RateToggle from '../../components/RateToggle'
import { useSingleCallResult } from 'state/multicall/hooks'
import RangeBadge from '../../components/Badge/RangeBadge'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink' import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import useUSDCPrice from 'hooks/useUSDCPrice' import { usePositionTokenURI } from '../../hooks/usePositionTokenURI'
import Loader from 'components/Loader' import useTheme from '../../hooks/useTheme'
import Toggle from 'components/Toggle' import { TransactionType } from '../../state/transactions/actions'
import { Bound } from 'state/mint/v3/actions' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import useIsTickAtLimit from 'hooks/useIsTickAtLimit' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { formatTickPrice } from 'utils/formatTickPrice'
import { SupportedChainId } from 'constants/chains'
import { LoadingRows } from './styleds' import { LoadingRows } from './styleds'
const PageWrapper = styled.div` const PageWrapper = styled.div`
...@@ -459,7 +460,9 @@ export function PositionPage({ ...@@ -459,7 +460,9 @@ export function PositionPage({
}) })
addTransaction(response, { addTransaction(response, {
summary: `Collect ${feeValue0.currency.symbol}/${feeValue1.currency.symbol} fees`, type: TransactionType.COLLECT_FEES,
currencyId0: currencyId(feeValue0.currency),
currencyId1: currencyId(feeValue1.currency),
}) })
}) })
}) })
......
import { useCallback, useMemo, useState } from 'react'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { Redirect, RouteComponentProps } from 'react-router-dom'
import { WETH9_EXTENDED } from '../../constants/tokens'
import { calculateGasMargin } from '../../utils/calculateGasMargin'
import AppBody from '../AppBody'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import useDebouncedChangeHandler from 'hooks/useDebouncedChangeHandler' import { TransactionResponse } from '@ethersproject/providers'
import { useBurnV3ActionHandlers, useBurnV3State, useDerivedV3BurnInfo } from 'state/burn/v3/hooks' import { Trans } from '@lingui/macro'
import Slider from 'components/Slider' import { Percent } from '@uniswap/sdk-core'
import { AutoRow, RowBetween, RowFixed } from 'components/Row' import { NonfungiblePositionManager } from '@uniswap/v3-sdk'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal' import RangeBadge from 'components/Badge/RangeBadge'
import { AutoColumn } from 'components/Column'
import { ButtonConfirmed, ButtonPrimary } from 'components/Button' import { ButtonConfirmed, ButtonPrimary } from 'components/Button'
import { LightCard } from 'components/Card' import { LightCard } from 'components/Card'
import { Text } from 'rebass' import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo' import CurrencyLogo from 'components/CurrencyLogo'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import { Break } from 'components/earn/styled'
import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount' import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount'
import Loader from 'components/Loader'
import { AddRemoveTabs } from 'components/NavigationTabs'
import { AutoRow, RowBetween, RowFixed } from 'components/Row'
import Slider from 'components/Slider'
import Toggle from 'components/Toggle'
import { SupportedChainId } from 'constants/chains'
import { useV3NFTPositionManagerContract } from 'hooks/useContract' import { useV3NFTPositionManagerContract } from 'hooks/useContract'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks' import useDebouncedChangeHandler from 'hooks/useDebouncedChangeHandler'
import useTheme from 'hooks/useTheme'
import useTransactionDeadline from 'hooks/useTransactionDeadline' import useTransactionDeadline from 'hooks/useTransactionDeadline'
import ReactGA from 'react-ga' import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { useActiveWeb3React } from 'hooks/web3' import { useActiveWeb3React } from 'hooks/web3'
import { TransactionResponse } from '@ethersproject/providers' import { useCallback, useMemo, useState } from 'react'
import ReactGA from 'react-ga'
import { Redirect, RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
import { useBurnV3ActionHandlers, useBurnV3State, useDerivedV3BurnInfo } from 'state/burn/v3/hooks'
import { useTransactionAdder } from 'state/transactions/hooks' import { useTransactionAdder } from 'state/transactions/hooks'
import { Percent } from '@uniswap/sdk-core' import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { TYPE } from 'theme' import { TYPE } from 'theme'
import { Wrapper, SmallMaxButton, ResponsiveHeaderText } from './styled' import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import Loader from 'components/Loader' import { WETH9_EXTENDED } from '../../constants/tokens'
import DoubleCurrencyLogo from 'components/DoubleLogo' import { TransactionType } from '../../state/transactions/actions'
import { Break } from 'components/earn/styled' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { NonfungiblePositionManager } from '@uniswap/v3-sdk' import { currencyId } from '../../utils/currencyId'
import useTheme from 'hooks/useTheme' import AppBody from '../AppBody'
import { AddRemoveTabs } from 'components/NavigationTabs' import { ResponsiveHeaderText, SmallMaxButton, Wrapper } from './styled'
import RangeBadge from 'components/Badge/RangeBadge'
import Toggle from 'components/Toggle'
import { t, Trans } from '@lingui/macro'
import { SupportedChainId } from 'constants/chains'
const DEFAULT_REMOVE_V3_LIQUIDITY_SLIPPAGE_TOLERANCE = new Percent(5, 100) const DEFAULT_REMOVE_V3_LIQUIDITY_SLIPPAGE_TOLERANCE = new Percent(5, 100)
...@@ -152,7 +154,11 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { ...@@ -152,7 +154,11 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
setTxnHash(response.hash) setTxnHash(response.hash)
setAttemptingTxn(false) setAttemptingTxn(false)
addTransaction(response, { addTransaction(response, {
summary: t`Remove ${liquidityValue0.currency.symbol}/${liquidityValue1.currency.symbol} V3 liquidity`, type: TransactionType.REMOVE_LIQUIDITY_V3,
baseCurrencyId: currencyId(liquidityValue0.currency),
quoteCurrencyId: currencyId(liquidityValue1.currency),
expectedAmountBaseRaw: liquidityValue0.quotient.toString(),
expectedAmountQuoteRaw: liquidityValue1.quotient.toString(),
}) })
}) })
}) })
...@@ -187,9 +193,12 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { ...@@ -187,9 +193,12 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
setTxnHash('') setTxnHash('')
}, [onPercentSelectForSlider, txnHash]) }, [onPercentSelectForSlider, txnHash])
const pendingText = `Removing ${liquidityValue0?.toSignificant(6)} ${ const pendingText = (
liquidityValue0?.currency?.symbol <Trans>
} and ${liquidityValue1?.toSignificant(6)} ${liquidityValue1?.currency?.symbol}` Removing {liquidityValue0?.toSignificant(6)} {liquidityValue0?.currency?.symbol} and{' '}
{liquidityValue1?.toSignificant(6)} {liquidityValue1?.currency?.symbol}
</Trans>
)
function modalHeader() { function modalHeader() {
return ( return (
......
import { BigNumber } from '@ethersproject/bignumber'
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { t, Trans } from '@lingui/macro'
import { Currency, Percent } from '@uniswap/sdk-core' import { Currency, Percent } from '@uniswap/sdk-core'
import { useCallback, useContext, useMemo, useState } from 'react' import { useCallback, useContext, useMemo, useState } from 'react'
import { ArrowDown, Plus } from 'react-feather' import { ArrowDown, Plus } from 'react-feather'
...@@ -7,42 +9,40 @@ import ReactGA from 'react-ga' ...@@ -7,42 +9,40 @@ import ReactGA from 'react-ga'
import { RouteComponentProps } from 'react-router' import { RouteComponentProps } from 'react-router'
import { Text } from 'rebass' import { Text } from 'rebass'
import { ThemeContext } from 'styled-components/macro' import { ThemeContext } from 'styled-components/macro'
import { ButtonPrimary, ButtonLight, ButtonError, ButtonConfirmed } from '../../components/Button' import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
import { BlueCard, LightCard } from '../../components/Card' import { BlueCard, LightCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import CurrencyLogo from '../../components/CurrencyLogo'
import DoubleCurrencyLogo from '../../components/DoubleLogo' import DoubleCurrencyLogo from '../../components/DoubleLogo'
import { AddRemoveTabs } from '../../components/NavigationTabs' import { AddRemoveTabs } from '../../components/NavigationTabs'
import { MinimalPositionCard } from '../../components/PositionCard' import { MinimalPositionCard } from '../../components/PositionCard'
import Row, { RowBetween, RowFixed } from '../../components/Row' import Row, { RowBetween, RowFixed } from '../../components/Row'
import Slider from '../../components/Slider' import Slider from '../../components/Slider'
import CurrencyLogo from '../../components/CurrencyLogo' import { Dots } from '../../components/swap/styleds'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import { WETH9_EXTENDED } from '../../constants/tokens' import { WETH9_EXTENDED } from '../../constants/tokens'
import { useActiveWeb3React } from '../../hooks/web3'
import { useCurrency } from '../../hooks/Tokens' import { useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { usePairContract, useV2RouterContract } from '../../hooks/useContract' import { usePairContract, useV2RouterContract } from '../../hooks/useContract'
import useDebouncedChangeHandler from '../../hooks/useDebouncedChangeHandler'
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit' import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
import useTransactionDeadline from '../../hooks/useTransactionDeadline' import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { useActiveWeb3React } from '../../hooks/web3'
import { useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/burn/actions'
import { useBurnActionHandlers, useBurnState, useDerivedBurnInfo } from '../../state/burn/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
import { StyledInternalLink, TYPE } from '../../theme' import { StyledInternalLink, TYPE } from '../../theme'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { calculateSlippageAmount } from '../../utils/calculateSlippageAmount' import { calculateSlippageAmount } from '../../utils/calculateSlippageAmount'
import { currencyId } from '../../utils/currencyId' import { currencyId } from '../../utils/currencyId'
import useDebouncedChangeHandler from '../../hooks/useDebouncedChangeHandler'
import AppBody from '../AppBody' import AppBody from '../AppBody'
import { ClickableText, MaxButton, Wrapper } from '../Pool/styleds' import { ClickableText, MaxButton, Wrapper } from '../Pool/styleds'
import { useApproveCallback, ApprovalState } from '../../hooks/useApproveCallback'
import { Dots } from '../../components/swap/styleds'
import { useBurnActionHandlers } from '../../state/burn/hooks'
import { useDerivedBurnInfo, useBurnState } from '../../state/burn/hooks'
import { Field } from '../../state/burn/actions'
import { useWalletModalToggle } from '../../state/application/hooks'
import { useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
import { BigNumber } from '@ethersproject/bignumber'
import { t, Trans } from '@lingui/macro'
const DEFAULT_REMOVE_LIQUIDITY_SLIPPAGE_TOLERANCE = new Percent(5, 100) const DEFAULT_REMOVE_LIQUIDITY_SLIPPAGE_TOLERANCE = new Percent(5, 100)
...@@ -267,9 +267,11 @@ export default function RemoveLiquidity({ ...@@ -267,9 +267,11 @@ export default function RemoveLiquidity({
setAttemptingTxn(false) setAttemptingTxn(false)
addTransaction(response, { addTransaction(response, {
summary: t`Remove ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(3)} ${ type: TransactionType.REMOVE_LIQUIDITY_V3,
currencyA?.symbol baseCurrencyId: currencyId(currencyA),
} and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(3)} ${currencyB?.symbol}`, quoteCurrencyId: currencyId(currencyB),
expectedAmountBaseRaw: parsedAmounts[Field.CURRENCY_A]?.quotient.toString() ?? '0',
expectedAmountQuoteRaw: parsedAmounts[Field.CURRENCY_B]?.quotient.toString() ?? '0',
}) })
setTxHash(response.hash) setTxHash(response.hash)
...@@ -277,7 +279,7 @@ export default function RemoveLiquidity({ ...@@ -277,7 +279,7 @@ export default function RemoveLiquidity({
ReactGA.event({ ReactGA.event({
category: 'Liquidity', category: 'Liquidity',
action: 'Remove', action: 'Remove',
label: [currencyA?.symbol, currencyB?.symbol].join('/'), label: [currencyA.symbol, currencyB.symbol].join('/'),
}) })
}) })
.catch((error: Error) => { .catch((error: Error) => {
......
...@@ -33,8 +33,8 @@ import { ...@@ -33,8 +33,8 @@ import {
useProposalData, useProposalData,
useUserDelegatee, useUserDelegatee,
useUserVotesAsOfBlock, useUserVotesAsOfBlock,
VoteOption,
} from '../../state/governance/hooks' } from '../../state/governance/hooks'
import { VoteOption } from '../../state/governance/types'
import { useTokenBalance } from '../../state/wallet/hooks' import { useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, TYPE } from '../../theme' import { ExternalLink, StyledInternalLink, TYPE } from '../../theme'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
......
...@@ -3,8 +3,6 @@ import { createAction } from '@reduxjs/toolkit' ...@@ -3,8 +3,6 @@ import { createAction } from '@reduxjs/toolkit'
export type PopupContent = { export type PopupContent = {
txn: { txn: {
hash: string hash: string
success: boolean
summary?: string
} }
} }
......
...@@ -8,6 +8,7 @@ describe('application reducer', () => { ...@@ -8,6 +8,7 @@ describe('application reducer', () => {
beforeEach(() => { beforeEach(() => {
store = createStore(reducer, { store = createStore(reducer, {
chainId: null, chainId: null,
chainConnectivityWarning: false,
popupList: [], popupList: [],
blockNumber: { blockNumber: {
[1]: 3, [1]: 3,
...@@ -18,23 +19,23 @@ describe('application reducer', () => { ...@@ -18,23 +19,23 @@ describe('application reducer', () => {
describe('addPopup', () => { describe('addPopup', () => {
it('adds the popup to list with a generated id', () => { it('adds the popup to list with a generated id', () => {
store.dispatch(addPopup({ content: { txn: { hash: 'abc', summary: 'test', success: true } } })) store.dispatch(addPopup({ content: { txn: { hash: 'abc' } } }))
const list = store.getState().popupList const list = store.getState().popupList
expect(list).toHaveLength(1) expect(list).toHaveLength(1)
expect(typeof list[0].key).toEqual('string') expect(typeof list[0].key).toEqual('string')
expect(list[0].show).toEqual(true) expect(list[0].show).toEqual(true)
expect(list[0].content).toEqual({ txn: { hash: 'abc', summary: 'test', success: true } }) expect(list[0].content).toEqual({ txn: { hash: 'abc' } })
expect(list[0].removeAfterMs).toEqual(25000) expect(list[0].removeAfterMs).toEqual(25000)
}) })
it('replaces any existing popups with the same key', () => { it('replaces any existing popups with the same key', () => {
store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'abc', summary: 'test', success: true } } })) store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'abc' } } }))
store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'def', summary: 'test2', success: false } } })) store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'def' } } }))
const list = store.getState().popupList const list = store.getState().popupList
expect(list).toHaveLength(1) expect(list).toHaveLength(1)
expect(list[0].key).toEqual('abc') expect(list[0].key).toEqual('abc')
expect(list[0].show).toEqual(true) expect(list[0].show).toEqual(true)
expect(list[0].content).toEqual({ txn: { hash: 'def', summary: 'test2', success: false } }) expect(list[0].content).toEqual({ txn: { hash: 'def' } })
expect(list[0].removeAfterMs).toEqual(25000) expect(list[0].removeAfterMs).toEqual(25000)
}) })
}) })
...@@ -82,7 +83,7 @@ describe('application reducer', () => { ...@@ -82,7 +83,7 @@ describe('application reducer', () => {
describe('removePopup', () => { describe('removePopup', () => {
beforeEach(() => { beforeEach(() => {
store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'abc', summary: 'test', success: true } } })) store.dispatch(addPopup({ key: 'abc', content: { txn: { hash: 'abc' } } }))
}) })
it('hides the popup', () => { it('hides the popup', () => {
expect(store.getState().popupList[0].show).toBe(true) expect(store.getState().popupList[0].show).toBe(true)
......
import JSBI from 'jsbi'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { UNI } from '../../constants/tokens' import { UNI } from '../../constants/tokens'
import { useActiveWeb3React } from '../../hooks/web3'
import { useMerkleDistributorContract } from '../../hooks/useContract' import { useMerkleDistributorContract } from '../../hooks/useContract'
import { useActiveWeb3React } from '../../hooks/web3'
import { isAddress } from '../../utils'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { useSingleCallResult } from '../multicall/hooks' import { useSingleCallResult } from '../multicall/hooks'
import { isAddress } from '../../utils' import { TransactionType } from '../transactions/actions'
import { useTransactionAdder } from '../transactions/hooks' import { useTransactionAdder } from '../transactions/hooks'
interface UserClaimData { interface UserClaimData {
...@@ -165,8 +166,9 @@ export function useClaimCallback(account: string | null | undefined): { ...@@ -165,8 +166,9 @@ export function useClaimCallback(account: string | null | undefined): {
.claim(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) }) .claim(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: `Claimed ${unclaimedAmount?.toSignificant(4)} UNI`, type: TransactionType.CLAIM,
claim: { recipient: account }, recipient: account,
uniAmountRaw: unclaimedAmount?.quotient.toString(),
}) })
return response.hash return response.hash
}) })
......
...@@ -28,7 +28,9 @@ import { BRAVO_START_BLOCK, UNISWAP_GRANTS_START_BLOCK } from '../../constants/p ...@@ -28,7 +28,9 @@ import { BRAVO_START_BLOCK, UNISWAP_GRANTS_START_BLOCK } from '../../constants/p
import { UNI } from '../../constants/tokens' import { UNI } from '../../constants/tokens'
import { useLogs } from '../logs/hooks' import { useLogs } from '../logs/hooks'
import { useSingleCallResult, useSingleContractMultipleData } from '../multicall/hooks' import { useSingleCallResult, useSingleContractMultipleData } from '../multicall/hooks'
import { TransactionType } from '../transactions/actions'
import { useTransactionAdder } from '../transactions/hooks' import { useTransactionAdder } from '../transactions/hooks'
import { VoteOption } from './types'
interface ProposalDetail { interface ProposalDetail {
target: string target: string
...@@ -300,7 +302,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi ...@@ -300,7 +302,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi
return useCallback( return useCallback(
(delegatee: string | undefined) => { (delegatee: string | undefined) => {
if (!library || !chainId || !account || !isAddress(delegatee ?? '')) return undefined if (!library || !chainId || !account || !delegatee || !isAddress(delegatee ?? '')) return undefined
const args = [delegatee] const args = [delegatee]
if (!uniContract) throw new Error('No UNI Contract!') if (!uniContract) throw new Error('No UNI Contract!')
return uniContract.estimateGas.delegate(...args, {}).then((estimatedGasLimit) => { return uniContract.estimateGas.delegate(...args, {}).then((estimatedGasLimit) => {
...@@ -308,7 +310,8 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi ...@@ -308,7 +310,8 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi
.delegate(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) }) .delegate(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: t`Delegated votes`, type: TransactionType.DELEGATE,
delegatee,
}) })
return response.hash return response.hash
}) })
...@@ -318,12 +321,6 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi ...@@ -318,12 +321,6 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi
) )
} }
export enum VoteOption {
Against,
For,
Abstain,
}
export function useVoteCallback(): { export function useVoteCallback(): {
voteCallback: (proposalId: string | undefined, voteOption: VoteOption) => undefined | Promise<string> voteCallback: (proposalId: string | undefined, voteOption: VoteOption) => undefined | Promise<string>
} { } {
...@@ -342,9 +339,11 @@ export function useVoteCallback(): { ...@@ -342,9 +339,11 @@ export function useVoteCallback(): {
.castVote(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) }) .castVote(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: `Voted ${ type: TransactionType.VOTE,
voteOption === VoteOption.Against ? 'against' : voteOption === VoteOption.For ? 'for' : 'to abstain on' decision: voteOption,
} proposal ${proposalId}`, governorAddress: latestGovernanceContract.address,
proposalId: parseInt(proposalId),
reason: '',
}) })
return response.hash return response.hash
}) })
...@@ -380,7 +379,7 @@ export function useCreateProposalCallback(): ( ...@@ -380,7 +379,7 @@ export function useCreateProposalCallback(): (
.propose(...args, { gasLimit: calculateGasMargin(chainId, estimatedGasLimit) }) .propose(...args, { gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: t`Submitted new proposal`, type: TransactionType.SUBMIT_PROPOSAL,
}) })
return response.hash return response.hash
}) })
......
export enum VoteOption {
Against,
For,
Abstain,
}
import { createAction } from '@reduxjs/toolkit' import { createAction } from '@reduxjs/toolkit'
import { TradeType } from '@uniswap/sdk-core'
import { VoteOption } from '../governance/types'
export interface SerializableTransactionReceipt { export interface SerializableTransactionReceipt {
to: string to: string
...@@ -11,14 +13,168 @@ export interface SerializableTransactionReceipt { ...@@ -11,14 +13,168 @@ export interface SerializableTransactionReceipt {
status?: number status?: number
} }
/**
* Be careful adding to this enum, always assign a unique value (typescript will not prevent duplicate values).
* These values is persisted in state and if you change the value it will cause errors
*/
export enum TransactionType {
APPROVAL = 0,
SWAP = 1,
DEPOSIT_LIQUIDITY_STAKING = 2,
WITHDRAW_LIQUIDITY_STAKING = 3,
CLAIM = 4,
VOTE = 5,
DELEGATE = 6,
WRAP = 7,
CREATE_V3_POOL = 8,
ADD_LIQUIDITY_V3_POOL = 9,
ADD_LIQUIDITY_V2_POOL = 10,
MIGRATE_LIQUIDITY_V3 = 11,
COLLECT_FEES = 12,
REMOVE_LIQUIDITY_V3 = 13,
SUBMIT_PROPOSAL = 14,
}
export interface BaseTransactionInfo {
type: TransactionType
}
export interface VoteTransactionInfo extends BaseTransactionInfo {
type: TransactionType.VOTE
governorAddress: string
proposalId: number
decision: VoteOption
reason: string
}
export interface DelegateTransactionInfo extends BaseTransactionInfo {
type: TransactionType.DELEGATE
delegatee: string
}
export interface ApproveTransactionInfo extends BaseTransactionInfo {
type: TransactionType.APPROVAL
tokenAddress: string
spender: string
}
interface BaseSwapTransactionInfo extends BaseTransactionInfo {
type: TransactionType.SWAP
tradeType: TradeType
inputCurrencyId: string
outputCurrencyId: string
}
export interface ExactInputSwapTransactionInfo extends BaseSwapTransactionInfo {
tradeType: TradeType.EXACT_INPUT
inputCurrencyAmountRaw: string
expectedOutputCurrencyAmountRaw: string
minimumOutputCurrencyAmountRaw: string
}
export interface ExactOutputSwapTransactionInfo extends BaseSwapTransactionInfo {
tradeType: TradeType.EXACT_OUTPUT
outputCurrencyAmountRaw: string
expectedInputCurrencyAmountRaw: string
maximumInputCurrencyAmountRaw: string
}
export interface DepositLiquidityStakingTransactionInfo {
type: TransactionType.DEPOSIT_LIQUIDITY_STAKING
token0Address: string
token1Address: string
}
export interface WithdrawLiquidityStakingTransactionInfo {
type: TransactionType.WITHDRAW_LIQUIDITY_STAKING
token0Address: string
token1Address: string
}
export interface WrapTransactionInfo {
type: TransactionType.WRAP
unwrapped: boolean
currencyAmountRaw: string
}
export interface ClaimTransactionInfo {
type: TransactionType.CLAIM
recipient: string
uniAmountRaw?: string
}
export interface CreateV3PoolTransactionInfo {
type: TransactionType.CREATE_V3_POOL
baseCurrencyId: string
quoteCurrencyId: string
}
export interface AddLiquidityV3PoolTransactionInfo {
type: TransactionType.ADD_LIQUIDITY_V3_POOL
createPool: boolean
baseCurrencyId: string
quoteCurrencyId: string
feeAmount: number
expectedAmountBaseRaw: string
expectedAmountQuoteRaw: string
}
export interface AddLiquidityV2PoolTransactionInfo {
type: TransactionType.ADD_LIQUIDITY_V2_POOL
baseCurrencyId: string
quoteCurrencyId: string
expectedAmountBaseRaw: string
expectedAmountQuoteRaw: string
}
export interface MigrateV2LiquidityToV3TransactionInfo {
type: TransactionType.MIGRATE_LIQUIDITY_V3
baseCurrencyId: string
quoteCurrencyId: string
isFork: boolean
}
export interface CollectFeesTransactionInfo {
type: TransactionType.COLLECT_FEES
currencyId0: string
currencyId1: string
}
export interface RemoveLiquidityV3TransactionInfo {
type: TransactionType.REMOVE_LIQUIDITY_V3
baseCurrencyId: string
quoteCurrencyId: string
expectedAmountBaseRaw: string
expectedAmountQuoteRaw: string
}
export interface SubmitProposalTransactionInfo {
type: TransactionType.SUBMIT_PROPOSAL
}
export type TransactionInfo =
| ApproveTransactionInfo
| ExactOutputSwapTransactionInfo
| ExactInputSwapTransactionInfo
| ClaimTransactionInfo
| VoteTransactionInfo
| DelegateTransactionInfo
| DepositLiquidityStakingTransactionInfo
| WithdrawLiquidityStakingTransactionInfo
| WrapTransactionInfo
| CreateV3PoolTransactionInfo
| AddLiquidityV3PoolTransactionInfo
| AddLiquidityV2PoolTransactionInfo
| MigrateV2LiquidityToV3TransactionInfo
| CollectFeesTransactionInfo
| RemoveLiquidityV3TransactionInfo
| SubmitProposalTransactionInfo
export const addTransaction = export const addTransaction =
createAction<{ createAction<{
chainId: number chainId: number
hash: string hash: string
from: string from: string
approval?: { tokenAddress: string; spender: string } info: TransactionInfo
claim?: { recipient: string }
summary?: string
}>('transactions/addTransaction') }>('transactions/addTransaction')
export const clearAllTransactions = createAction<{ chainId: number }>('transactions/clearAllTransactions') export const clearAllTransactions = createAction<{ chainId: number }>('transactions/clearAllTransactions')
export const finalizeTransaction = createAction<{ export const finalizeTransaction = createAction<{
......
...@@ -3,26 +3,16 @@ import { useCallback, useMemo } from 'react' ...@@ -3,26 +3,16 @@ import { useCallback, useMemo } from 'react'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { useActiveWeb3React } from '../../hooks/web3' import { useActiveWeb3React } from '../../hooks/web3'
import { addTransaction } from './actions' import { addTransaction, TransactionInfo, TransactionType } from './actions'
import { TransactionDetails } from './reducer' import { TransactionDetails } from './reducer'
// helper that can take a ethers library transaction response and add it to the list of transactions // helper that can take a ethers library transaction response and add it to the list of transactions
export function useTransactionAdder(): ( export function useTransactionAdder(): (response: TransactionResponse, info: TransactionInfo) => void {
response: TransactionResponse,
customData?: { summary?: string; approval?: { tokenAddress: string; spender: string }; claim?: { recipient: string } }
) => void {
const { chainId, account } = useActiveWeb3React() const { chainId, account } = useActiveWeb3React()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
return useCallback( return useCallback(
( (response: TransactionResponse, info: TransactionInfo) => {
response: TransactionResponse,
{
summary,
approval,
claim,
}: { summary?: string; claim?: { recipient: string }; approval?: { tokenAddress: string; spender: string } } = {}
) => {
if (!account) return if (!account) return
if (!chainId) return if (!chainId) return
...@@ -30,7 +20,7 @@ export function useTransactionAdder(): ( ...@@ -30,7 +20,7 @@ export function useTransactionAdder(): (
if (!hash) { if (!hash) {
throw Error('No transaction hash found.') throw Error('No transaction hash found.')
} }
dispatch(addTransaction({ hash, from: account, chainId, approval, summary, claim })) dispatch(addTransaction({ hash, from: account, info, chainId }))
}, },
[dispatch, chainId, account] [dispatch, chainId, account]
) )
...@@ -92,9 +82,8 @@ export function useHasPendingApproval(tokenAddress: string | undefined, spender: ...@@ -92,9 +82,8 @@ export function useHasPendingApproval(tokenAddress: string | undefined, spender:
if (tx.receipt) { if (tx.receipt) {
return false return false
} else { } else {
const approval = tx.approval if (tx.info.type !== TransactionType.APPROVAL) return false
if (!approval) return false return tx.info.spender === spender && tx.info.tokenAddress === tokenAddress && isTransactionRecent(tx)
return approval.spender === spender && approval.tokenAddress === tokenAddress && isTransactionRecent(tx)
} }
}), }),
[allTransactions, spender, tokenAddress] [allTransactions, spender, tokenAddress]
...@@ -113,7 +102,7 @@ export function useUserHasSubmittedClaim(account?: string): { ...@@ -113,7 +102,7 @@ export function useUserHasSubmittedClaim(account?: string): {
const claimTxn = useMemo(() => { const claimTxn = useMemo(() => {
const txnIndex = Object.keys(allTransactions).find((hash) => { const txnIndex = Object.keys(allTransactions).find((hash) => {
const tx = allTransactions[hash] const tx = allTransactions[hash]
return tx.claim && tx.claim.recipient === account return tx.info.type === TransactionType.CLAIM && tx.info.recipient === account
}) })
return txnIndex && allTransactions[txnIndex] ? allTransactions[txnIndex] : undefined return txnIndex && allTransactions[txnIndex] ? allTransactions[txnIndex] : undefined
}, [account, allTransactions]) }, [account, allTransactions])
......
import { createStore, Store } from 'redux' import { createStore, Store } from 'redux'
import { addTransaction, checkedTransaction, clearAllTransactions, finalizeTransaction } from './actions' import { updateVersion } from '../global/actions'
import {
addTransaction,
checkedTransaction,
clearAllTransactions,
finalizeTransaction,
TransactionType,
} from './actions'
import reducer, { initialState, TransactionState } from './reducer' import reducer, { initialState, TransactionState } from './reducer'
describe('transaction reducer', () => { describe('transaction reducer', () => {
...@@ -9,16 +16,45 @@ describe('transaction reducer', () => { ...@@ -9,16 +16,45 @@ describe('transaction reducer', () => {
store = createStore(reducer, initialState) store = createStore(reducer, initialState)
}) })
describe('updateVersion', () => {
it('clears old format transactions that do not have info', () => {
store = createStore(reducer, {
[1]: {
abc: {
hash: 'abc',
} as any,
},
})
store.dispatch(updateVersion())
expect(store.getState()[1]['abc']).toBeUndefined()
})
it('keeps old format transactions that do have info', () => {
store = createStore(reducer, {
[1]: {
abc: {
hash: 'abc',
info: {},
} as any,
},
})
store.dispatch(updateVersion())
expect(store.getState()[1]['abc']).toBeTruthy()
})
})
describe('addTransaction', () => { describe('addTransaction', () => {
it('adds the transaction', () => { it('adds the transaction', () => {
const beforeTime = new Date().getTime() const beforeTime = new Date().getTime()
store.dispatch( store.dispatch(
addTransaction({ addTransaction({
chainId: 1, chainId: 1,
summary: 'hello world',
hash: '0x0', hash: '0x0',
approval: { tokenAddress: 'abc', spender: 'def' },
from: 'abc', from: 'abc',
info: {
type: TransactionType.APPROVAL,
tokenAddress: 'abc',
spender: 'def',
},
}) })
) )
const txs = store.getState() const txs = store.getState()
...@@ -27,10 +63,13 @@ describe('transaction reducer', () => { ...@@ -27,10 +63,13 @@ describe('transaction reducer', () => {
const tx = txs[1]?.['0x0'] const tx = txs[1]?.['0x0']
expect(tx).toBeTruthy() expect(tx).toBeTruthy()
expect(tx?.hash).toEqual('0x0') expect(tx?.hash).toEqual('0x0')
expect(tx?.summary).toEqual('hello world')
expect(tx?.approval).toEqual({ tokenAddress: 'abc', spender: 'def' })
expect(tx?.from).toEqual('abc') expect(tx?.from).toEqual('abc')
expect(tx?.addedTime).toBeGreaterThanOrEqual(beforeTime) expect(tx?.addedTime).toBeGreaterThanOrEqual(beforeTime)
expect(tx?.info).toEqual({
type: TransactionType.APPROVAL,
tokenAddress: 'abc',
spender: 'def',
})
}) })
}) })
...@@ -59,8 +98,7 @@ describe('transaction reducer', () => { ...@@ -59,8 +98,7 @@ describe('transaction reducer', () => {
addTransaction({ addTransaction({
hash: '0x0', hash: '0x0',
chainId: 4, chainId: 4,
approval: { spender: '0x0', tokenAddress: '0x0' }, info: { type: TransactionType.APPROVAL, spender: '0x0', tokenAddress: '0x0' },
summary: 'hello world',
from: '0x0', from: '0x0',
}) })
) )
...@@ -82,7 +120,6 @@ describe('transaction reducer', () => { ...@@ -82,7 +120,6 @@ describe('transaction reducer', () => {
}) })
) )
const tx = store.getState()[4]?.['0x0'] const tx = store.getState()[4]?.['0x0']
expect(tx?.summary).toEqual('hello world')
expect(tx?.confirmedTime).toBeGreaterThanOrEqual(beforeTime) expect(tx?.confirmedTime).toBeGreaterThanOrEqual(beforeTime)
expect(tx?.receipt).toEqual({ expect(tx?.receipt).toEqual({
status: 1, status: 1,
...@@ -113,8 +150,7 @@ describe('transaction reducer', () => { ...@@ -113,8 +150,7 @@ describe('transaction reducer', () => {
addTransaction({ addTransaction({
hash: '0x0', hash: '0x0',
chainId: 4, chainId: 4,
approval: { spender: '0x0', tokenAddress: '0x0' }, info: { type: TransactionType.APPROVAL, spender: '0x0', tokenAddress: '0x0' },
summary: 'hello world',
from: '0x0', from: '0x0',
}) })
) )
...@@ -133,8 +169,7 @@ describe('transaction reducer', () => { ...@@ -133,8 +169,7 @@ describe('transaction reducer', () => {
addTransaction({ addTransaction({
hash: '0x0', hash: '0x0',
chainId: 4, chainId: 4,
approval: { spender: '0x0', tokenAddress: '0x0' }, info: { type: TransactionType.APPROVAL, spender: '0x0', tokenAddress: '0x0' },
summary: 'hello world',
from: '0x0', from: '0x0',
}) })
) )
...@@ -162,18 +197,16 @@ describe('transaction reducer', () => { ...@@ -162,18 +197,16 @@ describe('transaction reducer', () => {
store.dispatch( store.dispatch(
addTransaction({ addTransaction({
chainId: 1, chainId: 1,
summary: 'hello world',
hash: '0x0', hash: '0x0',
approval: { tokenAddress: 'abc', spender: 'def' }, info: { type: TransactionType.APPROVAL, spender: 'abc', tokenAddress: 'def' },
from: 'abc', from: 'abc',
}) })
) )
store.dispatch( store.dispatch(
addTransaction({ addTransaction({
chainId: 4, chainId: 4,
summary: 'hello world',
hash: '0x1', hash: '0x1',
approval: { tokenAddress: 'abc', spender: 'def' }, info: { type: TransactionType.APPROVAL, spender: 'abc', tokenAddress: 'def' },
from: 'abc', from: 'abc',
}) })
) )
......
import { createReducer } from '@reduxjs/toolkit' import { createReducer } from '@reduxjs/toolkit'
import { updateVersion } from '../global/actions'
import { import {
addTransaction, addTransaction,
checkedTransaction, checkedTransaction,
clearAllTransactions, clearAllTransactions,
finalizeTransaction, finalizeTransaction,
SerializableTransactionReceipt, SerializableTransactionReceipt,
TransactionInfo,
} from './actions' } from './actions'
const now = () => new Date().getTime() const now = () => new Date().getTime()
export interface TransactionDetails { export interface TransactionDetails {
hash: string hash: string
approval?: { tokenAddress: string; spender: string }
summary?: string
claim?: { recipient: string }
receipt?: SerializableTransactionReceipt receipt?: SerializableTransactionReceipt
lastCheckedBlockNumber?: number lastCheckedBlockNumber?: number
addedTime: number addedTime: number
confirmedTime?: number confirmedTime?: number
from: string from: string
info: TransactionInfo
} }
export interface TransactionState { export interface TransactionState {
...@@ -31,12 +31,24 @@ export const initialState: TransactionState = {} ...@@ -31,12 +31,24 @@ export const initialState: TransactionState = {}
export default createReducer(initialState, (builder) => export default createReducer(initialState, (builder) =>
builder builder
.addCase(addTransaction, (transactions, { payload: { chainId, from, hash, approval, summary, claim } }) => { .addCase(updateVersion, (transactions) => {
// in case there are any transactions in the store with the old format, remove them
Object.keys(transactions).forEach((chainId) => {
const chainTransactions = transactions[chainId as unknown as number]
Object.keys(chainTransactions).forEach((hash) => {
if (!('info' in chainTransactions[hash])) {
// clear old transactions that don't have the right format
delete chainTransactions[hash]
}
})
})
})
.addCase(addTransaction, (transactions, { payload: { chainId, from, hash, info } }) => {
if (transactions[chainId]?.[hash]) { if (transactions[chainId]?.[hash]) {
throw Error('Attempted to add existing transaction.') throw Error('Attempted to add existing transaction.')
} }
const txs = transactions[chainId] ?? {} const txs = transactions[chainId] ?? {}
txs[hash] = { hash, approval, summary, claim, from, addedTime: now() } txs[hash] = { hash, info, from, addedTime: now() }
transactions[chainId] = txs transactions[chainId] = txs
}) })
.addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => { .addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => {
......
...@@ -106,8 +106,6 @@ export default function Updater(): null { ...@@ -106,8 +106,6 @@ export default function Updater(): null {
{ {
txn: { txn: {
hash, hash,
success: receipt.status === 1,
summary: transactions[hash]?.summary,
}, },
}, },
hash, hash,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment