stable version with updated balances, add liquidity using SDK, pair menu search
This commit is contained in:
parent
3091ebc158
commit
753e5f3423
11
package.json
11
package.json
@ -5,9 +5,14 @@
|
||||
"homepage": "https://uniswap.exchange",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ethersproject/units": "^5.0.0-beta.132",
|
||||
"@reach/dialog": "^0.2.8",
|
||||
"@reach/tooltip": "^0.2.0",
|
||||
"@uniswap/sdk": "^1.0.0-beta.4",
|
||||
"@types/jest": "^25.1.3",
|
||||
"@types/node": "^13.7.4",
|
||||
"@types/react": "^16.9.21",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@uniswap/sdk": "next",
|
||||
"@web3-react/core": "^6.0.2",
|
||||
"@web3-react/fortmatic-connector": "^6.0.2",
|
||||
"@web3-react/injected-connector": "^6.0.3",
|
||||
@ -36,7 +41,9 @@
|
||||
"react-spring": "^8.0.27",
|
||||
"react-switch": "^5.0.1",
|
||||
"react-use-gesture": "^6.0.14",
|
||||
"styled-components": "^4.2.0"
|
||||
"rebass": "^4.0.7",
|
||||
"styled-components": "^4.2.0",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
@ -9,7 +9,7 @@
|
||||
"swapAnyway": "Swap Anyway",
|
||||
"send": "Send",
|
||||
"sendAnyway": "Send Anyway",
|
||||
"pool": "Pool",
|
||||
"pool": "Supply",
|
||||
"betaWarning": "This project is in beta. Use at your own risk.",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
|
3
src/assets/images/blue-loader.svg
Normal file
3
src/assets/images/blue-loader.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="94" height="94" viewBox="0 0 94 94" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M92 47C92 22.1472 71.8528 2 47 2C22.1472 2 2 22.1472 2 47C2 71.8528 22.1472 92 47 92" stroke="#2172E5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 283 B |
@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="256px" height="417px" viewBox="0 0 256 417" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<polygon fill="#343434" points="127.9611 0 125.1661 9.5 125.1661 285.168 127.9611 287.958 255.9231 212.32"/>
|
||||
<polygon fill="#8C8C8C" points="127.962 0 0 212.32 127.962 287.959 127.962 154.158"/>
|
||||
<polygon fill="#3C3C3B" points="127.9611 312.1866 126.3861 314.1066 126.3861 412.3056 127.9611 416.9066 255.9991 236.5866"/>
|
||||
<polygon fill="#8C8C8C" points="127.962 416.9052 127.962 312.1852 0 236.5852"/>
|
||||
<polygon fill="#141414" points="127.9611 287.9577 255.9211 212.3207 127.9611 154.1587"/>
|
||||
<polygon fill="#393939" points="0.0009 212.3208 127.9609 287.9578 127.9609 154.1588"/>
|
||||
</g>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect width="20" height="20" fill="url(#pattern0)"/>
|
||||
<defs>
|
||||
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0" transform="scale(0.0078125)"/>
|
||||
</pattern>
|
||||
<image id="image0" width="128" height="128" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAADd9JREFUeNrtXdtzE+cV38kweWpm+tI8JZP8EZ0pL+UhferkfyAzFqEpadq0hbRNUlsYQ2ycBEgCgQQIgVxMbC6BAHbEJZg7dW4OpeCWwDTNDOyuLFuybMsXfT2/tWVkWavd1e5K58g6M9+Q8cTS+ju//b5z/R1Nq0F5Ppr46cpm8xcroubyFU3m+oYmY39Do97d0KRfoH/76d/b9K9JP89Yy/pv62f9s/9PN34Hv4vPwGfhM7W68JNVUfWTp9fqv440Ge2RRqOXFHiPFKhCWfTZ+A58F74T313XQIUlGlUP0lv5RCRqtkQa9YukjMnQFO6w8N3WM9Cz4JnwbHUNhSQrovGlkSZ9K216vFoKdwGIOJ4Rz1rXWADyTDTxeCSqv0wbe5Or0kuA4SaeHX9DXZMepSFq/Jw28FBDo5GVpviFy8hafwv9TXXNOh3zzfov6Qjtka90u1NB78HfWNd0gUTWmr+yLPgaVfwCIMCToL+5/sZH44+Qa9W5WBRfxK3sxB4sRlduSSRqrKYjMbVolX//WkhhL7Ani+O4j+rL6Ai8ttgVX+RauIa9qfG3Xm+tDcs+RI+B9qjmToNVLfFHKWJ2vq5gt6eBfr5mbIPIWuNJK9lSV6zXZWLvJB/5D1AApK1+5PsOIrVhL8UlbMiy7agrMKArgfZSTKKpoc14iHLpMUkb/OZHQ6pxa5x5zID2lPaWefIm9TC5M32SlP/MOl0Zg1PqWG9aUY6fu6vY99yG1M94GnsticfozhqQdryeOJ9W09NZ9cU/Rq2TQIBdMIC9ZvfmS1T+39+Kq6mp7BwATl8dVX/caIoAAfaczZ0v7djPrYE7EwqSAwDW/u6UlMhhX9VtAlim0gy+3Np9KKlykg8ArPXvDApJJhmxqnkHlp8v1NV77hVDJUembQHQfSFtGYeCXMTKxwlmgjwy/erevlGVL4UAwHqna1hS+VlbFcK7MiN8698dVNls1hEAWC+8bsqJGFYqbIzEjtTY/oqorn64O6kKxQ4AB0+NIF8vJncA3YSf0hWc1YOFX0zsAIDVvichKosYaip5Jp8vU/l/ftVUY+NZzwCIXRpVz24wBOUN9NbQKnkkZ/a+vD6u7KQUALDeP5KUVlSyLISjX24Z16Z9Q6qUOAEA6+U344KuAuNaoFcBoWqNVOX/pllXOiV7/ALg6NkR9XRUVC3BmsBKtyVX70JxTuIGAFhvfDgkKDagpwIpKyPLskuq8l98I64mJ7OBAeD0lVH1fJshySvo8t2xI7mS5vr3GeVG3AIA6+MTKVF74KsDSXK7FkK5bsULALCatw9KMgh7y27UlKp8+O1DqenQAIAiEhiXYiKg5TSkSu7SxV3tRbwCAGt7p6Rkkd7jze2jXnapysfxDIWGDQCs1a+ZcvbGCz/BDDmDyEZLdefHCeVVygXAgZicZBF06rK+L/G41JDvh8eSqhwpFwBYbbsTYkLEruhqwGsjUfko5hwdm644AGKX0mrVehmxAejWTdh3QCIArvSPqXLFDwCwdh9OSrkGbjqFfZdKVP6rlLP3I34BgPXiFhnJopIUduC4k6b8ldTNc9ecrDoAjpwZsSqOBBjKW+0bOhmTMNqtw6dHlF8JAgBYSDsLuAbiRUvJQXUqTfl/3WyqiYksGwCcogDU71sNAdeA+UQR699skQaA7/6dUUFIUADAgivK3xswW4qlfS9KUv62/cMqKAkSAFjRbbyTRdD1/FJvoj2vJuu217WqxVCDw1NsAXD8XNoyThnbAZPzqO7BfS/p7f/8YloFKUEDAGtrB2+DEDrPj/23S1F+07a452RPNQBwhlrNUYrO+BRoF1n4ceuHiUCVjzax67cy6pOeVOAg6Pw8xdgOyCsUCXXMSoAL9flBSYbcR/QKwHWDsta+PahadyXU3qNJq54gKBBs2MnUICSdW8rHMCQJyod/PTI67VvxQ8lpdfGbsQVKBgByC/wAOw8Mqx6yNfwCAPbKb1t47qk1CAsTsSQA4MLXY74U/6M+qc5SW/gZG0XlAyC31lFxyVtkzKG03A8Idh7kWT0E3dP9bz7FXfk4msuVgTsZetud3+RiAMhfm/YlrDu9XBAgaskwIrgc+f8NrN0VSrDg7fUiE9QL8PUNut8vuz/CnQCQWxupAOSDz5IWoZQXACBnwa16CLrXZocqsgVAVyzlWvGgfbn07ZirN75cAMzZCUQ0sYuO9pgHO+G195lVD5HuWVf/riGGjvGMs89/15hUvV/a3+9hACC3mncMqm1kJxxzYSecujxq8ROxqhbmnAP45sZ4ScX/578ZdSYgl61cAOSvzWQnHIiVthP2HU3yygk0YF4uU/7eYjJJxI7f3oT/ng40aBMEAHKr/b2ElRW0sxMY8RL3a7NDk9nx95qJ+ckexACufDcWaJAmLADk1gayE3YfGlYnL80HKxteYtK9xpHwCdm0nNyLT6lzX83E1sNQfJgAmIsnUGBp+/4hdbz3PhCY8BKb2uz4dHb8vbf/NxG60isFgDmDkQJLW4hj4OCpFA9eYtI9OwAcOzcyF5+v5KoEAObZCXvIaPxgiAEAmF0BSKGCzq3WAfA3KiNn0GFssjQCc02ex3rTNQcAXHG/40I7N2sE9nMOA2+hYxIBFOkAQJ0g7nxm4WDLDbwggd17z6dJsQAA5zDLcTSke5wA3XL6AOIWf68UALxEJFVcawFmVze7ZJBT2hRH6Cs7E6rnAt9IICJ9bhpE/lBtxjErGcQsHbyRwqiXKaPnxL6BaCF6A05f5QOAKC14MU73fO5KA41dVesBmsz1aAlbzu1ogvWPmj3k0J1cJRhWHx1PVR0AuJ5WNjuWYltkk4hzcIgEWgUhHEvCYP3nqn+RE8Cb7vQ7OHaPfjFScQDArXvWBUEE3FqEuPFdqHHg4A1YJWFci0L/sokYP8bvF4HeuJ2xegKcBkKg6OLk5fABALfODWsorrL8knPQz3NpILWKQjmXhReSPaKBA/kBpw3EG7mLsnBhAQCFKk7E0Whfe/fAwmdYt2OQiwt4T0RjCEq4CyVNqWHc+06EDPC/u2KpwAAAo81pmhieCYwlsUsLvRSMqmPZGMK5NQxvkh6fsi31dlNnhzp/sHuWCwDYF25KuWAPgCnEjm5+JSN20XmtYdybQ3FsIkVsJ6gAdoofYPNhedsVlNi5dX9qd3brYAugAsiWZfwqvwlk85pDJbSHoya/lIAWHlY2TgynDiPU5TkBAEaoU4s3XFR0ATvFIl7fy6saeEF7uASCCLyFbujf0fq1y0Unzks0AubTvON6LnxLP3cCERbKvbpdRCM7GNLLLyCIkEIRg+M4lXbXH/g9VRS1OMwABqgQeUT/HlxMN6FZXDUHTrqLNyBczXHiWFGKGCkkUYikeWn9Rk8hgOMUVnbyKJDDf++wt4wko+pfZ5IoSTRxqA/wIpgXCHewHNoW+Pub93mvSeBKJ29LEyeJKBIWfbERsE4Cd9JLDB5eQDlVSchhcJ0wZksUKY0qFj53uRyB/yRGkFJzAJHR6yizLhGJHqcrhy1V7GxQ6KYUEMCd88MLhJxBfn0eijd2dA77yiqilV0sWbREuviv/jXuizgCXgVoYXLegB/lc+r7K5suXtrACIRo/XIGBsEShiCUU55AxMAIiSNjQNrghzpuMdDFux4ZI3Vo1Gdn01UDAJM+v9LLy9AoiWPjVkTL5xD0AwDkKLgPjvI8Nk7q4MgXCqqIwgYAcv4i6OHLGRwpdXSsl5GxfgHAprrHbeHHYhke7ZVTsBwAoORMwl74Gh49Wy/YKQ0ASOfei0+FBgBUH6+UMDuYdKf5FQodPkJGREoaCHA8T05lAwcAij/WCBgZC51Bd1oQEokaqyVeBU5VROUAgB3Xn23Uz1itBSWUPlxCxsQ1aQCwqohuZQIDwMcnUjL+btIVdKYFKRRHXiZxpjAyc2AQ9QsAq7pHxIhYIwtdaWEIfXCrxKsAJBN+AYD0s4yjX2/VwpKZq0A/LxEEpaqInADw9idCXD7STeBHf6Gsaok/ypFb0E8VUSkAHDrFt7qnkPAJutEqIZG1xpMS7QFUAWWKVBHZAQCnRtW5/Nze+6QTrZJC6cU2iVfB3iJVRHYAABOJDG/HaNMqLXTXPEC+ZodEEGBYlBMA9h5JSvH3O6ALrRqC8mLimYlJA0BhFVEhAPhX98xx/MRsS7wrJQ1txkMUeOiTBoK2vCqiQgCA7kVAsKcPe69xkGeiqYfJEBmQBgK0bBcCAJ1HAoy+Aey5xkkiLYnHpIEgV0WUA4CE6h7sMfZa4yhApbTrAH37YBxBdQ+neT52xz67N7+YTSDNMEQfH1i8uBt8bO58N96BVBeRsav3oCZJrDiBFSySFzFkFeGjPayanx9c2Fhe7oBDbL/i4d0wE0hSs4jVyupVLLFTwSthyUw9Qf1KcCjmaA09pVvVK4GqVSSWl1WijCu0Sh6OpwGhfY3EauMwqnexFzX91pcsOW/UuxbxXd8VWOm26GuBulcktqH5adfy3bFTkycCNTFK60r22qVbdqPmYhL0ss+QVNSCx2AFcw557s+vywxdzQxnkbxUs5W1o2d3TctSF0eDcSk47jiTWeLZ8IyOVGx18ZdoAtUp+G5BelxNlnN8t/UM9Cx4JnEJm5oIMxPtObjvMQDB8iTCHH9Dn21Z8PRd+M4FlOt14SEYhoSJWJFG8ylrLiINSIQFPkuJ3z87MNvE+HRrWf9t/ax/5lQhTwS/Q7+Lz8BnzQ1YqjH5P29N0rBVv2N5AAAAAElFTkSuQmCC"/>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 840 B After Width: | Height: | Size: 5.2 KiB |
99
src/components/Button/index.js
Normal file
99
src/components/Button/index.js
Normal file
@ -0,0 +1,99 @@
|
||||
import React from 'react'
|
||||
import { Button as RebassButton } from 'rebass/styled-components'
|
||||
import styled from 'styled-components'
|
||||
import { darken } from 'polished'
|
||||
|
||||
import { RowBetween } from '../Row'
|
||||
import { ChevronDown } from 'react-feather'
|
||||
|
||||
const Base = styled(RebassButton)`
|
||||
padding: ${({ padding }) => (padding ? padding : '16px')};
|
||||
width: ${({ width }) => (width ? width : '100%')};
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
border-radius: 20px;
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
&:disabled {
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonPrimary = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.royalBlue};
|
||||
color: white;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.royalBlue)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.royalBlue)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.royalBlue)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.royalBlue)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.royalBlue)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme }) => theme.royalBlue};
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonSecondary = styled(Base)`
|
||||
background-color: #ebf4ff;
|
||||
color: #2172e5;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, '#ebf4ff')};
|
||||
background-color: ${({ theme }) => darken(0.05, '#ebf4ff')};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, '#ebf4ff')};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#ebf4ff')};
|
||||
background-color: ${({ theme }) => darken(0.1, '#ebf4ff')};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme }) => '#ebf4ff'};
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonEmpty = styled(Base)`
|
||||
border: 1px solid #edeef2;
|
||||
background-color: transparent;
|
||||
color: black;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, '#edeef2')};
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#edeef2')};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#edeef2')};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
export function ButtonDropwdown({ disabled, children, ...rest }) {
|
||||
return (
|
||||
<ButtonPrimary {...rest}>
|
||||
<RowBetween>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
|
||||
<ChevronDown size={24} />
|
||||
</RowBetween>
|
||||
</ButtonPrimary>
|
||||
)
|
||||
}
|
16
src/components/Card/index.js
Normal file
16
src/components/Card/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
import styled from 'styled-components'
|
||||
import { Box } from 'rebass/styled-components'
|
||||
|
||||
const Card = styled(Box)`
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
padding: 1rem;
|
||||
padding: ${({ padding }) => padding};
|
||||
border: ${({ border }) => border};
|
||||
`
|
||||
|
||||
export default Card
|
||||
|
||||
export const LightCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.outlineGrey};
|
||||
`
|
20
src/components/Column/index.js
Normal file
20
src/components/Column/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
const Column = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
`
|
||||
export const ColumnCenter = styled(Column)`
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
export const AutoColumn = styled.div`
|
||||
display: grid;
|
||||
grid-auto-rows: auto;
|
||||
grid-row-gap: ${({ gap }) => gap};
|
||||
justify-items: ${({ justify }) => justify && justify};
|
||||
`
|
||||
|
||||
export default Column
|
217
src/components/ConfirmationModal/index.js
Normal file
217
src/components/ConfirmationModal/index.js
Normal file
@ -0,0 +1,217 @@
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { ButtonPrimary } from '../../components/Button'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
import Row, { RowBetween, RowFlat, RowFixed } from '../Row'
|
||||
import { Text } from 'rebass'
|
||||
import Modal from '../Modal'
|
||||
import { CheckCircle } from 'react-feather'
|
||||
import DoubleTokenLogo from '../DoubleLogo'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import { CloseIcon } from '../../theme/components'
|
||||
import Loader from '../Loader'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
`
|
||||
const Section = styled(AutoColumn)`
|
||||
padding: 2rem;
|
||||
`
|
||||
|
||||
const BottomSection = styled(Section)`
|
||||
background-color: ${({ theme }) => theme.activeGray};
|
||||
`
|
||||
|
||||
const ConfirmedIcon = styled(ColumnCenter)`
|
||||
padding: 60px 0;
|
||||
`
|
||||
|
||||
export default function ConfirmationModal({
|
||||
isOpen,
|
||||
onDismiss,
|
||||
liquidityMinted,
|
||||
amount0,
|
||||
amount1,
|
||||
poolTokenPercentage,
|
||||
price
|
||||
}) {
|
||||
const { address: address0, symbol: symbol0 } = amount0?.token || {}
|
||||
const { address: address1, symbol: symbol1 } = amount1?.token || {}
|
||||
|
||||
const [confirmed, SetConfirmed] = useState(false)
|
||||
const [waitingForConfirmation, setWaitingForConfirmation] = useState(false)
|
||||
|
||||
function WrappedOnDismissed() {
|
||||
onDismiss()
|
||||
SetConfirmed(false)
|
||||
}
|
||||
|
||||
function fakeCall() {
|
||||
setTimeout(() => {
|
||||
setWaitingForConfirmation(false)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={100}>
|
||||
{!confirmed ? (
|
||||
<Wrapper>
|
||||
<Section gap="40px">
|
||||
<RowBetween>
|
||||
<Text fontWeight={500} fontSize={'20px'}>
|
||||
You will receive
|
||||
</Text>
|
||||
<CloseIcon onClick={WrappedOnDismissed} />
|
||||
</RowBetween>
|
||||
<AutoColumn gap="16px">
|
||||
<RowFlat>
|
||||
<Text fontSize="48px" fontWeight={500} lineHeight="32px" marginRight={10}>
|
||||
{liquidityMinted?.toFixed(6)}
|
||||
</Text>
|
||||
<DoubleTokenLogo a0={address0 || ''} a1={address1 || ''} size={20} />
|
||||
</RowFlat>
|
||||
<Row>
|
||||
<Text fontSize="24px">{symbol0 + ':' + symbol1 + ' Pool Tokens'}</Text>
|
||||
</Row>
|
||||
</AutoColumn>
|
||||
</Section>
|
||||
<BottomSection gap="12px">
|
||||
{/* <Text fontWeight={500} fontSize={16}>
|
||||
Deposited Tokens
|
||||
</Text> */}
|
||||
{/* <LightCard>
|
||||
<RowBetween>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{amountFormatter(amount0, decimals0, 4)}
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<TokenLogo address={token0 || ''} size={'24px'} />
|
||||
<Text fontWeight={500} fontSize={20} marginLeft="12px">
|
||||
{symbol0}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</LightCard>
|
||||
<ColumnCenter>
|
||||
<Plus size="16" color="#888D9B" />
|
||||
</ColumnCenter>
|
||||
<LightCard>
|
||||
<RowBetween>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{amountFormatter(amount1, decimals1, 4)}
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<TokenLogo address={token1 || ''} size={'24px'} />
|
||||
<Text fontWeight={500} fontSize={16} marginLeft="12px">
|
||||
{symbol1}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</LightCard> */}
|
||||
<AutoColumn gap="12px">
|
||||
<RowBetween>
|
||||
<Text color="#565A69" fontWeight={500} fontSize={16}>
|
||||
{symbol0} Deposited
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<TokenLogo address={address0 || ''} style={{ marginRight: '8px' }} />
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{amount0?.toSignificant(6)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text color="#565A69" fontWeight={500} fontSize={16}>
|
||||
{symbol1} Deposited
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<TokenLogo address={address1 || ''} style={{ marginRight: '8px' }} />
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{amount1?.toSignificant(6)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text color="#565A69" fontWeight={500} fontSize={16}>
|
||||
Rate
|
||||
</Text>
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{price && `1 ${symbol0} = ${price?.adjusted.toFixed(8)} ${symbol1}`}
|
||||
</Text>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text color="#565A69" fontWeight={500} fontSize={16}>
|
||||
Minted Pool Share:
|
||||
</Text>
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{poolTokenPercentage?.toFixed(6) + '%'}
|
||||
</Text>
|
||||
</RowBetween>
|
||||
<ButtonPrimary
|
||||
style={{ margin: '20px 0' }}
|
||||
onClick={() => {
|
||||
setWaitingForConfirmation(true)
|
||||
SetConfirmed(true)
|
||||
fakeCall()
|
||||
}}
|
||||
>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
Confirm Supply
|
||||
</Text>
|
||||
</ButtonPrimary>
|
||||
<Text fontSize={12} color="#565A69" textAlign="center">
|
||||
{`Output is estimated. You will receive at least ${liquidityMinted?.toFixed(
|
||||
6
|
||||
)} UNI ${symbol0}/${symbol1} or the transaction will revert.`}
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</BottomSection>
|
||||
</Wrapper>
|
||||
) : (
|
||||
<Wrapper>
|
||||
<Section>
|
||||
<RowBetween>
|
||||
<div />
|
||||
<CloseIcon onClick={WrappedOnDismissed} />
|
||||
</RowBetween>
|
||||
<ConfirmedIcon>
|
||||
{waitingForConfirmation ? <Loader size="90px" /> : <CheckCircle size={90} color="#27AE60" />}
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="24px" justify={'center'}>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{!waitingForConfirmation ? 'Transaction Submitted' : 'Waiting For Confirmation'}
|
||||
</Text>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<Text fontWeight={500} fontSize={16} color="#2172E5">
|
||||
Supplied
|
||||
</Text>
|
||||
<Text fontWeight={600} fontSize={16} color="#2172E5">
|
||||
{`${amount0?.toSignificant(6)} ${symbol0} and ${amount1?.toSignificant(6)} ${symbol1}`}
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
{!waitingForConfirmation && (
|
||||
<>
|
||||
<Text fontWeight={500} fontSize={14} color="#2172E5">
|
||||
View on Etherscan
|
||||
</Text>
|
||||
<ButtonPrimary onClick={WrappedOnDismissed} style={{ margin: '20px 0' }}>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
Close
|
||||
</Text>
|
||||
</ButtonPrimary>
|
||||
</>
|
||||
)}
|
||||
{waitingForConfirmation && <div style={{ height: '138px' }} />}
|
||||
<Text fontSize={12} color="#565A69">
|
||||
{waitingForConfirmation
|
||||
? 'Confirm this transaction in your wallet'
|
||||
: `Estimated time until confirmation: 3 min`}
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</Section>
|
||||
</Wrapper>
|
||||
)}
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -81,7 +81,6 @@ class ContextualInfo extends Component {
|
||||
if (!this.state.showDetails) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <Details>{this.props.renderTransactionDetails()}</Details>
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,23 @@
|
||||
import React, { useState, useRef, useMemo } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ethers } from 'ethers'
|
||||
import { BigNumber } from '@uniswap/sdk'
|
||||
import styled from 'styled-components'
|
||||
import escapeStringRegex from 'escape-string-regexp'
|
||||
import { darken } from 'polished'
|
||||
import Tooltip from '@reach/tooltip'
|
||||
import '@reach/tooltip/styles.css'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
|
||||
import { BorderlessInput } from '../../theme'
|
||||
import { useWeb3React, useTokenContract } from '../../hooks'
|
||||
import { isAddress, calculateGasMargin, formatToUsd, formatTokenBalance, formatEthBalance } from '../../utils'
|
||||
import { Text } from 'rebass'
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
import Modal from '../Modal'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import SearchIcon from '../../assets/images/magnifying-glass.svg'
|
||||
import SearchModal from '../SearchModal'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import { RowBetween } from '../Row'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
|
||||
import { useTokenDetails, useAllTokenDetails, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { useToken, useAllTokens } from '../../contexts/Tokens'
|
||||
import { useTokenContract } from '../../hooks'
|
||||
import { calculateGasMargin } from '../../utils'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { transparentize } from 'polished'
|
||||
import { Spinner } from '../../theme'
|
||||
import Circle from '../../assets/images/circle-grey.svg'
|
||||
import { useETHPriceInUSD, useAllBalances } from '../../contexts/Balances'
|
||||
|
||||
const GAS_MARGIN = ethers.utils.bigNumberify(1000)
|
||||
|
||||
@ -51,36 +44,23 @@ const InputRow = styled.div`
|
||||
padding: 0.25rem 0.85rem 0.75rem;
|
||||
`
|
||||
|
||||
const Input = styled(BorderlessInput)`
|
||||
font-size: 1.5rem;
|
||||
color: ${({ error, theme }) => error && theme.salmonRed};
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
-moz-appearance: textfield;
|
||||
`
|
||||
|
||||
const StyledBorderlessInput = styled(BorderlessInput)`
|
||||
min-height: 2.5rem;
|
||||
flex-shrink: 0;
|
||||
text-align: left;
|
||||
padding-left: 1.6rem;
|
||||
background-color: ${({ theme }) => theme.concreteGray};
|
||||
`
|
||||
|
||||
const CurrencySelect = styled.button`
|
||||
align-items: center;
|
||||
font-size: 1rem;
|
||||
font-size: 20px;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.buttonBackgroundPlain : theme.zumthorBlue)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.textColor : theme.royalBlue)};
|
||||
height: 2rem;
|
||||
border: 1px solid ${({ selected, theme }) => (selected ? theme.mercuryGray : theme.royalBlue)};
|
||||
border-radius: 2.5rem;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.concreteGray : theme.zumthorBlue)};
|
||||
border: 1px solid
|
||||
${({ selected, theme, disableTokenSelect }) =>
|
||||
disableTokenSelect ? theme.buttonBackgroundPlain : selected ? theme.buttonOutlinePlain : theme.royalBlue};
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
:hover {
|
||||
border: 1px solid
|
||||
${({ selected, theme }) => (selected ? darken(0.1, theme.mercuryGray) : darken(0.1, theme.royalBlue))};
|
||||
${({ selected, theme }) => (selected ? darken(0.1, theme.outlineGrey) : darken(0.1, theme.royalBlue))};
|
||||
}
|
||||
|
||||
:focus {
|
||||
@ -109,7 +89,6 @@ const StyledDropDown = styled(DropDown)`
|
||||
|
||||
const InputPanel = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadowColor)};
|
||||
position: relative;
|
||||
border-radius: 1.25rem;
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
@ -122,7 +101,7 @@ const Container = styled.div`
|
||||
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
:focus-within {
|
||||
border: 1px solid ${({ theme }) => theme.malibuBlue};
|
||||
border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.malibuBlue)};
|
||||
}
|
||||
`
|
||||
|
||||
@ -139,14 +118,6 @@ const LabelRow = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const LabelContainer = styled.div`
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
`
|
||||
|
||||
const ErrorSpan = styled.span`
|
||||
color: ${({ error, theme }) => error && theme.salmonRed};
|
||||
:hover {
|
||||
@ -155,158 +126,79 @@ const ErrorSpan = styled.span`
|
||||
}
|
||||
`
|
||||
|
||||
const TokenModal = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const ModalHeader = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0px 0px 0px 1rem;
|
||||
height: 60px;
|
||||
`
|
||||
|
||||
const CloseColor = styled(Close)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.textColor};
|
||||
}
|
||||
`
|
||||
|
||||
const CloseIcon = styled.div`
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 14px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
}
|
||||
`
|
||||
|
||||
const SearchContainer = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem 1.5rem;
|
||||
background-color: ${({ theme }) => theme.concreteGray};
|
||||
`
|
||||
|
||||
const TokenModalInfo = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 0.25rem 0.5rem;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
`
|
||||
|
||||
const TokenList = styled.div`
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
`
|
||||
|
||||
const TokenModalRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
#symbol {
|
||||
color: ${({ theme }) => theme.doveGrey};
|
||||
}
|
||||
|
||||
:hover {
|
||||
background-color: ${({ theme }) => theme.tokenRowHover};
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 0.8rem 1rem;
|
||||
padding-right: 2rem;
|
||||
`}
|
||||
`
|
||||
|
||||
const TokenRowLeft = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items : center;
|
||||
`
|
||||
|
||||
const TokenSymbolGroup = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
margin-left: 1rem;
|
||||
`
|
||||
|
||||
const TokenFullName = styled.div`
|
||||
color: ${({ theme }) => theme.chaliceGray};
|
||||
`
|
||||
|
||||
const FadedSpan = styled.span`
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
`
|
||||
|
||||
const TokenRowBalance = styled.div`
|
||||
font-size: 1rem;
|
||||
line-height: 20px;
|
||||
`
|
||||
|
||||
const TokenRowUsd = styled.div`
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
color: ${({ theme }) => theme.chaliceGray};
|
||||
`
|
||||
|
||||
const TokenRowRight = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
align-items: flex-end;
|
||||
`
|
||||
|
||||
const StyledTokenName = styled.span`
|
||||
margin: 0 0.25rem 0 0.25rem;
|
||||
margin: 0 0.25rem 0 0.75rem;
|
||||
`
|
||||
|
||||
const SpinnerWrapper = styled(Spinner)`
|
||||
margin: 0 0.25rem 0 0.25rem;
|
||||
color: ${({ theme }) => theme.chaliceGray};
|
||||
opacity: 0.6;
|
||||
const ClickableText = styled.div`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
const StyledBalanceMax = styled.button`
|
||||
height: 30px;
|
||||
background-color: ${({ theme }) => theme.zumthorBlue};
|
||||
border: 1px solid ${({ theme }) => theme.zumthorBlue};
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
:hover {
|
||||
border: 1px solid ${({ theme }) => theme.royalBlue};
|
||||
}
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => theme.royalBlue};
|
||||
outline: none;
|
||||
}
|
||||
`
|
||||
|
||||
export default function CurrencyInputPanel({
|
||||
onValueChange = () => {},
|
||||
allBalances,
|
||||
renderInput,
|
||||
onCurrencySelected = () => {},
|
||||
title,
|
||||
description,
|
||||
extraText,
|
||||
extraTextClickHander = () => {},
|
||||
errorMessage,
|
||||
disableUnlock,
|
||||
disableTokenSelect,
|
||||
selectedTokenAddress = '',
|
||||
showUnlock,
|
||||
value,
|
||||
urlAddedTokens
|
||||
field,
|
||||
onUserInput,
|
||||
selectedTokenAddress,
|
||||
onTokenSelection,
|
||||
title,
|
||||
onMax,
|
||||
atMax,
|
||||
error
|
||||
|
||||
// disableUnlock,
|
||||
// disableTokenSelect,
|
||||
// urlAddedTokens
|
||||
}) {
|
||||
const { account } = useWeb3React()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const disableUnlock = false
|
||||
|
||||
const disableTokenSelect = false
|
||||
|
||||
const urlAddedTokens = []
|
||||
|
||||
const errorMessage = error
|
||||
|
||||
const [modalIsOpen, setModalIsOpen] = useState(false)
|
||||
|
||||
const tokenContract = useTokenContract(selectedTokenAddress)
|
||||
const { exchangeAddress: selectedTokenExchangeAddress } = useTokenDetails(selectedTokenAddress)
|
||||
const { exchangeAddress: selectedTokenExchangeAddress } = useToken(selectedTokenAddress)
|
||||
|
||||
const pendingApproval = usePendingApproval(selectedTokenAddress)
|
||||
|
||||
const addTransaction = useTransactionAdder()
|
||||
|
||||
const allTokens = useAllTokenDetails()
|
||||
const allTokens = useAllTokens()
|
||||
|
||||
const { account } = useWeb3React()
|
||||
const token = useToken(selectedTokenAddress)
|
||||
|
||||
const userTokenBalance = useAddressBalance(account, selectedTokenAddress)
|
||||
const userTokenBalance = useAddressBalance(account, token)
|
||||
|
||||
const [showUnlock] = useState(false)
|
||||
|
||||
const [showMax, setShowMax] = useState(false)
|
||||
|
||||
function renderUnlockButton() {
|
||||
if (disableUnlock || !showUnlock || selectedTokenAddress === 'ETH' || !selectedTokenAddress) {
|
||||
@ -350,335 +242,62 @@ export default function CurrencyInputPanel({
|
||||
}
|
||||
}
|
||||
|
||||
function _renderInput() {
|
||||
if (typeof renderInput === 'function') {
|
||||
return renderInput()
|
||||
}
|
||||
|
||||
return (
|
||||
<InputRow>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.000000000000000001"
|
||||
error={!!errorMessage}
|
||||
placeholder="0.0"
|
||||
onChange={e => onValueChange(e.target.value)}
|
||||
onKeyPress={e => {
|
||||
const charCode = e.which ? e.which : e.keyCode
|
||||
|
||||
// Prevent 'minus' character
|
||||
if (charCode === 45) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
{renderUnlockButton()}
|
||||
<CurrencySelect
|
||||
selected={!!selectedTokenAddress}
|
||||
onClick={() => {
|
||||
if (!disableTokenSelect) {
|
||||
setModalIsOpen(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Aligner>
|
||||
{selectedTokenAddress ? <TokenLogo address={selectedTokenAddress} /> : null}
|
||||
{
|
||||
<StyledTokenName>
|
||||
{(allTokens[selectedTokenAddress] && allTokens[selectedTokenAddress].symbol) || t('selectToken')}
|
||||
</StyledTokenName>
|
||||
}
|
||||
{!disableTokenSelect && <StyledDropDown selected={!!selectedTokenAddress} />}
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
</InputRow>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<InputPanel>
|
||||
<Container error={!!errorMessage}>
|
||||
<LabelRow>
|
||||
<LabelContainer>
|
||||
<span>{title}</span> <span>{description}</span>
|
||||
</LabelContainer>
|
||||
|
||||
<ErrorSpan
|
||||
data-tip={'Enter max'}
|
||||
error={!!errorMessage}
|
||||
onClick={() => {
|
||||
extraTextClickHander()
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
label="Enter Max"
|
||||
style={{
|
||||
background: 'hsla(0, 0%, 0%, 0.75)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '24px',
|
||||
padding: '0.5em 1em',
|
||||
marginTop: '-64px'
|
||||
}}
|
||||
>
|
||||
<span>{extraText}</span>
|
||||
</Tooltip>
|
||||
</ErrorSpan>
|
||||
<RowBetween>
|
||||
<Text>{title}</Text>
|
||||
<ErrorSpan data-tip={'Enter max'} error={!!errorMessage} onClick={() => {}}></ErrorSpan>
|
||||
<ClickableText onClick={onMax}>
|
||||
<Text>Balance: {userTokenBalance?.toFixed(2)}</Text>
|
||||
</ClickableText>
|
||||
</RowBetween>
|
||||
</LabelRow>
|
||||
{_renderInput()}
|
||||
<InputRow>
|
||||
<NumericalInput
|
||||
field={field}
|
||||
value={value}
|
||||
onUserInput={onUserInput}
|
||||
onFocus={() => {
|
||||
setShowMax(true)
|
||||
}}
|
||||
/>
|
||||
{!!selectedTokenAddress && !atMax && showMax && <StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>}
|
||||
{renderUnlockButton()}
|
||||
<CurrencySelect
|
||||
selected={!!selectedTokenAddress}
|
||||
onClick={() => {
|
||||
if (!disableTokenSelect) {
|
||||
setModalIsOpen(true)
|
||||
}
|
||||
}}
|
||||
disableTokenSelect={disableTokenSelect}
|
||||
>
|
||||
<Aligner>
|
||||
{selectedTokenAddress ? <TokenLogo address={selectedTokenAddress} size={'24px'} /> : null}
|
||||
{
|
||||
<StyledTokenName>
|
||||
{(allTokens[selectedTokenAddress] && allTokens[selectedTokenAddress].symbol) || t('selectToken')}
|
||||
</StyledTokenName>
|
||||
}
|
||||
{!disableTokenSelect && <StyledDropDown selected={!!selectedTokenAddress} />}
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
</InputRow>
|
||||
</Container>
|
||||
{!disableTokenSelect && (
|
||||
<CurrencySelectModal
|
||||
<SearchModal
|
||||
isOpen={modalIsOpen}
|
||||
onDismiss={() => {
|
||||
setModalIsOpen(false)
|
||||
}}
|
||||
filterType="tokens"
|
||||
urlAddedTokens={urlAddedTokens}
|
||||
onTokenSelect={onCurrencySelected}
|
||||
allBalances={allBalances}
|
||||
field={field}
|
||||
onTokenSelect={onTokenSelection}
|
||||
/>
|
||||
)}
|
||||
</InputPanel>
|
||||
)
|
||||
}
|
||||
|
||||
function CurrencySelectModal({ isOpen, onDismiss, onTokenSelect, urlAddedTokens }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const { exchangeAddress } = useTokenDetails(searchQuery)
|
||||
|
||||
const allTokens = useAllTokenDetails()
|
||||
|
||||
const { account, chainId } = useWeb3React()
|
||||
|
||||
// BigNumber.js instance
|
||||
const ethPrice = useETHPriceInUSD()
|
||||
|
||||
// all balances for both account and exchanges
|
||||
let allBalances = useAllBalances()
|
||||
|
||||
const _usdAmounts = Object.keys(allTokens).map(k => {
|
||||
if (ethPrice && allBalances[account] && allBalances[account][k] && allBalances[account][k].value) {
|
||||
let ethRate = 1 // default for ETH
|
||||
let exchangeDetails = allBalances[allTokens[k].exchangeAddress]
|
||||
if (
|
||||
exchangeDetails &&
|
||||
exchangeDetails[k] &&
|
||||
exchangeDetails[k].value &&
|
||||
exchangeDetails['ETH'] &&
|
||||
exchangeDetails['ETH'].value
|
||||
) {
|
||||
const tokenBalance = new BigNumber(exchangeDetails[k].value.toString())
|
||||
const ethBalance = new BigNumber(exchangeDetails['ETH'].value.toString())
|
||||
ethRate = ethBalance.div(tokenBalance)
|
||||
}
|
||||
const USDRate = ethPrice
|
||||
.times(ethRate)
|
||||
.times(new BigNumber(10).pow(allTokens[k].decimals).div(new BigNumber(10).pow(18)))
|
||||
const balanceBigNumber = new BigNumber(allBalances[account][k].value.toString())
|
||||
const usdBalance = balanceBigNumber.times(USDRate).div(new BigNumber(10).pow(allTokens[k].decimals))
|
||||
return usdBalance
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
const usdAmounts =
|
||||
_usdAmounts &&
|
||||
Object.keys(allTokens).reduce(
|
||||
(accumulator, currentValue, i) => Object.assign({ [currentValue]: _usdAmounts[i] }, accumulator),
|
||||
{}
|
||||
)
|
||||
|
||||
const tokenList = useMemo(() => {
|
||||
return Object.keys(allTokens)
|
||||
.sort((a, b) => {
|
||||
if (allTokens[a].symbol && allTokens[b].symbol) {
|
||||
const aSymbol = allTokens[a].symbol.toLowerCase()
|
||||
const bSymbol = allTokens[b].symbol.toLowerCase()
|
||||
|
||||
// pin ETH to top
|
||||
if (aSymbol === 'ETH'.toLowerCase() || bSymbol === 'ETH'.toLowerCase()) {
|
||||
return aSymbol === bSymbol ? 0 : aSymbol === 'ETH'.toLowerCase() ? -1 : 1
|
||||
}
|
||||
|
||||
// then tokens with balance
|
||||
if (usdAmounts[a] && !usdAmounts[b]) {
|
||||
return -1
|
||||
} else if (usdAmounts[b] && !usdAmounts[a]) {
|
||||
return 1
|
||||
}
|
||||
|
||||
// sort by balance
|
||||
if (usdAmounts[a] && usdAmounts[b]) {
|
||||
const aUSD = usdAmounts[a]
|
||||
const bUSD = usdAmounts[b]
|
||||
|
||||
return aUSD.gt(bUSD) ? -1 : aUSD.lt(bUSD) ? 1 : 0
|
||||
}
|
||||
|
||||
// sort alphabetically
|
||||
return aSymbol < bSymbol ? -1 : aSymbol > bSymbol ? 1 : 0
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
.map(k => {
|
||||
let balance
|
||||
let usdBalance
|
||||
// only update if we have data
|
||||
if (k === 'ETH' && allBalances[account] && allBalances[account][k] && allBalances[account][k].value) {
|
||||
balance = formatEthBalance(allBalances[account][k].value)
|
||||
usdBalance = usdAmounts[k]
|
||||
} else if (allBalances[account] && allBalances[account][k] && allBalances[account][k].value) {
|
||||
balance = formatTokenBalance(allBalances[account][k].value, allTokens[k].decimals)
|
||||
usdBalance = usdAmounts[k]
|
||||
}
|
||||
return {
|
||||
name: allTokens[k].name,
|
||||
symbol: allTokens[k].symbol,
|
||||
address: k,
|
||||
balance: balance,
|
||||
usdBalance: usdBalance
|
||||
}
|
||||
})
|
||||
}, [allBalances, allTokens, usdAmounts, account])
|
||||
|
||||
const filteredTokenList = useMemo(() => {
|
||||
return tokenList.filter(tokenEntry => {
|
||||
const inputIsAddress = searchQuery.slice(0, 2) === '0x'
|
||||
|
||||
// check the regex for each field
|
||||
const regexMatches = Object.keys(tokenEntry).map(tokenEntryKey => {
|
||||
// if address field only search if input starts with 0x
|
||||
if (tokenEntryKey === 'address') {
|
||||
return (
|
||||
inputIsAddress &&
|
||||
typeof tokenEntry[tokenEntryKey] === 'string' &&
|
||||
!!tokenEntry[tokenEntryKey].match(new RegExp(escapeStringRegex(searchQuery), 'i'))
|
||||
)
|
||||
}
|
||||
return (
|
||||
typeof tokenEntry[tokenEntryKey] === 'string' &&
|
||||
!!tokenEntry[tokenEntryKey].match(new RegExp(escapeStringRegex(searchQuery), 'i'))
|
||||
)
|
||||
})
|
||||
return regexMatches.some(m => m)
|
||||
})
|
||||
}, [tokenList, searchQuery])
|
||||
|
||||
function _onTokenSelect(address) {
|
||||
setSearchQuery('')
|
||||
onTokenSelect(address)
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
function renderTokenList() {
|
||||
if (isAddress(searchQuery) && exchangeAddress === undefined) {
|
||||
return <TokenModalInfo>Searching for Exchange...</TokenModalInfo>
|
||||
}
|
||||
if (isAddress(searchQuery) && exchangeAddress === ethers.constants.AddressZero) {
|
||||
return (
|
||||
<>
|
||||
<TokenModalInfo>{t('noExchange')}</TokenModalInfo>
|
||||
<TokenModalInfo>
|
||||
<Link to={`/create-exchange/${searchQuery}`}>{t('createExchange')}</Link>
|
||||
</TokenModalInfo>
|
||||
</>
|
||||
)
|
||||
}
|
||||
if (!filteredTokenList.length) {
|
||||
return <TokenModalInfo>{t('noExchange')}</TokenModalInfo>
|
||||
}
|
||||
|
||||
return filteredTokenList.map(({ address, symbol, name, balance, usdBalance }) => {
|
||||
const urlAdded = urlAddedTokens && urlAddedTokens.hasOwnProperty(address)
|
||||
const customAdded =
|
||||
address !== 'ETH' &&
|
||||
INITIAL_TOKENS_CONTEXT[chainId] &&
|
||||
!INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(address) &&
|
||||
!urlAdded
|
||||
|
||||
return (
|
||||
<TokenModalRow key={address} onClick={() => _onTokenSelect(address)}>
|
||||
<TokenRowLeft>
|
||||
<TokenLogo address={address} size={'2rem'} />
|
||||
<TokenSymbolGroup>
|
||||
<div>
|
||||
<span id="symbol">{symbol}</span>
|
||||
<FadedSpan>
|
||||
{urlAdded && '(Added by URL)'} {customAdded && '(Added by user)'}
|
||||
</FadedSpan>
|
||||
</div>
|
||||
<TokenFullName> {name}</TokenFullName>
|
||||
</TokenSymbolGroup>
|
||||
</TokenRowLeft>
|
||||
<TokenRowRight>
|
||||
{balance ? (
|
||||
<TokenRowBalance>{balance && (balance > 0 || balance === '<0.0001') ? balance : '-'}</TokenRowBalance>
|
||||
) : account ? (
|
||||
<SpinnerWrapper src={Circle} alt="loader" />
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
<TokenRowUsd>
|
||||
{usdBalance && !usdBalance.isNaN()
|
||||
? usdBalance.isZero()
|
||||
? ''
|
||||
: usdBalance.lt(0.01)
|
||||
? '<$0.01'
|
||||
: '$' + formatToUsd(usdBalance)
|
||||
: ''}
|
||||
</TokenRowUsd>
|
||||
</TokenRowRight>
|
||||
</TokenModalRow>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// manage focus on modal show
|
||||
const inputRef = useRef()
|
||||
|
||||
function onInput(event) {
|
||||
const input = event.target.value
|
||||
const checksummedInput = isAddress(input)
|
||||
setSearchQuery(checksummedInput || input)
|
||||
}
|
||||
|
||||
function clearInputAndDismiss() {
|
||||
setSearchQuery('')
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onDismiss={clearInputAndDismiss}
|
||||
minHeight={60}
|
||||
maxHeight={50}
|
||||
initialFocusRef={isMobile ? undefined : inputRef}
|
||||
>
|
||||
<TokenModal>
|
||||
<ModalHeader>
|
||||
<p>Select Token</p>
|
||||
<CloseIcon onClick={clearInputAndDismiss}>
|
||||
<CloseColor alt={'close icon'} />
|
||||
</CloseIcon>
|
||||
</ModalHeader>
|
||||
<SearchContainer>
|
||||
<img src={SearchIcon} alt="search" />
|
||||
<StyledBorderlessInput
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
placeholder={isMobile ? t('searchOrPasteMobile') : t('searchOrPaste')}
|
||||
onChange={onInput}
|
||||
/>
|
||||
</SearchContainer>
|
||||
<TokenList>{renderTokenList()}</TokenList>
|
||||
</TokenModal>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
28
src/components/DoubleLogo/index.js
Normal file
28
src/components/DoubleLogo/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
|
||||
export default function DoubleTokenLogo({ a0, a1, size = 16, margin = false }) {
|
||||
const TokenWrapper = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 2 + 10).toString() + 'px'};
|
||||
`
|
||||
|
||||
const HigherLogo = styled(TokenLogo)`
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const CoveredLogo = styled(TokenLogo)`
|
||||
position: absolute;
|
||||
left: ${({ sizeraw }) => (sizeraw / 2).toString() + 'px'};
|
||||
`
|
||||
|
||||
return (
|
||||
<TokenWrapper sizeraw={size} margin={margin}>
|
||||
<HigherLogo address={a0} size={size.toString() + 'px'} />
|
||||
<CoveredLogo address={a1} size={size.toString() + 'px'} sizeraw={size} />
|
||||
</TokenWrapper>
|
||||
)
|
||||
}
|
@ -4,14 +4,13 @@ import { createBrowserHistory } from 'history'
|
||||
import { ethers } from 'ethers'
|
||||
import styled from 'styled-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BigNumber } from '@uniswap/sdk'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { brokenTokens } from '../../constants'
|
||||
import { amountFormatter, calculateGasMargin, isAddress } from '../../utils'
|
||||
|
||||
import { useExchangeContract } from '../../hooks'
|
||||
import { useTokenDetails, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { useToken, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { useTransactionAdder } from '../../contexts/Transactions'
|
||||
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
|
||||
import { useAddressAllowance } from '../../contexts/Allowances'
|
||||
@ -205,7 +204,6 @@ function getExchangeRate(inputValue, inputDecimals, outputValue, outputDecimals,
|
||||
(outputDecimals || outputDecimals === 0)
|
||||
) {
|
||||
const factor = ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))
|
||||
|
||||
if (invert) {
|
||||
return inputValue
|
||||
.mul(factor)
|
||||
@ -334,10 +332,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params
|
||||
const swapType = getSwapType(inputCurrency, outputCurrency)
|
||||
|
||||
// get decimals and exchange address for each of the currency types
|
||||
const { symbol: inputSymbol, decimals: inputDecimals, exchangeAddress: inputExchangeAddress } = useTokenDetails(
|
||||
const { symbol: inputSymbol, decimals: inputDecimals, exchangeAddress: inputExchangeAddress } = useToken(
|
||||
inputCurrency
|
||||
)
|
||||
const { symbol: outputSymbol, decimals: outputDecimals, exchangeAddress: outputExchangeAddress } = useTokenDetails(
|
||||
const { symbol: outputSymbol, decimals: outputDecimals, exchangeAddress: outputExchangeAddress } = useToken(
|
||||
outputCurrency
|
||||
)
|
||||
|
||||
@ -353,8 +351,8 @@ export default function ExchangePage({ initialCurrency, sending = false, params
|
||||
const { reserveETH: outputReserveETH, reserveToken: outputReserveToken } = useExchangeReserves(outputCurrency)
|
||||
|
||||
// get balances for each of the currency types
|
||||
const inputBalance = useAddressBalance(account, inputCurrency)
|
||||
const outputBalance = useAddressBalance(account, outputCurrency)
|
||||
const inputBalance = 0
|
||||
const outputBalance = 0
|
||||
const inputBalanceFormatted = !!(inputBalance && Number.isInteger(inputDecimals))
|
||||
? amountFormatter(inputBalance, inputDecimals, Math.min(4, inputDecimals))
|
||||
: ''
|
||||
@ -631,8 +629,8 @@ export default function ExchangePage({ initialCurrency, sending = false, params
|
||||
} else if (outputCurrency === 'ETH') {
|
||||
ethTransactionSize = inputValueFormatted * amountFormatter(exchangeRate, 18, 6, false)
|
||||
} else {
|
||||
const tokenBalance = inputReserveToken && new BigNumber(inputReserveToken.toString())
|
||||
const ethBalance = inputReserveETH && new BigNumber(inputReserveETH.toString())
|
||||
const tokenBalance = 1
|
||||
const ethBalance = 1
|
||||
let ethRate = ethBalance && tokenBalance && ethBalance.div(tokenBalance)
|
||||
ethTransactionSize = inputValueFormatted * ethRate
|
||||
}
|
||||
|
15
src/components/Loader/index.js
Normal file
15
src/components/Loader/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { Spinner } from '../../theme'
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
|
||||
const SpinnerWrapper = styled(Spinner)`
|
||||
height: ${({ size }) => size};
|
||||
width: ${({ size }) => size};
|
||||
`
|
||||
|
||||
export default function Loader({ size }) {
|
||||
return <SpinnerWrapper src={Circle} alt="loader" size={size} />
|
||||
}
|
@ -54,7 +54,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
|
||||
padding: 0px;
|
||||
width: 50vw;
|
||||
|
||||
max-width: 650px;
|
||||
max-width: 500px;
|
||||
${({ maxHeight }) =>
|
||||
maxHeight &&
|
||||
css`
|
||||
|
@ -1,18 +1,16 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import { withRouter, NavLink } from 'react-router-dom'
|
||||
import { withRouter, NavLink, Link as HistoryLink } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
import { transparentize, darken } from 'polished'
|
||||
|
||||
import { useWeb3React, useBodyKeyDown } from '../../hooks'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
import { isAddress } from '../../utils'
|
||||
import {
|
||||
useBetaMessageManager,
|
||||
useSaiHolderMessageManager,
|
||||
useGeneralDaiMessageManager
|
||||
} from '../../contexts/LocalStorage'
|
||||
import { Link } from '../../theme/components'
|
||||
import { RowBetween } from '../Row'
|
||||
import QuestionHelper from '../Question'
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
|
||||
import { useBodyKeyDown } from '../../hooks'
|
||||
|
||||
import { useBetaMessageManager } from '../../contexts/LocalStorage'
|
||||
|
||||
const tabOrder = [
|
||||
{
|
||||
@ -26,9 +24,9 @@ const tabOrder = [
|
||||
regex: /\/send/
|
||||
},
|
||||
{
|
||||
path: '/add-liquidity',
|
||||
path: '/supply',
|
||||
textKey: 'pool',
|
||||
regex: /\/add-liquidity|\/remove-liquidity|\/create-exchange.*/
|
||||
regex: /\/supply/
|
||||
}
|
||||
]
|
||||
|
||||
@ -61,57 +59,12 @@ const BetaMessage = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const DaiMessage = styled(BetaMessage)`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
word-wrap: wrap;
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
padding: 1rem 1rem;
|
||||
padding-right: 2rem;
|
||||
line-height: 1.2rem;
|
||||
cursor: default;
|
||||
color: ${({ theme }) => theme.textColor};
|
||||
div {
|
||||
width: 100%;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
}
|
||||
`
|
||||
|
||||
const CloseIcon = styled.div`
|
||||
width: 10px !important;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
position: absolute;
|
||||
color: ${({ theme }) => theme.wisteriaPurple};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
const WarningHeader = styled.div`
|
||||
margin-bottom: 10px;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.uniswapPink};
|
||||
`
|
||||
|
||||
const WarningFooter = styled.div`
|
||||
margin-top: 10px;
|
||||
font-size: 10px;
|
||||
text-decoration: italic;
|
||||
color: ${({ theme }) => theme.greyText};
|
||||
`
|
||||
|
||||
const Tabs = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
height: 2.5rem;
|
||||
background-color: ${({ theme }) => theme.concreteGray};
|
||||
height: 3rem;
|
||||
border-radius: 3rem;
|
||||
/* border: 1px solid ${({ theme }) => theme.mercuryGray}; */
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 20px;
|
||||
`
|
||||
|
||||
const activeClassName = 'ACTIVE'
|
||||
@ -123,53 +76,42 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 2.5rem;
|
||||
border: 1px solid ${({ theme }) => transparentize(1, theme.mercuryGray)};
|
||||
flex: 1 0 auto;
|
||||
border-radius: 3rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.doveGray};
|
||||
font-size: 1rem;
|
||||
font-size: 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.${activeClassName} {
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
border-radius: 3rem;
|
||||
border: 1px solid ${({ theme }) => theme.mercuryGray};
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadowColor)};
|
||||
box-sizing: border-box;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
:hover {
|
||||
/* border: 1px solid ${({ theme }) => darken(0.1, theme.mercuryGray)}; */
|
||||
background-color: ${({ theme }) => darken(0.01, theme.inputBackground)};
|
||||
}
|
||||
color: ${({ theme }) => theme.black};
|
||||
}
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
color: ${({ theme }) => darken(0.1, theme.royalBlue)};
|
||||
color: ${({ theme }) => darken(0.1, theme.black)};
|
||||
}
|
||||
`
|
||||
|
||||
const ActiveText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
`
|
||||
|
||||
const ArrowLink = styled(ArrowLeft)`
|
||||
color: ${({ theme }) => theme.black};
|
||||
`
|
||||
|
||||
function NavigationTabs({ location: { pathname }, history }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [showBetaMessage, dismissBetaMessage] = useBetaMessageManager()
|
||||
|
||||
const [showGeneralDaiMessage, dismissGeneralDaiMessage] = useGeneralDaiMessageManager()
|
||||
|
||||
const [showSaiHolderMessage, dismissSaiHolderMessage] = useSaiHolderMessageManager()
|
||||
|
||||
const { account } = useWeb3React()
|
||||
|
||||
const daiBalance = useAddressBalance(account, isAddress('0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'))
|
||||
|
||||
const daiPoolTokenBalance = useAddressBalance(account, isAddress('0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14'))
|
||||
|
||||
const onLiquidityPage = pathname === '/pool' || pathname === '/add-liquidity' || pathname === '/remove-liquidity'
|
||||
|
||||
const navigate = useCallback(
|
||||
direction => {
|
||||
const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex))
|
||||
@ -187,54 +129,30 @@ function NavigationTabs({ location: { pathname }, history }) {
|
||||
useBodyKeyDown('ArrowRight', navigateRight)
|
||||
useBodyKeyDown('ArrowLeft', navigateLeft)
|
||||
|
||||
const providerMessage =
|
||||
showSaiHolderMessage && daiPoolTokenBalance && !daiPoolTokenBalance.isZero() && onLiquidityPage
|
||||
const generalMessage = showGeneralDaiMessage && daiBalance && !daiBalance.isZero()
|
||||
const adding = pathname.match('/add')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs>
|
||||
{tabOrder.map(({ path, textKey, regex }) => (
|
||||
<StyledNavLink key={path} to={path} isActive={(_, { pathname }) => pathname.match(regex)}>
|
||||
{t(textKey)}
|
||||
</StyledNavLink>
|
||||
))}
|
||||
</Tabs>
|
||||
{providerMessage && (
|
||||
<DaiMessage>
|
||||
<CloseIcon onClick={dismissSaiHolderMessage}>✕</CloseIcon>
|
||||
<WarningHeader>Missing your DAI?</WarningHeader>
|
||||
<div>
|
||||
Don’t worry, check the{' '}
|
||||
<Link href={'/remove-liquidity?poolTokenAddress=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'}>
|
||||
SAI liquidity pool.
|
||||
</Link>{' '}
|
||||
Your old DAI is now SAI. If you want to migrate,{' '}
|
||||
<Link href="/remove-liquidity?poolTokenAddress=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359">
|
||||
remove your SAI liquidity,
|
||||
</Link>{' '}
|
||||
migrate using the <Link href="https://migrate.makerdao.com/">migration tool</Link> then add your migrated
|
||||
DAI to the{' '}
|
||||
<Link href="add-liquidity?token=0x6B175474E89094C44Da98b954EedeAC495271d0F">new DAI liquidity pool.</Link>
|
||||
</div>
|
||||
<WarningFooter>
|
||||
<Link href="https://blog.makerdao.com/looking-ahead-how-to-upgrade-to-multi-collateral-dai/">
|
||||
Read more
|
||||
</Link>{' '}
|
||||
about this change on the official Maker blog.
|
||||
</WarningFooter>
|
||||
</DaiMessage>
|
||||
)}
|
||||
{generalMessage && !providerMessage && (
|
||||
<DaiMessage>
|
||||
<CloseIcon onClick={dismissGeneralDaiMessage}>✕</CloseIcon>
|
||||
<WarningHeader>DAI has upgraded!</WarningHeader>
|
||||
<div>
|
||||
Your old DAI is now SAI. To upgrade use the{' '}
|
||||
<Link href="https://migrate.makerdao.com/">migration tool.</Link>
|
||||
</div>
|
||||
</DaiMessage>
|
||||
{adding ? (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem' }}>
|
||||
<HistoryLink to="/supply">
|
||||
<ArrowLink />
|
||||
</HistoryLink>
|
||||
<ActiveText>Add Liquidity</ActiveText>
|
||||
<QuestionHelper text={'helper text'} />
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
) : (
|
||||
<Tabs>
|
||||
{tabOrder.map(({ path, textKey, regex }) => (
|
||||
<StyledNavLink key={path} to={path} isActive={(_, { pathname }) => pathname.match(regex)}>
|
||||
{t(textKey)}
|
||||
</StyledNavLink>
|
||||
))}
|
||||
</Tabs>
|
||||
)}
|
||||
|
||||
{showBetaMessage && (
|
||||
<BetaMessage onClick={dismissBetaMessage}>
|
||||
<span role="img" aria-label="warning">
|
||||
|
64
src/components/NumericalInput/index.tsx
Normal file
64
src/components/NumericalInput/index.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const StyledInput = styled.input`
|
||||
color: ${({ error, theme }) => error && theme.salmonRed};
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
color: ${({ theme }) => theme.textColor};
|
||||
font-size: 20px;
|
||||
outline: none;
|
||||
border: none;
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
|
||||
[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
::-webkit-outer-spin-button,
|
||||
::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: ${({ theme }) => theme.chaliceGray};
|
||||
}
|
||||
`
|
||||
|
||||
const inputRegex = RegExp(`^\\d*(?:\\\\.)?\\d*$`) // match escaped "." characters via in a non-capturing group
|
||||
|
||||
function escapeRegExp(string: string): string {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
|
||||
}
|
||||
|
||||
export const Input = React.memo(({ field, value, onUserInput, ...rest }: any) => {
|
||||
function enforcer(nextUserInput: string) {
|
||||
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
|
||||
onUserInput(field, nextUserInput)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledInput
|
||||
{...rest}
|
||||
value={value}
|
||||
onChange={event => {
|
||||
enforcer(event.target.value)
|
||||
}}
|
||||
// universal input options
|
||||
inputMode="decimal"
|
||||
title="Token Amount"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
// text-specific options
|
||||
type="text"
|
||||
placeholder="0.0"
|
||||
minLength={1}
|
||||
maxLength={79}
|
||||
spellCheck="false"
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export default Input
|
90
src/components/Question/index.js
Normal file
90
src/components/Question/index.js
Normal file
@ -0,0 +1,90 @@
|
||||
import React, { useState } from 'react'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
|
||||
import question from '../../assets/images/question.svg'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const QuestionWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 0.4rem;
|
||||
padding: 0.2rem;
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
cursor: default;
|
||||
border-radius: 36px;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
opacity: 0.7;
|
||||
}
|
||||
`
|
||||
|
||||
const HelpCircleStyled = styled.img`
|
||||
height: 24px;
|
||||
width: 23px;
|
||||
`
|
||||
|
||||
const fadeIn = keyframes`
|
||||
from {
|
||||
opacity : 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity : 1;
|
||||
}
|
||||
`
|
||||
|
||||
const Popup = styled.div`
|
||||
position: absolute;
|
||||
width: 228px;
|
||||
z-index: 9999;
|
||||
left: 40px;
|
||||
top: -10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.6rem 1rem;
|
||||
line-height: 150%;
|
||||
background: ${({ theme }) => theme.inputBackground};
|
||||
border: 1px solid ${({ theme }) => theme.mercuryGray};
|
||||
|
||||
border-radius: 8px;
|
||||
|
||||
animation: ${fadeIn} 0.15s linear;
|
||||
|
||||
color: ${({ theme }) => theme.textColor};
|
||||
font-style: italic;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
left: -20px;
|
||||
`}
|
||||
`
|
||||
|
||||
export default function QuestionHelper({ text }) {
|
||||
const [showPopup, setPopup] = useState(false)
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<QuestionWrapper
|
||||
onClick={() => {
|
||||
setPopup(!showPopup)
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setPopup(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setPopup(false)
|
||||
}}
|
||||
>
|
||||
<HelpCircleStyled src={question} alt="popup" />
|
||||
</QuestionWrapper>
|
||||
{showPopup && <Popup>{text}</Popup>}
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
31
src/components/Row/index.js
Normal file
31
src/components/Row/index.js
Normal file
@ -0,0 +1,31 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
const Row = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
export const RowBetween = styled(Row)`
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
export const RowFlat = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
`
|
||||
|
||||
export const AutoRow = styled(Row)`
|
||||
flex-wrap: wrap;
|
||||
margin: -${({ gap }) => gap};
|
||||
|
||||
& > * {
|
||||
margin: ${({ gap }) => gap} !important;
|
||||
}
|
||||
`
|
||||
|
||||
export const RowFixed = styled(Row)`
|
||||
width: fit-content;
|
||||
`
|
||||
|
||||
export default Row
|
398
src/components/SearchModal/index.js
Normal file
398
src/components/SearchModal/index.js
Normal file
@ -0,0 +1,398 @@
|
||||
import React, { useState, useRef, useMemo, useEffect } from 'react'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ethers } from 'ethers'
|
||||
import styled from 'styled-components'
|
||||
import escapeStringRegex from 'escape-string-regexp'
|
||||
import '@reach/tooltip/styles.css'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
|
||||
import { Text } from 'rebass'
|
||||
import Column, { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import { CloseIcon } from '../../theme/components'
|
||||
import DoubleTokenLogo from '../DoubleLogo'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { isAddress } from '../../utils'
|
||||
import Modal from '../Modal'
|
||||
import { useToken, useAllTokens, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { Spinner } from '../../theme'
|
||||
import Circle from '../../assets/images/circle.svg'
|
||||
import { useAllBalances } from '../../contexts/Balances'
|
||||
import { useAllExchanges } from '../../contexts/Exchanges'
|
||||
|
||||
const TokenModalInfo = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 0.25rem 0.5rem;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
`
|
||||
|
||||
const TokenList = styled.div`
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
`
|
||||
|
||||
const FadedSpan = styled.span`
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
`
|
||||
|
||||
const SpinnerWrapper = styled(Spinner)`
|
||||
margin: 0 0.25rem 0 0.25rem;
|
||||
color: ${({ theme }) => theme.chaliceGray};
|
||||
opacity: 0.6;
|
||||
`
|
||||
|
||||
const Input = styled.input`
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
border: 1px solid #edeef2;
|
||||
box-sizing: border-box;
|
||||
border-radius: 20px;
|
||||
color: ${({ theme }) => theme.textColor};
|
||||
font-size: 18px;
|
||||
|
||||
::placeholder {
|
||||
color: ${({ theme }) => theme.fadedText};
|
||||
}
|
||||
`
|
||||
|
||||
const TokenModal = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const FilterWrapper = styled(RowFixed)`
|
||||
padding: 8px;
|
||||
background-color: ${({ selected, theme }) => selected && theme.backgroundColor};
|
||||
color: ${({ selected, theme }) => (selected ? theme.black : '#888D9B')};
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
& > * {
|
||||
user-select: none;
|
||||
}
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
const PaddedColumn = styled(AutoColumn)`
|
||||
padding: 24px;
|
||||
padding-bottom: 12px;
|
||||
`
|
||||
|
||||
const MenuItem = styled(RowBetween)`
|
||||
padding: 4px 24px;
|
||||
width: calc(100% - 48px);
|
||||
height: 56px;
|
||||
cursor: pointer;
|
||||
|
||||
:hover {
|
||||
background-color: ${({ theme }) => theme.tokenRowHover};
|
||||
}
|
||||
`
|
||||
function SearchModal({ history, isOpen, onDismiss, onTokenSelect, field, urlAddedTokens, filterType }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const { exchangeAddress } = useToken(searchQuery)
|
||||
|
||||
const allTokens = useAllTokens()
|
||||
|
||||
const { account, chainId } = useWeb3React()
|
||||
|
||||
// all balances for both account and exchanges
|
||||
let allBalances = useAllBalances()
|
||||
|
||||
const tokenList = useMemo(() => {
|
||||
return Object.keys(allTokens)
|
||||
.sort((a, b) => {
|
||||
if (allTokens[a].symbol && allTokens[b].symbol) {
|
||||
const aSymbol = allTokens[a].symbol.toLowerCase()
|
||||
const bSymbol = allTokens[b].symbol.toLowerCase()
|
||||
|
||||
// pin ETH to top
|
||||
if (aSymbol === 'ETH'.toLowerCase() || bSymbol === 'ETH'.toLowerCase()) {
|
||||
return aSymbol === bSymbol ? 0 : aSymbol === 'ETH'.toLowerCase() ? -1 : 1
|
||||
}
|
||||
|
||||
// sort alphabetically
|
||||
return aSymbol < bSymbol ? -1 : aSymbol > bSymbol ? 1 : 0
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
.map(k => {
|
||||
let balance
|
||||
// only update if we have data
|
||||
if (k === 'ETH' && allBalances[account] && allBalances[account][k] && allBalances[account][k].value) {
|
||||
balance = allBalances[account][k].value
|
||||
} else if (allBalances[account] && allBalances[account][k] && allBalances[account][k].value) {
|
||||
balance = (allBalances[account][k].value, allTokens[k].decimals)
|
||||
}
|
||||
return {
|
||||
name: allTokens[k].name,
|
||||
symbol: allTokens[k].symbol,
|
||||
address: k,
|
||||
balance: balance
|
||||
}
|
||||
})
|
||||
}, [allBalances, allTokens, account])
|
||||
|
||||
const filteredTokenList = useMemo(() => {
|
||||
return tokenList.filter(tokenEntry => {
|
||||
const inputIsAddress = searchQuery.slice(0, 2) === '0x'
|
||||
|
||||
// check the regex for each field
|
||||
const regexMatches = Object.keys(tokenEntry).map(tokenEntryKey => {
|
||||
// if address field only search if input starts with 0x
|
||||
if (tokenEntryKey === 'address') {
|
||||
return (
|
||||
inputIsAddress &&
|
||||
typeof tokenEntry[tokenEntryKey] === 'string' &&
|
||||
!!tokenEntry[tokenEntryKey].match(new RegExp(escapeStringRegex(searchQuery), 'i'))
|
||||
)
|
||||
}
|
||||
return (
|
||||
typeof tokenEntry[tokenEntryKey] === 'string' &&
|
||||
!!tokenEntry[tokenEntryKey].match(new RegExp(escapeStringRegex(searchQuery), 'i'))
|
||||
)
|
||||
})
|
||||
return regexMatches.some(m => m)
|
||||
})
|
||||
}, [tokenList, searchQuery])
|
||||
|
||||
function _onTokenSelect(address) {
|
||||
setSearchQuery('')
|
||||
onTokenSelect(field, address)
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
// manage focus on modal show
|
||||
const inputRef = useRef()
|
||||
|
||||
function onInput(event) {
|
||||
const input = event.target.value
|
||||
const checksummedInput = isAddress(input)
|
||||
setSearchQuery(checksummedInput || input)
|
||||
}
|
||||
|
||||
function clearInputAndDismiss() {
|
||||
setSearchQuery('')
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
// get all exchanges
|
||||
const allExchanges = useAllExchanges()
|
||||
|
||||
// amount of tokens to display at once
|
||||
const [tokensShown, setTokensShown] = useState(0)
|
||||
const [pairsShown, setPairsShown] = useState(0)
|
||||
|
||||
// filters on results
|
||||
const FILTERS = {
|
||||
VOLUME: 'VOLUME',
|
||||
LIQUIDITY: 'LIQUIDITY',
|
||||
BALANCES: 'BALANCES'
|
||||
}
|
||||
const [activeFilter, setActiveFilter] = useState(FILTERS.BALANCES)
|
||||
const [sortDirection, setSortDirection] = useState(true)
|
||||
|
||||
// sort tokens
|
||||
const escapeStringRegexp = string => string
|
||||
|
||||
// sort pairs
|
||||
const filteredPairList = useMemo(() => {
|
||||
// check if the search is an address
|
||||
const isAddress = searchQuery.slice(0, 2) === '0x'
|
||||
|
||||
return Object.keys(allExchanges).filter(token0Address => {
|
||||
return Object.keys(allExchanges[token0Address]).map(token1Address => {
|
||||
if (searchQuery === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
const token0 = allTokens[token0Address]
|
||||
const token1 = allTokens[token1Address]
|
||||
|
||||
const regexMatches = Object.keys(token0).map(field => {
|
||||
if (
|
||||
(field === 'address' && isAddress) ||
|
||||
(field === 'name' && !isAddress) ||
|
||||
(field === 'symbol' && !isAddress)
|
||||
) {
|
||||
return (
|
||||
token0[field].match(new RegExp(escapeStringRegexp(searchQuery), 'i')) ||
|
||||
token1[field].match(new RegExp(escapeStringRegexp(searchQuery), 'i'))
|
||||
)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return regexMatches.some(m => m)
|
||||
})
|
||||
})
|
||||
}, [allExchanges, allTokens, searchQuery])
|
||||
|
||||
// update the amount shown as filtered list changes
|
||||
useEffect(() => {
|
||||
setTokensShown(Math.min(Object.keys(filteredTokenList).length, 3))
|
||||
}, [filteredTokenList])
|
||||
useEffect(() => {
|
||||
setPairsShown(Math.min(Object.keys(filteredPairList).length, 3))
|
||||
}, [filteredPairList])
|
||||
|
||||
function renderPairsList() {
|
||||
return (
|
||||
filteredPairList &&
|
||||
filteredPairList.map((token0Address, i) => {
|
||||
return Object.keys(allExchanges[token0Address]).map(token1Address => {
|
||||
const token0 = allTokens[token0Address]
|
||||
const token1 = allTokens[token1Address]
|
||||
|
||||
const exchangeAddress = allExchanges[token0Address][token1Address]
|
||||
const balance = allBalances?.[account]?.[exchangeAddress]?.toSignificant(6)
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={i}
|
||||
onClick={() => {
|
||||
history.push('/add/' + token0.address + '-' + token1.address)
|
||||
onDismiss()
|
||||
}}
|
||||
>
|
||||
<RowFixed>
|
||||
<DoubleTokenLogo a0={token0?.address || ''} a1={token1?.address || ''} size={24} margin={true} />
|
||||
<Text fontWeight={500} fontSize={16}>{`${token0?.symbol}/${token1?.symbol}`}</Text>
|
||||
</RowFixed>
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{balance ? balance.toString() : '-'}
|
||||
</Text>
|
||||
</MenuItem>
|
||||
)
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function renderTokenList() {
|
||||
if (isAddress(searchQuery) && exchangeAddress === undefined) {
|
||||
return <Text>Searching for Exchange...</Text>
|
||||
}
|
||||
if (isAddress(searchQuery) && exchangeAddress === ethers.constants.AddressZero) {
|
||||
return (
|
||||
<>
|
||||
<TokenModalInfo>{t('noExchange')}</TokenModalInfo>
|
||||
<TokenModalInfo>
|
||||
<Link to={`/create-exchange/${searchQuery}`}>{t('createExchange')}</Link>
|
||||
</TokenModalInfo>
|
||||
</>
|
||||
)
|
||||
}
|
||||
if (!filteredTokenList.length) {
|
||||
return <TokenModalInfo>{t('noExchange')}</TokenModalInfo>
|
||||
}
|
||||
|
||||
return filteredTokenList.map(({ address, symbol, balance }) => {
|
||||
const urlAdded = urlAddedTokens && urlAddedTokens.hasOwnProperty(address)
|
||||
const customAdded =
|
||||
address !== 'ETH' &&
|
||||
INITIAL_TOKENS_CONTEXT[chainId] &&
|
||||
!INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(address) &&
|
||||
!urlAdded
|
||||
return (
|
||||
<MenuItem key={address} onClick={() => _onTokenSelect(address)}>
|
||||
<RowFixed>
|
||||
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
|
||||
<Column>
|
||||
<Text fontWeight={500}>{symbol}</Text>
|
||||
<FadedSpan>
|
||||
{urlAdded && '(Added by URL)'} {customAdded && '(Added by user)'}
|
||||
</FadedSpan>
|
||||
</Column>
|
||||
</RowFixed>
|
||||
<AutoColumn gap="4px" justify="end">
|
||||
{balance ? (
|
||||
<Text>{balance && (balance > 0 || balance === '<0.0001') ? balance : '-'}</Text>
|
||||
) : account ? (
|
||||
<SpinnerWrapper src={Circle} alt="loader" />
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</AutoColumn>
|
||||
</MenuItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const Filter = ({ title, filter }) => {
|
||||
return (
|
||||
<FilterWrapper
|
||||
onClick={() => {
|
||||
setActiveFilter(filter)
|
||||
setSortDirection(!sortDirection)
|
||||
}}
|
||||
selected={filter === activeFilter}
|
||||
>
|
||||
<Text fontSize={14} fontWeight={500}>
|
||||
{title}
|
||||
</Text>
|
||||
{filter === activeFilter && (
|
||||
<Text fontSize={14} fontWeight={500}>
|
||||
{sortDirection ? '↓' : '↑'}
|
||||
</Text>
|
||||
)}
|
||||
</FilterWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onDismiss={clearInputAndDismiss}
|
||||
minHeight={60}
|
||||
maxHeight={50}
|
||||
initialFocusRef={isMobile ? undefined : inputRef}
|
||||
>
|
||||
<TokenModal>
|
||||
<PaddedColumn gap="20px">
|
||||
<RowBetween>
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{filterType === 'tokens' ? 'Find A Token' : 'Find A Pool'}
|
||||
</Text>
|
||||
<CloseIcon onClick={onDismiss} />
|
||||
</RowBetween>
|
||||
<Input
|
||||
type={'text'}
|
||||
placeholder={'Search name or address'}
|
||||
value={searchQuery}
|
||||
ref={inputRef}
|
||||
onChange={onInput}
|
||||
/>
|
||||
<RowBetween>
|
||||
<div />
|
||||
<div />
|
||||
<Filter title="Your Balances" filter={FILTERS.BALANCES} />
|
||||
</RowBetween>
|
||||
</PaddedColumn>
|
||||
<div style={{ width: '100%', height: '1px', backgroundColor: '#E1E1E1' }} />
|
||||
<TokenList>{filterType === 'tokens' ? renderTokenList() : renderPairsList()}</TokenList>
|
||||
</TokenModal>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default withRouter(SearchModal)
|
@ -35,7 +35,7 @@ export default function TokenLogo({ address, size = '1rem', ...rest }) {
|
||||
|
||||
let path = ''
|
||||
if (address === 'ETH') {
|
||||
return <StyledEthereumLogo size={size} />
|
||||
return <StyledEthereumLogo size={size} {...rest} />
|
||||
} else if (!error && !BAD_IMAGES[address]) {
|
||||
path = TOKEN_ICON_API(address.toLowerCase())
|
||||
} else {
|
||||
@ -51,7 +51,7 @@ export default function TokenLogo({ address, size = '1rem', ...rest }) {
|
||||
return (
|
||||
<Image
|
||||
{...rest}
|
||||
alt={address}
|
||||
// alt={address}
|
||||
src={path}
|
||||
size={size}
|
||||
onError={() => {
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useTokenDetails } from '../../contexts/Tokens'
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
|
||||
import { Link } from '../../theme'
|
||||
@ -128,7 +128,7 @@ const Text = styled.div`
|
||||
function WarningCard({ onDismiss, urlAddedTokens, currency }) {
|
||||
const [showPopup, setPopup] = useState()
|
||||
const { chainId } = useWeb3React()
|
||||
const { symbol: inputSymbol, name: inputName } = useTokenDetails(currency)
|
||||
const { symbol: inputSymbol, name: inputName } = useToken(currency)
|
||||
const fromURL = urlAddedTokens.hasOwnProperty(currency)
|
||||
|
||||
return (
|
||||
|
@ -18,7 +18,7 @@ export const network = new NetworkConnector({
|
||||
})
|
||||
|
||||
export const injected = new InjectedConnector({
|
||||
supportedChainIds: [Number(process.env.REACT_APP_CHAIN_ID)]
|
||||
supportedChainIds: [Number(process.env.REACT_APP_CHAIN_ID), 4]
|
||||
})
|
||||
|
||||
// mainnet only
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
import {} from '@web3-react/core'
|
||||
import { Token, TokenAmount } from '@uniswap/sdk'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { safeAccess, isAddress, getTokenAllowance } from '../utils'
|
||||
@ -7,7 +7,7 @@ import { useBlockNumber } from './Application'
|
||||
|
||||
const UPDATE = 'UPDATE'
|
||||
|
||||
const AllowancesContext = createContext()
|
||||
const AllowancesContext = createContext([])
|
||||
|
||||
function useAllowancesContext() {
|
||||
return useContext(AllowancesContext)
|
||||
@ -54,18 +54,18 @@ export default function Provider({ children }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function useAddressAllowance(address, tokenAddress, spenderAddress) {
|
||||
export function useAddressAllowance(address: string, token: Token, spenderAddress: string): TokenAmount {
|
||||
const { library, chainId } = useWeb3React()
|
||||
|
||||
const globalBlockNumber = useBlockNumber()
|
||||
|
||||
const [state, { update }] = useAllowancesContext()
|
||||
const { value, blockNumber } = safeAccess(state, [chainId, address, tokenAddress, spenderAddress]) || {}
|
||||
const { value, blockNumber } = safeAccess(state, [chainId, address, token.address, spenderAddress]) || {}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isAddress(address) &&
|
||||
isAddress(tokenAddress) &&
|
||||
isAddress(token.address) &&
|
||||
isAddress(spenderAddress) &&
|
||||
(value === undefined || blockNumber !== globalBlockNumber) &&
|
||||
(chainId || chainId === 0) &&
|
||||
@ -73,15 +73,15 @@ export function useAddressAllowance(address, tokenAddress, spenderAddress) {
|
||||
) {
|
||||
let stale = false
|
||||
|
||||
getTokenAllowance(address, tokenAddress, spenderAddress, library)
|
||||
getTokenAllowance(address, token.address, spenderAddress, library)
|
||||
.then(value => {
|
||||
if (!stale) {
|
||||
update(chainId, address, tokenAddress, spenderAddress, value, globalBlockNumber)
|
||||
update(chainId, address, token.address, spenderAddress, value, globalBlockNumber)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!stale) {
|
||||
update(chainId, address, tokenAddress, spenderAddress, null, globalBlockNumber)
|
||||
update(chainId, address, token.address, spenderAddress, null, globalBlockNumber)
|
||||
}
|
||||
})
|
||||
|
||||
@ -89,7 +89,8 @@ export function useAddressAllowance(address, tokenAddress, spenderAddress) {
|
||||
stale = true
|
||||
}
|
||||
}
|
||||
}, [address, tokenAddress, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
|
||||
}, [address, token.address, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
|
||||
|
||||
return value
|
||||
const newTokenAmount: TokenAmount = value ? new TokenAmount(token, value) : null
|
||||
return newTokenAmount
|
||||
}
|
@ -1,310 +0,0 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { BigNumber } from '@uniswap/sdk'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { safeAccess, isAddress, getEtherBalance, getTokenBalance } from '../utils'
|
||||
import { useBlockNumber } from './Application'
|
||||
import { useTokenDetails, useAllTokenDetails } from './Tokens'
|
||||
import { getUSDPrice } from '../utils/price'
|
||||
|
||||
const UPDATE = 'UPDATE'
|
||||
const UPDATE_ALL_FOR_ACCOUNT = 'UPDATE_ALL_FOR_ACCOUNT'
|
||||
const UPDATE_ALL_FOR_EXCHANGES = 'UPDATE_ALL_FOR_EXCHANGES'
|
||||
|
||||
const BalancesContext = createContext()
|
||||
|
||||
function useBalancesContext() {
|
||||
return useContext(BalancesContext)
|
||||
}
|
||||
|
||||
function reducer(state, { type, payload }) {
|
||||
switch (type) {
|
||||
case UPDATE: {
|
||||
const { networkId, address, tokenAddress, value, blockNumber } = payload
|
||||
return {
|
||||
...state,
|
||||
[networkId]: {
|
||||
...(safeAccess(state, [networkId]) || {}),
|
||||
[address]: {
|
||||
...(safeAccess(state, [networkId, address]) || {}),
|
||||
[tokenAddress]: {
|
||||
value,
|
||||
blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case UPDATE_ALL_FOR_ACCOUNT: {
|
||||
const { networkId, address, tokenAddresses, values } = payload
|
||||
return {
|
||||
...state,
|
||||
[networkId]: {
|
||||
...(safeAccess(state, [networkId]) || {}),
|
||||
[address]: {
|
||||
...tokenAddresses.reduce((accumulator, currentValue, i) => {
|
||||
accumulator[currentValue] = { value: values[i] }
|
||||
return accumulator
|
||||
}, {}),
|
||||
...(safeAccess(state, [networkId, address]) || {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case UPDATE_ALL_FOR_EXCHANGES: {
|
||||
const { networkId, exchangeAddresses, tokenAddresses, values } = payload
|
||||
return {
|
||||
...state,
|
||||
[networkId]: {
|
||||
...(safeAccess(state, [networkId]) || {}),
|
||||
...exchangeAddresses.reduce((accumulator, currentValue, i) => {
|
||||
accumulator[currentValue] = {
|
||||
...safeAccess(state, [networkId, currentValue]),
|
||||
[tokenAddresses[i]]: {
|
||||
value: values[i]
|
||||
}
|
||||
}
|
||||
return accumulator
|
||||
}, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unexpected action type in BalancesContext reducer: '${type}'.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Provider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, {})
|
||||
|
||||
const update = useCallback((networkId, address, tokenAddress, value, blockNumber) => {
|
||||
dispatch({ type: UPDATE, payload: { networkId, address, tokenAddress, value, blockNumber } })
|
||||
}, [])
|
||||
|
||||
const updateAllForAccount = useCallback((networkId, address, tokenAddresses, values) => {
|
||||
dispatch({ type: UPDATE_ALL_FOR_ACCOUNT, payload: { networkId, address, tokenAddresses, values } })
|
||||
}, [])
|
||||
|
||||
const updateAllForExchanges = useCallback((networkId, exchangeAddresses, tokenAddresses, values) => {
|
||||
dispatch({ type: UPDATE_ALL_FOR_EXCHANGES, payload: { networkId, exchangeAddresses, tokenAddresses, values } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<BalancesContext.Provider
|
||||
value={useMemo(() => [state, { update, updateAllForAccount, updateAllForExchanges }], [
|
||||
state,
|
||||
update,
|
||||
updateAllForAccount,
|
||||
updateAllForExchanges
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
</BalancesContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const STAGGER_TIME = 2500
|
||||
export function Updater() {
|
||||
const { library, chainId, account } = useWeb3React()
|
||||
|
||||
const allTokens = useAllTokenDetails()
|
||||
|
||||
const [state, { updateAllForAccount, updateAllForExchanges }] = useBalancesContext()
|
||||
const stateRef = useRef(state)
|
||||
stateRef.current = state
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
if (chainId && library && account) {
|
||||
// get 1 eth + all token balances for the account
|
||||
Promise.all(
|
||||
Object.keys(allTokens).map(async tokenAddress => {
|
||||
await new Promise(resolve => setTimeout(resolve, STAGGER_TIME * Math.random()))
|
||||
|
||||
const { value: existingValue } = safeAccess(stateRef.current, [chainId, account, tokenAddress]) || {}
|
||||
return (
|
||||
existingValue ||
|
||||
(await (tokenAddress === 'ETH'
|
||||
? getEtherBalance(account, library).catch(() => null)
|
||||
: getTokenBalance(tokenAddress, account, library).catch(() => null)))
|
||||
)
|
||||
})
|
||||
).then(balances => {
|
||||
updateAllForAccount(chainId, account, Object.keys(allTokens), balances)
|
||||
})
|
||||
|
||||
const allTokensWithAnExchange = Object.keys(allTokens).filter(tokenAddress => tokenAddress !== 'ETH')
|
||||
// get all eth balances for all exchanges
|
||||
Promise.all(
|
||||
allTokensWithAnExchange.map(async tokenAddress => {
|
||||
await new Promise(resolve => setTimeout(resolve, STAGGER_TIME * Math.random()))
|
||||
|
||||
const exchangeAddress = allTokens[tokenAddress].exchangeAddress
|
||||
const { value: existingValue } = safeAccess(stateRef.current, [chainId, exchangeAddress, 'ETH']) || {}
|
||||
return existingValue || (await getEtherBalance(exchangeAddress, library).catch(() => null))
|
||||
})
|
||||
).then(ethBalances => {
|
||||
updateAllForExchanges(
|
||||
chainId,
|
||||
allTokensWithAnExchange.map(tokenAddress => allTokens[tokenAddress].exchangeAddress),
|
||||
Array(allTokensWithAnExchange.length).fill('ETH'),
|
||||
ethBalances
|
||||
)
|
||||
})
|
||||
|
||||
// get all token balances for all exchanges
|
||||
Promise.all(
|
||||
allTokensWithAnExchange.map(async tokenAddress => {
|
||||
await new Promise(resolve => setTimeout(resolve, STAGGER_TIME * Math.random()))
|
||||
|
||||
const exchangeAddress = allTokens[tokenAddress].exchangeAddress
|
||||
const { value: existingValue } =
|
||||
safeAccess(stateRef.current, [chainId, exchangeAddress, tokenAddress]) || {}
|
||||
return existingValue || (await getTokenBalance(tokenAddress, exchangeAddress, library).catch(() => null))
|
||||
})
|
||||
).then(tokenBalances => {
|
||||
updateAllForExchanges(
|
||||
chainId,
|
||||
allTokensWithAnExchange.map(tokenAddress => allTokens[tokenAddress].exchangeAddress),
|
||||
allTokensWithAnExchange.map(tokenAddress => tokenAddress),
|
||||
tokenBalances
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getData()
|
||||
}, [chainId, library, account, allTokens, updateAllForAccount, updateAllForExchanges])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function useAllBalances() {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state] = useBalancesContext()
|
||||
const balances = safeAccess(state, [chainId]) || {}
|
||||
return balances
|
||||
}
|
||||
|
||||
export function useAddressBalance(address, tokenAddress) {
|
||||
const { library, chainId } = useWeb3React()
|
||||
|
||||
const globalBlockNumber = useBlockNumber()
|
||||
|
||||
const [state, { update }] = useBalancesContext()
|
||||
const { value, blockNumber } = safeAccess(state, [chainId, address, tokenAddress]) || {}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isAddress(address) &&
|
||||
(tokenAddress === 'ETH' || isAddress(tokenAddress)) &&
|
||||
(value === undefined || blockNumber !== globalBlockNumber) &&
|
||||
(chainId || chainId === 0) &&
|
||||
library
|
||||
) {
|
||||
let stale = false
|
||||
;(tokenAddress === 'ETH' ? getEtherBalance(address, library) : getTokenBalance(tokenAddress, address, library))
|
||||
.then(value => {
|
||||
if (!stale) {
|
||||
update(chainId, address, tokenAddress, value, globalBlockNumber)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!stale) {
|
||||
update(chainId, address, tokenAddress, null, globalBlockNumber)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
stale = true
|
||||
}
|
||||
}
|
||||
}, [address, tokenAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
export function useExchangeReserves(tokenAddress) {
|
||||
const { exchangeAddress } = useTokenDetails(tokenAddress)
|
||||
|
||||
const reserveETH = useAddressBalance(exchangeAddress, 'ETH')
|
||||
const reserveToken = useAddressBalance(exchangeAddress, tokenAddress)
|
||||
|
||||
return { reserveETH, reserveToken }
|
||||
}
|
||||
|
||||
const buildReserveObject = (chainId, tokenAddress, ethReserveAmount, tokenReserveAmount, decimals) => ({
|
||||
token: {
|
||||
chainId,
|
||||
address: tokenAddress,
|
||||
decimals
|
||||
},
|
||||
ethReserve: {
|
||||
token: {
|
||||
chainId,
|
||||
decimals: 18
|
||||
},
|
||||
amount: ethReserveAmount
|
||||
},
|
||||
tokenReserve: {
|
||||
token: {
|
||||
chainId,
|
||||
address: tokenAddress,
|
||||
decimals
|
||||
},
|
||||
amount: tokenReserveAmount
|
||||
}
|
||||
})
|
||||
const daiTokenAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'
|
||||
const daiExchangeAddress = '0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667'
|
||||
const usdcTokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
|
||||
const usdcExchangeAddress = '0x97deC872013f6B5fB443861090ad931542878126'
|
||||
const tusdTokenAddress = '0x0000000000085d4780B73119b644AE5ecd22b376'
|
||||
const tusdExchangeAddress = '0x5048b9d01097498Fd72F3F14bC9Bc74A5aAc8fA7'
|
||||
export function useETHPriceInUSD() {
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
let daiReserveETH = useAddressBalance(daiExchangeAddress, 'ETH')
|
||||
let daiReserveToken = useAddressBalance(daiExchangeAddress, daiTokenAddress)
|
||||
let usdcReserveETH = useAddressBalance(usdcExchangeAddress, 'ETH')
|
||||
let usdcReserveToken = useAddressBalance(usdcExchangeAddress, usdcTokenAddress)
|
||||
let tusdReserveETH = useAddressBalance(tusdExchangeAddress, 'ETH')
|
||||
let tusdReserveToken = useAddressBalance(tusdExchangeAddress, tusdTokenAddress)
|
||||
|
||||
const [price, setPrice] = useState()
|
||||
useEffect(() => {
|
||||
if (daiReserveETH && daiReserveToken && usdcReserveETH && usdcReserveToken && tusdReserveETH && tusdReserveToken) {
|
||||
const daiReservesObject = buildReserveObject(
|
||||
chainId,
|
||||
daiTokenAddress,
|
||||
new BigNumber(daiReserveETH.toString()),
|
||||
new BigNumber(daiReserveToken.toString()),
|
||||
18
|
||||
)
|
||||
const tusdReservesObject = buildReserveObject(
|
||||
chainId,
|
||||
tusdTokenAddress,
|
||||
new BigNumber(tusdReserveETH.toString()),
|
||||
new BigNumber(tusdReserveToken.toString()),
|
||||
18
|
||||
)
|
||||
const usdcReservesObject = buildReserveObject(
|
||||
chainId,
|
||||
usdcTokenAddress,
|
||||
new BigNumber(usdcReserveETH.toString()),
|
||||
new BigNumber(usdcReserveToken.toString()),
|
||||
6
|
||||
)
|
||||
|
||||
const stablecoinReserves = [daiReservesObject, usdcReservesObject, tusdReservesObject]
|
||||
|
||||
try {
|
||||
setPrice(getUSDPrice(stablecoinReserves))
|
||||
} catch {
|
||||
setPrice(null)
|
||||
}
|
||||
}
|
||||
}, [daiReserveETH, daiReserveToken, usdcReserveETH, usdcReserveToken, tusdReserveETH, tusdReserveToken, chainId])
|
||||
|
||||
return price
|
||||
}
|
499
src/contexts/Balances.tsx
Normal file
499
src/contexts/Balances.tsx
Normal file
@ -0,0 +1,499 @@
|
||||
import React, { createContext, useContext, useReducer, useRef, useMemo, useCallback, useEffect, ReactNode } from 'react'
|
||||
import { TokenAmount, Token, JSBI } from '@uniswap/sdk'
|
||||
|
||||
import { useWeb3React, useDebounce } from '../hooks'
|
||||
import { getEtherBalance, getTokenBalance, isAddress } from '../utils'
|
||||
import { useBlockNumber } from './Application'
|
||||
import { useAllTokens } from './Tokens'
|
||||
import { useAllExchanges } from './Exchanges'
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'BALANCES'
|
||||
const SHORT_BLOCK_TIMEOUT = (60 * 2) / 15 // in seconds, represented as a block number delta
|
||||
const LONG_BLOCK_TIMEOUT = (60 * 15) / 15 // in seconds, represented as a block number delta
|
||||
|
||||
const EXCHANGES_BLOCK_TIMEOUT = (60 * 5) / 15 // in seconds, represented as a block number delta
|
||||
|
||||
const TRACK_LP_BLANCES = 'TRACK_LP_BLANCES'
|
||||
|
||||
const UPDATABLE_KEYS = [TRACK_LP_BLANCES]
|
||||
|
||||
interface BalancesState {
|
||||
[chainId: number]: {
|
||||
[address: string]: {
|
||||
[tokenAddress: string]: {
|
||||
value?: string | null
|
||||
blockNumber?: number
|
||||
listenerCount: number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(): BalancesState {
|
||||
try {
|
||||
return JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_KEY) as string)
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
enum Action {
|
||||
START_LISTENING,
|
||||
STOP_LISTENING,
|
||||
UPDATE,
|
||||
BATCH_UPDATE_ACCOUNT,
|
||||
BATCH_UPDATE_EXCHANGES,
|
||||
UPDATE_KEY
|
||||
}
|
||||
|
||||
function reducer(state: BalancesState, { type, payload }: { type: Action; payload: any }) {
|
||||
switch (type) {
|
||||
case Action.START_LISTENING: {
|
||||
const { chainId, address, tokenAddress } = payload
|
||||
const uninitialized = !!!state?.[chainId]?.[address]?.[tokenAddress]
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...state?.[chainId],
|
||||
[address]: {
|
||||
...state?.[chainId]?.[address],
|
||||
[tokenAddress]: uninitialized
|
||||
? {
|
||||
listenerCount: 1
|
||||
}
|
||||
: {
|
||||
...state[chainId][address][tokenAddress],
|
||||
listenerCount: state[chainId][address][tokenAddress].listenerCount + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Action.STOP_LISTENING: {
|
||||
const { chainId, address, tokenAddress } = payload
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...state?.[chainId],
|
||||
[address]: {
|
||||
...state?.[chainId]?.[address],
|
||||
[tokenAddress]: {
|
||||
...state?.[chainId]?.[address]?.[tokenAddress],
|
||||
listenerCount: state[chainId][address][tokenAddress].listenerCount - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Action.UPDATE: {
|
||||
const { chainId, address, tokenAddress, value, blockNumber } = payload
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...state?.[chainId],
|
||||
[address]: {
|
||||
...state?.[chainId]?.[address],
|
||||
[tokenAddress]: {
|
||||
...state?.[chainId]?.[address]?.[tokenAddress],
|
||||
value,
|
||||
blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Action.BATCH_UPDATE_ACCOUNT: {
|
||||
const { chainId, address, tokenAddresses, values, blockNumber } = payload
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...state?.[chainId],
|
||||
[address]: {
|
||||
...state?.[chainId]?.[address],
|
||||
...tokenAddresses.reduce((accumulator: any, tokenAddress: string, i: number) => {
|
||||
const value = values[i]
|
||||
accumulator[tokenAddress] = {
|
||||
...state?.[chainId]?.[address]?.[tokenAddress],
|
||||
value,
|
||||
blockNumber
|
||||
}
|
||||
return accumulator
|
||||
}, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Action.BATCH_UPDATE_EXCHANGES: {
|
||||
const { chainId, exchangeAddresses, tokenAddresses, values, blockNumber } = payload
|
||||
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...state?.[chainId],
|
||||
...exchangeAddresses.reduce((accumulator: any, exchangeAddress: string, i: number) => {
|
||||
const tokenAddress = tokenAddresses[i]
|
||||
const value = values[i]
|
||||
accumulator[exchangeAddress] = {
|
||||
...state?.[chainId]?.[exchangeAddress],
|
||||
...accumulator?.[exchangeAddress],
|
||||
[tokenAddress]: {
|
||||
...state?.[chainId]?.[exchangeAddress]?.[tokenAddress],
|
||||
value,
|
||||
blockNumber
|
||||
}
|
||||
}
|
||||
return accumulator
|
||||
}, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
case Action.UPDATE_KEY: {
|
||||
const { key, value } = payload
|
||||
if (!UPDATABLE_KEYS.some(k => k === key)) {
|
||||
throw Error(`Unexpected key in LocalStorageContext reducer: '${key}'.`)
|
||||
} else {
|
||||
return {
|
||||
...state,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unexpected action type in BalancesContext reducer: '${type}'.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const BalancesContext = createContext<[BalancesState, { [k: string]: (...args: any) => void }]>([{}, {}])
|
||||
|
||||
function useBalancesContext() {
|
||||
return useContext(BalancesContext)
|
||||
}
|
||||
|
||||
export default function Provider({ children }: { children: ReactNode }) {
|
||||
const [state, dispatch] = useReducer(reducer, undefined, initialize)
|
||||
|
||||
const startListening = useCallback((chainId, address, tokenAddress) => {
|
||||
dispatch({ type: Action.START_LISTENING, payload: { chainId, address, tokenAddress } })
|
||||
}, [])
|
||||
|
||||
const stopListening = useCallback((chainId, address, tokenAddress) => {
|
||||
dispatch({ type: Action.STOP_LISTENING, payload: { chainId, address, tokenAddress } })
|
||||
}, [])
|
||||
|
||||
const update = useCallback((chainId, address, tokenAddress, value, blockNumber) => {
|
||||
dispatch({ type: Action.UPDATE, payload: { chainId, address, tokenAddress, value, blockNumber } })
|
||||
}, [])
|
||||
|
||||
const batchUpdateAccount = useCallback((chainId, address, tokenAddresses, values, blockNumber) => {
|
||||
dispatch({ type: Action.BATCH_UPDATE_ACCOUNT, payload: { chainId, address, tokenAddresses, values, blockNumber } })
|
||||
}, [])
|
||||
|
||||
const batchUpdateExchanges = useCallback((chainId, exchangeAddresses, tokenAddresses, values, blockNumber) => {
|
||||
dispatch({
|
||||
type: Action.BATCH_UPDATE_EXCHANGES,
|
||||
payload: { chainId, exchangeAddresses, tokenAddresses, values, blockNumber }
|
||||
})
|
||||
}, [])
|
||||
|
||||
const updateKey = useCallback((key, value) => {
|
||||
dispatch({ type: Action.UPDATE_KEY, payload: { key, value } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<BalancesContext.Provider
|
||||
value={useMemo(
|
||||
() => [state, { startListening, stopListening, update, batchUpdateAccount, batchUpdateExchanges, updateKey }],
|
||||
[state, startListening, stopListening, update, batchUpdateAccount, batchUpdateExchanges, updateKey]
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</BalancesContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function Updater() {
|
||||
const { chainId, account, library } = useWeb3React()
|
||||
const blockNumber = useBlockNumber()
|
||||
const [state, { update, batchUpdateAccount, batchUpdateExchanges }] = useBalancesContext()
|
||||
|
||||
// debounce state a little bit to prevent useEffect craziness
|
||||
const debouncedState = useDebounce(state, 1000)
|
||||
// cache this debounced state in localstorage
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(debouncedState))
|
||||
}, [debouncedState])
|
||||
|
||||
// (slightly janky) balances-wide cache to prevent double/triple/etc. fetching
|
||||
const fetchedAsOfCache = useRef<{
|
||||
[chainId: number]: {
|
||||
[address: string]: {
|
||||
[tokenAddress: string]: number
|
||||
}
|
||||
}
|
||||
}>({})
|
||||
|
||||
// generic balances fetcher abstracting away difference between fetching ETH + token balances
|
||||
const fetchBalance = useCallback(
|
||||
(address: string, tokenAddress: string) =>
|
||||
(tokenAddress === 'ETH' ? getEtherBalance(address, library) : getTokenBalance(tokenAddress, address, library))
|
||||
.then(value => {
|
||||
return value.toString()
|
||||
})
|
||||
.catch(() => {
|
||||
return null
|
||||
}),
|
||||
[library]
|
||||
)
|
||||
|
||||
// ensure that all balances with >=1 listeners are updated every block
|
||||
useEffect(() => {
|
||||
if (typeof chainId === 'number' && typeof blockNumber === 'number') {
|
||||
for (const address of Object.keys(debouncedState?.[chainId] ?? {})) {
|
||||
for (const tokenAddress of Object.keys(debouncedState?.[chainId][address])) {
|
||||
const active = debouncedState[chainId][address][tokenAddress].listenerCount > 0
|
||||
if (active) {
|
||||
const cachedFetchedAsOf = fetchedAsOfCache.current?.[chainId]?.[address]?.[tokenAddress]
|
||||
const fetchedAsOf = debouncedState[chainId][address][tokenAddress]?.blockNumber ?? cachedFetchedAsOf
|
||||
if (fetchedAsOf !== blockNumber) {
|
||||
// fetch the balance...
|
||||
fetchBalance(address, tokenAddress).then(value => {
|
||||
update(chainId, address, tokenAddress, value, blockNumber)
|
||||
})
|
||||
// ...and cache the fetch
|
||||
fetchedAsOfCache.current = {
|
||||
...fetchedAsOfCache.current,
|
||||
[chainId]: {
|
||||
...fetchedAsOfCache.current?.[chainId],
|
||||
[address]: {
|
||||
...fetchedAsOfCache.current?.[chainId]?.[address],
|
||||
[tokenAddress]: blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [chainId, blockNumber, debouncedState, fetchBalance, update])
|
||||
|
||||
// get a state ref for batch updates
|
||||
const stateRef = useRef(state)
|
||||
useEffect(() => {
|
||||
stateRef.current = state
|
||||
}, [state])
|
||||
const allTokenDetails = useAllTokens()
|
||||
|
||||
// ensure that we have the user balances for all tokens
|
||||
const allTokens = useMemo(() => Object.keys(allTokenDetails), [allTokenDetails])
|
||||
useEffect(() => {
|
||||
if (typeof chainId === 'number' && typeof account === 'string' && typeof blockNumber === 'number') {
|
||||
Promise.all(
|
||||
allTokens
|
||||
.filter(tokenAddress => {
|
||||
const hasValue = !!stateRef.current?.[chainId]?.[account]?.[tokenAddress]?.value
|
||||
const cachedFetchedAsOf = fetchedAsOfCache.current?.[chainId]?.[account]?.[tokenAddress]
|
||||
const fetchedAsOf = stateRef.current?.[chainId]?.[account][tokenAddress]?.blockNumber ?? cachedFetchedAsOf
|
||||
|
||||
// if there's no value, and it's not being fetched, we need to fetch!
|
||||
if (!hasValue && typeof cachedFetchedAsOf !== 'number') {
|
||||
return true
|
||||
// else, if there's a value, check if it's stale
|
||||
} else if (hasValue) {
|
||||
const blocksElapsedSinceLastCheck = blockNumber - fetchedAsOf
|
||||
const stale =
|
||||
blocksElapsedSinceLastCheck >=
|
||||
(stateRef.current[chainId][account][tokenAddress].value === '0'
|
||||
? LONG_BLOCK_TIMEOUT
|
||||
: SHORT_BLOCK_TIMEOUT)
|
||||
return stale
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
.map(async tokenAddress => {
|
||||
fetchedAsOfCache.current = {
|
||||
...fetchedAsOfCache.current,
|
||||
[chainId]: {
|
||||
...fetchedAsOfCache.current?.[chainId],
|
||||
[account]: {
|
||||
...fetchedAsOfCache.current?.[chainId]?.[account],
|
||||
[tokenAddress]: blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
return fetchBalance(account, tokenAddress).then(value => ({ tokenAddress, value }))
|
||||
})
|
||||
).then(results => {
|
||||
batchUpdateAccount(
|
||||
chainId,
|
||||
account,
|
||||
results.map(result => result.tokenAddress),
|
||||
results.map(result => result.value),
|
||||
blockNumber
|
||||
)
|
||||
})
|
||||
}
|
||||
}, [chainId, account, blockNumber, allTokens, fetchBalance, batchUpdateAccount])
|
||||
|
||||
// ensure token balances for all exchanges
|
||||
const allExchangeDetails = useAllExchanges()
|
||||
|
||||
// format so we can index by exchange and only update specifc values
|
||||
const allExchanges = useMemo(() => {
|
||||
const formattedExchanges = {}
|
||||
Object.keys(allExchangeDetails).map(token0Address => {
|
||||
return Object.keys(allExchangeDetails[token0Address]).map(token1Address => {
|
||||
const exchangeAddress = allExchangeDetails[token0Address][token1Address]
|
||||
return (formattedExchanges[exchangeAddress] = {
|
||||
token0: token0Address,
|
||||
token1: token1Address
|
||||
})
|
||||
})
|
||||
})
|
||||
return formattedExchanges
|
||||
}, [allExchangeDetails])
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof chainId === 'number' && typeof blockNumber === 'number') {
|
||||
Promise.all(
|
||||
Object.keys(allExchanges)
|
||||
.filter(exchangeAddress => {
|
||||
const token0 = allExchanges[exchangeAddress].token0
|
||||
const token1 = allExchanges[exchangeAddress].token1
|
||||
|
||||
const hasValueToken0 = !!stateRef.current?.[chainId]?.[exchangeAddress]?.[token0]?.value
|
||||
const hasValueToken1 = !!stateRef.current?.[chainId]?.[exchangeAddress]?.[token1]?.value
|
||||
|
||||
const cachedFetchedAsOfToken0 = fetchedAsOfCache.current?.[chainId]?.[exchangeAddress]?.token0
|
||||
const cachedFetchedAsOfToken1 = fetchedAsOfCache.current?.[chainId]?.[exchangeAddress]?.token1
|
||||
|
||||
const fetchedAsOfToken0 =
|
||||
stateRef.current?.[chainId]?.[exchangeAddress]?.[token0]?.blockNumber ?? cachedFetchedAsOfToken0
|
||||
const fetchedAsOfToken1 =
|
||||
stateRef.current?.[chainId]?.[exchangeAddress]?.[token1]?.blockNumber ?? cachedFetchedAsOfToken1
|
||||
|
||||
// if there's no values, and they're not being fetched, we need to fetch!
|
||||
if (
|
||||
(!hasValueToken0 || !hasValueToken1) &&
|
||||
(typeof cachedFetchedAsOfToken0 !== 'number' || typeof cachedFetchedAsOfToken1 !== 'number')
|
||||
) {
|
||||
return true
|
||||
// else, if there are values, check if they's stale
|
||||
} else if (hasValueToken0 && hasValueToken0) {
|
||||
const blocksElapsedSinceLastCheckToken0 = blockNumber - fetchedAsOfToken0
|
||||
const blocksElapsedSinceLastCheckToken1 = blockNumber - fetchedAsOfToken1
|
||||
|
||||
const stale =
|
||||
fetchedAsOfToken0 !== fetchedAsOfToken1 ||
|
||||
blocksElapsedSinceLastCheckToken0 >= EXCHANGES_BLOCK_TIMEOUT ||
|
||||
blocksElapsedSinceLastCheckToken1 >= EXCHANGES_BLOCK_TIMEOUT
|
||||
|
||||
return stale
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
.map(async exchangeAddress => {
|
||||
const token0 = allExchanges[exchangeAddress].token0
|
||||
const token1 = allExchanges[exchangeAddress].token1
|
||||
|
||||
fetchedAsOfCache.current = {
|
||||
...fetchedAsOfCache.current,
|
||||
[chainId]: {
|
||||
...fetchedAsOfCache.current?.[chainId],
|
||||
[exchangeAddress]: {
|
||||
...fetchedAsOfCache.current?.[chainId]?.[exchangeAddress],
|
||||
[token0]: blockNumber,
|
||||
[token1]: blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all([
|
||||
fetchBalance(exchangeAddress, token0),
|
||||
fetchBalance(exchangeAddress, token1)
|
||||
]).then(([valueToken0, valueToken1]) => ({ exchangeAddress, token0, token1, valueToken0, valueToken1 }))
|
||||
})
|
||||
).then(results => {
|
||||
batchUpdateExchanges(
|
||||
chainId,
|
||||
results.flatMap(result => [result.exchangeAddress, result.exchangeAddress]),
|
||||
results.flatMap(result => [result.token0, result.token1]),
|
||||
results.flatMap(result => [result.valueToken0, result.valueToken1]),
|
||||
blockNumber
|
||||
)
|
||||
})
|
||||
}
|
||||
}, [chainId, account, blockNumber, allExchanges, fetchBalance, batchUpdateExchanges])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function useAllBalances(): Array<TokenAmount> {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state] = useBalancesContext()
|
||||
|
||||
const allTokens = useAllTokens()
|
||||
|
||||
const formattedBalances = useMemo(() => {
|
||||
if (!state?.[chainId]) {
|
||||
return {}
|
||||
} else {
|
||||
let newBalances = {}
|
||||
Object.keys(state[chainId]).map(address => {
|
||||
return Object.keys(state[chainId][address]).map(tokenAddress => {
|
||||
return (newBalances[chainId] = {
|
||||
...newBalances[chainId],
|
||||
[address]: {
|
||||
...newBalances[chainId]?.[address],
|
||||
[tokenAddress]: new TokenAmount(
|
||||
// if token not in token list, must be an exchange -
|
||||
/**
|
||||
* @TODO
|
||||
*
|
||||
* should we live fetch data here if token not in list
|
||||
*
|
||||
*/
|
||||
allTokens && allTokens[tokenAddress] ? allTokens[tokenAddress] : new Token(chainId, tokenAddress, 18),
|
||||
JSBI.BigInt(state?.[chainId][address][tokenAddress].value)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return newBalances
|
||||
}
|
||||
}, [allTokens, chainId, state])
|
||||
|
||||
return useMemo(() => (typeof chainId === 'number' ? formattedBalances?.[chainId] ?? {} : {}), [
|
||||
chainId,
|
||||
formattedBalances
|
||||
])
|
||||
}
|
||||
|
||||
export function useAddressBalance(address: string, token: Token): TokenAmount | undefined | null {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state, { startListening, stopListening }] = useBalancesContext()
|
||||
|
||||
const allTokens = useAllTokens()
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof chainId === 'number' && isAddress(address) && isAddress(token.address)) {
|
||||
startListening(chainId, address, token.address)
|
||||
return () => {
|
||||
stopListening(chainId, address, token.address)
|
||||
}
|
||||
}
|
||||
}, [chainId, address, token, startListening, stopListening])
|
||||
|
||||
const value = typeof chainId === 'number' ? state?.[chainId]?.[address]?.[token.address]?.value : undefined
|
||||
|
||||
const formattedValue = value && new TokenAmount(allTokens?.[token.address], value)
|
||||
|
||||
return useMemo(() => formattedValue, [formattedValue])
|
||||
}
|
||||
|
||||
export function useExchangeReserves(tokenAddress: string) {
|
||||
return []
|
||||
}
|
117
src/contexts/Exchanges.tsx
Normal file
117
src/contexts/Exchanges.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
import { ChainId, WETH, Token, Exchange } from '@uniswap/sdk'
|
||||
import { INITIAL_TOKENS_CONTEXT, useToken } from './Tokens'
|
||||
import { useAddressBalance } from './Balances'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
|
||||
const UPDATE = 'UPDATE'
|
||||
|
||||
const ALL_EXCHANGES: [Token, Token][] = [
|
||||
[
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY][WETH[ChainId.RINKEBY].address],
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']
|
||||
]
|
||||
]
|
||||
|
||||
const EXCHANGE_MAP: {
|
||||
[chainId: number]: { [token0Address: string]: { [token1Address: string]: string } }
|
||||
} = ALL_EXCHANGES.reduce((exchangeMap, [tokenA, tokenB]) => {
|
||||
const tokens: [Token, Token] = tokenA?.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
|
||||
// ensure exchanges are unique
|
||||
if (exchangeMap?.[tokens[0].chainId]?.[tokens[0].address]?.[tokens[1].address] !== undefined)
|
||||
throw Error(`Duplicate exchange: ${tokenA} ${tokenB}`)
|
||||
|
||||
return {
|
||||
...exchangeMap,
|
||||
[tokens[0].chainId]: {
|
||||
...exchangeMap?.[tokens[0].chainId],
|
||||
[tokens[0].address]: {
|
||||
...exchangeMap?.[tokens[0].chainId]?.[tokens[0].address],
|
||||
[tokens[1].address]: Exchange.getAddress(...tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {})
|
||||
|
||||
const ExchangeContext = createContext([])
|
||||
|
||||
function useExchangesContext() {
|
||||
return useContext(ExchangeContext)
|
||||
}
|
||||
|
||||
function reducer(state, { type, payload }) {
|
||||
switch (type) {
|
||||
case UPDATE: {
|
||||
const { tokens } = payload
|
||||
const tokensSorted: [Token, Token] = tokens[0].sortsBefore(tokens[1])
|
||||
? [tokens[0], tokens[1]]
|
||||
: [tokens[1], tokens[0]]
|
||||
return {
|
||||
...state,
|
||||
[tokensSorted[0].chainId]: {
|
||||
...state?.[tokensSorted[0].chainId],
|
||||
[tokensSorted[0].address]: {
|
||||
...state?.[tokensSorted[0].chainId]?.[tokensSorted[0].address],
|
||||
[tokensSorted[1].address]: Exchange.getAddress(tokensSorted[0], tokensSorted[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unexpected action type in ExchangesContext reducer: '${type}'.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Provider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, EXCHANGE_MAP)
|
||||
|
||||
const update = useCallback((chainId, tokens) => {
|
||||
dispatch({ type: UPDATE, payload: { chainId, tokens } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ExchangeContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
|
||||
{children}
|
||||
</ExchangeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useExchangeAddress(tokenA?: Token, tokenB?: Token): string | undefined {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state, { update }] = useExchangesContext()
|
||||
|
||||
const tokens: [Token, Token] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
|
||||
|
||||
const address = state?.[chainId]?.[tokens[0].address]?.[tokens[1].address]
|
||||
|
||||
useEffect(() => {
|
||||
if (address === undefined && tokenA && tokenB) {
|
||||
const exchangeAddress = Exchange.getAddress(...tokens)
|
||||
exchangeAddress && update(chainId, tokens)
|
||||
}
|
||||
}, [chainId, address, tokenA, tokenB, tokens, update])
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
export function useExchange(tokenA?: Token, tokenB?: Token): Exchange | undefined {
|
||||
const address = useExchangeAddress(tokenA, tokenB)
|
||||
|
||||
const tokenAmountA = useAddressBalance(address, tokenA)
|
||||
const tokenAmountB = useAddressBalance(address, tokenB)
|
||||
|
||||
const exchange = tokenAmountA && tokenAmountB && new Exchange(tokenAmountA, tokenAmountB)
|
||||
|
||||
return exchange
|
||||
}
|
||||
|
||||
export function useAllExchanges() {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state] = useExchangesContext()
|
||||
|
||||
return useMemo(() => {
|
||||
return state?.[chainId] || {}
|
||||
}, [state, chainId])
|
||||
}
|
@ -1,700 +0,0 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
import {
|
||||
isAddress,
|
||||
getTokenName,
|
||||
getTokenSymbol,
|
||||
getTokenDecimals,
|
||||
getTokenExchangeAddressFromFactory,
|
||||
safeAccess
|
||||
} from '../utils'
|
||||
|
||||
const NAME = 'name'
|
||||
const SYMBOL = 'symbol'
|
||||
const DECIMALS = 'decimals'
|
||||
const EXCHANGE_ADDRESS = 'exchangeAddress'
|
||||
|
||||
const UPDATE = 'UPDATE'
|
||||
|
||||
const ETH = {
|
||||
ETH: {
|
||||
[NAME]: 'Ethereum',
|
||||
[SYMBOL]: 'ETH',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: null
|
||||
}
|
||||
}
|
||||
|
||||
export const INITIAL_TOKENS_CONTEXT = {
|
||||
1: {
|
||||
'0xB6eD7644C69416d67B522e20bC294A9a9B405B31': {
|
||||
[NAME]: '0xBitcoin Token',
|
||||
[SYMBOL]: '0xBTC',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0x701564Aa6E26816147D4fa211a0779F1B774Bb9B'
|
||||
},
|
||||
'0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d': {
|
||||
[NAME]: 'Aave Interest bearing DAI',
|
||||
[SYMBOL]: 'aDAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x7cfab87AaC0899c093235b342AC0e5B1ACF159EB'
|
||||
},
|
||||
'0x737F98AC8cA59f2C68aD658E3C3d8C8963E40a4c': {
|
||||
[NAME]: 'Amon',
|
||||
[SYMBOL]: 'AMN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xE6C198d27a5B71144B40cFa2362ae3166728e0C8'
|
||||
},
|
||||
'0xD46bA6D942050d489DBd938a2C909A5d5039A161': {
|
||||
[NAME]: 'Ampleforth',
|
||||
[SYMBOL]: 'AMPL',
|
||||
[DECIMALS]: 9,
|
||||
[EXCHANGE_ADDRESS]: '0x042dBBDc27F75d277C3D99efE327DB21Bc4fde75'
|
||||
},
|
||||
'0xcD62b1C403fa761BAadFC74C525ce2B51780b184': {
|
||||
[NAME]: 'Aragon Network Juror',
|
||||
[SYMBOL]: 'ANJ',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x64A9edD3f5fce0252Cd708E26c8dd11205742826'
|
||||
},
|
||||
'0x960b236A07cf122663c4303350609A66A7B288C0': {
|
||||
[NAME]: 'Aragon Network Token',
|
||||
[SYMBOL]: 'ANT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x077d52B047735976dfdA76feF74d4d988AC25196'
|
||||
},
|
||||
'0x0D8775F648430679A709E98d2b0Cb6250d2887EF': {
|
||||
[NAME]: 'Basic Attention Token',
|
||||
[SYMBOL]: 'BAT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x2E642b8D59B45a1D8c5aEf716A84FF44ea665914'
|
||||
},
|
||||
'0x107c4504cd79C5d2696Ea0030a8dD4e92601B82e': {
|
||||
[NAME]: 'Bloom Token',
|
||||
[SYMBOL]: 'BLT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x0E6A53B13688018A3df8C69f99aFB19A3068D04f'
|
||||
},
|
||||
'0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C': {
|
||||
[NAME]: 'Bancor Network Token',
|
||||
[SYMBOL]: 'BNT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x87d80DBD37E551F58680B4217b23aF6a752DA83F'
|
||||
},
|
||||
'0x26E75307Fc0C021472fEb8F727839531F112f317': {
|
||||
[NAME]: 'Crypto20',
|
||||
[SYMBOL]: 'C20',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xF7B5A4b934658025390ff69dB302BC7F2AC4a542'
|
||||
},
|
||||
'0x4F9254C83EB525f9FCf346490bbb3ed28a81C667': {
|
||||
[NAME]: 'CelerToken',
|
||||
[SYMBOL]: 'CELR',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x1e3740A030AF8c755c888a0ee83aC9E79e09f4F1'
|
||||
},
|
||||
'0xF5DCe57282A584D2746FaF1593d3121Fcac444dC': {
|
||||
[NAME]: 'Compound Dai',
|
||||
[SYMBOL]: 'cSAI',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0x45A2FDfED7F7a2c791fb1bdF6075b83faD821ddE'
|
||||
},
|
||||
'0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643': {
|
||||
[NAME]: 'Compound Dai',
|
||||
[SYMBOL]: 'cDAI',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0x34E89740adF97C3A9D3f63Cc2cE4a914382c230b'
|
||||
},
|
||||
'0x06AF07097C9Eeb7fD685c692751D5C66dB49c215': {
|
||||
[NAME]: 'Chai',
|
||||
[SYMBOL]: 'CHAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x6C3942B383bc3d0efd3F36eFa1CBE7C8E12C8A2B'
|
||||
},
|
||||
'0x41e5560054824eA6B0732E656E3Ad64E20e94E45': {
|
||||
[NAME]: 'Civic',
|
||||
[SYMBOL]: 'CVC',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0x1C6c712b1F4a7c263B1DBd8F97fb447c945d3b9a'
|
||||
},
|
||||
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359': {
|
||||
[NAME]: 'Dai Stablecoin v1.0 (SAI)',
|
||||
[SYMBOL]: 'SAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14'
|
||||
},
|
||||
'0x6B175474E89094C44Da98b954EedeAC495271d0F': {
|
||||
[NAME]: 'Dai Stablecoin',
|
||||
[SYMBOL]: 'DAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667'
|
||||
},
|
||||
'0x0Cf0Ee63788A0849fE5297F3407f701E122cC023': {
|
||||
[NAME]: 'Streamr DATAcoin',
|
||||
[SYMBOL]: 'DATA',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x4F0d6E2179938828CfF93dA40a8BA1Df7519Ca8C'
|
||||
},
|
||||
'0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A': {
|
||||
[NAME]: 'DigixDAO',
|
||||
[SYMBOL]: 'DGD',
|
||||
[DECIMALS]: 9,
|
||||
[EXCHANGE_ADDRESS]: '0xD55C1cA9F5992A2e5E379DCe49Abf24294ABe055'
|
||||
},
|
||||
'0x4f3AfEC4E5a3F2A6a1A411DEF7D7dFe50eE057bF': {
|
||||
[NAME]: 'Digix Gold Token',
|
||||
[SYMBOL]: 'DGX',
|
||||
[DECIMALS]: 9,
|
||||
[EXCHANGE_ADDRESS]: '0xb92dE8B30584392Af27726D5ce04Ef3c4e5c9924'
|
||||
},
|
||||
'0xc719d010B63E5bbF2C0551872CD5316ED26AcD83': {
|
||||
[NAME]: 'Decentralized Insurance Protocol',
|
||||
[SYMBOL]: 'DIP',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x61792F290e5100FBBcBb2309F03A1Bab869fb850'
|
||||
},
|
||||
'0xC0F9bD5Fa5698B6505F643900FFA515Ea5dF54A9': {
|
||||
[NAME]: 'Donut',
|
||||
[SYMBOL]: 'DONUT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xD552119eD44EC8Fa8f87c568769C67Bd02B5b3FB'
|
||||
},
|
||||
'0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c': {
|
||||
[NAME]: 'Enjin Coin',
|
||||
[SYMBOL]: 'ENJ',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xb99A23b1a4585fc56d0EC3B76528C27cAd427473'
|
||||
},
|
||||
'0x06f65b8CfCb13a9FE37d836fE9708dA38Ecb29B2': {
|
||||
[NAME]: 'SAINT FAME: Genesis Shirt',
|
||||
[SYMBOL]: 'FAME',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x5e7907aC70b9a781365c72F2acEE96710bdA042e'
|
||||
},
|
||||
'0x4946Fcea7C692606e8908002e55A582af44AC121': {
|
||||
[NAME]: 'FOAM Token',
|
||||
[SYMBOL]: 'FOAM',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xf79cb3BEA83BD502737586A6E8B133c378FD1fF2'
|
||||
},
|
||||
'0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b': {
|
||||
[NAME]: 'FunFair',
|
||||
[SYMBOL]: 'FUN',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0x60a87cC7Fca7E53867facB79DA73181B1bB4238B'
|
||||
},
|
||||
'0x543Ff227F64Aa17eA132Bf9886cAb5DB55DCAddf': {
|
||||
[NAME]: 'DAOstack',
|
||||
[SYMBOL]: 'GEN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x26Cc0EAb6Cb650B0Db4D0d0dA8cB5BF69F4ad692'
|
||||
},
|
||||
'0x6810e776880C02933D47DB1b9fc05908e5386b96': {
|
||||
[NAME]: 'Gnosis Token',
|
||||
[SYMBOL]: 'GNO',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xe8e45431b93215566BA923a7E611B7342Ea954DF'
|
||||
},
|
||||
'0x12B19D3e2ccc14Da04FAe33e63652ce469b3F2FD': {
|
||||
[NAME]: 'GRID Token',
|
||||
[SYMBOL]: 'GRID',
|
||||
[DECIMALS]: 12,
|
||||
[EXCHANGE_ADDRESS]: '0x4B17685b330307C751B47f33890c8398dF4Fe407'
|
||||
},
|
||||
'0x0000000000b3F879cb30FE243b4Dfee438691c04': {
|
||||
[NAME]: 'Gastoken.io',
|
||||
[SYMBOL]: 'GST2',
|
||||
[DECIMALS]: 2,
|
||||
[EXCHANGE_ADDRESS]: '0x929507CD3D90Ab11eC4822E9eB5A48eb3a178F19'
|
||||
},
|
||||
'0x493C57C4763932315A328269E1ADaD09653B9081': {
|
||||
[NAME]: 'Fulcrum DAI iToken ',
|
||||
[SYMBOL]: 'iDAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x3E0349F5D38414008B9Bb1907ea422739BE7CD4C'
|
||||
},
|
||||
'0x14094949152EDDBFcd073717200DA82fEd8dC960': {
|
||||
[NAME]: 'Fulcrum SAI iToken ',
|
||||
[SYMBOL]: 'iSAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x81eeD7F1EcbD7FA9978fcc7584296Fb0C215Dc5C'
|
||||
},
|
||||
'0x3212b29E33587A00FB1C83346f5dBFA69A458923': {
|
||||
[NAME]: 'The Tokenized Bitcoin',
|
||||
[SYMBOL]: 'imBTC',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0xFFcf45b540e6C9F094Ae656D2e34aD11cdfdb187'
|
||||
},
|
||||
'0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69': {
|
||||
[NAME]: 'IoTeX Network',
|
||||
[SYMBOL]: 'IOTX',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x084f002671a5f03D5498B1e5fb15fc0cfee9a470'
|
||||
},
|
||||
'0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5': {
|
||||
[NAME]: 'Kin',
|
||||
[SYMBOL]: 'KIN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xb7520a5F8c832c573d6BD0Df955fC5c9b72400F7'
|
||||
},
|
||||
'0xdd974D5C2e2928deA5F71b9825b8b646686BD200': {
|
||||
[NAME]: 'Kyber Network Crystal',
|
||||
[SYMBOL]: 'KNC',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x49c4f9bc14884f6210F28342ceD592A633801a8b'
|
||||
},
|
||||
'0x514910771AF9Ca656af840dff83E8264EcF986CA': {
|
||||
[NAME]: 'ChainLink Token',
|
||||
[SYMBOL]: 'LINK',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xF173214C720f58E03e194085B1DB28B50aCDeeaD'
|
||||
},
|
||||
'0x6c6EE5e31d828De241282B9606C8e98Ea48526E2': {
|
||||
[NAME]: 'HoloToken',
|
||||
[SYMBOL]: 'HOT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xd4777E164c6C683E10593E08760B803D58529a8E'
|
||||
},
|
||||
'0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD': {
|
||||
[NAME]: 'LoopringCoin V2',
|
||||
[SYMBOL]: 'LRC',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xA539BAaa3aCA455c986bB1E25301CEF936CE1B65'
|
||||
},
|
||||
'0x80fB784B7eD66730e8b1DBd9820aFD29931aab03': {
|
||||
[NAME]: 'EthLend Token',
|
||||
[SYMBOL]: 'LEND',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xcaA7e4656f6A2B59f5f99c745F91AB26D1210DCe'
|
||||
},
|
||||
'0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0': {
|
||||
[NAME]: 'LoomToken',
|
||||
[SYMBOL]: 'LOOM',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x417CB32bc991fBbDCaE230C7c4771CC0D69daA6b'
|
||||
},
|
||||
'0x58b6A8A3302369DAEc383334672404Ee733aB239': {
|
||||
[NAME]: 'Livepeer Token',
|
||||
[SYMBOL]: 'LPT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xc4a1C45D5546029Fd57128483aE65b56124BFA6A'
|
||||
},
|
||||
'0xD29F0b5b3F50b07Fe9a9511F7d86F4f4bAc3f8c4': {
|
||||
[NAME]: 'Liquidity.Network Token',
|
||||
[SYMBOL]: 'LQD',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xe3406e7D0155E0a83236eC25D34Cd3D903036669'
|
||||
},
|
||||
'0x0F5D2fB29fb7d3CFeE444a200298f468908cC942': {
|
||||
[NAME]: 'Decentraland MANA',
|
||||
[SYMBOL]: 'MANA',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xC6581Ce3A005e2801c1e0903281BBd318eC5B5C2'
|
||||
},
|
||||
'0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0': {
|
||||
[NAME]: 'Matic Token',
|
||||
[SYMBOL]: 'MATIC',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x9a7A75E66B325a3BD46973B2b57c9b8d9D26a621'
|
||||
},
|
||||
'0x8888889213DD4dA823EbDD1e235b09590633C150': {
|
||||
[NAME]: 'Marblecoin',
|
||||
[SYMBOL]: 'MBC',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xE1b7AeC3639068b474bFbcB916580fc28A20717B'
|
||||
},
|
||||
'0x80f222a749a2e18Eb7f676D371F19ad7EFEEe3b7': {
|
||||
[NAME]: 'Magnolia Token',
|
||||
[SYMBOL]: 'MGN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xdd80Ca8062c7Ef90FcA2547E6a2A126C596e611F'
|
||||
},
|
||||
'0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2': {
|
||||
[NAME]: 'Maker',
|
||||
[SYMBOL]: 'MKR',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x2C4Bd064b998838076fa341A83d007FC2FA50957'
|
||||
},
|
||||
'0xec67005c4E498Ec7f55E092bd1d35cbC47C91892': {
|
||||
[NAME]: 'Melon Token',
|
||||
[SYMBOL]: 'MLN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xA931F4eB165AC307fD7431b5EC6eADde53E14b0C'
|
||||
},
|
||||
'0x957c30aB0426e0C93CD8241E2c60392d08c6aC8e': {
|
||||
[NAME]: 'Modum Token',
|
||||
[SYMBOL]: 'MOD',
|
||||
[DECIMALS]: 0,
|
||||
[EXCHANGE_ADDRESS]: '0xCCB98654CD486216fFF273dd025246588E77cFC1'
|
||||
},
|
||||
'0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206': {
|
||||
[NAME]: 'Nexo',
|
||||
[SYMBOL]: 'NEXO',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x069C97DBA948175D10af4b2414969e0B88d44669'
|
||||
},
|
||||
'0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671': {
|
||||
[NAME]: 'Numeraire',
|
||||
[SYMBOL]: 'NMR',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x2Bf5A5bA29E60682fC56B2Fcf9cE07Bef4F6196f'
|
||||
},
|
||||
'0x4575f41308EC1483f3d399aa9a2826d74Da13Deb': {
|
||||
[NAME]: 'Orchid',
|
||||
[SYMBOL]: 'OXT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xe9a5bbe41dc63D555E06746b047d624E3343EA52'
|
||||
},
|
||||
'0xD56daC73A4d6766464b38ec6D91eB45Ce7457c44': {
|
||||
[NAME]: 'Panvala pan',
|
||||
[SYMBOL]: 'PAN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xF53bBFBff01c50F2D42D542b09637DcA97935fF7'
|
||||
},
|
||||
'0x8E870D67F660D95d5be530380D0eC0bd388289E1': {
|
||||
[NAME]: 'PAX',
|
||||
[SYMBOL]: 'PAX',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xC040d51b07Aea5d94a89Bc21E8078B77366Fc6C7'
|
||||
},
|
||||
'0x45804880De22913dAFE09f4980848ECE6EcbAf78': {
|
||||
[NAME]: 'Paxos Gold',
|
||||
[SYMBOL]: 'PAXG',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x0d2E1a84638bD1B6c0C260c758c39451D4587be1'
|
||||
},
|
||||
'0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d': {
|
||||
[NAME]: 'Pinakion',
|
||||
[SYMBOL]: 'PNK',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xF506828B166de88cA2EDb2A98D960aBba0D2402A'
|
||||
},
|
||||
'0x6758B7d441a9739b98552B373703d8d3d14f9e62': {
|
||||
[NAME]: 'POA ERC20 on Foundation',
|
||||
[SYMBOL]: 'POA20',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xA2E6B3EF205FeAEe475937c4883b24E6eB717eeF'
|
||||
},
|
||||
'0x687BfC3E73f6af55F0CccA8450114D107E781a0e': {
|
||||
[NAME]: 'QChi',
|
||||
[SYMBOL]: 'QCH',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x755899F0540c3548b99E68C59AdB0f15d2695188'
|
||||
},
|
||||
'0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d': {
|
||||
[NAME]: 'Quantstamp Token',
|
||||
[SYMBOL]: 'QSP',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x82Db9FC4956Fa40efe1e35d881004612B5CB2cc2'
|
||||
},
|
||||
'0xF970b8E36e23F7fC3FD752EeA86f8Be8D83375A6': {
|
||||
[NAME]: 'Ripio Credit Network Token',
|
||||
[SYMBOL]: 'RCN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xD91FF16Ef92568fC27F466C3c5613e43313Ab1dc'
|
||||
},
|
||||
'0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6': {
|
||||
[NAME]: 'Raiden Token',
|
||||
[SYMBOL]: 'RDN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x7D03CeCb36820b4666F45E1b4cA2538724Db271C'
|
||||
},
|
||||
'0x408e41876cCCDC0F92210600ef50372656052a38': {
|
||||
[NAME]: 'Republic Token',
|
||||
[SYMBOL]: 'REN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x43892992B0b102459E895B88601Bb2C76736942c'
|
||||
},
|
||||
'0x1985365e9f78359a9B6AD760e32412f4a445E862': {
|
||||
[NAME]: 'Reputation',
|
||||
[SYMBOL]: 'REP',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x48B04d2A05B6B604d8d5223Fd1984f191DED51af'
|
||||
},
|
||||
'0x9469D013805bFfB7D3DEBe5E7839237e535ec483': {
|
||||
[NAME]: 'Darwinia Network Native Token',
|
||||
[SYMBOL]: 'RING',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xeBD8AA50b26bFa63007d61eBa777A9DdE7e43c64'
|
||||
},
|
||||
'0x607F4C5BB672230e8672085532f7e901544a7375': {
|
||||
[NAME]: 'iEx.ec Network Token',
|
||||
[SYMBOL]: 'RLC',
|
||||
[DECIMALS]: 9,
|
||||
[EXCHANGE_ADDRESS]: '0xA825CAE02B310E9901b4776806CE25db520c8642'
|
||||
},
|
||||
'0xB4EFd85c19999D84251304bDA99E90B92300Bd93': {
|
||||
[NAME]: 'Rocket Pool',
|
||||
[SYMBOL]: 'RPL',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x3Fb2F18065926DdB33E7571475c509541d15dA0e'
|
||||
},
|
||||
'0x4156D3342D5c385a87D264F90653733592000581': {
|
||||
[NAME]: 'Salt',
|
||||
[SYMBOL]: 'SALT',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0xC0C59cDe851bfcbdddD3377EC10ea54A18Efb937'
|
||||
},
|
||||
'0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098': {
|
||||
[NAME]: 'SANtiment network token',
|
||||
[SYMBOL]: 'SAN',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x8a8D7aD4b89D91983cd069C58C4AA9F2f4166298'
|
||||
},
|
||||
'0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb': {
|
||||
[NAME]: 'Synth sETH',
|
||||
[SYMBOL]: 'sETH',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xe9Cf7887b93150D4F2Da7dFc6D502B216438F244'
|
||||
},
|
||||
'0x3A9FfF453d50D4Ac52A6890647b823379ba36B9E': {
|
||||
[NAME]: 'Shuffle.Monster V3',
|
||||
[SYMBOL]: 'SHUF',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x536956Fab86774fb55CfaAcF496BC25E4d2B435C'
|
||||
},
|
||||
'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E': {
|
||||
[NAME]: 'Status Network Token',
|
||||
[SYMBOL]: 'SNT',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x1aEC8F11A7E78dC22477e91Ed924Fab46e3A88Fd'
|
||||
},
|
||||
'0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F': {
|
||||
[NAME]: 'Synthetix Network Token',
|
||||
[SYMBOL]: 'SNX',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x3958B4eC427F8fa24eB60F42821760e88d485f7F'
|
||||
},
|
||||
'0x23B608675a2B2fB1890d3ABBd85c5775c51691d5': {
|
||||
[NAME]: 'Unisocks Edition 0',
|
||||
[SYMBOL]: 'SOCKS',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x22d8432cc7aA4f8712a655fC4cdfB1baEC29FCA9'
|
||||
},
|
||||
'0x42d6622deCe394b54999Fbd73D108123806f6a18': {
|
||||
[NAME]: 'SPANK',
|
||||
[SYMBOL]: 'SPANK',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x4e395304655F0796bc3bc63709DB72173b9DdF98'
|
||||
},
|
||||
'0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC': {
|
||||
[NAME]: 'StorjToken',
|
||||
[SYMBOL]: 'STORJ',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0xA7298541E52f96d42382eCBe4f242cBcBC534d02'
|
||||
},
|
||||
'0x57Ab1ec28D129707052df4dF418D58a2D46d5f51': {
|
||||
[NAME]: 'Synth sUSD',
|
||||
[SYMBOL]: 'sUSD',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xB944d13b2f4047fc7bd3F7013bcf01b115fb260d'
|
||||
},
|
||||
'0x00006100F7090010005F1bd7aE6122c3C2CF0090': {
|
||||
[NAME]: 'TrueAUD',
|
||||
[SYMBOL]: 'TAUD',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x88dF13889E20EFa93Ff9a0C08f101F431bD9DDD7'
|
||||
},
|
||||
'0x00000100F2A2bd000715001920eB70D229700085': {
|
||||
[NAME]: 'TrueCAD',
|
||||
[SYMBOL]: 'TCAD',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xF996D7d9BaCb9217ca64BBce1b1cD72E0E886Be6'
|
||||
},
|
||||
'0x00000000441378008EA67F4284A57932B1c000a5': {
|
||||
[NAME]: 'TrueGBP',
|
||||
[SYMBOL]: 'TGBP',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x6bFa119a191576Ba26Bc5e711432aCA0cFda04DE'
|
||||
},
|
||||
'0x0000852600CEB001E08e00bC008be620d60031F2': {
|
||||
[NAME]: 'TrueHKD',
|
||||
[SYMBOL]: 'THKD',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x505C02B4aa1286375FBDF0c390AC0fe9209DCB05'
|
||||
},
|
||||
'0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a': {
|
||||
[NAME]: 'Monolith TKN',
|
||||
[SYMBOL]: 'TKN',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0xb6cFBf322db47D39331E306005DC7E5e6549942B'
|
||||
},
|
||||
'0xCb94be6f13A1182E4A4B6140cb7bf2025d28e41B': {
|
||||
[NAME]: 'Trustcoin',
|
||||
[SYMBOL]: 'TRST',
|
||||
[DECIMALS]: 6,
|
||||
[EXCHANGE_ADDRESS]: '0x95E4649F5209dD292cAF1F087b8F1Db3bE24927f'
|
||||
},
|
||||
'0x2C537E5624e4af88A7ae4060C022609376C8D0EB': {
|
||||
[NAME]: 'BiLira',
|
||||
[SYMBOL]: 'TRYB',
|
||||
[DECIMALS]: 6,
|
||||
[EXCHANGE_ADDRESS]: '0x122327Fd43B2C66DD9e4B6c91c8f071E217558eF'
|
||||
},
|
||||
'0x0000000000085d4780B73119b644AE5ecd22b376': {
|
||||
[NAME]: 'TrueUSD',
|
||||
[SYMBOL]: 'TUSD',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x5048b9d01097498Fd72F3F14bC9Bc74A5aAc8fA7'
|
||||
},
|
||||
'0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14': {
|
||||
[NAME]: 'Uniswap V1',
|
||||
[SYMBOL]: 'UNI-V1:SAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x601c32E0580D3aef9437dB52D09f5a5D7E60eC22'
|
||||
},
|
||||
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': {
|
||||
[NAME]: 'USD//C',
|
||||
[SYMBOL]: 'USDC',
|
||||
[DECIMALS]: 6,
|
||||
[EXCHANGE_ADDRESS]: '0x97deC872013f6B5fB443861090ad931542878126'
|
||||
},
|
||||
'0xA4Bdb11dc0a2bEC88d24A3aa1E6Bb17201112eBe': {
|
||||
[NAME]: 'StableUSD',
|
||||
[SYMBOL]: 'USDS',
|
||||
[DECIMALS]: 6,
|
||||
[EXCHANGE_ADDRESS]: '0x7Ef7191AB91dDB4D7cC347fbFA170355acbaf02D'
|
||||
},
|
||||
'0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374': {
|
||||
[NAME]: 'Veritaseum',
|
||||
[SYMBOL]: 'VERI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x17e5BF07D696eaf0d14caA4B44ff8A1E17B34de3'
|
||||
},
|
||||
'0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599': {
|
||||
[NAME]: 'Wrapped BTC',
|
||||
[SYMBOL]: 'WBTC',
|
||||
[DECIMALS]: 8,
|
||||
[EXCHANGE_ADDRESS]: '0x4d2f5cFbA55AE412221182D8475bC85799A5644b'
|
||||
},
|
||||
'0x09fE5f0236F0Ea5D930197DCE254d77B04128075': {
|
||||
[NAME]: 'Wrapped CryptoKitties',
|
||||
[SYMBOL]: 'WCK',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x4FF7Fa493559c40aBd6D157a0bfC35Df68d8D0aC'
|
||||
},
|
||||
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2': {
|
||||
[NAME]: 'Wrapped Ether',
|
||||
[SYMBOL]: 'WETH',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xA2881A90Bf33F03E7a3f803765Cd2ED5c8928dFb'
|
||||
},
|
||||
'0xB4272071eCAdd69d933AdcD19cA99fe80664fc08': {
|
||||
[NAME]: 'CryptoFranc',
|
||||
[SYMBOL]: 'XCHF',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x8dE0d002DC83478f479dC31F76cB0a8aa7CcEa17'
|
||||
},
|
||||
'0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704': {
|
||||
[NAME]: 'XIO Network',
|
||||
[SYMBOL]: 'XIO',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0x7B6E5278a14d5318571d65aceD036d09c998C707'
|
||||
},
|
||||
'0xE41d2489571d322189246DaFA5ebDe1F4699F498': {
|
||||
[NAME]: '0x Protocol Token',
|
||||
[SYMBOL]: 'ZRX',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xaE76c84C9262Cdb9abc0C2c8888e62Db8E22A0bF'
|
||||
}
|
||||
},
|
||||
4: {
|
||||
'0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa': {
|
||||
[NAME]: 'Dai',
|
||||
[SYMBOL]: 'DAI',
|
||||
[DECIMALS]: 18,
|
||||
[EXCHANGE_ADDRESS]: '0xaF51BaAA766b65E8B3Ee0C2c33186325ED01eBD5'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TokensContext = createContext()
|
||||
|
||||
function useTokensContext() {
|
||||
return useContext(TokensContext)
|
||||
}
|
||||
|
||||
function reducer(state, { type, payload }) {
|
||||
switch (type) {
|
||||
case UPDATE: {
|
||||
const { networkId, tokenAddress, name, symbol, decimals, exchangeAddress } = payload
|
||||
return {
|
||||
...state,
|
||||
[networkId]: {
|
||||
...(safeAccess(state, [networkId]) || {}),
|
||||
[tokenAddress]: {
|
||||
[NAME]: name,
|
||||
[SYMBOL]: symbol,
|
||||
[DECIMALS]: decimals,
|
||||
[EXCHANGE_ADDRESS]: exchangeAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unexpected action type in TokensContext reducer: '${type}'.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Provider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, INITIAL_TOKENS_CONTEXT)
|
||||
|
||||
const update = useCallback((networkId, tokenAddress, name, symbol, decimals, exchangeAddress) => {
|
||||
dispatch({ type: UPDATE, payload: { networkId, tokenAddress, name, symbol, decimals, exchangeAddress } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<TokensContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
|
||||
{children}
|
||||
</TokensContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useTokenDetails(tokenAddress) {
|
||||
const { library, chainId } = useWeb3React()
|
||||
|
||||
const [state, { update }] = useTokensContext()
|
||||
const allTokensInNetwork = { ...ETH, ...(safeAccess(state, [chainId]) || {}) }
|
||||
const { [NAME]: name, [SYMBOL]: symbol, [DECIMALS]: decimals, [EXCHANGE_ADDRESS]: exchangeAddress } =
|
||||
safeAccess(allTokensInNetwork, [tokenAddress]) || {}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isAddress(tokenAddress) &&
|
||||
(name === undefined || symbol === undefined || decimals === undefined || exchangeAddress === undefined) &&
|
||||
(chainId || chainId === 0) &&
|
||||
library
|
||||
) {
|
||||
let stale = false
|
||||
const namePromise = getTokenName(tokenAddress, library).catch(() => null)
|
||||
const symbolPromise = getTokenSymbol(tokenAddress, library).catch(() => null)
|
||||
const decimalsPromise = getTokenDecimals(tokenAddress, library).catch(() => null)
|
||||
const exchangeAddressPromise = getTokenExchangeAddressFromFactory(tokenAddress, chainId, library).catch(
|
||||
() => null
|
||||
)
|
||||
|
||||
Promise.all([namePromise, symbolPromise, decimalsPromise, exchangeAddressPromise]).then(
|
||||
([resolvedName, resolvedSymbol, resolvedDecimals, resolvedExchangeAddress]) => {
|
||||
if (!stale) {
|
||||
update(chainId, tokenAddress, resolvedName, resolvedSymbol, resolvedDecimals, resolvedExchangeAddress)
|
||||
}
|
||||
}
|
||||
)
|
||||
return () => {
|
||||
stale = true
|
||||
}
|
||||
}
|
||||
}, [tokenAddress, name, symbol, decimals, exchangeAddress, chainId, library, update])
|
||||
|
||||
return { name, symbol, decimals, exchangeAddress }
|
||||
}
|
||||
|
||||
export function useAllTokenDetails() {
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
const [state] = useTokensContext()
|
||||
|
||||
return useMemo(() => ({ ...ETH, ...(safeAccess(state, [chainId]) || {}) }), [state, chainId])
|
||||
}
|
108
src/contexts/Tokens.tsx
Normal file
108
src/contexts/Tokens.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
import { ChainId, WETH, Token } from '@uniswap/sdk'
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { isAddress, getTokenName, getTokenSymbol, getTokenDecimals, safeAccess } from '../utils'
|
||||
|
||||
const UPDATE = 'UPDATE'
|
||||
|
||||
export const ALL_TOKENS = [
|
||||
WETH[ChainId.RINKEBY],
|
||||
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin')
|
||||
]
|
||||
|
||||
// only meant to be used in exchanges.ts!
|
||||
export const INITIAL_TOKENS_CONTEXT = ALL_TOKENS.reduce((tokenMap, token) => {
|
||||
// ensure tokens are unique
|
||||
if (tokenMap?.[token.chainId]?.[token.address] !== undefined) throw Error(`Duplicate token: ${token}`)
|
||||
return {
|
||||
...tokenMap,
|
||||
[token.chainId]: {
|
||||
...tokenMap?.[token.chainId],
|
||||
[token.address]: token
|
||||
}
|
||||
}
|
||||
}, {})
|
||||
|
||||
const TokensContext = createContext([])
|
||||
|
||||
function useTokensContext() {
|
||||
return useContext(TokensContext)
|
||||
}
|
||||
|
||||
function reducer(state, { type, payload }) {
|
||||
switch (type) {
|
||||
case UPDATE: {
|
||||
const { chainId, token } = payload
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...(state?.[chainId] || {}),
|
||||
[token.address]: token
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unexpected action type in TokensContext reducer: '${type}'.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Provider({ children }) {
|
||||
const [state, dispatch] = useReducer(reducer, INITIAL_TOKENS_CONTEXT)
|
||||
|
||||
const update = useCallback((chainId, token) => {
|
||||
dispatch({ type: UPDATE, payload: { chainId, token } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<TokensContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
|
||||
{children}
|
||||
</TokensContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useToken(tokenAddress: string): Token {
|
||||
const { library, chainId } = useWeb3React()
|
||||
|
||||
const [state, { update }] = useTokensContext()
|
||||
const allTokensInNetwork = state?.[chainId] || {}
|
||||
|
||||
const token = safeAccess(allTokensInNetwork, [tokenAddress]) || {}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isAddress(tokenAddress) &&
|
||||
(token === undefined || token.name === undefined || token.symbol === undefined || token.decimals === undefined) &&
|
||||
(chainId || chainId === 0) &&
|
||||
library
|
||||
) {
|
||||
let stale = false
|
||||
const namePromise = getTokenName(tokenAddress, library).catch(() => null)
|
||||
const symbolPromise = getTokenSymbol(tokenAddress, library).catch(() => null)
|
||||
const decimalsPromise = getTokenDecimals(tokenAddress, library).catch(() => null)
|
||||
|
||||
Promise.all([namePromise, symbolPromise, decimalsPromise]).then(
|
||||
([resolvedName, resolvedSymbol, resolvedDecimals]) => {
|
||||
if (!stale && resolvedDecimals) {
|
||||
const newToken: Token = new Token(chainId, tokenAddress, resolvedDecimals, resolvedSymbol, resolvedName)
|
||||
update(chainId, newToken)
|
||||
}
|
||||
}
|
||||
)
|
||||
return () => {
|
||||
stale = true
|
||||
}
|
||||
}
|
||||
}, [tokenAddress, token, chainId, library, update])
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
export function useAllTokens() {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state] = useTokensContext()
|
||||
|
||||
return useMemo(() => {
|
||||
return state?.[chainId] || {}
|
||||
}, [state, chainId])
|
||||
}
|
13
src/index.js
13
src/index.js
@ -11,6 +11,7 @@ import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from
|
||||
import TransactionContextProvider, { Updater as TransactionContextUpdater } from './contexts/Transactions'
|
||||
import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances'
|
||||
import TokensContextProvider from './contexts/Tokens'
|
||||
import ExchangesContextProvider from './contexts/Exchanges'
|
||||
import AllowancesContextProvider from './contexts/Allowances'
|
||||
import App from './pages/App'
|
||||
import ThemeProvider, { GlobalStyle } from './theme'
|
||||
@ -40,11 +41,13 @@ function ContextProviders({ children }) {
|
||||
<LocalStorageContextProvider>
|
||||
<ApplicationContextProvider>
|
||||
<TransactionContextProvider>
|
||||
<TokensContextProvider>
|
||||
<BalancesContextProvider>
|
||||
<AllowancesContextProvider>{children}</AllowancesContextProvider>
|
||||
</BalancesContextProvider>
|
||||
</TokensContextProvider>
|
||||
<ExchangesContextProvider>
|
||||
<TokensContextProvider>
|
||||
<BalancesContextProvider>
|
||||
<AllowancesContextProvider>{children}</AllowancesContextProvider>
|
||||
</BalancesContextProvider>
|
||||
</TokensContextProvider>
|
||||
</ExchangesContextProvider>
|
||||
</TransactionContextProvider>
|
||||
</ApplicationContextProvider>
|
||||
</LocalStorageContextProvider>
|
||||
|
@ -11,7 +11,9 @@ import { isAddress, getAllQueryParams } from '../utils'
|
||||
|
||||
const Swap = lazy(() => import('./Swap'))
|
||||
const Send = lazy(() => import('./Send'))
|
||||
const Pool = lazy(() => import('./Pool'))
|
||||
const Pool = lazy(() => import('./Supply'))
|
||||
const Add = lazy(() => import('./Supply/AddLiquidity'))
|
||||
const Remove = lazy(() => import('./Supply/RemoveLiquidity'))
|
||||
|
||||
const AppWrapper = styled.div`
|
||||
display: flex;
|
||||
@ -42,9 +44,13 @@ const BodyWrapper = styled.div`
|
||||
`
|
||||
|
||||
const Body = styled.div`
|
||||
max-width: 35rem;
|
||||
max-width: 28rem;
|
||||
width: 90%;
|
||||
/* margin: 0 1.25rem 1.25rem 1.25rem; */
|
||||
background: ${({ theme }) => theme.panelBackground};
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.04);
|
||||
border-radius: 20px;
|
||||
padding: 2rem 1rem;
|
||||
`
|
||||
|
||||
export default function App() {
|
||||
@ -96,16 +102,27 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Route exaxct path={'/supply'} component={() => <Pool params={params} />} />
|
||||
<Route
|
||||
path={[
|
||||
'/add-liquidity',
|
||||
'/remove-liquidity',
|
||||
'/create-exchange',
|
||||
'/create-exchange/:tokenAddress?'
|
||||
]}
|
||||
component={() => <Pool params={params} />}
|
||||
exact
|
||||
strict
|
||||
path={'/add/:tokens'}
|
||||
component={({ match }) => {
|
||||
const tokens = match.params.tokens.split('-')
|
||||
let t0
|
||||
let t1
|
||||
if (tokens) {
|
||||
t0 = tokens[0] === 'ETH' ? 'ETH' : isAddress(tokens[0])
|
||||
t1 = tokens[1] === 'ETH' ? 'ETH' : isAddress(tokens[1])
|
||||
}
|
||||
if (t0 && t1) {
|
||||
return <Add params={params} token0={t0} token1={t1} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/supply' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Redirect to="/swap" />
|
||||
<Route exaxct path={'/remove'} component={() => <Remove params={params} />} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</BrowserRouter>
|
||||
|
@ -1,722 +0,0 @@
|
||||
import React, { useReducer, useState, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { createBrowserHistory } from 'history'
|
||||
import { ethers } from 'ethers'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { Button } from '../../theme'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import OversizedPanel from '../../components/OversizedPanel'
|
||||
import ContextualInfo from '../../components/ContextualInfo'
|
||||
import { ReactComponent as Plus } from '../../assets/images/plus-blue.svg'
|
||||
import WarningCard from '../../components/WarningCard'
|
||||
|
||||
import { useWeb3React, useExchangeContract } from '../../hooks'
|
||||
import { brokenTokens } from '../../constants'
|
||||
import { amountFormatter, calculateGasMargin } from '../../utils'
|
||||
import { useTransactionAdder } from '../../contexts/Transactions'
|
||||
import { useTokenDetails, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
|
||||
import { useAddressAllowance } from '../../contexts/Allowances'
|
||||
|
||||
const INPUT = 0
|
||||
const OUTPUT = 1
|
||||
|
||||
// denominated in bips
|
||||
const ALLOWED_SLIPPAGE = ethers.utils.bigNumberify(200)
|
||||
|
||||
// denominated in seconds
|
||||
const DEADLINE_FROM_NOW = 60 * 15
|
||||
|
||||
// denominated in bips
|
||||
const GAS_MARGIN = ethers.utils.bigNumberify(1000)
|
||||
|
||||
const BlueSpan = styled.span`
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
`
|
||||
|
||||
const NewExchangeWarning = styled.div`
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid rgba($pizazz-orange, 0.4);
|
||||
background-color: rgba($pizazz-orange, 0.1);
|
||||
border-radius: 1rem;
|
||||
`
|
||||
|
||||
const NewExchangeWarningText = styled.div`
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
text-align: center;
|
||||
|
||||
:first-child {
|
||||
padding-bottom: 0.3rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
`
|
||||
|
||||
const LastSummaryText = styled.div`
|
||||
margin-top: 1rem;
|
||||
`
|
||||
|
||||
const DownArrowBackground = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`
|
||||
const SummaryPanel = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
padding: 1rem 0;
|
||||
`
|
||||
|
||||
const ExchangeRateWrapper = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.doveGray};
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 1rem 0;
|
||||
`
|
||||
|
||||
const ExchangeRate = styled.span`
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
color: ${({ theme }) => theme.doveGray};
|
||||
`
|
||||
|
||||
const Flex = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
|
||||
button {
|
||||
max-width: 20rem;
|
||||
}
|
||||
`
|
||||
|
||||
const WrappedPlus = ({ isError, highSlippageWarning, ...rest }) => <Plus {...rest} />
|
||||
const ColoredWrappedPlus = styled(WrappedPlus)`
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
position: relative;
|
||||
padding: 0.875rem;
|
||||
path {
|
||||
stroke: ${({ active, theme }) => (active ? theme.royalBlue : theme.chaliceGray)};
|
||||
}
|
||||
`
|
||||
|
||||
function calculateSlippageBounds(value) {
|
||||
if (value) {
|
||||
const offset = value.mul(ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000))
|
||||
const minimum = value.sub(offset)
|
||||
const maximum = value.add(offset)
|
||||
return {
|
||||
minimum: minimum.lt(ethers.constants.Zero) ? ethers.constants.Zero : minimum,
|
||||
maximum: maximum.gt(ethers.constants.MaxUint256) ? ethers.constants.MaxUint256 : maximum
|
||||
}
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function calculateMaxOutputVal(value) {
|
||||
if (value) {
|
||||
return value.mul(ethers.utils.bigNumberify(10000)).div(ALLOWED_SLIPPAGE.add(ethers.utils.bigNumberify(10000)))
|
||||
}
|
||||
}
|
||||
|
||||
function initialAddLiquidityState(state) {
|
||||
return {
|
||||
inputValue: state.ethAmountURL ? state.ethAmountURL : '',
|
||||
outputValue: state.tokenAmountURL && !state.ethAmountURL ? state.tokenAmountURL : '',
|
||||
lastEditedField: state.tokenAmountURL && state.ethAmountURL === '' ? OUTPUT : INPUT,
|
||||
outputCurrency: state.tokenURL ? state.tokenURL : ''
|
||||
}
|
||||
}
|
||||
|
||||
function addLiquidityStateReducer(state, action) {
|
||||
switch (action.type) {
|
||||
case 'SELECT_CURRENCY': {
|
||||
return {
|
||||
...state,
|
||||
outputCurrency: action.payload
|
||||
}
|
||||
}
|
||||
case 'UPDATE_VALUE': {
|
||||
const { inputValue, outputValue } = state
|
||||
const { field, value } = action.payload
|
||||
return {
|
||||
...state,
|
||||
inputValue: field === INPUT ? value : inputValue,
|
||||
outputValue: field === OUTPUT ? value : outputValue,
|
||||
lastEditedField: field
|
||||
}
|
||||
}
|
||||
case 'UPDATE_DEPENDENT_VALUE': {
|
||||
const { inputValue, outputValue } = state
|
||||
const { field, value } = action.payload
|
||||
return {
|
||||
...state,
|
||||
inputValue: field === INPUT ? value : inputValue,
|
||||
outputValue: field === OUTPUT ? value : outputValue
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return initialAddLiquidityState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getExchangeRate(inputValue, inputDecimals, outputValue, outputDecimals, invert = false) {
|
||||
try {
|
||||
if (
|
||||
inputValue &&
|
||||
(inputDecimals || inputDecimals === 0) &&
|
||||
outputValue &&
|
||||
(outputDecimals || outputDecimals === 0)
|
||||
) {
|
||||
const factor = ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))
|
||||
|
||||
if (invert) {
|
||||
return inputValue
|
||||
.mul(factor)
|
||||
.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(outputDecimals)))
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(inputDecimals)))
|
||||
.div(outputValue)
|
||||
} else {
|
||||
return outputValue
|
||||
.mul(factor)
|
||||
.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(inputDecimals)))
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(outputDecimals)))
|
||||
.div(inputValue)
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function getMarketRate(reserveETH, reserveToken, decimals, invert = false) {
|
||||
return getExchangeRate(reserveETH, 18, reserveToken, decimals, invert)
|
||||
}
|
||||
|
||||
export default function AddLiquidity({ params }) {
|
||||
const { t } = useTranslation()
|
||||
const { library, account, active, chainId } = useWeb3React()
|
||||
|
||||
const urlAddedTokens = {}
|
||||
if (params.token) {
|
||||
urlAddedTokens[params.token] = true
|
||||
}
|
||||
|
||||
// clear url of query
|
||||
useEffect(() => {
|
||||
const history = createBrowserHistory()
|
||||
history.push(window.location.pathname + '')
|
||||
}, [])
|
||||
|
||||
const [addLiquidityState, dispatchAddLiquidityState] = useReducer(
|
||||
addLiquidityStateReducer,
|
||||
{ ethAmountURL: params.ethAmount, tokenAmountURL: params.tokenAmount, tokenURL: params.token },
|
||||
initialAddLiquidityState
|
||||
)
|
||||
const { inputValue, outputValue, lastEditedField, outputCurrency } = addLiquidityState
|
||||
const inputCurrency = 'ETH'
|
||||
|
||||
const [inputValueParsed, setInputValueParsed] = useState()
|
||||
const [outputValueParsed, setOutputValueParsed] = useState()
|
||||
const [inputError, setInputError] = useState()
|
||||
const [outputError, setOutputError] = useState()
|
||||
const [zeroDecimalError, setZeroDecimalError] = useState()
|
||||
|
||||
const [brokenTokenWarning, setBrokenTokenWarning] = useState()
|
||||
|
||||
const { symbol, decimals, exchangeAddress } = useTokenDetails(outputCurrency)
|
||||
const exchangeContract = useExchangeContract(exchangeAddress)
|
||||
|
||||
const [totalPoolTokens, setTotalPoolTokens] = useState()
|
||||
const fetchPoolTokens = useCallback(() => {
|
||||
if (exchangeContract) {
|
||||
exchangeContract.totalSupply().then(totalSupply => {
|
||||
setTotalPoolTokens(totalSupply)
|
||||
})
|
||||
}
|
||||
}, [exchangeContract])
|
||||
useEffect(() => {
|
||||
fetchPoolTokens()
|
||||
library.on('block', fetchPoolTokens)
|
||||
|
||||
return () => {
|
||||
library.removeListener('block', fetchPoolTokens)
|
||||
}
|
||||
}, [fetchPoolTokens, library])
|
||||
|
||||
const poolTokenBalance = useAddressBalance(account, exchangeAddress)
|
||||
const exchangeETHBalance = useAddressBalance(exchangeAddress, 'ETH')
|
||||
const exchangeTokenBalance = useAddressBalance(exchangeAddress, outputCurrency)
|
||||
|
||||
const { reserveETH, reserveToken } = useExchangeReserves(outputCurrency)
|
||||
const isNewExchange = !!(reserveETH && reserveToken && reserveETH.isZero() && reserveToken.isZero())
|
||||
|
||||
// 18 decimals
|
||||
const poolTokenPercentage =
|
||||
poolTokenBalance && totalPoolTokens && isNewExchange === false && !totalPoolTokens.isZero()
|
||||
? poolTokenBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
|
||||
: undefined
|
||||
const ethShare =
|
||||
exchangeETHBalance && poolTokenPercentage
|
||||
? exchangeETHBalance
|
||||
.mul(poolTokenPercentage)
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
|
||||
: undefined
|
||||
const tokenShare =
|
||||
exchangeTokenBalance && poolTokenPercentage
|
||||
? exchangeTokenBalance
|
||||
.mul(poolTokenPercentage)
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
|
||||
: undefined
|
||||
|
||||
const liquidityMinted = isNewExchange
|
||||
? inputValueParsed
|
||||
: totalPoolTokens && inputValueParsed && exchangeETHBalance && !exchangeETHBalance.isZero()
|
||||
? totalPoolTokens.mul(inputValueParsed).div(exchangeETHBalance)
|
||||
: undefined
|
||||
|
||||
// user balances
|
||||
const inputBalance = useAddressBalance(account, inputCurrency)
|
||||
const outputBalance = useAddressBalance(account, outputCurrency)
|
||||
|
||||
const ethPerLiquidityToken =
|
||||
exchangeETHBalance && totalPoolTokens && isNewExchange === false && !totalPoolTokens.isZero()
|
||||
? exchangeETHBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
|
||||
: undefined
|
||||
const tokenPerLiquidityToken =
|
||||
exchangeTokenBalance && totalPoolTokens && isNewExchange === false && !totalPoolTokens.isZero()
|
||||
? exchangeTokenBalance.mul(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))).div(totalPoolTokens)
|
||||
: undefined
|
||||
|
||||
const outputValueMax = outputValueParsed && calculateSlippageBounds(outputValueParsed).maximum
|
||||
const liquidityTokensMin = liquidityMinted && calculateSlippageBounds(liquidityMinted).minimum
|
||||
|
||||
const marketRate = useMemo(() => {
|
||||
return getMarketRate(reserveETH, reserveToken, decimals)
|
||||
}, [reserveETH, reserveToken, decimals])
|
||||
const marketRateInverted = useMemo(() => {
|
||||
return getMarketRate(reserveETH, reserveToken, decimals, true)
|
||||
}, [reserveETH, reserveToken, decimals])
|
||||
|
||||
function renderTransactionDetails() {
|
||||
const b = text => <BlueSpan>{text}</BlueSpan>
|
||||
|
||||
if (isNewExchange) {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{t('youAreAdding')} {b(`${inputValue} ETH`)} {t('and')} {b(`${outputValue} ${symbol}`)} {t('intoPool')}
|
||||
</div>
|
||||
<LastSummaryText>
|
||||
{t('youAreSettingExRate')}{' '}
|
||||
{b(
|
||||
`1 ETH = ${amountFormatter(
|
||||
getMarketRate(inputValueParsed, outputValueParsed, decimals),
|
||||
18,
|
||||
4,
|
||||
false
|
||||
)} ${symbol}`
|
||||
)}
|
||||
.
|
||||
</LastSummaryText>
|
||||
<LastSummaryText>
|
||||
{t('youWillMint')} {b(`${inputValue}`)} {t('liquidityTokens')}
|
||||
</LastSummaryText>
|
||||
<LastSummaryText>{t('totalSupplyIs0')}</LastSummaryText>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{t('youAreAdding')} {b(`${amountFormatter(inputValueParsed, 18, 4)} ETH`)} {t('and')} {'at most'}{' '}
|
||||
{b(`${amountFormatter(outputValueMax, decimals, Math.min(decimals, 4))} ${symbol}`)} {t('intoPool')}
|
||||
</div>
|
||||
<LastSummaryText>
|
||||
{t('youWillMint')} {b(amountFormatter(liquidityMinted, 18, 4))} {t('liquidityTokens')}
|
||||
</LastSummaryText>
|
||||
<LastSummaryText>
|
||||
{t('totalSupplyIs')} {b(amountFormatter(totalPoolTokens, 18, 4))}
|
||||
</LastSummaryText>
|
||||
<LastSummaryText>
|
||||
{t('tokenWorth')} {b(amountFormatter(ethPerLiquidityToken, 18, 4))} ETH {t('and')}{' '}
|
||||
{b(amountFormatter(tokenPerLiquidityToken, decimals, Math.min(decimals, 4)))} {symbol}
|
||||
</LastSummaryText>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function renderSummary() {
|
||||
let contextualInfo = ''
|
||||
let isError = false
|
||||
if (brokenTokenWarning) {
|
||||
contextualInfo = t('brokenToken')
|
||||
isError = true
|
||||
} else if (zeroDecimalError) {
|
||||
contextualInfo = zeroDecimalError
|
||||
} else if (inputError || outputError) {
|
||||
contextualInfo = inputError || outputError
|
||||
isError = true
|
||||
} else if (!inputCurrency || !outputCurrency) {
|
||||
contextualInfo = t('selectTokenCont')
|
||||
} else if (!inputValue) {
|
||||
contextualInfo = t('enterValueCont')
|
||||
} else if (!account) {
|
||||
contextualInfo = t('noWallet')
|
||||
isError = true
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextualInfo
|
||||
openDetailsText={t('transactionDetails')}
|
||||
closeDetailsText={t('hideDetails')}
|
||||
contextualInfo={contextualInfo}
|
||||
isError={isError}
|
||||
renderTransactionDetails={renderTransactionDetails}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const addTransaction = useTransactionAdder()
|
||||
|
||||
async function onAddLiquidity() {
|
||||
// take ETH amount, multiplied by ETH rate and 2 for total tx size
|
||||
let ethTransactionSize = (inputValueParsed / 1e18) * 2
|
||||
|
||||
const deadline = Math.ceil(Date.now() / 1000) + DEADLINE_FROM_NOW
|
||||
|
||||
const estimatedGasLimit = await exchangeContract.estimate.addLiquidity(
|
||||
isNewExchange ? ethers.constants.Zero : liquidityTokensMin,
|
||||
isNewExchange ? outputValueParsed : outputValueMax,
|
||||
deadline,
|
||||
{
|
||||
value: inputValueParsed
|
||||
}
|
||||
)
|
||||
|
||||
const gasLimit = calculateGasMargin(estimatedGasLimit, GAS_MARGIN)
|
||||
|
||||
exchangeContract
|
||||
.addLiquidity(
|
||||
isNewExchange ? ethers.constants.Zero : liquidityTokensMin,
|
||||
isNewExchange ? outputValueParsed : outputValueMax,
|
||||
deadline,
|
||||
{
|
||||
value: inputValueParsed,
|
||||
gasLimit
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
// log pool added to and total usd amount
|
||||
ReactGA.event({
|
||||
category: 'Transaction',
|
||||
action: 'Add Liquidity',
|
||||
label: outputCurrency,
|
||||
value: ethTransactionSize,
|
||||
dimension1: response.hash
|
||||
})
|
||||
ReactGA.event({
|
||||
category: 'Hash',
|
||||
action: response.hash,
|
||||
label: ethTransactionSize.toString()
|
||||
})
|
||||
addTransaction(response)
|
||||
})
|
||||
}
|
||||
|
||||
function formatBalance(value) {
|
||||
return `Balance: ${value}`
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setBrokenTokenWarning(false)
|
||||
for (let i = 0; i < brokenTokens.length; i++) {
|
||||
if (brokenTokens[i].toLowerCase() === outputCurrency.toLowerCase()) {
|
||||
setBrokenTokenWarning(true)
|
||||
}
|
||||
}
|
||||
}, [outputCurrency])
|
||||
|
||||
useEffect(() => {
|
||||
if (isNewExchange) {
|
||||
setZeroDecimalError()
|
||||
if (inputValue) {
|
||||
const parsedInputValue = ethers.utils.parseUnits(inputValue, 18)
|
||||
setInputValueParsed(parsedInputValue)
|
||||
}
|
||||
if (outputValue) {
|
||||
try {
|
||||
const parsedOutputValue = ethers.utils.parseUnits(outputValue, decimals)
|
||||
setOutputValueParsed(parsedOutputValue)
|
||||
} catch {
|
||||
setZeroDecimalError('Invalid input. For 0 decimal tokens only supply whole number token amounts.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [decimals, inputValue, isNewExchange, outputValue])
|
||||
|
||||
// parse input value
|
||||
useEffect(() => {
|
||||
if (
|
||||
isNewExchange === false &&
|
||||
inputValue &&
|
||||
marketRate &&
|
||||
lastEditedField === INPUT &&
|
||||
(decimals || decimals === 0)
|
||||
) {
|
||||
try {
|
||||
const parsedValue = ethers.utils.parseUnits(inputValue, 18)
|
||||
|
||||
if (parsedValue.lte(ethers.constants.Zero) || parsedValue.gte(ethers.constants.MaxUint256)) {
|
||||
throw Error()
|
||||
}
|
||||
|
||||
setInputValueParsed(parsedValue)
|
||||
|
||||
const currencyAmount = marketRate
|
||||
.mul(parsedValue)
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18)))
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18 - decimals)))
|
||||
|
||||
setOutputValueParsed(currencyAmount)
|
||||
dispatchAddLiquidityState({
|
||||
type: 'UPDATE_DEPENDENT_VALUE',
|
||||
payload: { field: OUTPUT, value: amountFormatter(currencyAmount, decimals, Math.min(decimals, 4), false) }
|
||||
})
|
||||
|
||||
return () => {
|
||||
setOutputError()
|
||||
setInputValueParsed()
|
||||
setOutputValueParsed()
|
||||
dispatchAddLiquidityState({
|
||||
type: 'UPDATE_DEPENDENT_VALUE',
|
||||
payload: { field: OUTPUT, value: '' }
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
setOutputError(t('inputNotValid'))
|
||||
}
|
||||
}
|
||||
}, [inputValue, isNewExchange, lastEditedField, marketRate, decimals, t])
|
||||
|
||||
// parse output value
|
||||
useEffect(() => {
|
||||
if (
|
||||
isNewExchange === false &&
|
||||
outputValue &&
|
||||
marketRateInverted &&
|
||||
lastEditedField === OUTPUT &&
|
||||
(decimals || decimals === 0)
|
||||
) {
|
||||
try {
|
||||
const parsedValue = ethers.utils.parseUnits(outputValue, decimals)
|
||||
|
||||
if (parsedValue.lte(ethers.constants.Zero) || parsedValue.gte(ethers.constants.MaxUint256)) {
|
||||
throw Error()
|
||||
}
|
||||
|
||||
setOutputValueParsed(parsedValue)
|
||||
|
||||
const currencyAmount = marketRateInverted
|
||||
.mul(parsedValue)
|
||||
.div(ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(decimals)))
|
||||
|
||||
setInputValueParsed(currencyAmount)
|
||||
dispatchAddLiquidityState({
|
||||
type: 'UPDATE_DEPENDENT_VALUE',
|
||||
payload: { field: INPUT, value: amountFormatter(currencyAmount, 18, 4, false) }
|
||||
})
|
||||
|
||||
return () => {
|
||||
setInputError()
|
||||
setOutputValueParsed()
|
||||
setInputValueParsed()
|
||||
dispatchAddLiquidityState({
|
||||
type: 'UPDATE_DEPENDENT_VALUE',
|
||||
payload: { field: INPUT, value: '' }
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
setInputError(t('inputNotValid'))
|
||||
}
|
||||
}
|
||||
}, [outputValue, isNewExchange, lastEditedField, marketRateInverted, decimals, t])
|
||||
|
||||
// input validation
|
||||
useEffect(() => {
|
||||
if (inputValueParsed && inputBalance) {
|
||||
if (inputValueParsed.gt(inputBalance)) {
|
||||
setInputError(t('insufficientBalance'))
|
||||
} else {
|
||||
setInputError(null)
|
||||
}
|
||||
}
|
||||
|
||||
if (outputValueMax && outputBalance) {
|
||||
if (outputValueMax.gt(outputBalance)) {
|
||||
setOutputError(t('insufficientBalance'))
|
||||
} else {
|
||||
setOutputError(null)
|
||||
}
|
||||
}
|
||||
}, [inputValueParsed, inputBalance, outputValueMax, outputBalance, t])
|
||||
|
||||
const allowance = useAddressAllowance(account, outputCurrency, exchangeAddress)
|
||||
|
||||
const [showUnlock, setShowUnlock] = useState(false)
|
||||
useEffect(() => {
|
||||
if (outputValueParsed && allowance) {
|
||||
if (allowance.lt(outputValueParsed)) {
|
||||
setOutputError(t('unlockTokenCont'))
|
||||
setShowUnlock(true)
|
||||
}
|
||||
return () => {
|
||||
setOutputError()
|
||||
setShowUnlock(false)
|
||||
}
|
||||
}
|
||||
}, [outputValueParsed, allowance, t])
|
||||
|
||||
const isActive = active && account
|
||||
const isValid =
|
||||
(inputError === null || outputError === null) && !zeroDecimalError && !showUnlock && !brokenTokenWarning
|
||||
|
||||
const newOutputDetected =
|
||||
outputCurrency !== 'ETH' && outputCurrency && !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(outputCurrency)
|
||||
|
||||
const [showOutputWarning, setShowOutputWarning] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (newOutputDetected) {
|
||||
setShowOutputWarning(true)
|
||||
} else {
|
||||
setShowOutputWarning(false)
|
||||
}
|
||||
}, [newOutputDetected, setShowOutputWarning])
|
||||
return (
|
||||
<>
|
||||
{isNewExchange ? (
|
||||
<NewExchangeWarning>
|
||||
<NewExchangeWarningText>
|
||||
<span role="img" aria-label="first-liquidity">
|
||||
🚰
|
||||
</span>{' '}
|
||||
{t('firstLiquidity')}
|
||||
</NewExchangeWarningText>
|
||||
<NewExchangeWarningText>{t('initialExchangeRate', { symbol })}</NewExchangeWarningText>
|
||||
</NewExchangeWarning>
|
||||
) : null}
|
||||
{showOutputWarning && (
|
||||
<WarningCard
|
||||
onDismiss={() => {
|
||||
setShowOutputWarning(false)
|
||||
}}
|
||||
urlAddedTokens={urlAddedTokens}
|
||||
currency={outputCurrency}
|
||||
/>
|
||||
)}
|
||||
<CurrencyInputPanel
|
||||
title={t('deposit')}
|
||||
extraText={inputBalance && formatBalance(amountFormatter(inputBalance, 18, 4))}
|
||||
onValueChange={inputValue => {
|
||||
dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: inputValue, field: INPUT } })
|
||||
}}
|
||||
extraTextClickHander={() => {
|
||||
if (inputBalance) {
|
||||
const valueToSet = inputBalance.sub(ethers.utils.parseEther('.1'))
|
||||
if (valueToSet.gt(ethers.constants.Zero)) {
|
||||
dispatchAddLiquidityState({
|
||||
type: 'UPDATE_VALUE',
|
||||
payload: { value: amountFormatter(valueToSet, 18, 18, false), field: INPUT }
|
||||
})
|
||||
}
|
||||
}
|
||||
}}
|
||||
selectedTokenAddress="ETH"
|
||||
value={inputValue}
|
||||
errorMessage={inputError}
|
||||
disableTokenSelect
|
||||
/>
|
||||
<OversizedPanel>
|
||||
<DownArrowBackground>
|
||||
<ColoredWrappedPlus active={isActive} alt="plus" />
|
||||
</DownArrowBackground>
|
||||
</OversizedPanel>
|
||||
<CurrencyInputPanel
|
||||
title={t('deposit')}
|
||||
description={isNewExchange ? '' : outputValue ? `(${t('estimated')})` : ''}
|
||||
extraText={
|
||||
outputBalance && decimals && formatBalance(amountFormatter(outputBalance, decimals, Math.min(decimals, 4)))
|
||||
}
|
||||
urlAddedTokens={urlAddedTokens}
|
||||
selectedTokenAddress={outputCurrency}
|
||||
onCurrencySelected={outputCurrency => {
|
||||
dispatchAddLiquidityState({ type: 'SELECT_CURRENCY', payload: outputCurrency })
|
||||
}}
|
||||
onValueChange={outputValue => {
|
||||
dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: outputValue, field: OUTPUT } })
|
||||
}}
|
||||
extraTextClickHander={() => {
|
||||
if (outputBalance) {
|
||||
dispatchAddLiquidityState({
|
||||
type: 'UPDATE_VALUE',
|
||||
payload: {
|
||||
value: amountFormatter(calculateMaxOutputVal(outputBalance), decimals, decimals, false),
|
||||
field: OUTPUT
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
value={outputValue}
|
||||
showUnlock={showUnlock}
|
||||
errorMessage={outputError}
|
||||
/>
|
||||
<OversizedPanel hideBottom>
|
||||
<SummaryPanel>
|
||||
<ExchangeRateWrapper>
|
||||
<ExchangeRate>{t('exchangeRate')}</ExchangeRate>
|
||||
<span>{marketRate ? `1 ETH = ${amountFormatter(marketRate, 18, 4)} ${symbol}` : ' - '}</span>
|
||||
</ExchangeRateWrapper>
|
||||
<ExchangeRateWrapper>
|
||||
<ExchangeRate>{t('currentPoolSize')}</ExchangeRate>
|
||||
<span>
|
||||
{exchangeETHBalance && exchangeTokenBalance
|
||||
? `${amountFormatter(exchangeETHBalance, 18, 4)} ETH + ${amountFormatter(
|
||||
exchangeTokenBalance,
|
||||
decimals,
|
||||
Math.min(4, decimals)
|
||||
)} ${symbol}`
|
||||
: ' - '}
|
||||
</span>
|
||||
</ExchangeRateWrapper>
|
||||
<ExchangeRateWrapper>
|
||||
<ExchangeRate>
|
||||
{t('yourPoolShare')} ({exchangeETHBalance && amountFormatter(poolTokenPercentage, 16, 2)}%)
|
||||
</ExchangeRate>
|
||||
<span>
|
||||
{ethShare && tokenShare
|
||||
? `${amountFormatter(ethShare, 18, 4)} ETH + ${amountFormatter(
|
||||
tokenShare,
|
||||
decimals,
|
||||
Math.min(4, decimals)
|
||||
)} ${symbol}`
|
||||
: ' - '}
|
||||
</span>
|
||||
</ExchangeRateWrapper>
|
||||
</SummaryPanel>
|
||||
</OversizedPanel>
|
||||
{renderSummary()}
|
||||
<Flex>
|
||||
<Button disabled={!isValid} onClick={onAddLiquidity}>
|
||||
{t('addLiquidity')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import { withRouter, NavLink } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import OversizedPanel from '../../components/OversizedPanel'
|
||||
import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg'
|
||||
|
||||
import Modal from '../../components/Modal'
|
||||
import { useBodyKeyDown } from '../../hooks'
|
||||
|
||||
import { lighten } from 'polished'
|
||||
|
||||
const poolTabOrder = [
|
||||
{
|
||||
path: '/add-liquidity',
|
||||
textKey: 'addLiquidity',
|
||||
regex: /\/add-liquidity/
|
||||
},
|
||||
{
|
||||
path: '/remove-liquidity',
|
||||
textKey: 'removeLiquidity',
|
||||
regex: /\/remove-liquidity/
|
||||
},
|
||||
{
|
||||
path: '/create-exchange',
|
||||
textKey: 'createExchange',
|
||||
regex: /\/create-exchange.*/
|
||||
}
|
||||
]
|
||||
|
||||
const LiquidityContainer = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
align-items: center;
|
||||
padding: 1rem 1rem;
|
||||
font-size: 1rem;
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
|
||||
:hover {
|
||||
color: ${({ theme }) => lighten(0.1, theme.royalBlue)};
|
||||
}
|
||||
|
||||
img {
|
||||
height: 0.75rem;
|
||||
width: 0.75rem;
|
||||
}
|
||||
`
|
||||
|
||||
const LiquidityLabel = styled.span`
|
||||
flex: 1 0 auto;
|
||||
`
|
||||
|
||||
const activeClassName = 'MODE'
|
||||
|
||||
const StyledNavLink = styled(NavLink).attrs({
|
||||
activeClassName
|
||||
})`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
padding: 1rem;
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.doveGray};
|
||||
font-size: 1rem;
|
||||
|
||||
&.${activeClassName} {
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
border-radius: 3rem;
|
||||
border: 1px solid ${({ theme }) => theme.mercuryGray};
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.royalBlue};
|
||||
}
|
||||
`
|
||||
|
||||
const PoolModal = styled.div`
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 2rem 0 2rem 0;
|
||||
`
|
||||
|
||||
const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} />
|
||||
const ColoredDropdown = styled(WrappedDropdown)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.royalBlue};
|
||||
}
|
||||
`
|
||||
|
||||
function ModeSelector({ location: { pathname }, history }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [modalIsOpen, setModalIsOpen] = useState(false)
|
||||
|
||||
const activeTabKey = poolTabOrder[poolTabOrder.findIndex(({ regex }) => pathname.match(regex))].textKey
|
||||
|
||||
const navigate = useCallback(
|
||||
direction => {
|
||||
const tabIndex = poolTabOrder.findIndex(({ regex }) => pathname.match(regex))
|
||||
history.push(poolTabOrder[(tabIndex + poolTabOrder.length + direction) % poolTabOrder.length].path)
|
||||
},
|
||||
[pathname, history]
|
||||
)
|
||||
const navigateRight = useCallback(() => {
|
||||
navigate(1)
|
||||
}, [navigate])
|
||||
const navigateLeft = useCallback(() => {
|
||||
navigate(-1)
|
||||
}, [navigate])
|
||||
|
||||
useBodyKeyDown('ArrowDown', navigateRight, modalIsOpen)
|
||||
useBodyKeyDown('ArrowUp', navigateLeft, modalIsOpen)
|
||||
|
||||
return (
|
||||
<OversizedPanel hideTop>
|
||||
<LiquidityContainer
|
||||
onClick={() => {
|
||||
setModalIsOpen(true)
|
||||
}}
|
||||
>
|
||||
<LiquidityLabel>{t(activeTabKey)}</LiquidityLabel>
|
||||
<ColoredDropdown alt="arrow down" />
|
||||
</LiquidityContainer>
|
||||
<Modal
|
||||
isOpen={modalIsOpen}
|
||||
maxHeight={50}
|
||||
onDismiss={() => {
|
||||
setModalIsOpen(false)
|
||||
}}
|
||||
>
|
||||
<PoolModal>
|
||||
{poolTabOrder.map(({ path, textKey, regex }) => (
|
||||
<StyledNavLink
|
||||
key={path}
|
||||
to={path}
|
||||
isActive={(_, { pathname }) => pathname.match(regex)}
|
||||
onClick={() => {
|
||||
setModalIsOpen(false)
|
||||
}}
|
||||
>
|
||||
{t(textKey)}
|
||||
</StyledNavLink>
|
||||
))}
|
||||
</PoolModal>
|
||||
</Modal>
|
||||
</OversizedPanel>
|
||||
)
|
||||
}
|
||||
|
||||
export default withRouter(ModeSelector)
|
@ -1,43 +0,0 @@
|
||||
import React, { Suspense, lazy, useEffect } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Switch, Route, Redirect } from 'react-router-dom'
|
||||
import ModeSelector from './ModeSelector'
|
||||
|
||||
const AddLiquidity = lazy(() => import('./AddLiquidity'))
|
||||
const RemoveLiquidity = lazy(() => import('./RemoveLiquidity'))
|
||||
const CreateExchange = lazy(() => import('./CreateExchange'))
|
||||
|
||||
export default function Pool({ params }) {
|
||||
useEffect(() => {
|
||||
ReactGA.pageview(window.location.pathname + window.location.search)
|
||||
}, [])
|
||||
|
||||
const AddLiquidityParams = () => <AddLiquidity params={params} />
|
||||
|
||||
const RemoveLiquidityParams = () => <RemoveLiquidity params={params} />
|
||||
|
||||
const CreateExchangeParams = () => <CreateExchange params={params} />
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModeSelector />
|
||||
{/* this Suspense is for route code-splitting */}
|
||||
<Suspense fallback={null}>
|
||||
<Switch>
|
||||
<Route exact strict path="/add-liquidity" component={AddLiquidityParams} />
|
||||
<Route exact strict path="/remove-liquidity" component={RemoveLiquidityParams} />
|
||||
<Route exact strict path="/create-exchange" component={CreateExchangeParams} />
|
||||
<Route
|
||||
path="/create-exchange/:tokenAddress"
|
||||
render={({ match }) => {
|
||||
return (
|
||||
<Redirect to={{ pathname: '/create-exchange', state: { tokenAddress: match.params.tokenAddress } }} />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Redirect to="/add-liquidity" />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</>
|
||||
)
|
||||
}
|
446
src/pages/Supply/AddLiquidity.tsx
Normal file
446
src/pages/Supply/AddLiquidity.tsx
Normal file
@ -0,0 +1,446 @@
|
||||
import React, { useReducer, useState, useCallback, useEffect } from 'react'
|
||||
import { WETH, TokenAmount, JSBI, Percent, Route } from '@uniswap/sdk'
|
||||
import { parseUnits, parseEther } from '@ethersproject/units'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { Text } from 'rebass'
|
||||
import { ChevronDown } from 'react-feather'
|
||||
import { ButtonPrimary, ButtonEmpty } from '../../components/Button'
|
||||
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { RowBetween } from '../../components/Row'
|
||||
import DoubleLogo from '../../components/DoubleLogo'
|
||||
import { ArrowDown, Plus } from 'react-feather'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { LightCard } from '../../components/Card'
|
||||
import SearchModal from '../../components/SearchModal'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
import { useExchange } from '../../contexts/Exchanges'
|
||||
import { useExchangeContract } from '../../hooks'
|
||||
|
||||
const ErrorText = styled(Text)`
|
||||
color: ${({ theme, error }) => (error ? theme.salmonRed : theme.chaliceGray)};
|
||||
`
|
||||
|
||||
const ALLOWED_SLIPPAGE = JSBI.BigInt(200)
|
||||
|
||||
enum Field {
|
||||
INPUT,
|
||||
OUTPUT
|
||||
}
|
||||
|
||||
interface AddState {
|
||||
independentField: Field
|
||||
typedValue: string
|
||||
[Field.INPUT]: {
|
||||
address: string | undefined
|
||||
}
|
||||
[Field.OUTPUT]: {
|
||||
address: string | undefined
|
||||
}
|
||||
}
|
||||
|
||||
function initializeAddState(inputAddress?: string, outputAddress?: string): AddState {
|
||||
return {
|
||||
independentField: Field.INPUT,
|
||||
typedValue: '',
|
||||
[Field.INPUT]: {
|
||||
address: inputAddress
|
||||
},
|
||||
[Field.OUTPUT]: {
|
||||
address: outputAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AddAction {
|
||||
SELECT_TOKEN,
|
||||
SWITCH_TOKENS,
|
||||
TYPE
|
||||
}
|
||||
|
||||
interface Payload {
|
||||
[AddAction.SELECT_TOKEN]: {
|
||||
field: Field
|
||||
address: string
|
||||
}
|
||||
[AddAction.SWITCH_TOKENS]: undefined
|
||||
[AddAction.TYPE]: {
|
||||
field: Field
|
||||
typedValue: string
|
||||
}
|
||||
}
|
||||
|
||||
function reducer(
|
||||
state: AddState,
|
||||
action: {
|
||||
type: AddAction
|
||||
payload: Payload[AddAction]
|
||||
}
|
||||
): AddState {
|
||||
switch (action.type) {
|
||||
case AddAction.SELECT_TOKEN: {
|
||||
const { field, address } = action.payload as Payload[AddAction.SELECT_TOKEN]
|
||||
const otherField = field === Field.INPUT ? Field.OUTPUT : Field.INPUT
|
||||
if (address === state[otherField].address) {
|
||||
// the case where we have to swap the order
|
||||
return {
|
||||
...state,
|
||||
independentField: state.independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT,
|
||||
[field]: { address },
|
||||
[otherField]: { address: state[field].address }
|
||||
}
|
||||
} else {
|
||||
// the normal case
|
||||
return {
|
||||
...state,
|
||||
[field]: { address }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case AddAction.TYPE: {
|
||||
const { field, typedValue } = action.payload as Payload[AddAction.TYPE]
|
||||
return {
|
||||
...state,
|
||||
independentField: field,
|
||||
typedValue
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function AddLiquidity() {
|
||||
// mock to set initial values either from URL or route from supply page
|
||||
const token1 = '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'
|
||||
const token0 = '0xc778417E063141139Fce010982780140Aa0cD5Ab'
|
||||
|
||||
const { account, chainId, library } = useWeb3React()
|
||||
|
||||
// modal state
|
||||
const [showSearch, toggleSearch] = useState(false)
|
||||
|
||||
// input state
|
||||
const [state, dispatch] = useReducer(reducer, initializeAddState(token0, token1))
|
||||
const { independentField, typedValue, ...fieldData } = state
|
||||
|
||||
// get derived state
|
||||
const dependentField = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
|
||||
|
||||
// get basic SDK entities
|
||||
const tokens = {
|
||||
[Field.INPUT]: useToken(fieldData[Field.INPUT].address),
|
||||
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address)
|
||||
}
|
||||
const exchange = useExchange(tokens[Field.INPUT], tokens[Field.OUTPUT])
|
||||
const route = exchange ? new Route([exchange], tokens[independentField]) : undefined
|
||||
|
||||
// get user- and token-specific lookup data
|
||||
const userBalances = {
|
||||
[Field.INPUT]: useAddressBalance(account, tokens[Field.INPUT]),
|
||||
[Field.OUTPUT]: useAddressBalance(account, tokens[Field.OUTPUT])
|
||||
}
|
||||
|
||||
const parsedAmounts: { [field: number]: TokenAmount } = {}
|
||||
// try to parse typed value
|
||||
if (typedValue !== '' && typedValue !== '.' && tokens[independentField]) {
|
||||
try {
|
||||
const typedValueParsed = parseUnits(typedValue, tokens[independentField].decimals).toString()
|
||||
if (typedValueParsed !== '0')
|
||||
parsedAmounts[independentField] = new TokenAmount(tokens[independentField], typedValueParsed)
|
||||
} catch (error) {
|
||||
// should only fail if the user specifies too many decimal places of precision (or maybe exceed max uint?)
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// get the price data and update dependent field
|
||||
if (
|
||||
route &&
|
||||
parsedAmounts[independentField] &&
|
||||
JSBI.greaterThan(parsedAmounts[independentField].raw, JSBI.BigInt(0))
|
||||
) {
|
||||
parsedAmounts[dependentField] = route.midPrice.quote(parsedAmounts[independentField])
|
||||
}
|
||||
|
||||
// get formatted amounts
|
||||
const formattedAmounts = {
|
||||
[independentField]: typedValue,
|
||||
[dependentField]: parsedAmounts[dependentField] ? parsedAmounts[dependentField].toSignificant(8) : ''
|
||||
}
|
||||
|
||||
// pool token data
|
||||
const poolToken = useToken(exchange?.address)
|
||||
const [totalPoolTokens, setTotalPoolTokens] = useState<TokenAmount>()
|
||||
|
||||
const exchangeContract = useExchangeContract(exchange?.address)
|
||||
const fetchPoolTokens = useCallback(() => {
|
||||
if (exchangeContract) {
|
||||
exchangeContract.totalSupply().then(totalSupply => {
|
||||
if (totalSupply !== undefined && poolToken?.decimals) {
|
||||
const supplyFormatted = JSBI.BigInt(totalSupply)
|
||||
const tokenSupplyFormatted = new TokenAmount(poolToken, supplyFormatted)
|
||||
setTotalPoolTokens(tokenSupplyFormatted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [exchangeContract, poolToken])
|
||||
useEffect(() => {
|
||||
fetchPoolTokens()
|
||||
library.on('block', fetchPoolTokens)
|
||||
|
||||
return () => {
|
||||
library.removeListener('block', fetchPoolTokens)
|
||||
}
|
||||
}, [fetchPoolTokens, library])
|
||||
|
||||
function minTokenAmount(x: JSBI, y: JSBI): JSBI {
|
||||
return JSBI.lessThan(x, y) ? x : y
|
||||
}
|
||||
|
||||
// check for estimated liquidity minted
|
||||
const liquidityMinted =
|
||||
!!poolToken && !!parsedAmounts[Field.INPUT] && !!parsedAmounts[Field.OUTPUT] && !!totalPoolTokens && exchange
|
||||
? new TokenAmount(
|
||||
poolToken,
|
||||
minTokenAmount(
|
||||
JSBI.divide(
|
||||
JSBI.multiply(parsedAmounts[Field.INPUT].raw, totalPoolTokens.raw),
|
||||
exchange.reserveOf(tokens[Field.INPUT]).raw
|
||||
),
|
||||
JSBI.divide(
|
||||
JSBI.multiply(parsedAmounts[Field.OUTPUT].raw, totalPoolTokens.raw),
|
||||
exchange.reserveOf(tokens[Field.OUTPUT]).raw
|
||||
)
|
||||
)
|
||||
)
|
||||
: undefined
|
||||
|
||||
const poolTokenPercentage =
|
||||
!!liquidityMinted && !!totalPoolTokens
|
||||
? new Percent(liquidityMinted.raw, totalPoolTokens.add(liquidityMinted).raw)
|
||||
: undefined
|
||||
|
||||
const onTokenSelection = useCallback((field: Field, address: string) => {
|
||||
dispatch({
|
||||
type: AddAction.SELECT_TOKEN,
|
||||
payload: { field, address }
|
||||
})
|
||||
}, [])
|
||||
|
||||
const onUserInput = useCallback((field: Field, typedValue: string) => {
|
||||
dispatch({ type: AddAction.TYPE, payload: { field, typedValue } })
|
||||
}, [])
|
||||
|
||||
const onMaxInput = useCallback((typedValue: string) => {
|
||||
dispatch({
|
||||
type: AddAction.TYPE,
|
||||
payload: {
|
||||
field: Field.INPUT,
|
||||
typedValue
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const onMaxOutput = useCallback((typedValue: string) => {
|
||||
dispatch({
|
||||
type: AddAction.TYPE,
|
||||
payload: {
|
||||
field: Field.OUTPUT,
|
||||
typedValue
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const MIN_ETHER = new TokenAmount(WETH[chainId], JSBI.BigInt(parseEther('.01')))
|
||||
const maxAmountInput =
|
||||
!!userBalances[Field.INPUT] &&
|
||||
JSBI.greaterThan(
|
||||
userBalances[Field.INPUT].raw,
|
||||
tokens[Field.INPUT].equals(WETH[chainId]) ? MIN_ETHER.raw : JSBI.BigInt(0)
|
||||
)
|
||||
? tokens[Field.INPUT].equals(WETH[chainId])
|
||||
? userBalances[Field.INPUT].subtract(MIN_ETHER)
|
||||
: userBalances[Field.INPUT]
|
||||
: undefined
|
||||
const atMaxAmountInput =
|
||||
!!maxAmountInput && !!parsedAmounts[Field.INPUT]
|
||||
? JSBI.equal(maxAmountInput.raw, parsedAmounts[Field.INPUT].raw)
|
||||
: undefined
|
||||
|
||||
const maxAmountOutput =
|
||||
!!userBalances[Field.OUTPUT] &&
|
||||
JSBI.greaterThan(
|
||||
userBalances[Field.OUTPUT].raw,
|
||||
tokens[Field.OUTPUT].equals(WETH[chainId]) ? MIN_ETHER.raw : JSBI.BigInt(0)
|
||||
)
|
||||
? tokens[Field.OUTPUT].equals(WETH[chainId])
|
||||
? userBalances[Field.OUTPUT].subtract(MIN_ETHER)
|
||||
: userBalances[Field.OUTPUT]
|
||||
: undefined
|
||||
|
||||
const atMaxAmountOutput =
|
||||
!!maxAmountOutput && !!parsedAmounts[Field.OUTPUT]
|
||||
? JSBI.equal(maxAmountOutput.raw, parsedAmounts[Field.OUTPUT].raw)
|
||||
: undefined
|
||||
|
||||
// state for confirmation popup
|
||||
const [showConfirm, toggleConfirm] = useState(false)
|
||||
|
||||
// errors
|
||||
const [inputError, setInputError] = useState()
|
||||
const [outputError, setOutputError] = useState()
|
||||
const [errorText, setErrorText] = useState(' ')
|
||||
const [isError, setIsError] = useState(false)
|
||||
|
||||
// update errors live
|
||||
useEffect(() => {
|
||||
// reset errors
|
||||
setInputError(null)
|
||||
setOutputError(null)
|
||||
setIsError(false)
|
||||
if (parseFloat(parsedAmounts?.[Field.INPUT]?.toExact()) > parseFloat(userBalances?.[Field.INPUT]?.toExact())) {
|
||||
setInputError('Insufficient balance.')
|
||||
setIsError(true)
|
||||
}
|
||||
if (parseFloat(parsedAmounts?.[Field.OUTPUT]?.toExact()) > parseFloat(userBalances?.[Field.OUTPUT]?.toExact())) {
|
||||
setOutputError('Insufficient balance.')
|
||||
setIsError(true)
|
||||
}
|
||||
}, [parsedAmounts, userBalances])
|
||||
|
||||
// set error text based on all errors
|
||||
useEffect(() => {
|
||||
setErrorText(null)
|
||||
if (!parsedAmounts[Field.INPUT]) {
|
||||
setErrorText('Enter an amount to continue')
|
||||
} else if (outputError) {
|
||||
setErrorText(outputError)
|
||||
} else if (inputError) {
|
||||
setErrorText(inputError)
|
||||
return
|
||||
}
|
||||
}, [inputError, outputError, parsedAmounts])
|
||||
|
||||
// error state for button
|
||||
const isValid = !errorText
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmationModal
|
||||
isOpen={showConfirm}
|
||||
onDismiss={() => {
|
||||
toggleConfirm(false)
|
||||
}}
|
||||
liquidityMinted={liquidityMinted}
|
||||
amount0={
|
||||
parsedAmounts[independentField]?.token.equals(exchange?.token0)
|
||||
? parsedAmounts[independentField]
|
||||
: parsedAmounts[dependentField]
|
||||
}
|
||||
amount1={
|
||||
parsedAmounts[independentField]?.token.equals(exchange?.token0)
|
||||
? parsedAmounts[dependentField]
|
||||
: parsedAmounts[independentField]
|
||||
}
|
||||
poolTokenPercentage={poolTokenPercentage}
|
||||
price={route?.midPrice}
|
||||
/>
|
||||
<SearchModal
|
||||
isOpen={showSearch}
|
||||
onDismiss={() => {
|
||||
toggleSearch(false)
|
||||
}}
|
||||
/>
|
||||
<AutoColumn gap="20px">
|
||||
<ButtonEmpty
|
||||
padding={'1rem'}
|
||||
onClick={() => {
|
||||
toggleSearch(true)
|
||||
}}
|
||||
>
|
||||
<RowBetween>
|
||||
<DoubleLogo a0={exchange?.token0?.address || ''} a1={exchange?.token1?.address || ''} size={24} />
|
||||
<Text fontSize={20}>
|
||||
{exchange?.token0?.symbol && exchange?.token1?.symbol
|
||||
? exchange?.token0?.symbol + ' / ' + exchange?.token1?.symbol + ' Pool'
|
||||
: ''}
|
||||
</Text>
|
||||
<ChevronDown size={24} />
|
||||
</RowBetween>
|
||||
</ButtonEmpty>
|
||||
<CurrencyInputPanel
|
||||
field={Field.INPUT}
|
||||
value={formattedAmounts[Field.INPUT]}
|
||||
onUserInput={onUserInput}
|
||||
onMax={() => {
|
||||
onMaxInput(maxAmountInput.toExact())
|
||||
}}
|
||||
atMax={atMaxAmountInput}
|
||||
selectedTokenAddress={tokens[Field.INPUT]?.address}
|
||||
onTokenSelection={onTokenSelection}
|
||||
title={'Deposit'}
|
||||
error={inputError}
|
||||
/>
|
||||
<ColumnCenter>
|
||||
<Plus size="16" color="#888D9B" />
|
||||
</ColumnCenter>
|
||||
<CurrencyInputPanel
|
||||
field={Field.OUTPUT}
|
||||
value={formattedAmounts[Field.OUTPUT]}
|
||||
onUserInput={onUserInput}
|
||||
onMax={() => {
|
||||
onMaxOutput(maxAmountOutput.toExact())
|
||||
}}
|
||||
atMax={atMaxAmountOutput}
|
||||
selectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
||||
onTokenSelection={onTokenSelection}
|
||||
title={'Deposit'}
|
||||
error={outputError}
|
||||
/>
|
||||
<ColumnCenter>
|
||||
<ArrowDown size="16" color="#888D9B" />
|
||||
</ColumnCenter>
|
||||
<LightCard>
|
||||
<AutoColumn gap="10px">
|
||||
<RowBetween>
|
||||
Minted pool tokens:
|
||||
<div>{liquidityMinted ? liquidityMinted.toFixed(6) : '-'}</div>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
Minted pool share:
|
||||
<div>{poolTokenPercentage ? +poolTokenPercentage.toFixed(4) + '%' : '-'}</div>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
Rate:
|
||||
<div>
|
||||
1 {exchange?.token0.symbol} = {route?.midPrice.toSignificant(6)} {exchange?.token1.symbol}
|
||||
</div>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
<ColumnCenter style={{ height: '20px' }}>
|
||||
<ErrorText fontSize={12} error={isError}>
|
||||
{errorText && errorText}
|
||||
</ErrorText>
|
||||
</ColumnCenter>
|
||||
<ButtonPrimary
|
||||
onClick={() => {
|
||||
toggleConfirm(true)
|
||||
}}
|
||||
disabled={!isValid}
|
||||
>
|
||||
<Text fontSize={20} fontWeight={500}>
|
||||
Supply
|
||||
</Text>
|
||||
</ButtonPrimary>
|
||||
</AutoColumn>
|
||||
</>
|
||||
)
|
||||
}
|
@ -7,7 +7,7 @@ import styled from 'styled-components'
|
||||
|
||||
import { useWeb3React, useExchangeContract } from '../../hooks'
|
||||
import { useTransactionAdder } from '../../contexts/Transactions'
|
||||
import { useTokenDetails, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { useToken, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
|
||||
import { calculateGasMargin, amountFormatter } from '../../utils'
|
||||
@ -188,7 +188,7 @@ export default function RemoveLiquidity({ params }) {
|
||||
}
|
||||
}, [t, value])
|
||||
|
||||
const { symbol, decimals, exchangeAddress } = useTokenDetails(outputCurrency)
|
||||
const { symbol, decimals, exchangeAddress } = useToken(outputCurrency)
|
||||
|
||||
const [totalPoolTokens, setTotalPoolTokens] = useState()
|
||||
const poolTokenBalance = useAddressBalance(account, exchangeAddress)
|
236
src/pages/Supply/index.js
Normal file
236
src/pages/Supply/index.js
Normal file
@ -0,0 +1,236 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { TokenAmount, JSBI, Percent, Token } from '@uniswap/sdk'
|
||||
import ReactGA from 'react-ga'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
|
||||
import { Link } from '../../theme'
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import Row, { RowBetween, RowFixed } from '../../components/Row'
|
||||
import { ButtonDropwdown, ButtonSecondary } from '../../components/Button'
|
||||
import Card from '../../components/Card'
|
||||
import { Text } from 'rebass'
|
||||
import DoubleLogo from '../../components/DoubleLogo'
|
||||
import SearchModal from '../../components/SearchModal'
|
||||
import { ArrowRight } from 'react-feather'
|
||||
|
||||
import { useAllExchanges } from '../../contexts/Exchanges'
|
||||
import { useAllBalances } from '../../contexts/Balances'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { useAllTokens } from '../../contexts/Tokens'
|
||||
import { useExchangeContract } from '../../hooks'
|
||||
import TokenLogo from '../../components/TokenLogo'
|
||||
|
||||
const Positions = styled.div`
|
||||
position: relative;
|
||||
margin-top: 38px;
|
||||
`
|
||||
|
||||
const FixedBottom = styled.div`
|
||||
position: absolute;
|
||||
bottom: -240px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
function ExchangeCard({ exchangeAddress, token0, token1, history, allBalances }) {
|
||||
const { account, chainId } = useWeb3React()
|
||||
|
||||
const userPoolBalance = allBalances?.[account]?.[exchangeAddress]
|
||||
const [totalPoolTokens, setTotalPoolTokens] = useState()
|
||||
// get the total pool token supply
|
||||
const exchangeContract = useExchangeContract(exchangeAddress)
|
||||
const fetchPoolTokens = useCallback(() => {
|
||||
if (exchangeContract) {
|
||||
exchangeContract.totalSupply().then(totalSupply => {
|
||||
if (totalSupply !== undefined) {
|
||||
const supplyFormatted = JSBI.BigInt(totalSupply)
|
||||
const tokenSupplyFormatted = new TokenAmount(new Token(chainId, exchangeAddress, 18), supplyFormatted)
|
||||
setTotalPoolTokens(tokenSupplyFormatted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [exchangeContract, chainId, exchangeAddress])
|
||||
useEffect(() => {
|
||||
fetchPoolTokens()
|
||||
}, [fetchPoolTokens])
|
||||
|
||||
const poolTokenPercentage =
|
||||
!!userPoolBalance && !!totalPoolTokens ? new Percent(userPoolBalance.raw, totalPoolTokens.raw) : undefined
|
||||
|
||||
const token0Deposited = poolTokenPercentage?.multiply(allBalances[exchangeAddress][token0.address])
|
||||
const token1Deposited = poolTokenPercentage?.multiply(allBalances[exchangeAddress][token1.address])
|
||||
|
||||
return (
|
||||
<Card border="1px solid #EDEEF2">
|
||||
<AutoColumn gap="20px">
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{token0?.symbol}:{token1?.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
|
||||
</Text>
|
||||
</RowBetween>
|
||||
<AutoColumn gap="12px">
|
||||
<RowBetween>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token0?.symbol} Deposited:
|
||||
</Text>
|
||||
{token0Deposited ? (
|
||||
<RowFixed>
|
||||
<TokenLogo address={token0?.address || ''} />
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token0Deposited?.toSignificant(6)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token1?.symbol} Deposited:
|
||||
</Text>
|
||||
{token1Deposited ? (
|
||||
<RowFixed>
|
||||
<TokenLogo address={token1.address || ''} />
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token1Deposited?.toSignificant(6)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
Your pool share:
|
||||
</Text>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
|
||||
</Text>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
<RowBetween>
|
||||
<ButtonSecondary
|
||||
width="48%"
|
||||
onClick={() => {
|
||||
history.push('/add/' + token0?.address + '-' + token1?.address)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</ButtonSecondary>
|
||||
<ButtonSecondary
|
||||
width="48%"
|
||||
onClick={() => {
|
||||
history.push('/remove')
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</ButtonSecondary>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function Supply({ history }) {
|
||||
useEffect(() => {
|
||||
ReactGA.pageview(window.location.pathname + window.location.search)
|
||||
}, [])
|
||||
|
||||
const { account } = useWeb3React()
|
||||
|
||||
const [showSearch, toggleSearch] = useState(false)
|
||||
|
||||
const exchanges = useAllExchanges()
|
||||
|
||||
const allTokens = useAllTokens()
|
||||
|
||||
const allBalances = useAllBalances()
|
||||
|
||||
const filteredPairList = Object.keys(exchanges).map((token0Address, i) => {
|
||||
return Object.keys(exchanges[token0Address]).map(token1Address => {
|
||||
const exchangeAddress = exchanges[token0Address][token1Address]
|
||||
|
||||
/**
|
||||
* we need the users exchnage balance over all exchanges
|
||||
*
|
||||
* right now we dont
|
||||
*
|
||||
* if they go to supplu page, flip switch to look for balances
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// gate on positive address
|
||||
if (allBalances?.[account]?.[exchangeAddress]) {
|
||||
const token0 = allTokens[token0Address]
|
||||
const token1 = allTokens[token1Address]
|
||||
return (
|
||||
<ExchangeCard
|
||||
history={history}
|
||||
key={i}
|
||||
exchangeAddress={exchangeAddress}
|
||||
token0={token0}
|
||||
token1={token1}
|
||||
allBalances={allBalances}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return ''
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<ButtonDropwdown
|
||||
onClick={() => {
|
||||
toggleSearch(true)
|
||||
}}
|
||||
>
|
||||
<Text fontSize={20}>Find a pool</Text>
|
||||
</ButtonDropwdown>
|
||||
<Positions>
|
||||
<AutoColumn gap="20px">
|
||||
<RowBetween>
|
||||
<Text fontWeight={500}>Your positions</Text>
|
||||
<Link>
|
||||
<Text fontWeight={500}>View on Uniswap.info</Text>
|
||||
</Link>
|
||||
</RowBetween>
|
||||
{filteredPairList}
|
||||
</AutoColumn>
|
||||
<FixedBottom>
|
||||
<Card bg="rgba(255, 255, 255, 0.6)" padding={'24px'}>
|
||||
<AutoColumn gap="30px">
|
||||
<Text fontSize="20px" fontWeight={500}>
|
||||
Earn fees with pooled market making.
|
||||
</Text>
|
||||
<Text fontSize="12px">
|
||||
<Link>Provide liquidity </Link>to earn .03% spread fees for providing market depth.
|
||||
</Text>
|
||||
<Link>
|
||||
<Row>
|
||||
Learn More <ArrowRight size="16" />
|
||||
</Row>
|
||||
</Link>
|
||||
</AutoColumn>
|
||||
</Card>
|
||||
</FixedBottom>
|
||||
</Positions>
|
||||
<SearchModal
|
||||
isOpen={showSearch}
|
||||
onDismiss={() => {
|
||||
toggleSearch(false)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default withRouter(Supply)
|
1
src/react-app-env.d.ts
vendored
Normal file
1
src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
@ -1,5 +1,6 @@
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import { darken } from 'polished'
|
||||
import { X } from 'react-feather'
|
||||
|
||||
export const Button = styled.button.attrs(({ warning, theme }) => ({
|
||||
backgroundColor: warning ? theme.salmonRed : theme.royalBlue
|
||||
@ -31,6 +32,12 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({
|
||||
}
|
||||
`
|
||||
|
||||
export const CloseIcon = styled(X)`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
export const Link = styled.a.attrs({
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer'
|
||||
|
@ -45,15 +45,20 @@ const theme = darkMode => ({
|
||||
black,
|
||||
textColor: darkMode ? white : '#010101',
|
||||
greyText: darkMode ? white : '#6C7284',
|
||||
fadedText: darkMode ? white : '#C3C5CB',
|
||||
|
||||
panelBackground: darkMode ? '#292C2F' : '#FFFFFF',
|
||||
|
||||
// for setting css on <html>
|
||||
backgroundColor: darkMode ? '#333639' : white,
|
||||
|
||||
backgroundColor: darkMode ? '#333639' : '#F7F8FA',
|
||||
modalBackground: darkMode ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.5)',
|
||||
inputBackground: darkMode ? '#202124' : white,
|
||||
placeholderGray: darkMode ? '#5F5F5F' : '#E1E1E1',
|
||||
shadowColor: darkMode ? '#000' : '#2F80ED',
|
||||
|
||||
buttonBackgroundPlain: darkMode ? '#333639' : white,
|
||||
buttonOutlinePlain: darkMode ? '#292C2F' : white,
|
||||
|
||||
// grays
|
||||
concreteGray: darkMode ? '#292C2F' : '#FAFAFA',
|
||||
mercuryGray: darkMode ? '#333333' : '#E1E1E1',
|
||||
@ -65,12 +70,15 @@ const theme = darkMode => ({
|
||||
buttonOutlineGrey: darkMode ? '#FAFAFA' : '#F2F2F2',
|
||||
tokenRowHover: darkMode ? '#404040' : '#F2F2F2',
|
||||
|
||||
outlineGrey: darkMode ? '#292C2F' : '#EDEEF2',
|
||||
|
||||
//blacks
|
||||
charcoalBlack: darkMode ? '#F2F2F2' : '#404040',
|
||||
// blues
|
||||
zumthorBlue: darkMode ? '#212529' : '#EBF4FF',
|
||||
malibuBlue: darkMode ? '#E67AEF' : '#5CA2FF',
|
||||
royalBlue: darkMode ? '#DC6BE5' : '#2F80ED',
|
||||
disabledBlue: darkMode ? '#2172E5' : '#2172E5',
|
||||
loadingBlue: darkMode ? '#e4f0ff' : '#e4f0ff',
|
||||
|
||||
// purples
|
||||
|
@ -5,7 +5,6 @@ import EXCHANGE_ABI from '../constants/abis/exchange'
|
||||
import ERC20_ABI from '../constants/abis/erc20'
|
||||
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32'
|
||||
import { FACTORY_ADDRESSES, SUPPORTED_THEMES } from '../constants'
|
||||
import { formatFixed } from '@uniswap/sdk'
|
||||
|
||||
import UncheckedJsonRpcSigner from './signer'
|
||||
|
||||
@ -256,11 +255,7 @@ export function formatTokenBalance(balance, decimal) {
|
||||
|
||||
export function formatToUsd(price) {
|
||||
const format = { decimalSeparator: '.', groupSeparator: ',', groupSize: 3 }
|
||||
const usdPrice = formatFixed(price, {
|
||||
decimalPlaces: 2,
|
||||
dropTrailingZeros: false,
|
||||
format
|
||||
})
|
||||
const usdPrice = 1
|
||||
return usdPrice
|
||||
}
|
||||
|
||||
|
@ -21,15 +21,5 @@ export function getUSDPrice(reserves) {
|
||||
ethPrices,
|
||||
forEachStablecoin(i => reserves[i].ethReserve.amount)
|
||||
)
|
||||
|
||||
// const _stablecoinWeights = [
|
||||
// getMean([medianWeights[0], meanWeights[0], weightedMeanWeights[0]])[0],
|
||||
// getMean([medianWeights[1], meanWeights[1], weightedMeanWeights[1]])[0],
|
||||
// getMean([medianWeights[2], meanWeights[2], weightedMeanWeights[2]])[0]
|
||||
// ]
|
||||
// const stablecoinWeights = forEachStablecoin((i, stablecoin) => ({
|
||||
// [stablecoin]: _stablecoinWeights[i]
|
||||
// })).reduce((accumulator, currentValue) => ({ ...accumulator, ...currentValue }), {})
|
||||
|
||||
return getMean([median, mean, weightedMean])[0]
|
||||
}
|
||||
|
31
tsconfig.json
Normal file
31
tsconfig.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"downlevelIteration": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
]
|
||||
}
|
9
webpack.config
Normal file
9
webpack.config
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
entry: [
|
||||
path.join(process.cwd(), 'app/app.tsx'), // or whatever the path of your root file is
|
||||
]
|
||||
module: {
|
||||
rules:[{ test: /\.tsx?$/, loader: 'awesome-typescript-loader' }], // other loader configuration goes in the array
|
||||
resolve: {extensions: ['.js', '.jsx', '.react.js', '.ts', '.tsx']}
|
||||
}
|
||||
}
|
642
yarn.lock
642
yarn.lock
@ -1090,23 +1090,159 @@
|
||||
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
|
||||
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
|
||||
|
||||
"@emotion/is-prop-valid@^0.8.1":
|
||||
"@emotion/cache@^10.0.27":
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.27.tgz#7895db204e2c1a991ae33d51262a3a44f6737303"
|
||||
integrity sha512-Zp8BEpbMunFsTcqAK4D7YTm3MvCp1SekflSLJH8lze2fCcSZ/yMkXHo8kb3t1/1Tdd3hAqf3Fb7z9VZ+FMiC9w==
|
||||
dependencies:
|
||||
"@emotion/sheet" "0.9.4"
|
||||
"@emotion/stylis" "0.8.5"
|
||||
"@emotion/utils" "0.11.3"
|
||||
"@emotion/weak-memoize" "0.2.5"
|
||||
|
||||
"@emotion/core@^10.0.0":
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.27.tgz#7c3f78be681ab2273f3bf11ca3e2edc4a9dd1fdc"
|
||||
integrity sha512-XbD5R36pVbohQMnKfajHv43g8EbN4NHdF6Zh9zg/C0nr0jqwOw3gYnC07Xj3yG43OYSRyrGsoQ5qPwc8ycvLZw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
"@emotion/cache" "^10.0.27"
|
||||
"@emotion/css" "^10.0.27"
|
||||
"@emotion/serialize" "^0.11.15"
|
||||
"@emotion/sheet" "0.9.4"
|
||||
"@emotion/utils" "0.11.3"
|
||||
|
||||
"@emotion/css@^10.0.27":
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
|
||||
integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
|
||||
dependencies:
|
||||
"@emotion/serialize" "^0.11.15"
|
||||
"@emotion/utils" "0.11.3"
|
||||
babel-plugin-emotion "^10.0.27"
|
||||
|
||||
"@emotion/hash@0.7.4":
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.7.4.tgz#f14932887422c9056b15a8d222a9074a7dfa2831"
|
||||
integrity sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A==
|
||||
|
||||
"@emotion/is-prop-valid@0.8.6", "@emotion/is-prop-valid@^0.8.1":
|
||||
version "0.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.6.tgz#4757646f0a58e9dec614c47c838e7147d88c263c"
|
||||
integrity sha512-mnZMho3Sq8BfzkYYRVc8ilQTnc8U02Ytp6J1AwM6taQStZ3AhsEJBX2LzhA/LJirNCwM2VtHL3VFIZ+sNJUgUQ==
|
||||
dependencies:
|
||||
"@emotion/memoize" "0.7.4"
|
||||
|
||||
"@emotion/memoize@0.7.4":
|
||||
"@emotion/memoize@0.7.4", "@emotion/memoize@^0.7.1":
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
|
||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||
|
||||
"@emotion/unitless@^0.7.0":
|
||||
"@emotion/serialize@^0.11.15":
|
||||
version "0.11.15"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.15.tgz#9a0f5873fb458d87d4f23e034413c12ed60a705a"
|
||||
integrity sha512-YE+qnrmGwyR+XB5j7Bi+0GT1JWsdcjM/d4POu+TXkcnrRs4RFCCsi3d/Ebf+wSStHqAlTT2+dfd+b9N9EO2KBg==
|
||||
dependencies:
|
||||
"@emotion/hash" "0.7.4"
|
||||
"@emotion/memoize" "0.7.4"
|
||||
"@emotion/unitless" "0.7.5"
|
||||
"@emotion/utils" "0.11.3"
|
||||
csstype "^2.5.7"
|
||||
|
||||
"@emotion/sheet@0.9.4":
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5"
|
||||
integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
|
||||
|
||||
"@emotion/styled-base@^10.0.27":
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.27.tgz#d9efa307ae4e938fcc4d0596b40b7e8bc10f7c7c"
|
||||
integrity sha512-ufHM/HhE3nr309hJG9jxuFt71r6aHn7p+bwXduFxcwPFEfBIqvmZUMtZ9YxIsY61PVwK3bp4G1XhaCzy9smVvw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
"@emotion/is-prop-valid" "0.8.6"
|
||||
"@emotion/serialize" "^0.11.15"
|
||||
"@emotion/utils" "0.11.3"
|
||||
|
||||
"@emotion/styled@^10.0.0":
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf"
|
||||
integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q==
|
||||
dependencies:
|
||||
"@emotion/styled-base" "^10.0.27"
|
||||
babel-plugin-emotion "^10.0.27"
|
||||
|
||||
"@emotion/stylis@0.8.5":
|
||||
version "0.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
|
||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
||||
|
||||
"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.0":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||
|
||||
"@emotion/utils@0.11.3":
|
||||
version "0.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924"
|
||||
integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
|
||||
|
||||
"@emotion/weak-memoize@0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||
|
||||
"@ethersproject/abi@>=5.0.0-beta.137":
|
||||
version "5.0.0-beta.145"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.145.tgz#dd80c3e7fd01f9c13d3bb4cff543e56afbbf7600"
|
||||
integrity sha512-buO1aiUe408qo8guMzwvzezUVKmQyhM21sbNaG8XrktrnxTNLvRghN1NYbtQi8Ov0ZT+P93VPz1whKu1Y2s2Xg==
|
||||
dependencies:
|
||||
"@ethersproject/address" ">=5.0.0-beta.128"
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/hash" ">=5.0.0-beta.128"
|
||||
"@ethersproject/keccak256" ">=5.0.0-beta.127"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
"@ethersproject/strings" ">=5.0.0-beta.130"
|
||||
|
||||
"@ethersproject/abstract-provider@>=5.0.0-beta.131":
|
||||
version "5.0.0-beta.138"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.0-beta.138.tgz#0e960ef55dc7af59f0a873cedd2f5e6e50941352"
|
||||
integrity sha512-bIEBUMm62TxTF+zHOUHPfMQLf3gvN5cVR1Gyv5/LVuDz3UESgFTBHBpFLHPlW2/RLHDo7K0jx16a38vs6/r2gQ==
|
||||
dependencies:
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/networks" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
"@ethersproject/transactions" ">=5.0.0-beta.128"
|
||||
"@ethersproject/web" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/abstract-signer@>=5.0.0-beta.132":
|
||||
version "5.0.0-beta.140"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.0-beta.140.tgz#46b97f812740a1a7603960da2a85029f7e504f12"
|
||||
integrity sha512-fROSxYPhtXqxK1y6/mJMpCUcjfUzCSuqNfACcPy2pwoOHMz1hsqS7m7HOEXy0HGAsQPw4fVuo4fywfA+Q62kmQ==
|
||||
dependencies:
|
||||
"@ethersproject/abstract-provider" ">=5.0.0-beta.131"
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
|
||||
"@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.0-beta.134":
|
||||
version "5.0.0-beta.134"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.0-beta.134.tgz#9c1790c87b763dc547ac12e2dbc9fa78d0799a71"
|
||||
integrity sha512-FHhUVJTUIg2pXvOOhIt8sB1cQbcwrzZKzf9CPV7JM1auli20nGoYhyMFYGK7u++GXzTMJduIkU1OwlIBupewDw==
|
||||
dependencies:
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/keccak256" ">=5.0.0-beta.127"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/rlp" ">=5.0.0-beta.126"
|
||||
bn.js "^4.4.0"
|
||||
|
||||
"@ethersproject/address@^5.0.0-beta.125":
|
||||
version "5.0.0-beta.133"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.0-beta.133.tgz#b6bf2c298da5701c61038e4cdfac2e0038b884ce"
|
||||
@ -1119,6 +1255,13 @@
|
||||
"@ethersproject/rlp" ">=5.0.0-beta.126"
|
||||
bn.js "^4.4.0"
|
||||
|
||||
"@ethersproject/base64@>=5.0.0-beta.126":
|
||||
version "5.0.0-beta.131"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.0-beta.131.tgz#eb308ed6fd614cb1a7bc0bb50fd05abedb6ca965"
|
||||
integrity sha512-pjgZZyDlGpSBkbuO87hnmVrOa92znIt5EIGBW1Mly5Nby8PU4YVwK3WoRP2vGd7hViNVLPCgfbmhh6LQhWK1sg==
|
||||
dependencies:
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/bignumber@>=5.0.0-beta.130":
|
||||
version "5.0.0-beta.135"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.0-beta.135.tgz#9d464df8967f5d314d109497e4f25ab82314c098"
|
||||
@ -1143,7 +1286,33 @@
|
||||
dependencies:
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
|
||||
"@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0-beta.130":
|
||||
"@ethersproject/contracts@^5.0.0-beta.143":
|
||||
version "5.0.0-beta.143"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.0-beta.143.tgz#5fc2ed962f3364534b80048cb742a231354a4b80"
|
||||
integrity sha512-xjWNlnbhfAkWxBW1ICi6l/O9iplf6GW8IlN57DddERCuzJnj3t2g3PinVTacH27ySqApnWF2FbFQIf0BRYp5hQ==
|
||||
dependencies:
|
||||
"@ethersproject/abi" ">=5.0.0-beta.137"
|
||||
"@ethersproject/abstract-provider" ">=5.0.0-beta.131"
|
||||
"@ethersproject/abstract-signer" ">=5.0.0-beta.132"
|
||||
"@ethersproject/address" ">=5.0.0-beta.128"
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
"@ethersproject/transactions" ">=5.0.0-beta.128"
|
||||
|
||||
"@ethersproject/hash@>=5.0.0-beta.128":
|
||||
version "5.0.0-beta.133"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.0-beta.133.tgz#bda0c74454a82359642033f27c5157963495fcdf"
|
||||
integrity sha512-tfF11QxFlJCy92rMtUZ0kImchWhlYXkN5Gj5cYfTcCdWEUKwNq1LljDnlrjV2JabO6s5enb8uiUj4RBTo2+Rgw==
|
||||
dependencies:
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/keccak256" ">=5.0.0-beta.127"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/strings" ">=5.0.0-beta.130"
|
||||
|
||||
"@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0-beta.130", "@ethersproject/keccak256@^5.0.0-beta.131":
|
||||
version "5.0.0-beta.131"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.0-beta.131.tgz#b5778723ee75208065b9b9ad30c71d480f41bb31"
|
||||
integrity sha512-KQnqMwGV0IMOjAr/UTFO8DuLrmN1uaMvcV3zh9hiXhh3rCuY+WXdeUh49w1VQ94kBKmaP0qfGb7z4SdhUWUHjw==
|
||||
@ -1156,6 +1325,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.0-beta.133.tgz#2d62d495ed413c7045054d4f99a0fb4920079b2e"
|
||||
integrity sha512-1ISf7rFKFbMHlEB37JS7Oy3FgFlvzF2Ze2uFZMJHGKp9xgDvFy1VHNMBM1KrJPK4AqCZXww0//e2keLsN3g/Cw==
|
||||
|
||||
"@ethersproject/networks@>=5.0.0-beta.129", "@ethersproject/networks@^5.0.0-beta.135":
|
||||
version "5.0.0-beta.135"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.0-beta.135.tgz#c4a19ea19e01ec84d8b6a4ae75994b7417b694fa"
|
||||
integrity sha512-9wesbAlsewNe0dU8B/lhK449GZml+08Opjf6nFpcV8BwWlYnPLs7EnOsK3GpMM0KDPZlksVYltyq+X1B7YxOcA==
|
||||
dependencies:
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/properties@>=5.0.0-beta.131":
|
||||
version "5.0.0-beta.135"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.0-beta.135.tgz#51a0a5d72ca034b5ae845d43ed409eb3576a3ca7"
|
||||
@ -1163,6 +1339,35 @@
|
||||
dependencies:
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/providers@^5.0.0-beta.153":
|
||||
version "5.0.0-beta.153"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.0-beta.153.tgz#a24923146870af50cdcfefbd4eeef77bfdda5507"
|
||||
integrity sha512-FJ/dNM5fj25m1XAEJzdC1B6GM9On3eRn/Vls6cUcSBWTOfKsTRf0uRSkBq1kNbPdx3lF0zQT4xIVmCCklA+DaQ==
|
||||
dependencies:
|
||||
"@ethersproject/abstract-provider" ">=5.0.0-beta.131"
|
||||
"@ethersproject/abstract-signer" ">=5.0.0-beta.132"
|
||||
"@ethersproject/address" ">=5.0.0-beta.128"
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/hash" ">=5.0.0-beta.128"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/networks" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
"@ethersproject/random" ">=5.0.0-beta.128"
|
||||
"@ethersproject/rlp" ">=5.0.0-beta.126"
|
||||
"@ethersproject/strings" ">=5.0.0-beta.130"
|
||||
"@ethersproject/transactions" ">=5.0.0-beta.128"
|
||||
"@ethersproject/web" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/random@>=5.0.0-beta.128":
|
||||
version "5.0.0-beta.133"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.0-beta.133.tgz#5a7a05bc8bba446995eee77008ec2d48c938071f"
|
||||
integrity sha512-walmaJK9MWy02I0SAHmv5Dg8His0Vn4x/ehqlu081z5gpm0WRo9H+3tlhaHQzAI0aK6B3mRV0bsG+PvWRh41Jg==
|
||||
dependencies:
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/rlp@>=5.0.0-beta.126":
|
||||
version "5.0.0-beta.131"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.0-beta.131.tgz#4a0c0c314e26ed7f01be6bca16308d629a8022d2"
|
||||
@ -1170,6 +1375,25 @@
|
||||
dependencies:
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/signing-key@>=5.0.0-beta.129":
|
||||
version "5.0.0-beta.135"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.0-beta.135.tgz#f739e800aad9e01b77a8ec2c353b9b66ce5738fa"
|
||||
integrity sha512-D4w5svi8F8eYs+LTuroKzOR8le6ZKtmH/mDmtuz15vz3XdOkLPGVne5mqqqLJd8APBnOEDtsAqmg7ZCrAk8Mag==
|
||||
dependencies:
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
elliptic "6.5.2"
|
||||
|
||||
"@ethersproject/strings@>=5.0.0-beta.130":
|
||||
version "5.0.0-beta.136"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.0-beta.136.tgz#053cbf4f9f96a7537cbc50300597f2d707907f51"
|
||||
integrity sha512-Hb9RvTrgGcOavHvtQZz+AuijB79BO3g1cfF2MeMfCU9ID4j3mbZv/olzDMS2pK9r4aERJpAS94AmlWzCgoY2LQ==
|
||||
dependencies:
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/strings@^5.0.0-beta.125":
|
||||
version "5.0.0-beta.135"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.0-beta.135.tgz#7754a805831383bf1838cd097d26c6135e96a745"
|
||||
@ -1179,6 +1403,41 @@
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/transactions@>=5.0.0-beta.128":
|
||||
version "5.0.0-beta.134"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.0-beta.134.tgz#2cce01ca0519beb60a3a85c078b086580fec834d"
|
||||
integrity sha512-06VxNv6UHds153Ey3WJ2YDPReNkwmIm8fyuJOXRZ6IoYh5ns5CfR4fkmHSBtw7+/KIVjmRoMQZ4Yg/tcGmzz0A==
|
||||
dependencies:
|
||||
"@ethersproject/address" ">=5.0.0-beta.128"
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/bytes" ">=5.0.0-beta.129"
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/keccak256" ">=5.0.0-beta.127"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
"@ethersproject/rlp" ">=5.0.0-beta.126"
|
||||
"@ethersproject/signing-key" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/units@^5.0.0-beta.132":
|
||||
version "5.0.0-beta.132"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.0-beta.132.tgz#54c03c821e515a09ef79a22704ad57994ee66c45"
|
||||
integrity sha512-3GZDup1uTydvqaP5wpwoRF36irp6kx/gd3buPG+aoGWLPCoPjyk76OiGoxNQKfEaynOdZ7zG2lM8WevlBDJ57g==
|
||||
dependencies:
|
||||
"@ethersproject/bignumber" ">=5.0.0-beta.130"
|
||||
"@ethersproject/constants" ">=5.0.0-beta.128"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
|
||||
"@ethersproject/web@>=5.0.0-beta.129":
|
||||
version "5.0.0-beta.135"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.0-beta.135.tgz#f2ddc5d7f3fbeda69f77c20aff271784f6c3ae5c"
|
||||
integrity sha512-5g0jGtQvLeU/O0KTlp9cqm5PpuDUZzopJaO8LxZpFW9t+5uVuBcJe8uoA8z+OsAnsRKPc6bGBc9esrWEW0hKLw==
|
||||
dependencies:
|
||||
"@ethersproject/base64" ">=5.0.0-beta.126"
|
||||
"@ethersproject/logger" ">=5.0.0-beta.129"
|
||||
"@ethersproject/properties" ">=5.0.0-beta.131"
|
||||
"@ethersproject/strings" ">=5.0.0-beta.130"
|
||||
cross-fetch "3.0.4"
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
@ -1359,6 +1618,16 @@
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^13.0.0"
|
||||
|
||||
"@jest/types@^25.1.0":
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.1.0.tgz#b26831916f0d7c381e11dbb5e103a72aed1b4395"
|
||||
integrity sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^15.0.0"
|
||||
chalk "^3.0.0"
|
||||
|
||||
"@ledgerhq/devices@^4.78.0":
|
||||
version "4.78.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-4.78.0.tgz#149b572f0616096e2bd5eb14ce14d0061c432be6"
|
||||
@ -1567,6 +1836,105 @@
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
|
||||
|
||||
"@styled-system/background@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/background/-/background-5.1.2.tgz#75c63d06b497ab372b70186c0bf608d62847a2ba"
|
||||
integrity sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/border@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/border/-/border-5.1.2.tgz#34c81c6110f638550f1dda535edb44a82ee9fe49"
|
||||
integrity sha512-mSSxyQGXELdNSOlf4RqaOKsX+w6//zooR3p6qDj5Zgc5pIdEsJm63QLz6EST/6xBJwTX0Z1w4ExItdd6Q7rlTQ==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/color@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/color/-/color-5.1.2.tgz#b8d6b4af481faabe4abca1a60f8daa4ccc2d9f43"
|
||||
integrity sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/core@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/core/-/core-5.1.2.tgz#b8b7b86455d5a0514f071c4fa8e434b987f6a772"
|
||||
integrity sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
|
||||
"@styled-system/css@^5.0.0", "@styled-system/css@^5.1.4":
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/css/-/css-5.1.4.tgz#fc51d0789a69b3831e00e6f8daf9f1d345eebdc3"
|
||||
integrity sha512-79IFT37Kxb6dlbx/0hwIGOakNHkK5oU3cMypGziShnEK8WMgK/+vuAi4MHO7uLI+FZ5U8MGYvGY9Gtk0mBzxSg==
|
||||
|
||||
"@styled-system/flexbox@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/flexbox/-/flexbox-5.1.2.tgz#077090f43f61c3852df63da24e4108087a8beecf"
|
||||
integrity sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/grid@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/grid/-/grid-5.1.2.tgz#7165049877732900b99cd00759679fbe45c6c573"
|
||||
integrity sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/layout@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/layout/-/layout-5.1.2.tgz#12d73e79887e10062f4dbbbc2067462eace42339"
|
||||
integrity sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/position@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/position/-/position-5.1.2.tgz#56961266566836f57a24d8e8e33ce0c1adb59dd3"
|
||||
integrity sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/shadow@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/shadow/-/shadow-5.1.2.tgz#beddab28d7de03cd0177a87ac4ed3b3b6d9831fd"
|
||||
integrity sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/should-forward-prop@^5.0.0":
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/should-forward-prop/-/should-forward-prop-5.1.4.tgz#1d32d7f0942692319e1e1798aad95fb75df36967"
|
||||
integrity sha512-WvKlXdbzz64QX8E66dlt/t+AsHhE5mJPyxMAufKeUKn5DSj+1w7CfLtwVH2oYje7XFcrcZOV9elzaeMWE0znTw==
|
||||
dependencies:
|
||||
"@emotion/is-prop-valid" "^0.8.1"
|
||||
"@emotion/memoize" "^0.7.1"
|
||||
styled-system "^5.1.4"
|
||||
|
||||
"@styled-system/space@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/space/-/space-5.1.2.tgz#38925d2fa29a41c0eb20e65b7c3efb6e8efce953"
|
||||
integrity sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/typography@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/typography/-/typography-5.1.2.tgz#65fb791c67d50cd2900d234583eaacdca8c134f7"
|
||||
integrity sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
|
||||
"@styled-system/variant@^5.1.4":
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@styled-system/variant/-/variant-5.1.4.tgz#7902de8e690b94e70b9b1026233feb38245398bf"
|
||||
integrity sha512-4bI2AYQfWU/ljvWlysKU8T+6gsVx5xXEI/yBvg2De7Jd6o03ZQ9tsL3OJwbzyMkIKg+UZp7YG190txEOb8K6tg==
|
||||
dependencies:
|
||||
"@styled-system/core" "^5.1.2"
|
||||
"@styled-system/css" "^5.1.4"
|
||||
|
||||
"@svgr/babel-plugin-add-jsx-attribute@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1"
|
||||
@ -1717,6 +2085,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
|
||||
"@types/eslint-visitor-keys@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||
@ -1770,6 +2143,14 @@
|
||||
"@types/istanbul-lib-coverage" "*"
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@^25.1.3":
|
||||
version "25.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.3.tgz#9b0b5addebccfb631175870be8ba62182f1bc35a"
|
||||
integrity sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg==
|
||||
dependencies:
|
||||
jest-diff "^25.1.0"
|
||||
pretty-format "^25.1.0"
|
||||
|
||||
"@types/json-schema@^7.0.3":
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
|
||||
@ -1790,6 +2171,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
|
||||
integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
|
||||
|
||||
"@types/node@^13.7.4":
|
||||
version "13.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.4.tgz#76c3cb3a12909510f52e5dc04a6298cdf9504ffd"
|
||||
integrity sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw==
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
@ -1805,6 +2191,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
||||
|
||||
"@types/react-dom@^16.9.5":
|
||||
version "16.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7"
|
||||
integrity sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "16.9.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.17.tgz#58f0cc0e9ec2425d1441dd7b623421a867aa253e"
|
||||
@ -1813,6 +2206,14 @@
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/react@^16.9.21":
|
||||
version "16.9.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.21.tgz#99e274e2ecfab6bb93920e918341daa3198b348d"
|
||||
integrity sha512-xpmenCMeBwJRct8vmIfczlgdOXWIWASoOM857kxKfHlVQvDltRh7IFRVfGws79iO2jkNPXOeWREyKoClzhBaQA==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/stack-utils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
@ -1837,6 +2238,13 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yargs@^15.0.0":
|
||||
version "15.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.3.tgz#41453a0bc7ab393e995d1f5451455638edbd2baf"
|
||||
integrity sha512-XCMQRK6kfpNBixHLyHUsGmXrpEmFFxzMrcnSXFMziHd8CoNJo8l16FkHyQq4x+xbM7E2XL83/O78OD8u+iZTdQ==
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.8.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.14.0.tgz#c74447400537d4eb7aae1e31879ab43e6c662a8a"
|
||||
@ -1880,14 +2288,22 @@
|
||||
semver "^6.3.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@uniswap/sdk@^1.0.0-beta.4":
|
||||
version "1.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-1.0.0-beta.4.tgz#d14a0ddc15e8de9173f3d97075e7ebbaa2570f1a"
|
||||
integrity sha512-a2GnlZrmg0Qxr4HFIBPCKygLFxu5f/hhIOKYVm12hG8vzsCVh72DEqZkpaOSFH3o6LDez4p2KnPvmwZkWVUnRg==
|
||||
"@uniswap/sdk@next":
|
||||
version "2.0.0-beta.15"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-2.0.0-beta.15.tgz#66487b8402fbc5b083c3744a890e64fb4024409c"
|
||||
integrity sha512-Vj4r0EBr1eHaOV7OfwqVUjkbNsB93xRfJuCpEx2+OitDLWkM+arkSnw1jcixPZ7Dt+GTPXflgXGv3rB4s+ODog==
|
||||
dependencies:
|
||||
bignumber.js "^9.0.0"
|
||||
ethers "^4.0.28"
|
||||
lodash.clonedeepwith "^4.5.0"
|
||||
"@ethersproject/address" "^5.0.0-beta.134"
|
||||
"@ethersproject/contracts" "^5.0.0-beta.143"
|
||||
"@ethersproject/keccak256" "^5.0.0-beta.131"
|
||||
"@ethersproject/networks" "^5.0.0-beta.135"
|
||||
"@ethersproject/providers" "^5.0.0-beta.153"
|
||||
big.js "^5.2.2"
|
||||
decimal.js-light "^2.5.0"
|
||||
jsbi "^3.1.1"
|
||||
tiny-invariant "^1.1.0"
|
||||
tiny-warning "^1.0.3"
|
||||
toformat "^2.0.0"
|
||||
|
||||
"@walletconnect/browser@^1.0.0-beta.42":
|
||||
version "1.0.0-beta.42"
|
||||
@ -2383,6 +2799,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
|
||||
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
|
||||
dependencies:
|
||||
"@types/color-name" "^1.1.1"
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
|
||||
@ -2942,6 +3366,22 @@ babel-plugin-dynamic-import-node@2.3.0, babel-plugin-dynamic-import-node@^2.3.0:
|
||||
dependencies:
|
||||
object.assign "^4.1.0"
|
||||
|
||||
babel-plugin-emotion@^10.0.27:
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.27.tgz#59001cf5de847c1d61f2079cd906a90a00d3184f"
|
||||
integrity sha512-SUNYcT4FqhOqvwv0z1oeYhqgheU8qrceLojuHyX17ngo7WtWqN5I9l3IGHzf21Xraj465CVzF4IvOlAF+3ed0A==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.0.0"
|
||||
"@emotion/hash" "0.7.4"
|
||||
"@emotion/memoize" "0.7.4"
|
||||
"@emotion/serialize" "^0.11.15"
|
||||
babel-plugin-macros "^2.0.0"
|
||||
babel-plugin-syntax-jsx "^6.18.0"
|
||||
convert-source-map "^1.5.0"
|
||||
escape-string-regexp "^1.0.5"
|
||||
find-root "^1.1.0"
|
||||
source-map "^0.5.7"
|
||||
|
||||
babel-plugin-istanbul@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854"
|
||||
@ -2968,6 +3408,15 @@ babel-plugin-macros@2.7.1:
|
||||
cosmiconfig "^6.0.0"
|
||||
resolve "^1.12.0"
|
||||
|
||||
babel-plugin-macros@^2.0.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138"
|
||||
integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.7.2"
|
||||
cosmiconfig "^6.0.0"
|
||||
resolve "^1.12.0"
|
||||
|
||||
babel-plugin-named-asset-import@^0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.5.tgz#d3fa1a7f1f4babd4ed0785b75e2f926df0d70d0d"
|
||||
@ -3462,11 +3911,6 @@ bignumber.js@^8.1.1:
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885"
|
||||
integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==
|
||||
|
||||
bignumber.js@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
|
||||
integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==
|
||||
|
||||
bignumber.js@~8.0.2:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.0.2.tgz#d8c4e1874359573b1ef03011a2d861214aeef137"
|
||||
@ -4038,6 +4482,14 @@ chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chardet@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
@ -4269,12 +4721,19 @@ color-convert@^1.3.0, color-convert@^1.9.0, color-convert@^1.9.1:
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
|
||||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
color-name@^1.0.0:
|
||||
color-name@^1.0.0, color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
@ -4591,6 +5050,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
cross-fetch@3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c"
|
||||
integrity sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==
|
||||
dependencies:
|
||||
node-fetch "2.6.0"
|
||||
whatwg-fetch "3.0.0"
|
||||
|
||||
cross-fetch@^2.1.0, cross-fetch@^2.1.1:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e"
|
||||
@ -4854,7 +5321,7 @@ cssstyle@^1.0.0, cssstyle@^1.1.1:
|
||||
dependencies:
|
||||
cssom "0.3.x"
|
||||
|
||||
csstype@^2.2.0:
|
||||
csstype@^2.2.0, csstype@^2.5.7:
|
||||
version "2.6.8"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431"
|
||||
integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==
|
||||
@ -4926,6 +5393,11 @@ decamelize@^1.1.1, decamelize@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
|
||||
decimal.js-light@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.0.tgz#ca7faf504c799326df94b0ab920424fdfc125348"
|
||||
integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg==
|
||||
|
||||
decode-uri-component@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
@ -5180,6 +5652,11 @@ diff-sequences@^24.9.0:
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
|
||||
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==
|
||||
|
||||
diff-sequences@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32"
|
||||
integrity sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
|
||||
@ -6275,10 +6752,10 @@ ethers@4.0.0-beta.3:
|
||||
uuid "2.0.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
|
||||
ethers@^4.0.28, ethers@~4.0.4:
|
||||
version "4.0.41"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.41.tgz#a0cff526f08c2e08c525cf82ef4483f6333b8000"
|
||||
integrity sha512-QpW2CPZajquwiA7rVDozMksOuvdUBKIruamAakt0++EKBB/VWtLB9zSRZDInLDpp9fZYgOT/0trPD38r6CzLIg==
|
||||
ethers@^4.0.44:
|
||||
version "4.0.44"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.44.tgz#f2608cbc0b4d099b7e10a01c0efc3a1037013b4e"
|
||||
integrity sha512-kCkMPkpYjBkxzqjcuYUfDY7VHDbf5EXnfRPUOazdqdf59SvXaT+w5lgauxLlk1UjxnAiNfeNS87rkIXnsTaM7Q==
|
||||
dependencies:
|
||||
aes-js "3.0.0"
|
||||
bn.js "^4.4.0"
|
||||
@ -6290,10 +6767,10 @@ ethers@^4.0.28, ethers@~4.0.4:
|
||||
uuid "2.0.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
|
||||
ethers@^4.0.44:
|
||||
version "4.0.44"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.44.tgz#f2608cbc0b4d099b7e10a01c0efc3a1037013b4e"
|
||||
integrity sha512-kCkMPkpYjBkxzqjcuYUfDY7VHDbf5EXnfRPUOazdqdf59SvXaT+w5lgauxLlk1UjxnAiNfeNS87rkIXnsTaM7Q==
|
||||
ethers@~4.0.4:
|
||||
version "4.0.41"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.41.tgz#a0cff526f08c2e08c525cf82ef4483f6333b8000"
|
||||
integrity sha512-QpW2CPZajquwiA7rVDozMksOuvdUBKIruamAakt0++EKBB/VWtLB9zSRZDInLDpp9fZYgOT/0trPD38r6CzLIg==
|
||||
dependencies:
|
||||
aes-js "3.0.0"
|
||||
bn.js "^4.4.0"
|
||||
@ -6705,6 +7182,11 @@ find-cache-dir@^3.0.0:
|
||||
make-dir "^3.0.0"
|
||||
pkg-dir "^4.1.0"
|
||||
|
||||
find-root@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
||||
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
||||
|
||||
find-up@3.0.0, find-up@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||
@ -7415,6 +7897,11 @@ has-flag@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has-flag@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
has-symbol-support-x@^1.4.1:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455"
|
||||
@ -8530,6 +9017,16 @@ jest-diff@^24.9.0:
|
||||
jest-get-type "^24.9.0"
|
||||
pretty-format "^24.9.0"
|
||||
|
||||
jest-diff@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.1.0.tgz#58b827e63edea1bc80c1de952b80cec9ac50e1ad"
|
||||
integrity sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
diff-sequences "^25.1.0"
|
||||
jest-get-type "^25.1.0"
|
||||
pretty-format "^25.1.0"
|
||||
|
||||
jest-docblock@^24.3.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2"
|
||||
@ -8585,6 +9082,11 @@ jest-get-type@^24.9.0:
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e"
|
||||
integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==
|
||||
|
||||
jest-get-type@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876"
|
||||
integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==
|
||||
|
||||
jest-haste-map@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d"
|
||||
@ -8883,6 +9385,11 @@ js-yaml@^3.13.1:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsbi@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.1.tgz#8ea18b3e08d102c6cc09acaa9a099921d775f4fa"
|
||||
integrity sha512-+HQESPaV0mRiH614z4JPVPAftcRC2p53x92lySPzUzFwJbJTMpzHz8OYUkcXPN3fOcHUe0NdVcHnCtX/1+eCrA==
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
@ -9479,11 +9986,6 @@ lodash._reinterpolate@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
|
||||
|
||||
lodash.clonedeepwith@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz#6ee30573a03a1a60d670a62ef33c10cf1afdbdd4"
|
||||
integrity sha1-buMFc6A6GmDWcKYu8zwQzxr9vdQ=
|
||||
|
||||
lodash.flatmap@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e"
|
||||
@ -10160,6 +10662,11 @@ node-fetch@2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
|
||||
integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
|
||||
|
||||
node-fetch@2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
|
||||
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
|
||||
|
||||
node-fetch@^1.0.1, node-fetch@~1.7.1:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
||||
@ -11801,6 +12308,16 @@ pretty-format@^24.9.0:
|
||||
ansi-styles "^3.2.0"
|
||||
react-is "^16.8.4"
|
||||
|
||||
pretty-format@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.1.0.tgz#ed869bdaec1356fc5ae45de045e2c8ec7b07b0c8"
|
||||
integrity sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==
|
||||
dependencies:
|
||||
"@jest/types" "^25.1.0"
|
||||
ansi-regex "^5.0.0"
|
||||
ansi-styles "^4.0.0"
|
||||
react-is "^16.12.0"
|
||||
|
||||
pretty-hrtime@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
|
||||
@ -12224,7 +12741,7 @@ react-i18next@^10.7.0:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
html-parse-stringify2 "2.0.1"
|
||||
|
||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
|
||||
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
|
||||
version "16.12.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
|
||||
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
|
||||
@ -12480,6 +12997,13 @@ realpath-native@^1.1.0:
|
||||
dependencies:
|
||||
util.promisify "^1.0.0"
|
||||
|
||||
rebass@^4.0.7:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/rebass/-/rebass-4.0.7.tgz#0a84e5558750c1f416c3baf41ec4c7fc8d64a98a"
|
||||
integrity sha512-GJot6j6Qcr7jk1QIgf9qBoud75CGRpN8pGcEo98TSp4KNSWV01ZLvGwFKGI35oEBuNs+lpEd3+pnwkQUTSFytg==
|
||||
dependencies:
|
||||
reflexbox "^4.0.6"
|
||||
|
||||
rechoir@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
||||
@ -12494,6 +13018,17 @@ recursive-readdir@2.2.2:
|
||||
dependencies:
|
||||
minimatch "3.0.4"
|
||||
|
||||
reflexbox@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/reflexbox/-/reflexbox-4.0.6.tgz#fc756d2cc1ca493baf9b96bb27dd640ad8154cf1"
|
||||
integrity sha512-UNUL4YoJEXAPjRKHuty1tuOk+LV1nDJ2KYViDcH7lYm5yU3AQ+EKNXxPU3E14bQNK/pE09b1hYl+ZKdA94tWLQ==
|
||||
dependencies:
|
||||
"@emotion/core" "^10.0.0"
|
||||
"@emotion/styled" "^10.0.0"
|
||||
"@styled-system/css" "^5.0.0"
|
||||
"@styled-system/should-forward-prop" "^5.0.0"
|
||||
styled-system "^5.0.0"
|
||||
|
||||
regenerate-unicode-properties@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
||||
@ -13873,6 +14408,25 @@ styled-components@^4.2.0:
|
||||
stylis-rule-sheet "^0.0.10"
|
||||
supports-color "^5.5.0"
|
||||
|
||||
styled-system@^5.0.0, styled-system@^5.1.4:
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/styled-system/-/styled-system-5.1.4.tgz#953003bbda659092e5630e23da2ab7e855c65879"
|
||||
integrity sha512-b1EdfZ41NDcR6vnvZauylhpFvSjsFl1yyQEUA+v3rLjcKdM//EIFY195Nh3YLwgj+hWIWsG0Tk1Kl0tq1xLw8Q==
|
||||
dependencies:
|
||||
"@styled-system/background" "^5.1.2"
|
||||
"@styled-system/border" "^5.1.2"
|
||||
"@styled-system/color" "^5.1.2"
|
||||
"@styled-system/core" "^5.1.2"
|
||||
"@styled-system/flexbox" "^5.1.2"
|
||||
"@styled-system/grid" "^5.1.2"
|
||||
"@styled-system/layout" "^5.1.2"
|
||||
"@styled-system/position" "^5.1.2"
|
||||
"@styled-system/shadow" "^5.1.2"
|
||||
"@styled-system/space" "^5.1.2"
|
||||
"@styled-system/typography" "^5.1.2"
|
||||
"@styled-system/variant" "^5.1.4"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
stylehacks@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
|
||||
@ -13911,6 +14465,13 @@ supports-color@^6.1.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
|
||||
integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
sver-compat@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8"
|
||||
@ -14159,6 +14720,11 @@ tiny-invariant@^1.0.2, tiny-invariant@^1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
|
||||
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
|
||||
|
||||
tiny-invariant@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||
|
||||
tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
@ -14248,6 +14814,11 @@ to-through@^2.0.0:
|
||||
dependencies:
|
||||
through2 "^2.0.3"
|
||||
|
||||
toformat@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8"
|
||||
integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ==
|
||||
|
||||
toggle-selection@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
|
||||
@ -14372,6 +14943,11 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@^3.7.5:
|
||||
version "3.7.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
|
||||
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
|
||||
|
||||
typewise-core@^1.2, typewise-core@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195"
|
||||
@ -15321,7 +15897,7 @@ whatwg-fetch@2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
|
||||
integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==
|
||||
|
||||
whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0:
|
||||
whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
|
||||
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
|
||||
|
Loading…
Reference in New Issue
Block a user