Skip to content

Commit 63f0a4b

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents fdaf0da + a097302 commit 63f0a4b

28 files changed

+891
-117
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ before_install:
1616
$TRAVIS_BRANCH = master || \
1717
$TRAVIS_BRANCH = release/* || \
1818
$TRAVIS_BRANCH = hotfix/* || \
19-
$TRAVIS_TAG != "" ]] && \
20-
echo "production" || echo "develop")
19+
$TRAVIS_TAG != "" ]] && echo "production" || echo "develop")
2120
before_deploy:
2221
# zip ./dist folder if tag IS present
2322
- if [[ $TRAVIS_TAG != "" && $IPFS_HASH = "" ]]; then
@@ -72,7 +71,7 @@ deploy:
7271
# add release_v*.zip file to tagged Releases on github
7372
- provider: releases
7473
api_key: $GITHUB_RELEASE_TAG_API_TOKEN
75-
body: "IPFS hash: $IPFS_HASH"
74+
release_notes: "IPFS hash: $IPFS_HASH"
7675
file:
7776
- web_$TRAVIS_TAG.zip
7877
- ipfs_hash.txt

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@gnosis.pm/dex-react",
3-
"version": "1.1.2",
3+
"version": "1.2.0",
44
"description": "",
55
"main": "src/index.js",
66
"sideEffects": false,

src/api/deposit/DepositApi.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export interface DepositApi {
4141
getBalance(params: GetBalanceParams): Promise<BN>
4242
getPendingDeposit(params: GetPendingDepositParams): Promise<PendingFlux>
4343
getPendingWithdraw(params: GetPendingWithdrawParams): Promise<PendingFlux>
44+
getLastCreditBatchId(params: GetPendingDepositParams): Promise<number>
4445

4546
deposit(params: DepositParams): Promise<Receipt>
4647
requestWithdraw(params: RequestWithdrawParams): Promise<Receipt>
@@ -87,6 +88,16 @@ export class DepositApiImpl implements DepositApi {
8788
return +batchId
8889
}
8990

91+
public async getLastCreditBatchId({
92+
userAddress,
93+
tokenAddress,
94+
networkId,
95+
}: GetPendingDepositParams): Promise<number> {
96+
const contract = await this._getContract(networkId)
97+
const lastCreditBatchId = await contract.methods.lastCreditBatchId(userAddress, tokenAddress).call()
98+
return +lastCreditBatchId
99+
}
100+
90101
public async getSecondsRemainingInBatch(networkId: number): Promise<number> {
91102
const contract = await this._getContract(networkId)
92103
const secondsRemainingInBatch = await contract.methods.getSecondsRemainingInBatch().call()

src/api/deposit/DepositApiMock.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Erc20Api } from 'api/erc20/Erc20Api'
2222

2323
export interface BalanceState {
2424
balance: BN
25+
lastCreditedBatchId: number
2526
pendingDeposits: PendingFlux
2627
pendingWithdraws: PendingFlux
2728
}
@@ -47,6 +48,16 @@ export class DepositApiMock implements DepositApi {
4748
return BATCH_TIME
4849
}
4950

51+
public async getLastCreditBatchId({ userAddress, tokenAddress }: GetPendingDepositParams): Promise<number> {
52+
const userBalanceStates = this._balanceStates[userAddress]
53+
if (!userBalanceStates) {
54+
return 0
55+
}
56+
const balanceState = userBalanceStates[tokenAddress]
57+
58+
return balanceState ? balanceState.lastCreditedBatchId : 0
59+
}
60+
5061
public async getCurrentBatchId(_networkId = 0): Promise<number> {
5162
return Math.floor(getEpoch() / BATCH_TIME)
5263
}
@@ -191,6 +202,7 @@ export class DepositApiMock implements DepositApi {
191202
if (!balanceState) {
192203
balanceState = {
193204
balance: ZERO,
205+
lastCreditedBatchId: 0,
194206
pendingDeposits: {
195207
batchId: currentBatchId,
196208
amount: ZERO,

src/api/gasStation.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ const GAS_STATIONS = {
55
4: 'https://safe-relay.staging.gnosisdev.com/api/v1/gas-station/',
66
}
77

8-
const GAS_PRICE_LEVEL: Exclude<keyof GasStationResponse, 'lastUpdate'> = 'standard'
8+
export type GasPriceLevel = Exclude<keyof GasStationResponse, 'lastUpdate'>
9+
10+
const GAS_PRICE_LEVEL: GasPriceLevel = 'standard'
911

1012
let cacheKey = ''
1113
let cachedGasPrice: string
@@ -26,7 +28,9 @@ interface GasStationResponse {
2628
fastest: string
2729
}
2830

29-
const fetchGasPriceFactory = (walletApi: WalletApi) => async (): Promise<string | undefined> => {
31+
const fetchGasPriceFactory = (walletApi: WalletApi) => async (
32+
gasPriceLevel: GasPriceLevel = GAS_PRICE_LEVEL,
33+
): Promise<string | undefined> => {
3034
const { blockchainState } = walletApi
3135

3236
if (!blockchainState) return undefined
@@ -47,7 +51,7 @@ const fetchGasPriceFactory = (walletApi: WalletApi) => async (): Promise<string
4751
const response = await fetch(gasStationURL)
4852
const json: GasStationResponse = await response.json()
4953

50-
const gasPrice = json[GAS_PRICE_LEVEL]
54+
const gasPrice = json[gasPriceLevel]
5155
if (gasPrice) {
5256
cacheKey = key
5357
cachedGasPrice = gasPrice

src/api/wallet/WalletApi.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { logDebug, toBN, txDataEncoder, generateWCOptions } from 'utils'
1313
import { subscribeToWeb3Event } from './subscriptionHelpers'
1414
import { getMatchingScreenSize, subscribeToScreenSizeChange } from 'utils/mediaQueries'
1515
import { composeProvider } from './composeProvider'
16-
import fetchGasPriceFactory from 'api/gasStation'
16+
import fetchGasPriceFactory, { GasPriceLevel } from 'api/gasStation'
1717
import { earmarkTxData } from 'api/earmark'
1818
import { Provider, isMetamaskProvider, isWalletConnectProvider, ProviderRpcError } from './providerUtils'
1919

@@ -53,6 +53,7 @@ export interface WalletApi {
5353
getProviderInfo(): ProviderInfo
5454
blockchainState: BlockchainUpdatePrompt
5555
userPrintAsync: Promise<string>
56+
getGasPrice(gasPriceLevel?: GasPriceLevel): Promise<number | null>
5657
}
5758

5859
export interface WalletInfo {
@@ -249,6 +250,7 @@ export class WalletApiImpl implements WalletApi {
249250
public blockchainState: BlockchainUpdatePrompt
250251

251252
private _unsubscribe: Command
253+
private _fetchGasPrice: ReturnType<typeof fetchGasPriceFactory> = async () => undefined
252254

253255
public constructor(web3: Web3) {
254256
this._listeners = []
@@ -349,6 +351,9 @@ export class WalletApiImpl implements WalletApi {
349351
closeOpenWebSocketConnection(this._web3)
350352

351353
const fetchGasPrice = fetchGasPriceFactory(this)
354+
355+
this._fetchGasPrice = fetchGasPrice
356+
352357
const earmarkingFunction = async (data?: string): Promise<string> => earmarkTxData(data, await this.userPrintAsync)
353358

354359
const composedProvider = composeProvider(provider, { fetchGasPrice, earmarkTxData: earmarkingFunction })
@@ -457,6 +462,26 @@ export class WalletApiImpl implements WalletApi {
457462
await this._notifyListeners()
458463
}
459464

465+
public async getGasPrice(gasPriceLevel?: GasPriceLevel): Promise<number | null> {
466+
// this never errors
467+
// returns undefined if unable to fetch
468+
let gasPrice = await this._fetchGasPrice(gasPriceLevel)
469+
470+
if (gasPrice) return +gasPrice
471+
try {
472+
// fallback to gasPrice from provider
473+
// {"jsonrpc":"2.0","method":"eth_gasPrice"} call
474+
gasPrice = await this._web3.eth.getGasPrice()
475+
476+
if (gasPrice) return +gasPrice
477+
} catch (error) {
478+
console.error('Error fetching gas price', error)
479+
}
480+
481+
// unable to fetch
482+
return null
483+
}
484+
460485
public async getAddress(): Promise<string> {
461486
assert(await this._connected, 'The wallet is not connected')
462487

src/api/wallet/WalletApiMock.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ export class WalletApiMock implements WalletApi {
5858
return this.connect()
5959
}
6060

61+
public async getGasPrice(): Promise<null> {
62+
return null
63+
}
64+
6165
public async getAddress(): Promise<string> {
6266
assert(this._connected, 'The wallet is not connected')
6367

src/components/DepositWidget/Row.tsx

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import BN from 'bn.js'
33

44
// Assets
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
6-
import { faClock, faPlus, faMinus } from '@fortawesome/free-solid-svg-icons'
6+
import { faClock, faPlus, faMinus, faBaby } from '@fortawesome/free-solid-svg-icons'
77
import { MinusSVG, PlusSVG } from 'assets/img/SVG'
88

99
// const, utils, types
@@ -24,6 +24,7 @@ import { TokenRow, RowClaimButton, RowClaimSpan } from 'components/DepositWidget
2424
import useNoScroll from 'hooks/useNoScroll'
2525
import { TokenLocalState } from 'reducers-actions'
2626
import { TokenSymbol } from 'components/TokenSymbol'
27+
import { HelpTooltip, HelpTooltipContainer } from 'components/Tooltip'
2728

2829
export interface RowProps extends Record<keyof TokenLocalState, boolean> {
2930
ethBalance: BN | null
@@ -37,6 +38,12 @@ export interface RowProps extends Record<keyof TokenLocalState, boolean> {
3738
}
3839

3940
const spinner = <Spinner style={{ marginRight: 7 }} />
41+
const ImmatureClaimTooltip: React.FC<{ displayName: string }> = ({ displayName }) => (
42+
<HelpTooltipContainer>
43+
You cannot withdraw in this batch because you received {displayName} in the previous batch and the protocol requires
44+
one additional batch for settling the received tokens. Wait for the next batch (max 5 min) and try again.
45+
</HelpTooltipContainer>
46+
)
4047

4148
export const Row: React.FC<RowProps> = (props: RowProps) => {
4249
const {
@@ -53,6 +60,7 @@ export const Row: React.FC<RowProps> = (props: RowProps) => {
5360
claiming,
5461
withdrawing,
5562
depositing,
63+
immatureClaim: isImmatureClaim,
5664
} = props
5765

5866
const {
@@ -72,10 +80,11 @@ export const Row: React.FC<RowProps> = (props: RowProps) => {
7280
} = tokenBalances
7381

7482
const [visibleForm, showForm] = useState<'deposit' | 'withdraw' | void>()
83+
// block background scrolling on open modals
84+
useNoScroll(!!visibleForm)
7585

7686
// Checks innerWidth
7787
const showResponsive = !!innerWidth && innerWidth < MEDIA.MOBILE_LARGE_PX
78-
useNoScroll(!!visibleForm && showResponsive)
7988

8089
let className
8190
if (highlighted) {
@@ -116,7 +125,15 @@ export const Row: React.FC<RowProps> = (props: RowProps) => {
116125
<RowClaimButton className="success" onClick={onClaim} disabled={claiming}>
117126
{(claiming || withdrawing) && spinner}
118127
{formatSmart(pendingWithdraw.amount, decimals)}
119-
<RowClaimSpan className={claiming || withdrawing ? 'disabled' : 'success'}>Claim</RowClaimSpan>
128+
<div>
129+
<RowClaimSpan className={claiming || withdrawing ? 'disabled' : 'success'}>Claim</RowClaimSpan>
130+
{isImmatureClaim && (
131+
<span className="immatureClaimTooltip">
132+
<FontAwesomeIcon icon={faBaby} />
133+
<HelpTooltip tooltip={<ImmatureClaimTooltip displayName={symbol || name || 'tokens'} />} />
134+
</span>
135+
)}
136+
</div>
120137
</RowClaimButton>
121138
</>
122139
) : pendingWithdraw.amount.gt(ZERO) ? (
@@ -129,17 +146,24 @@ export const Row: React.FC<RowProps> = (props: RowProps) => {
129146
<>{withdrawing && spinner}0</>
130147
)}
131148
</td>
132-
<td data-label="Wallet">
149+
<td
150+
data-label="Wallet"
151+
title={(!isWeth && formatAmountFull({ amount: walletBalance, precision: decimals })) || ''}
152+
>
133153
{isWeth ? (
134154
<ul>
135155
<li title={ethBalance ? formatAmountFull({ amount: ethBalance, precision: decimals }) : undefined}>
136-
{ethBalance ? formatSmart(ethBalance, decimals) : '-'} ETH{' '}
156+
<span>
157+
{ethBalance ? formatSmart(ethBalance, decimals) : '-'} <span>ETH</span>
158+
</span>{' '}
137159
<WrapEtherBtn label="Wrap" className="wrapUnwrapEther" />
138160
</li>
139161
<li title={formatAmountFull({ amount: walletBalance, precision: decimals }) || undefined}>
140162
{(claiming || depositing) && spinner}
141-
{formatSmart(walletBalance, decimals) + ' '}
142-
WETH <UnwrapEtherBtn label="Unwrap" className="wrapUnwrapEther" />
163+
<span>
164+
{formatSmart(walletBalance, decimals)} <span>WETH</span>
165+
</span>{' '}
166+
<UnwrapEtherBtn label="Unwrap" className="wrapUnwrapEther" />
143167
</li>
144168
</ul>
145169
) : (

src/components/DepositWidget/Styled.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,18 @@ export const TokenRow = styled.tr`
8585
padding: 0;
8686
8787
li {
88-
white-space: nowrap;
88+
display: flex;
89+
align-items: baseline;
90+
justify-content: flex-end;
91+
92+
> span {
93+
margin-right: 0.5rem;
94+
}
8995
}
9096
9197
button.wrapUnwrapEther {
9298
font-size: 1rem;
93-
padding 0.2rem 0.5rem;
99+
padding: 0.2rem 0.5rem;
94100
margin: 0.2rem 0;
95101
display: inline;
96102
min-width: 5rem;
@@ -123,6 +129,20 @@ export const RowClaimButton = styled.button`
123129
cursor: not-allowed;
124130
opacity: 0.5;
125131
}
132+
133+
> div {
134+
display: flex;
135+
align-items: inherit;
136+
137+
> .immatureClaimTooltip {
138+
color: #d2a827;
139+
margin-left: 0.5rem;
140+
141+
> span {
142+
margin: 0.5rem;
143+
}
144+
}
145+
}
126146
`
127147

128148
export const RowClaimSpan = styled.span`

src/components/DepositWidget/index.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ const NoTokensMessage = styled.tr`
171171
interface BalanceDisplayProps extends TokenLocalState {
172172
enableToken: (tokenAddress: string, onTxHash?: (hash: string) => void) => Promise<void>
173173
depositToken: (amount: BN, tokenAddress: string, onTxHash?: (hash: string) => void) => Promise<void>
174-
claimToken: (tokenAddress: string, onTxHash?: (hash: string) => void) => Promise<void>
174+
claimToken: (tokenAddress: string, onTxHash?: (hash: string) => void) => Promise<void | React.ReactText>
175175
ethBalance: BN | null
176176
balances: TokenBalanceDetails[]
177177
error: boolean
@@ -181,6 +181,7 @@ interface BalanceDisplayProps extends TokenLocalState {
181181
claimable: boolean,
182182
onTxHash: (hash: string) => void,
183183
): Promise<void>
184+
hasTokensToShow?: boolean
184185
}
185186

186187
const customFilterFnFactory = (searchTxt: string) => (token: TokenBalanceDetails): boolean => {
@@ -210,7 +211,9 @@ const BalancesDisplay: React.FC<BalanceDisplayProps> = ({
210211
highlighted,
211212
enabling,
212213
enabled,
214+
immatureClaim,
213215
requestWithdrawConfirmation,
216+
hasTokensToShow = false,
214217
}) => {
215218
const windowSpecs = useWindowSizes()
216219

@@ -305,17 +308,18 @@ const BalancesDisplay: React.FC<BalanceDisplayProps> = ({
305308
onTxHash,
306309
)
307310
}}
308-
onClaim={(): Promise<void> => claimToken(tokenBalances.address)}
311+
onClaim={(): Promise<void | React.ReactText> => claimToken(tokenBalances.address)}
309312
claiming={claiming.has(tokenBalances.address)}
310313
withdrawing={withdrawing.has(tokenBalances.address)}
311314
depositing={depositing.has(tokenBalances.address)}
312315
highlighted={highlighted.has(tokenBalances.address)}
313316
enabling={enabling.has(tokenBalances.address)}
314317
enabled={enabled.has(tokenBalances.address)}
318+
immatureClaim={immatureClaim.has(tokenBalances.address)}
315319
{...windowSpecs}
316320
/>
317321
))
318-
) : balances.length === 0 ? (
322+
) : balances.length === 0 && hasTokensToShow ? (
319323
<NoTokensMessage>
320324
<td>
321325
All tokens disabled. Enable some in <a onClick={toggleModal}>Manage Tokens</a>
@@ -401,6 +405,7 @@ const DepositWidget: React.FC = () => {
401405
<BalancesDisplayMemoed
402406
ethBalance={ethBalance}
403407
balances={balances}
408+
hasTokensToShow={allBalances.length > 0}
404409
error={error}
405410
{...restActions}
406411
requestWithdrawConfirmation={requestWithdrawConfirmation}

0 commit comments

Comments
 (0)