Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement(swap): Better swap errors for FoT #1015

Merged
merged 31 commits into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1c69d38
move the gas estimation stuff into its own hook and report errors fro…
moodysalem Jul 30, 2020
b997e65
fix linter errors
moodysalem Jul 30, 2020
a5098a6
show the swap callback error separately
moodysalem Jul 30, 2020
dbb28b5
rename some variables
moodysalem Jul 30, 2020
b05e07b
use a manually specified key for gas estimates
moodysalem Jul 30, 2020
3e707e9
Merge branch 'master' into better-swap-errors
moodysalem Jul 30, 2020
646c44a
Merge branch 'master' into better-swap-errors
moodysalem Jul 31, 2020
e818962
flip price... thought i did this already
moodysalem Jul 31, 2020
532f2d7
only show swap callback error if approval state is approved
moodysalem Jul 31, 2020
504f10c
some clean up to the swap components
moodysalem Jul 31, 2020
9934e87
stop proactively looking for gas estimates
moodysalem Aug 3, 2020
b67d5aa
improve some retry stuff, show errors inline
moodysalem Aug 3, 2020
7d83a2e
add another retry test
moodysalem Aug 3, 2020
f7ce0f1
latest ethers
moodysalem Aug 3, 2020
f84aa07
fix integration tests
moodysalem Aug 4, 2020
e798d82
simplify modal and fix jitter on open in mobile
moodysalem Aug 4, 2020
69a2cee
refactor confirmation modal into pieces before creating the error con…
moodysalem Aug 4, 2020
a024e92
finish refactoring of transaction confirmation modal
moodysalem Aug 4, 2020
368c782
show error state in the transaction confirmation modal
moodysalem Aug 4, 2020
1c5ba1d
fix lint errors
moodysalem Aug 4, 2020
74b6639
error not always relevant
moodysalem Aug 4, 2020
69e6f17
fix lint errors, remove action item
moodysalem Aug 4, 2020
a3ebbb1
move a lot of code into ConfirmSwapModal.tsx
moodysalem Aug 4, 2020
00d2bf6
show accept changes flow, not styled
moodysalem Aug 4, 2020
dc2b6f9
Adjust styles for slippage error states
callil Aug 5, 2020
9d47801
Add styles for updated price prompt
callil Aug 5, 2020
b368a62
Add input/output highlighting
callil Aug 5, 2020
2aa81ca
lint errors
moodysalem Aug 5, 2020
dc02608
fix link to wallets in modal
moodysalem Aug 5, 2020
0946891
use total supply instead of reserves for `noLiquidity` (fixes #701)
moodysalem Aug 5, 2020
120c766
bump the walletconnect version to the fixed alpha
moodysalem Aug 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/Popups/PopupItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useInterval from '../../hooks/useInterval'
import { PopupContent } from '../../state/application/actions'
import { useRemovePopup } from '../../state/application/hooks'
import ListUpdatePopup from './ListUpdatePopup'
import TxnPopup from './TxnPopup'
import TransactionPopup from './TransactionPopup'

export const StyledClose = styled(X)`
position: absolute;
Expand Down Expand Up @@ -68,7 +68,7 @@ export default function PopupItem({ content, popKey }: { content: PopupContent;
const {
txn: { hash, success, summary }
} = content
popupContent = <TxnPopup hash={hash} success={success} summary={summary} />
popupContent = <TransactionPopup hash={hash} success={success} summary={summary} />
} else if ('listUpdate' in content) {
const {
listUpdate: { listUrl, oldList, newList, auto }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import React, { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather'
import { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components'
import { getEtherscanLink } from '../../utils'
import { AutoColumn } from '../Column'
import { AutoRow } from '../Row'

export default function TxnPopup({ hash, success, summary }: { hash: string; success?: boolean; summary?: string }) {
const RowNoFlex = styled(AutoRow)`
flex-wrap: nowrap;
`

export default function TransactionPopup({
hash,
success,
summary
}: {
hash: string
success?: boolean
summary?: string
}) {
const { chainId } = useActiveWeb3React()

const theme = useContext(ThemeContext)

return (
<AutoRow>
<RowNoFlex>
<div style={{ paddingRight: 16 }}>
{success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />}
</div>
<AutoColumn gap="8px">
<TYPE.body fontWeight={500}>{summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.body>
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</ExternalLink>
</AutoColumn>
</AutoRow>
</RowNoFlex>
)
}
4 changes: 2 additions & 2 deletions src/components/swap/AdvancedSwapDetailsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import styled from 'styled-components'
import useLast from '../../hooks/useLast'
import { useLastTruthy } from '../../hooks/useLast'
import { AdvancedSwapDetails, AdvancedSwapDetailsProps } from './AdvancedSwapDetails'

const AdvancedDetailsFooter = styled.div<{ show: boolean }>`
Expand All @@ -20,7 +20,7 @@ const AdvancedDetailsFooter = styled.div<{ show: boolean }>`
`

export default function AdvancedSwapDetailsDropdown({ trade, ...rest }: AdvancedSwapDetailsProps) {
const lastTrade = useLast(trade)
const lastTrade = useLastTruthy(trade)

return (
<AdvancedDetailsFooter show={Boolean(trade)}>
Expand Down
31 changes: 12 additions & 19 deletions src/components/swap/styleds.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { darken } from 'polished'
import styled, { css } from 'styled-components'
import { AutoColumn } from '../Column'
import { Text } from 'rebass'

import NumericalInput from '../NumericalInput'

export const Wrapper = styled.div`
position: relative;
`
Expand All @@ -30,7 +28,6 @@ export const SectionBreak = styled.div`

export const BottomGrouping = styled.div`
margin-top: 12px;
position: relative;
`

export const ErrorText = styled(Text)<{ severity?: 0 | 1 | 2 | 3 | 4 }>`
Expand All @@ -44,21 +41,6 @@ export const ErrorText = styled(Text)<{ severity?: 0 | 1 | 2 | 3 | 4 }>`
: theme.green1};
`

export const InputGroup = styled(AutoColumn)`
position: relative;
padding: 40px 0 20px 0;
`

export const StyledNumerical = styled(NumericalInput)`
text-align: center;
font-size: 48px;
font-weight: 500px;
width: 100%;

::placeholder {
color: ${({ theme }) => theme.text4};
}
`
export const StyledBalanceMaxMini = styled.button`
height: 22px;
width: 22px;
Expand Down Expand Up @@ -112,3 +94,14 @@ export const Dots = styled.span`
}
}
`

export const SwapCallbackError = styled.div`
background-color: ${({ theme }) => theme.red1};
border-radius: 1rem;
padding: 1rem;
display: flex;
align-items: center;
margin-top: 1rem;
border: 1px solid ${({ theme }) => darken(0.1, theme.red1)};
color: ${({ theme }) => theme.white};
`
94 changes: 77 additions & 17 deletions src/connectors/NetworkConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,83 @@ class RequestError extends Error {
}
}

interface BatchItem {
request: { jsonrpc: '2.0'; id: number; method: string; params: unknown }
resolve: (result: any) => void
reject: (error: Error) => void
}

class MiniRpcProvider implements AsyncSendable {
Copy link
Contributor Author

@moodysalem moodysalem Jul 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

batches eth_estimateGas, eth_call and potentially eth_blockNumber though that is usually sequential with the other two

public readonly isMetaMask: false = false
public readonly chainId: number
public readonly url: string
public readonly host: string
public readonly path: string
public readonly batchWaitTimeMs: number

constructor(chainId: number, url: string) {
private nextId = 1
private batchTimeoutId: ReturnType<typeof setTimeout> | null = null
private batch: BatchItem[] = []

constructor(chainId: number, url: string, batchWaitTimeMs?: number) {
this.chainId = chainId
this.url = url
const parsed = new URL(url)
this.host = parsed.host
this.path = parsed.pathname
// how long to wait to batch calls
this.batchWaitTimeMs = batchWaitTimeMs ?? 50
}

public readonly clearBatch = async () => {
console.debug('Clearing batch', this.batch)
const batch = this.batch
this.batch = []
this.batchTimeoutId = null
let response: Response
try {
response = await fetch(this.url, {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify(batch.map(item => item.request))
})
} catch (error) {
batch.forEach(({ reject }) => reject(new Error('Failed to send batch call')))
return
}

if (!response.ok) {
batch.forEach(({ reject }) => reject(new RequestError(`${response.status}: ${response.statusText}`, -32000)))
return
}

let json
try {
json = await response.json()
} catch (error) {
batch.forEach(({ reject }) => reject(new Error('Failed to parse JSON response')))
return
}
const byKey = batch.reduce<{ [id: number]: BatchItem }>((memo, current) => {
memo[current.request.id] = current
return memo
}, {})
for (const result of json) {
const {
resolve,
reject,
request: { method }
} = byKey[result.id]
if (resolve && reject) {
if ('error' in result) {
reject(new RequestError(result?.error?.message, result?.error?.code, result?.error?.data))
} else if ('result' in result) {
resolve(result.result)
} else {
reject(new RequestError(`Received unexpected JSON-RPC response to ${method} request.`, -32000, result))
}
}
}
}

public readonly sendAsync = (
Expand All @@ -56,24 +120,20 @@ class MiniRpcProvider implements AsyncSendable {
if (method === 'eth_chainId') {
return `0x${this.chainId.toString(16)}`
}
const response = await fetch(this.url, {
method: 'POST',
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method,
params
const promise = new Promise((resolve, reject) => {
this.batch.push({
request: {
jsonrpc: '2.0',
id: this.nextId++,
method,
params
},
resolve,
reject
})
})
if (!response.ok) throw new RequestError(`${response.status}: ${response.statusText}`, -32000)
const body = await response.json()
if ('error' in body) {
throw new RequestError(body?.error?.message, body?.error?.code, body?.error?.data)
} else if ('result' in body) {
return body.result
} else {
throw new RequestError(`Received unexpected JSON-RPC response to ${method} request.`, -32000, body)
}
this.batchTimeoutId = this.batchTimeoutId ?? setTimeout(this.clearBatch, this.batchWaitTimeMs)
return promise
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/data/V1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function useV1Trade(
? new Trade(route, exactAmount, isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT)
: undefined
} catch (error) {
console.error('Failed to create V1 trade', error)
console.debug('Failed to create V1 trade', error)
}
return v1Trade
}
Expand Down
Loading