Commit 03913d9c authored by Moody Salem's avatar Moody Salem

UNI

parent 23c7f4ce
name: Lint
on:
push:
branches:
- master
pull_request:
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: Set up node
uses: actions/setup-node@v1
with:
node-version: 12
always-auth: true
registry-url: https://registry.npmjs.org
- name: Set output of cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Node dependency cache
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-
- name: Install dependencies
run: yarn --frozen-lockfile
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Run linters
uses: wearerequired/lint-action@v1
with:
github_token: ${{ secrets.github_token }}
eslint: true
eslint_extensions: js,jsx,ts,tsx,json
auto_fix: true
...@@ -37,9 +37,13 @@ jobs: ...@@ -37,9 +37,13 @@ jobs:
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: '12' node-version: '12'
always-auth: true
registry-url: https://registry.npmjs.org
- name: Install dependencies - name: Install dependencies
run: yarn install --frozen-lockfile run: yarn install --frozen-lockfile
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Build the IPFS bundle - name: Build the IPFS bundle
run: yarn build run: yarn build
......
...@@ -6,6 +6,7 @@ on: ...@@ -6,6 +6,7 @@ on:
pull_request: pull_request:
branches: branches:
- master - master
jobs: jobs:
integration-tests: integration-tests:
name: Integration tests name: Integration tests
...@@ -16,6 +17,9 @@ jobs: ...@@ -16,6 +17,9 @@ jobs:
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: '12' node-version: '12'
always-auth: true
registry-url: https://registry.npmjs.org
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
...@@ -27,9 +31,12 @@ jobs: ...@@ -27,9 +31,12 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile - run: yarn install --frozen-lockfile
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: yarn cypress install - run: yarn cypress install
- run: yarn build - run: yarn build
env: env:
CI: false
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847" REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
- run: yarn integration-test - run: yarn integration-test
...@@ -42,28 +49,9 @@ jobs: ...@@ -42,28 +49,9 @@ jobs:
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: '12' node-version: '12'
- name: Get yarn cache directory path always-auth: true
id: yarn-cache-dir-path registry-url: https://registry.npmjs.org
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile
- run: yarn test
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12'
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
...@@ -75,5 +63,7 @@ jobs: ...@@ -75,5 +63,7 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile - run: yarn install --frozen-lockfile
- run: yarn lint env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: yarn test
describe('Swap', () => { describe('Lists', () => {
beforeEach(() => { beforeEach(() => {
cy.visit('/swap') cy.visit('/swap')
}) })
it('list selection persists', () => { it('defaults to uniswap list', () => {
cy.get('#swap-currency-output .open-currency-select-button').click() cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('#list-introduction-choose-a-list').click() cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
cy.get('#list-row-tokens-uniswap-eth .select-button').click()
cy.reload()
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('#list-introduction-choose-a-list').should('not.exist')
}) })
it('change list', () => { it('change list', () => {
cy.get('#swap-currency-output .open-currency-select-button').click() cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('#list-introduction-choose-a-list').click()
cy.get('#list-row-tokens-uniswap-eth .select-button').click()
cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
cy.get('#currency-search-change-list-button').click() cy.get('#currency-search-change-list-button').click()
cy.get('#list-row-tokens-1inch-eth .select-button').click() cy.get('#list-row-tokens-1inch-eth .select-button').click()
cy.get('#currency-search-selected-list-name').should('contain', '1inch') cy.get('#currency-search-selected-list-name').should('contain', '1inch')
cy.get('#currency-search-change-list-button').click()
cy.get('#list-row-tokens-uniswap-eth .select-button').click()
cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
}) })
}) })
...@@ -34,8 +34,6 @@ describe('Swap', () => { ...@@ -34,8 +34,6 @@ describe('Swap', () => {
it('can swap ETH for DAI', () => { it('can swap ETH for DAI', () => {
cy.get('#swap-currency-output .open-currency-select-button').click() cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('#list-introduction-choose-a-list').click()
cy.get('#list-row-tokens-uniswap-eth .select-button').click()
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').should('be.visible') cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').should('be.visible')
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click({ force: true }) cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click({ force: true })
cy.get('#swap-currency-input .token-amount-input').should('be.visible') cy.get('#swap-currency-input .token-amount-input').should('be.visible')
......
...@@ -3,15 +3,8 @@ ...@@ -3,15 +3,8 @@
"strict": true, "strict": true,
"baseUrl": "../node_modules", "baseUrl": "../node_modules",
"target": "es5", "target": "es5",
"lib": [ "lib": ["es5", "dom"],
"es5", "types": ["cypress"]
"dom"
],
"types": [
"cypress"
]
}, },
"include": [ "include": ["**/*.ts"]
"**/*.ts" }
]
}
\ No newline at end of file
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
"@reduxjs/toolkit": "^1.3.5", "@reduxjs/toolkit": "^1.3.5",
"@types/jest": "^25.2.1", "@types/jest": "^25.2.1",
"@types/lodash.flatmap": "^4.5.6", "@types/lodash.flatmap": "^4.5.6",
"@types/luxon": "^1.24.4",
"@types/multicodec": "^1.0.0", "@types/multicodec": "^1.0.0",
"@types/node": "^13.13.5", "@types/node": "^13.13.5",
"@types/qs": "^6.9.2", "@types/qs": "^6.9.2",
...@@ -23,10 +24,13 @@ ...@@ -23,10 +24,13 @@
"@types/rebass": "^4.0.5", "@types/rebass": "^4.0.5",
"@types/styled-components": "^5.1.0", "@types/styled-components": "^5.1.0",
"@types/testing-library__cypress": "^5.0.5", "@types/testing-library__cypress": "^5.0.5",
"@types/wcag-contrast": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^2.31.0", "@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.31.0", "@typescript-eslint/parser": "^2.31.0",
"@uniswap/default-token-list": "^1.3.1", "@uniswap/governance": "^1.0.2",
"@uniswap/sdk": "3.0.3-beta.1", "@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/sdk": "3.0.3",
"@uniswap/token-lists": "^1.0.0-beta.16", "@uniswap/token-lists": "^1.0.0-beta.16",
"@uniswap/v2-core": "1.0.0", "@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0", "@uniswap/v2-periphery": "^1.1.0-beta.0",
...@@ -53,17 +57,21 @@ ...@@ -53,17 +57,21 @@
"inter-ui": "^3.13.1", "inter-ui": "^3.13.1",
"jazzicon": "^1.5.0", "jazzicon": "^1.5.0",
"lodash.flatmap": "^4.5.0", "lodash.flatmap": "^4.5.0",
"luxon": "^1.25.0",
"multicodec": "^2.0.0", "multicodec": "^2.0.0",
"multihashes": "^3.0.1", "multihashes": "^3.0.1",
"node-vibrant": "^3.1.5",
"polished": "^3.3.2", "polished": "^3.3.2",
"prettier": "^1.17.0", "prettier": "^1.17.0",
"qs": "^6.9.4", "qs": "^6.9.4",
"react": "^16.13.1", "react": "^16.13.1",
"react-confetti": "^6.0.0",
"react-device-detect": "^1.6.2", "react-device-detect": "^1.6.2",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-feather": "^2.0.8", "react-feather": "^2.0.8",
"react-ga": "^2.5.7", "react-ga": "^2.5.7",
"react-i18next": "^10.7.0", "react-i18next": "^10.7.0",
"react-markdown": "^4.3.1",
"react-popper": "^2.2.3", "react-popper": "^2.2.3",
"react-redux": "^7.2.0", "react-redux": "^7.2.0",
"react-router-dom": "^5.0.0", "react-router-dom": "^5.0.0",
...@@ -77,7 +85,9 @@ ...@@ -77,7 +85,9 @@
"serve": "^11.3.0", "serve": "^11.3.0",
"start-server-and-test": "^1.11.0", "start-server-and-test": "^1.11.0",
"styled-components": "^4.2.0", "styled-components": "^4.2.0",
"typescript": "^3.8.3" "typescript": "^3.8.3",
"use-count-up": "^2.2.5",
"wcag-contrast": "^3.0.0"
}, },
"resolutions": { "resolutions": {
"@walletconnect/web3-provider": "1.1.1-alpha.0" "@walletconnect/web3-provider": "1.1.1-alpha.0"
...@@ -87,11 +97,13 @@ ...@@ -87,11 +97,13 @@
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'" "integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app",
"ignorePatterns": [
"node_modules"
]
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
......
public/favicon.png

6.91 KB | W: | H:

public/favicon.png

2.77 KB | W: | H:

public/favicon.png
public/favicon.png
public/favicon.png
public/favicon.png
  • 2-up
  • Swipe
  • Onion skin
{ {
"noWallet": "No se encontró billetera de Ethereum", "noWallet": "No se encontró billetera de Ethereum",
"wrongNetwork": "Te encontrás en la red equivocada", "wrongNetwork": "Te encontrás en la red equivocada",
"switchNetwork": "Por favor cambia a {{ correctNetwork }}", "switchNetwork": "Por favor cambia a {{ correctNetwork }}",
"installWeb3MobileBrowser": "Por favor ingresá desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.", "installWeb3MobileBrowser": "Por favor ingresá desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.",
"installMetamask": "Por favor visítanos nuevamente luego de instalar Metamask en Chrome o Brave.", "installMetamask": "Por favor visítanos nuevamente luego de instalar Metamask en Chrome o Brave.",
"disconnected": "Desconectado", "disconnected": "Desconectado",
"swap": "Intercambiar", "swap": "Intercambiar",
"send": "Enviar", "send": "Enviar",
"pool": "Pool", "pool": "Pool",
"betaWarning": "Este proyecto se encuentra en beta. Usalo bajo tu propio riesgo.", "betaWarning": "Este proyecto se encuentra en beta. Usalo bajo tu propio riesgo.",
"input": "Entrada", "input": "Entrada",
"output": "Salida", "output": "Salida",
"estimated": "estimado", "estimated": "estimado",
"balance": "Saldo: {{ balanceInput }}", "balance": "Saldo: {{ balanceInput }}",
"unlock": "Desbloquear", "unlock": "Desbloquear",
"pending": "Pendiente", "pending": "Pendiente",
"selectToken": "Seleccioná un token", "selectToken": "Seleccioná un token",
"searchOrPaste": "Buscar Token o Pegar Dirección", "searchOrPaste": "Buscar Token o Pegar Dirección",
"noExchange": "No se encontró la divisa", "noExchange": "No se encontró la divisa",
"exchangeRate": "Tasa de Cambio", "exchangeRate": "Tasa de Cambio",
"enterValueCont": "Ingresá un valor en {{ missingCurrencyValue }} para continuar.", "enterValueCont": "Ingresá un valor en {{ missingCurrencyValue }} para continuar.",
"selectTokenCont": "Seleccioná un token para continuar.", "selectTokenCont": "Seleccioná un token para continuar.",
"noLiquidity": "Sin liquidez.", "noLiquidity": "Sin liquidez.",
"unlockTokenCont": "Por favor desbloqueá un token para continuar.", "unlockTokenCont": "Por favor desbloqueá un token para continuar.",
"transactionDetails": "Detalles de la transacción", "transactionDetails": "Detalles de la transacción",
"hideDetails": "Ocultar detalles", "hideDetails": "Ocultar detalles",
"youAreSelling": "Estás vendiendo", "youAreSelling": "Estás vendiendo",
"orTransFail": "o la transacción fallará.", "orTransFail": "o la transacción fallará.",
"youWillReceive": "Vas a recibir al menos", "youWillReceive": "Vas a recibir al menos",
"youAreBuying": "Estás comprando", "youAreBuying": "Estás comprando",
"itWillCost": "Costará a lo sumo", "itWillCost": "Costará a lo sumo",
"insufficientBalance": "Saldo insuficiente", "insufficientBalance": "Saldo insuficiente",
"inputNotValid": "No es un valor de entrada válido", "inputNotValid": "No es un valor de entrada válido",
"differentToken": "Debe ser un token distinto.", "differentToken": "Debe ser un token distinto.",
"noRecipient": "Ingresá una dirección de billetera para enviar.", "noRecipient": "Ingresá una dirección de billetera para enviar.",
"invalidRecipient": "Por favor ingrese una billetera de destino válida.", "invalidRecipient": "Por favor ingrese una billetera de destino válida.",
"recipientAddress": "Dirección del recipiente", "recipientAddress": "Dirección del recipiente",
"youAreSending": "Estás enviando", "youAreSending": "Estás enviando",
"willReceive": "recibirá al menos", "willReceive": "recibirá al menos",
"to": "a", "to": "a",
"addLiquidity": "Agregar liquidez", "addLiquidity": "Agregar liquidez",
"deposit": "Depositar", "deposit": "Depositar",
"currentPoolSize": "Tamaño del Pool Actual", "currentPoolSize": "Tamaño del Pool Actual",
"yourPoolShare": "Tu parte del Pool", "yourPoolShare": "Tu parte del Pool",
"noZero": "El monto no puede ser cero.", "noZero": "El monto no puede ser cero.",
"mustBeETH": "Una de las entradas debe ser ETH.", "mustBeETH": "Una de las entradas debe ser ETH.",
"enterCurrencyOrLabelCont": "Ingresá un valor de {{ inputCurrency }} o de {{ label }} para continuar.", "enterCurrencyOrLabelCont": "Ingresá un valor de {{ inputCurrency }} o de {{ label }} para continuar.",
"youAreAdding": "Estás agregando entre", "youAreAdding": "Estás agregando entre",
"and": "y", "and": "y",
"intoPool": "en el pool de liquidez.", "intoPool": "en el pool de liquidez.",
"outPool": "en el pool de liquidez.", "outPool": "en el pool de liquidez.",
"youWillMint": "Vas a acuñar", "youWillMint": "Vas a acuñar",
"liquidityTokens": "tokens de liquidez.", "liquidityTokens": "tokens de liquidez.",
"totalSupplyIs": "El actual suministro total de tokens de liquidez es", "totalSupplyIs": "El actual suministro total de tokens de liquidez es",
"youAreSettingExRate": "Está configurando el tipo de cambio inicial a", "youAreSettingExRate": "Está configurando el tipo de cambio inicial a",
"totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.", "totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.",
"tokenWorth": "Al tipo de cambio actual, cada token del pool vale", "tokenWorth": "Al tipo de cambio actual, cada token del pool vale",
"firstLiquidity": "Sos la primer persona en agregar liquidez!", "firstLiquidity": "Sos la primer persona en agregar liquidez!",
"initialExchangeRate": "El tipo de cambio inicial se establecerá en función de tus depósitos. Por favor, asegúrate de que tus depósitos en ETH y {{ label }} tengan el mismo valor fíat.", "initialExchangeRate": "El tipo de cambio inicial se establecerá en función de tus depósitos. Por favor, asegúrate de que tus depósitos en ETH y {{ label }} tengan el mismo valor fíat.",
"removeLiquidity": "Remover Liquidez", "removeLiquidity": "Remover Liquidez",
"poolTokens": "Pool de Tokens", "poolTokens": "Pool de Tokens",
"enterLabelCont": "Ingresá un valor de {{ label }} para continuar.", "enterLabelCont": "Ingresá un valor de {{ label }} para continuar.",
"youAreRemoving": "Estás quitando entre", "youAreRemoving": "Estás quitando entre",
"youWillRemove": "Vas a remover", "youWillRemove": "Vas a remover",
"createExchange": "Crear divisa", "createExchange": "Crear divisa",
"invalidTokenAddress": "No es una dirección de token válida", "invalidTokenAddress": "No es una dirección de token válida",
"exchangeExists": "La divisa {{ label }} ya existe!", "exchangeExists": "La divisa {{ label }} ya existe!",
"invalidSymbol": "Símbolo inválido", "invalidSymbol": "Símbolo inválido",
"invalidDecimals": "Decimales inválidos", "invalidDecimals": "Decimales inválidos",
"tokenAddress": "Dirección de Token", "tokenAddress": "Dirección de Token",
"label": "Etiqueta", "label": "Etiqueta",
"decimals": "Decimales", "decimals": "Decimales",
"enterTokenCont": "Ingresá una dirección de token para continuar" "enterTokenCont": "Ingresá una dirección de token para continuar"
} }
{ {
"noWallet": "No se ha encontrado billetera de Ethereum", "noWallet": "No se ha encontrado billetera de Ethereum",
"wrongNetwork": "Se encuentra en la red equivocada", "wrongNetwork": "Se encuentra en la red equivocada",
"switchNetwork": "Por favor cambie a {{ correctNetwork }}", "switchNetwork": "Por favor cambie a {{ correctNetwork }}",
"installWeb3MobileBrowser": "Por favor ingrese desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.", "installWeb3MobileBrowser": "Por favor ingrese desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.",
"installMetamask": "Por favor visítenos nuevamente luego de instalar Metamask en Chrome o Brave.", "installMetamask": "Por favor visítenos nuevamente luego de instalar Metamask en Chrome o Brave.",
"disconnected": "Desconectado", "disconnected": "Desconectado",
"swap": "Intercambiar", "swap": "Intercambiar",
"send": "Enviar", "send": "Enviar",
"pool": "Pool", "pool": "Pool",
"betaWarning": "Este proyecto se encuentra en beta. Úselo bajo tu propio riesgo.", "betaWarning": "Este proyecto se encuentra en beta. Úselo bajo tu propio riesgo.",
"input": "Entrada", "input": "Entrada",
"output": "Salida", "output": "Salida",
"estimated": "estimado", "estimated": "estimado",
"balance": "Saldo: {{ balanceInput }}", "balance": "Saldo: {{ balanceInput }}",
"unlock": "Desbloquear", "unlock": "Desbloquear",
"pending": "Pendiente", "pending": "Pendiente",
"selectToken": "Seleccione un token", "selectToken": "Seleccione un token",
"searchOrPaste": "Buscar Token o Pegar Dirección", "searchOrPaste": "Buscar Token o Pegar Dirección",
"noExchange": "No se ha encontrado la divisa", "noExchange": "No se ha encontrado la divisa",
"exchangeRate": "Tasa de Cambio", "exchangeRate": "Tasa de Cambio",
"enterValueCont": "Ingrese un valor en {{ missingCurrencyValue }} para continuar.", "enterValueCont": "Ingrese un valor en {{ missingCurrencyValue }} para continuar.",
"selectTokenCont": "Seleccione un token para continuar.", "selectTokenCont": "Seleccione un token para continuar.",
"noLiquidity": "Sin liquidez.", "noLiquidity": "Sin liquidez.",
"unlockTokenCont": "Por favor desbloquea un token para continuar.", "unlockTokenCont": "Por favor desbloquea un token para continuar.",
"transactionDetails": "Detalles de la transacción", "transactionDetails": "Detalles de la transacción",
"hideDetails": "Ocultar detalles", "hideDetails": "Ocultar detalles",
"youAreSelling": "Está vendiendo", "youAreSelling": "Está vendiendo",
"orTransFail": "o la transacción fallará.", "orTransFail": "o la transacción fallará.",
"youWillReceive": "Va a recibir al menos", "youWillReceive": "Va a recibir al menos",
"youAreBuying": "Está comprando", "youAreBuying": "Está comprando",
"itWillCost": "Costará a lo sumo", "itWillCost": "Costará a lo sumo",
"insufficientBalance": "Saldo insuficiente", "insufficientBalance": "Saldo insuficiente",
"inputNotValid": "No es un valor de entrada válido", "inputNotValid": "No es un valor de entrada válido",
"differentToken": "Debe ser un token distinto.", "differentToken": "Debe ser un token distinto.",
"noRecipient": "Ingrese una dirección de billetera para enviar.", "noRecipient": "Ingrese una dirección de billetera para enviar.",
"invalidRecipient": "Por favor ingrese una billetera de destino válida.", "invalidRecipient": "Por favor ingrese una billetera de destino válida.",
"recipientAddress": "Dirección del recipiente", "recipientAddress": "Dirección del recipiente",
"youAreSending": "Está enviando", "youAreSending": "Está enviando",
"willReceive": "recibirá al menos", "willReceive": "recibirá al menos",
"to": "a", "to": "a",
"addLiquidity": "Agregar liquidez", "addLiquidity": "Agregar liquidez",
"deposit": "Depositar", "deposit": "Depositar",
"currentPoolSize": "Tamaño del Pool Actual", "currentPoolSize": "Tamaño del Pool Actual",
"yourPoolShare": "Su parte del Pool", "yourPoolShare": "Su parte del Pool",
"noZero": "El monto no puede ser cero.", "noZero": "El monto no puede ser cero.",
"mustBeETH": "Una de las entradas debe ser ETH.", "mustBeETH": "Una de las entradas debe ser ETH.",
"enterCurrencyOrLabelCont": "Ingrese un valor de {{ inputCurrency }} o de {{ label }} para continuar.", "enterCurrencyOrLabelCont": "Ingrese un valor de {{ inputCurrency }} o de {{ label }} para continuar.",
"youAreAdding": "Está agregando entre", "youAreAdding": "Está agregando entre",
"and": "y", "and": "y",
"intoPool": "en el pool de liquidez.", "intoPool": "en el pool de liquidez.",
"outPool": "en el pool de liquidez.", "outPool": "en el pool de liquidez.",
"youWillMint": "Va a acuñar", "youWillMint": "Va a acuñar",
"liquidityTokens": "tokens de liquidez.", "liquidityTokens": "tokens de liquidez.",
"totalSupplyIs": "El actual suministro total de tokens de liquidez es", "totalSupplyIs": "El actual suministro total de tokens de liquidez es",
"youAreSettingExRate": "Está configurando el tipo de cambio inicial a", "youAreSettingExRate": "Está configurando el tipo de cambio inicial a",
"totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.", "totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.",
"tokenWorth": "Al tipo de cambio actual, cada token del pool vale", "tokenWorth": "Al tipo de cambio actual, cada token del pool vale",
"firstLiquidity": "Es la primer persona en agregar liquidez!", "firstLiquidity": "Es la primer persona en agregar liquidez!",
"initialExchangeRate": "El tipo de cambio inicial se establecerá en función de sus depósitos. Por favor, asegúrese de que sus depósitos en ETH y {{ label }} tengan el mismo valor fíat.", "initialExchangeRate": "El tipo de cambio inicial se establecerá en función de sus depósitos. Por favor, asegúrese de que sus depósitos en ETH y {{ label }} tengan el mismo valor fíat.",
"removeLiquidity": "Remover Liquidez", "removeLiquidity": "Remover Liquidez",
"poolTokens": "Pool de Tokens", "poolTokens": "Pool de Tokens",
"enterLabelCont": "Ingresa un valor de {{ label }} para continuar.", "enterLabelCont": "Ingresa un valor de {{ label }} para continuar.",
"youAreRemoving": "Está quitando entre", "youAreRemoving": "Está quitando entre",
"youWillRemove": "Va a remover", "youWillRemove": "Va a remover",
"createExchange": "Crear tipo de cambio", "createExchange": "Crear tipo de cambio",
"invalidTokenAddress": "No es una dirección de token válida", "invalidTokenAddress": "No es una dirección de token válida",
"exchangeExists": "El tipo de cambio {{ label }} ya existe!", "exchangeExists": "El tipo de cambio {{ label }} ya existe!",
"invalidSymbol": "Símbolo inválido", "invalidSymbol": "Símbolo inválido",
"invalidDecimals": "Decimales inválidos", "invalidDecimals": "Decimales inválidos",
"tokenAddress": "Dirección de Token", "tokenAddress": "Dirección de Token",
"label": "Etiqueta", "label": "Etiqueta",
"decimals": "Decimales", "decimals": "Decimales",
"enterTokenCont": "Ingrese una dirección de token para continuar" "enterTokenCont": "Ingrese una dirección de token para continuar"
} }
{ {
"noWallet": "Không tìm thấy ví tiền Ethereum", "noWallet": "Không tìm thấy ví tiền Ethereum",
"wrongNetwork": "Kết nối mạng không đúng", "wrongNetwork": "Kết nối mạng không đúng",
"switchNetwork": "Vui lòng chuyển sang {{ correctNetwork }}", "switchNetwork": "Vui lòng chuyển sang {{ correctNetwork }}",
"installWeb3MobileBrowser": "Vui lòng truy cập từ trình duyệt di động hỗ trợ web3 như là Ví Trust hoặc Ví Coinbase", "installWeb3MobileBrowser": "Vui lòng truy cập từ trình duyệt di động hỗ trợ web3 như là Ví Trust hoặc Ví Coinbase",
"installMetamask": "Vui lòng truy cập sau khi cài đặt Metamask trên Chrome hoặc Brave.", "installMetamask": "Vui lòng truy cập sau khi cài đặt Metamask trên Chrome hoặc Brave.",
"disconnected": "Ngắt kết nối rồi", "disconnected": "Ngắt kết nối rồi",
"swap": "Hoán đổi", "swap": "Hoán đổi",
"swapAnyway": "Tiếp tục hoán đổi?", "swapAnyway": "Tiếp tục hoán đổi?",
"send": "Gửi", "send": "Gửi",
"sendAnyway": "Tiếp tục gửi?", "sendAnyway": "Tiếp tục gửi?",
"pool": "Chung vốn", "pool": "Chung vốn",
"betaWarning": "Dự án này đang trong giai đoạn thử nghiệm. Sử dụng có rủi ro của riêng bạn", "betaWarning": "Dự án này đang trong giai đoạn thử nghiệm. Sử dụng có rủi ro của riêng bạn",
"input": "Đầu vào", "input": "Đầu vào",
"output": "Đầu ra", "output": "Đầu ra",
"estimated": "ước lượng", "estimated": "ước lượng",
"balance": "Số dư: {{ balanceInput }}", "balance": "Số dư: {{ balanceInput }}",
"unlock": "Mở khóa", "unlock": "Mở khóa",
"pending": "Đang chờ xử lý", "pending": "Đang chờ xử lý",
"selectToken": "Chọn một đồng tiền ảo", "selectToken": "Chọn một đồng tiền ảo",
"searchOrPaste": "Tìm kiếm tên, biểu tượng, hoặc địa chỉ của đồng tiền ảo", "searchOrPaste": "Tìm kiếm tên, biểu tượng, hoặc địa chỉ của đồng tiền ảo",
"searchOrPasteMobile": "Tên, Biểu tượng, hoặc Địa chỉ", "searchOrPasteMobile": "Tên, Biểu tượng, hoặc Địa chỉ",
"noExchange": "Không tìm thấy giao dịch", "noExchange": "Không tìm thấy giao dịch",
"exchangeRate": "Tỷ giá", "exchangeRate": "Tỷ giá",
"unknownError": "Rất tiếc! Xảy ra lỗi không xác định. Vui lòng làm mới trang, hoặc truy cập từ trình duyệt hay thiết bị khác.", "unknownError": "Rất tiếc! Xảy ra lỗi không xác định. Vui lòng làm mới trang, hoặc truy cập từ trình duyệt hay thiết bị khác.",
"enterValueCont": "Nhập một giá trị {{ missingCurrencyValue }} để tiếp tục.", "enterValueCont": "Nhập một giá trị {{ missingCurrencyValue }} để tiếp tục.",
"selectTokenCont": "Chọn một đồng tiền ảo để tiếp tục.", "selectTokenCont": "Chọn một đồng tiền ảo để tiếp tục.",
"noLiquidity": "Không có tính thanh khoản.", "noLiquidity": "Không có tính thanh khoản.",
"insufficientLiquidity": "Không đủ tính thanh khoản.", "insufficientLiquidity": "Không đủ tính thanh khoản.",
"unlockTokenCont": "Vui lòng mở khoá đồng tiền ảo để tiếp tục", "unlockTokenCont": "Vui lòng mở khoá đồng tiền ảo để tiếp tục",
"transactionDetails": "Chi tiết nâng cao", "transactionDetails": "Chi tiết nâng cao",
"hideDetails": "Ẩn chi tiết", "hideDetails": "Ẩn chi tiết",
"slippageWarning": "Cảnh báo trượt giá", "slippageWarning": "Cảnh báo trượt giá",
"highSlippageWarning": "Cảnh báo trượt giá cao", "highSlippageWarning": "Cảnh báo trượt giá cao",
"youAreSelling": "Bạn đang bán", "youAreSelling": "Bạn đang bán",
"orTransFail": "hoặc giao dịch sẽ thất bại.", "orTransFail": "hoặc giao dịch sẽ thất bại.",
"youWillReceive": "Bạn sẽ nhận dược ít nhất là", "youWillReceive": "Bạn sẽ nhận dược ít nhất là",
"youAreBuying": "Bạn đang mua", "youAreBuying": "Bạn đang mua",
"itWillCost": "Nó sẽ có giá cao nhất", "itWillCost": "Nó sẽ có giá cao nhất",
"forAtMost": "nhiều nhất", "forAtMost": "nhiều nhất",
"insufficientBalance": "Số dư không đủ", "insufficientBalance": "Số dư không đủ",
"inputNotValid": "Giá trị nhập vào không hợp lệ", "inputNotValid": "Giá trị nhập vào không hợp lệ",
"differentToken": "Đồng tiền ảo phải khác nhau.", "differentToken": "Đồng tiền ảo phải khác nhau.",
"noRecipient": "Nhập địa chỉ ví để gửi đến.", "noRecipient": "Nhập địa chỉ ví để gửi đến.",
"invalidRecipient": "Vui lòng nhập một người nhận địa chỉ ví hợp lệ.", "invalidRecipient": "Vui lòng nhập một người nhận địa chỉ ví hợp lệ.",
"recipientAddress": "Địa chỉ người nhận", "recipientAddress": "Địa chỉ người nhận",
"youAreSending": "Bạn đang gửi", "youAreSending": "Bạn đang gửi",
"willReceive": "sẽ nhận dược ít nhất là", "willReceive": "sẽ nhận dược ít nhất là",
"to": "đến", "to": "đến",
"addLiquidity": "Thêm tiền thanh khoản", "addLiquidity": "Thêm tiền thanh khoản",
"deposit": "Gửi tiền", "deposit": "Gửi tiền",
"currentPoolSize": "Quy mô hiện tại của quỹ", "currentPoolSize": "Quy mô hiện tại của quỹ",
"yourPoolShare": "Phần hùn của bạn trong quỹ", "yourPoolShare": "Phần hùn của bạn trong quỹ",
"noZero": "Số tiền không thể bằng không.", "noZero": "Số tiền không thể bằng không.",
"mustBeETH": "Một trong những đầu vào phải là ETH.", "mustBeETH": "Một trong những đầu vào phải là ETH.",
"enterCurrencyOrLabelCont": "Nhập giá trị {{ inputCurrency }} hoặc {{ label }} để tiếp tục.", "enterCurrencyOrLabelCont": "Nhập giá trị {{ inputCurrency }} hoặc {{ label }} để tiếp tục.",
"youAreAdding": "Bạn đang thêm", "youAreAdding": "Bạn đang thêm",
"and": "và", "and": "và",
"intoPool": "vào nhóm thanh khoản.", "intoPool": "vào nhóm thanh khoản.",
"outPool": "từ nhóm thanh khoản.", "outPool": "từ nhóm thanh khoản.",
"youWillMint": "Bạn sẽ đúc tiền", "youWillMint": "Bạn sẽ đúc tiền",
"liquidityTokens": "đồng thanh khoản.", "liquidityTokens": "đồng thanh khoản.",
"totalSupplyIs": "Tổng cung hiện tại của đồng thanh khoản là", "totalSupplyIs": "Tổng cung hiện tại của đồng thanh khoản là",
"youAreSettingExRate": "Bạn đang đặt tỷ giá hối đoái ban đầu thành", "youAreSettingExRate": "Bạn đang đặt tỷ giá hối đoái ban đầu thành",
"totalSupplyIs0": "Tổng cung hiện tại của đồng thanh khoản là 0.", "totalSupplyIs0": "Tổng cung hiện tại của đồng thanh khoản là 0.",
"tokenWorth": "Tại tỷ giá hối đoái hiện tại, giá trị đồng token của quỹ là", "tokenWorth": "Tại tỷ giá hối đoái hiện tại, giá trị đồng token của quỹ là",
"firstLiquidity": "Bạn là người đầu tiên thêm thanh khoản!", "firstLiquidity": "Bạn là người đầu tiên thêm thanh khoản!",
"initialExchangeRate": "Tỷ giá hối đoái ban đầu sẽ được thiết lập dựa trên tiền gửi của bạn. Vui lòng đảm bảo rằng tiền gửi ETH và {{ label }} của bạn có cùng giá trị tiền định danh.", "initialExchangeRate": "Tỷ giá hối đoái ban đầu sẽ được thiết lập dựa trên tiền gửi của bạn. Vui lòng đảm bảo rằng tiền gửi ETH và {{ label }} của bạn có cùng giá trị tiền định danh.",
"removeLiquidity": "Loại bỏ thanh khoản", "removeLiquidity": "Loại bỏ thanh khoản",
"poolTokens": "Đồng tiền ảo của quỹ", "poolTokens": "Đồng tiền ảo của quỹ",
"enterLabelCont": "Nhập giá trị {{ label }} để tiếp tục", "enterLabelCont": "Nhập giá trị {{ label }} để tiếp tục",
"youAreRemoving": "Bạn đang loại bỏ giữa", "youAreRemoving": "Bạn đang loại bỏ giữa",
"youWillRemove": "Bạn sẽ loại bỏ", "youWillRemove": "Bạn sẽ loại bỏ",
"createExchange": "Tạo giao dịch", "createExchange": "Tạo giao dịch",
"invalidTokenAddress": "Địa chỉ đồng tiền điện tử không hợp lệ", "invalidTokenAddress": "Địa chỉ đồng tiền điện tử không hợp lệ",
"exchangeExists": "{{ label }} Giao dịch đã tồn tại!", "exchangeExists": "{{ label }} Giao dịch đã tồn tại!",
"invalidSymbol": "Biểu tượng không hợp lệ", "invalidSymbol": "Biểu tượng không hợp lệ",
"invalidDecimals": "Số thập phân không hợp lệ", "invalidDecimals": "Số thập phân không hợp lệ",
"tokenAddress": "Địa chỉ đồng tiền điện tử", "tokenAddress": "Địa chỉ đồng tiền điện tử",
"label": "Nhãn", "label": "Nhãn",
"name": "Tên", "name": "Tên",
"symbol": "Biểu tượng", "symbol": "Biểu tượng",
"decimals": "Số thập phân", "decimals": "Số thập phân",
"enterTokenCont": "Nhập địa chỉ đồng tiền ảo để tiếp tục", "enterTokenCont": "Nhập địa chỉ đồng tiền ảo để tiếp tục",
"priceChange": "Trượt giá dự kiến", "priceChange": "Trượt giá dự kiến",
"forAtLeast": "cho ít nhất " "forAtLeast": "cho ít nhất "
} }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -16,7 +16,7 @@ const Base = styled(RebassButton)<{ ...@@ -16,7 +16,7 @@ const Base = styled(RebassButton)<{
width: ${({ width }) => (width ? width : '100%')}; width: ${({ width }) => (width ? width : '100%')};
font-weight: 500; font-weight: 500;
text-align: center; text-align: center;
border-radius: 20px; border-radius: 12px;
border-radius: ${({ borderRadius }) => borderRadius && borderRadius}; border-radius: ${({ borderRadius }) => borderRadius && borderRadius};
outline: none; outline: none;
border: 1px solid transparent; border: 1px solid transparent;
...@@ -110,28 +110,31 @@ export const ButtonGray = styled(Base)` ...@@ -110,28 +110,31 @@ export const ButtonGray = styled(Base)`
` `
export const ButtonSecondary = styled(Base)` export const ButtonSecondary = styled(Base)`
background-color: ${({ theme }) => theme.primary5}; border: 1px solid ${({ theme }) => theme.primary4};
color: ${({ theme }) => theme.primaryText1}; color: ${({ theme }) => theme.primary1};
background-color: transparent;
font-size: 16px; font-size: 16px;
border-radius: 8px; border-radius: 12px;
padding: ${({ padding }) => (padding ? padding : '10px')}; padding: ${({ padding }) => (padding ? padding : '10px')};
&:focus { &:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4}; box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4};
background-color: ${({ theme }) => theme.primary4}; border: 1px solid ${({ theme }) => theme.primary3};
} }
&:hover { &:hover {
background-color: ${({ theme }) => theme.primary4}; border: 1px solid ${({ theme }) => theme.primary3};
} }
&:active { &:active {
box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4}; box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4};
background-color: ${({ theme }) => theme.primary4}; border: 1px solid ${({ theme }) => theme.primary3};
} }
&:disabled { &:disabled {
background-color: ${({ theme }) => theme.primary5};
opacity: 50%; opacity: 50%;
cursor: auto; cursor: auto;
} }
a:hover {
text-decoration: none;
}
` `
export const ButtonPink = styled(Base)` export const ButtonPink = styled(Base)`
...@@ -184,13 +187,13 @@ export const ButtonEmpty = styled(Base)` ...@@ -184,13 +187,13 @@ export const ButtonEmpty = styled(Base)`
align-items: center; align-items: center;
&:focus { &:focus {
background-color: ${({ theme }) => theme.advancedBG}; text-decoration: underline;
} }
&:hover { &:hover {
background-color: ${({ theme }) => theme.advancedBG}; text-decoration: underline;
} }
&:active { &:active {
background-color: ${({ theme }) => theme.advancedBG}; text-decoration: underline;
} }
&:disabled { &:disabled {
opacity: 50%; opacity: 50%;
......
import React from 'react'
import ReactConfetti from 'react-confetti'
import { useWindowSize } from '../../hooks/useWindowSize'
// eslint-disable-next-line react/prop-types
export default function Confetti({ start, variant }: { start: boolean; variant?: string }) {
const { width, height } = useWindowSize()
const _variant = variant ? variant : height && width && height > 1.5 * width ? 'bottom' : variant
return start && width && height ? (
<ReactConfetti
style={{ zIndex: 1401 }}
numberOfPieces={400}
recycle={false}
run={true}
width={width}
height={height}
confettiSource={{
h: height,
w: width,
x: 0,
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5
}}
initialVelocityX={15}
initialVelocityY={30}
gravity={0.45}
tweenDuration={100}
wind={0.05}
/>
) : null
}
...@@ -129,6 +129,7 @@ interface CurrencyInputPanelProps { ...@@ -129,6 +129,7 @@ interface CurrencyInputPanelProps {
otherCurrency?: Currency | null otherCurrency?: Currency | null
id: string id: string
showCommonBases?: boolean showCommonBases?: boolean
customBalanceText?: string
} }
export default function CurrencyInputPanel({ export default function CurrencyInputPanel({
...@@ -145,7 +146,8 @@ export default function CurrencyInputPanel({ ...@@ -145,7 +146,8 @@ export default function CurrencyInputPanel({
hideInput = false, hideInput = false,
otherCurrency, otherCurrency,
id, id,
showCommonBases showCommonBases,
customBalanceText
}: CurrencyInputPanelProps) { }: CurrencyInputPanelProps) {
const { t } = useTranslation() const { t } = useTranslation()
...@@ -176,7 +178,7 @@ export default function CurrencyInputPanel({ ...@@ -176,7 +178,7 @@ export default function CurrencyInputPanel({
style={{ display: 'inline', cursor: 'pointer' }} style={{ display: 'inline', cursor: 'pointer' }}
> >
{!hideBalance && !!currency && selectedCurrencyBalance {!hideBalance && !!currency && selectedCurrencyBalance
? 'Balance: ' + selectedCurrencyBalance?.toSignificant(6) ? (customBalanceText ?? 'Balance: ') + selectedCurrencyBalance?.toSignificant(6)
: ' -'} : ' -'}
</TYPE.body> </TYPE.body>
)} )}
......
...@@ -20,6 +20,8 @@ const StyledEthereumLogo = styled.img<{ size: string }>` ...@@ -20,6 +20,8 @@ const StyledEthereumLogo = styled.img<{ size: string }>`
const StyledLogo = styled(Logo)<{ size: string }>` const StyledLogo = styled(Logo)<{ size: string }>`
width: ${({ size }) => size}; width: ${({ size }) => size};
height: ${({ size }) => size}; height: ${({ size }) => size};
border-radius: ${({ size }) => size};
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
` `
export default function CurrencyLogo({ export default function CurrencyLogo({
......
...@@ -22,7 +22,7 @@ const HigherLogo = styled(CurrencyLogo)` ...@@ -22,7 +22,7 @@ const HigherLogo = styled(CurrencyLogo)`
` `
const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>` const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>`
position: absolute; position: absolute;
left: ${({ sizeraw }) => (sizeraw / 2).toString() + 'px'}; left: ${({ sizeraw }) => '-' + (sizeraw / 2).toString() + 'px'} !important;
` `
export default function DoubleCurrencyLogo({ export default function DoubleCurrencyLogo({
......
import React from 'react'
import { CurrencyAmount, Fraction, JSBI } from '@uniswap/sdk'
const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))
export default function FormattedCurrencyAmount({
currencyAmount,
significantDigits = 4
}: {
currencyAmount: CurrencyAmount
significantDigits?: number
}) {
return (
<>
{currencyAmount.equalTo(JSBI.BigInt(0))
? '0'
: currencyAmount.greaterThan(CURRENCY_AMOUNT_MIN)
? currencyAmount.toSignificant(significantDigits)
: `<${CURRENCY_AMOUNT_MIN.toSignificant(1)}`}
</>
)
}
import React, { useState, useEffect } from 'react'
import styled, { keyframes } from 'styled-components'
import { TYPE, ExternalLink } from '../../theme'
import { useBlockNumber } from '../../state/application/hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks'
const StyledPolling = styled.div`
position: fixed;
display: flex;
right: 0;
bottom: 0;
padding: 1rem;
color: white;
transition: opacity 0.25s ease;
color: ${({ theme }) => theme.green1};
:hover {
opacity: 1;
}
${({ theme }) => theme.mediaWidth.upToMedium`
display: none;
`}
`
const StyledPollingDot = styled.div`
width: 8px;
height: 8px;
min-height: 8px;
min-width: 8px;
margin-left: 0.5rem;
margin-top: 3px;
border-radius: 50%;
position: relative;
background-color: ${({ theme }) => theme.green1};
`
const rotate360 = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
const Spinner = styled.div`
animation: ${rotate360} 1s cubic-bezier(0.83, 0, 0.17, 1) infinite;
transform: translateZ(0);
border-top: 1px solid transparent;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
border-left: 2px solid ${({ theme }) => theme.green1};
background: transparent;
width: 14px;
height: 14px;
border-radius: 50%;
position: relative;
left: -3px;
top: -3px;
`
export default function Polling() {
const { chainId } = useActiveWeb3React()
const blockNumber = useBlockNumber()
const [isMounted, setIsMounted] = useState(true)
useEffect(
() => {
const timer1 = setTimeout(() => setIsMounted(true), 1000)
// this will clear Timeout when component unmount like in willComponentUnmount
return () => {
setIsMounted(false)
clearTimeout(timer1)
}
},
[blockNumber] //useEffect will run only one time
//if you pass a value to array, like this [data] than clearTimeout will run every time this value changes (useEffect re-run)
)
return (
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
<StyledPolling>
<TYPE.small style={{ opacity: isMounted ? '0.2' : '0.6' }}>{blockNumber}</TYPE.small>
<StyledPollingDot>{!isMounted && <Spinner />}</StyledPollingDot>
</StyledPolling>
</ExternalLink>
)
}
import React from 'react'
import styled from 'styled-components'
import { AlertTriangle, X } from 'react-feather'
import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks'
import { isMobile } from 'react-device-detect'
const PhishAlert = styled.div<{ isActive: any }>`
width: 100%;
padding: 6px 6px;
background-color: ${({ theme }) => theme.blue1};
color: white;
font-size: 11px;
justify-content: space-between;
align-items: center;
display: ${({ isActive }) => (isActive ? 'flex' : 'none')};
`
export const StyledClose = styled(X)`
:hover {
cursor: pointer;
}
`
export default function URLWarning() {
const toggleURLWarning = useURLWarningToggle()
const showURLWarning = useURLWarningVisible()
return isMobile ? (
<PhishAlert isActive={showURLWarning}>
<div style={{ display: 'flex' }}>
<AlertTriangle style={{ marginRight: 6 }} size={12} /> Make sure the URL is
<code style={{ padding: '0 4px', display: 'inline', fontWeight: 'bold' }}>app.uniswap.org</code>
</div>
<StyledClose size={12} onClick={toggleURLWarning} />
</PhishAlert>
) : window.location.hostname === 'app.uniswap.org' ? (
<PhishAlert isActive={showURLWarning}>
<div style={{ display: 'flex' }}>
<AlertTriangle style={{ marginRight: 6 }} size={12} /> Always make sure the URL is
<code style={{ padding: '0 4px', display: 'inline', fontWeight: 'bold' }}>app.uniswap.org</code> - bookmark it
to be safe.
</div>
<StyledClose size={12} onClick={toggleURLWarning} />
</PhishAlert>
) : null
}
import { ChainId, TokenAmount } from '@uniswap/sdk'
import React, { useMemo } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components'
import tokenLogo from '../../assets/images/token-logo.png'
import { UNI } from '../../constants'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
import { useTotalUniEarned } from '../../state/stake/hooks'
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { computeUniCirculation } from '../../utils/computeUniCirculation'
import useUSDCPrice from '../../utils/useUSDCPrice'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
`
const ModalUpper = styled(DataCard)`
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #021d43 100%);
padding: 0.5rem;
`
const StyledClose = styled(X)`
position: absolute;
right: 16px;
top: 16px;
:hover {
cursor: pointer;
}
`
/**
* Content for balance stats modal
*/
export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowUniBalanceModal: any }) {
const { account, chainId } = useActiveWeb3React()
const uni = chainId ? UNI[chainId] : undefined
const total = useAggregateUniBalance()
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, uni)
const uniUnHarvested: TokenAmount | undefined = useTotalUniEarned()
const totalSupply: TokenAmount | undefined = useTotalSupply(uni)
const uniPrice = useUSDCPrice(uni)
const blockTimestamp = useCurrentBlockTimestamp()
const circulation: TokenAmount | undefined = useMemo(
() =>
blockTimestamp && uni && chainId === ChainId.MAINNET ? computeUniCirculation(uni, blockTimestamp) : totalSupply,
[blockTimestamp, chainId, totalSupply, uni]
)
return (
<ContentWrapper gap="lg">
<ModalUpper>
<CardBGImage />
<CardNoise />
<CardSection gap="md">
<RowBetween>
<TYPE.white color="white">Your UNI Breakdown</TYPE.white>{' '}
<StyledClose stroke="white" onClick={() => setShowUniBalanceModal(false)} />
</RowBetween>
</CardSection>
<Break />
<CardSection gap="sm">
<AutoColumn gap="md" justify="center">
<UniTokenAnimated width="48px" src={tokenLogo} />{' '}
<TYPE.white fontSize={48} fontWeight={600} color="white">
{total?.toFixed(2, { groupSeparator: ',' })}
</TYPE.white>
</AutoColumn>
<AutoColumn gap="md">
<RowBetween>
<TYPE.white color="white">Balance:</TYPE.white>
<TYPE.white color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white color="white">Unclaimed:</TYPE.white>
<TYPE.white color="white">
{uniUnHarvested?.toFixed(4, { groupSeparator: ',' })}{' '}
{uniUnHarvested && (
<StyledInternalLink onClick={() => setShowUniBalanceModal(false)} to="/uni">
(claim)
</StyledInternalLink>
)}
</TYPE.white>
</RowBetween>
</AutoColumn>
</CardSection>
<Break />
<CardSection gap="sm">
<AutoColumn gap="md">
<RowBetween>
<TYPE.white color="white">UNI price:</TYPE.white>
<TYPE.white color="white">${uniPrice?.toFixed(2) ?? '-'}</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white color="white">UNI in circulation:</TYPE.white>
<TYPE.white color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white color="white">Total Supply</TYPE.white>
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
{uni && uni.chainId === ChainId.MAINNET ? (
<ExternalLink href={`https://uniswap.info/token/${uni.address}`}>View UNI Analytics</ExternalLink>
) : null}
</AutoColumn>
</CardSection>
</ModalUpper>
</ContentWrapper>
)
}
import { stringify } from 'qs'
import React, { useCallback, useMemo } from 'react'
import { Link, useLocation } from 'react-router-dom'
import styled from 'styled-components'
import useParsedQueryString from '../../hooks/useParsedQueryString'
import useToggledVersion, { Version } from '../../hooks/useToggledVersion'
import { MouseoverTooltip } from '../Tooltip'
const VersionLabel = styled.span<{ enabled: boolean }>`
padding: 0.35rem 0.6rem;
border-radius: 12px;
background: ${({ theme, enabled }) => (enabled ? theme.primary1 : 'none')};
color: ${({ theme, enabled }) => (enabled ? theme.white : theme.text1)};
font-size: 1rem;
font-weight: ${({ enabled }) => (enabled ? '500' : '400')};
:hover {
user-select: ${({ enabled }) => (enabled ? 'none' : 'initial')};
background: ${({ theme, enabled }) => (enabled ? theme.primary1 : 'none')};
color: ${({ theme, enabled }) => (enabled ? theme.white : theme.text1)};
}
`
interface VersionToggleProps extends React.ComponentProps<typeof Link> {
enabled: boolean
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const VersionToggle = styled(({ enabled, ...rest }: VersionToggleProps) => <Link {...rest} />)<VersionToggleProps>`
border-radius: 12px;
opacity: ${({ enabled }) => (enabled ? 1 : 0.5)};
cursor: ${({ enabled }) => (enabled ? 'pointer' : 'default')};
background: ${({ theme }) => theme.bg3};
color: ${({ theme }) => theme.primary1};
display: flex;
width: fit-content;
margin-left: 0.5rem;
text-decoration: none;
:hover {
text-decoration: none;
}
`
export default function VersionSwitch() {
const version = useToggledVersion()
const location = useLocation()
const query = useParsedQueryString()
const versionSwitchAvailable = location.pathname === '/swap' || location.pathname === '/send'
const toggleDest = useMemo(() => {
return versionSwitchAvailable
? {
...location,
search: `?${stringify({ ...query, use: version === Version.v1 ? undefined : Version.v1 })}`
}
: location
}, [location, query, version, versionSwitchAvailable])
const handleClick = useCallback(
e => {
if (!versionSwitchAvailable) e.preventDefault()
},
[versionSwitchAvailable]
)
const toggle = (
<VersionToggle enabled={versionSwitchAvailable} to={toggleDest} onClick={handleClick}>
<VersionLabel enabled={version === Version.v2 || !versionSwitchAvailable}>V2</VersionLabel>
<VersionLabel enabled={version === Version.v1 && versionSwitchAvailable}>V1</VersionLabel>
</VersionToggle>
)
return versionSwitchAvailable ? (
toggle
) : (
<MouseoverTooltip text="This page is only compatible with Uniswap V2.">{toggle}</MouseoverTooltip>
)
}
This diff is collapsed.
...@@ -24,7 +24,15 @@ const StyledSVG = styled.svg<{ size: string; stroke?: string }>` ...@@ -24,7 +24,15 @@ const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
* Takes in custom size and stroke for circle color, default to primary color as fill, * Takes in custom size and stroke for circle color, default to primary color as fill,
* need ...rest for layered styles on top * need ...rest for layered styles on top
*/ */
export default function Loader({ size = '16px', stroke, ...rest }: { size?: string; stroke?: string }) { export default function Loader({
size = '16px',
stroke,
...rest
}: {
size?: string
stroke?: string
[k: string]: any
}) {
return ( return (
<StyledSVG viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" size={size} stroke={stroke} {...rest}> <StyledSVG viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" size={size} stroke={stroke} {...rest}>
<path <path
......
import React, { useRef } from 'react' import React, { useRef } from 'react'
import { Info, BookOpen, Code, PieChart, MessageCircle } from 'react-feather' import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
import styled from 'styled-components' import styled from 'styled-components'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg' import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useActiveWeb3React } from '../../hooks'
import { useOnClickOutside } from '../../hooks/useOnClickOutside' import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import useToggle from '../../hooks/useToggle' import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { ExternalLink } from '../../theme' import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
const StyledMenuIcon = styled(MenuIcon)` const StyledMenuIcon = styled(MenuIcon)`
path { path {
...@@ -53,15 +56,19 @@ const MenuFlyout = styled.span` ...@@ -53,15 +56,19 @@ const MenuFlyout = styled.span`
background-color: ${({ theme }) => theme.bg3}; background-color: ${({ theme }) => theme.bg3};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 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.01); 0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 0.5rem; border-radius: 12px;
padding: 0.5rem; padding: 0.5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: 1rem; font-size: 1rem;
position: absolute; position: absolute;
top: 3rem; top: 4rem;
right: 0rem; right: 0rem;
z-index: 100; z-index: 100;
${({ theme }) => theme.mediaWidth.upToMedium`
top: -17.25rem;
`};
` `
const MenuItem = styled(ExternalLink)` const MenuItem = styled(ExternalLink)`
...@@ -81,10 +88,13 @@ const MenuItem = styled(ExternalLink)` ...@@ -81,10 +88,13 @@ const MenuItem = styled(ExternalLink)`
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface' const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
export default function Menu() { export default function Menu() {
const node = useRef<HTMLDivElement>() const { account } = useActiveWeb3React()
const [open, toggle] = useToggle(false)
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.MENU)
const toggle = useToggleModal(ApplicationModal.MENU)
useOnClickOutside(node, open ? toggle : undefined) useOnClickOutside(node, open ? toggle : undefined)
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
return ( return (
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
...@@ -92,6 +102,7 @@ export default function Menu() { ...@@ -92,6 +102,7 @@ export default function Menu() {
<StyledMenuButton onClick={toggle}> <StyledMenuButton onClick={toggle}>
<StyledMenuIcon /> <StyledMenuIcon />
</StyledMenuButton> </StyledMenuButton>
{open && ( {open && (
<MenuFlyout> <MenuFlyout>
<MenuItem id="link" href="https://uniswap.org/"> <MenuItem id="link" href="https://uniswap.org/">
...@@ -114,6 +125,11 @@ export default function Menu() { ...@@ -114,6 +125,11 @@ export default function Menu() {
<PieChart size={14} /> <PieChart size={14} />
Analytics Analytics
</MenuItem> </MenuItem>
{account && (
<ButtonPrimary onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
Claim UNI
</ButtonPrimary>
)}
</MenuFlyout> </MenuFlyout>
)} )}
</StyledMenu> </StyledMenu>
......
...@@ -31,14 +31,16 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r ...@@ -31,14 +31,16 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
)).attrs({ )).attrs({
'aria-label': 'dialog' 'aria-label': 'dialog'
})` })`
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
&[data-reach-dialog-content] { &[data-reach-dialog-content] {
margin: 0 0 2rem 0; margin: 0 0 2rem 0;
border: 1px solid ${({ theme }) => theme.bg1};
background-color: ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.bg1};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)}; box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
padding: 0px; padding: 0px;
width: 50vw; width: 50vw;
overflow: hidden; overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
overflow-x: hidden;
align-self: ${({ mobile }) => (mobile ? 'flex-end' : 'center')}; align-self: ${({ mobile }) => (mobile ? 'flex-end' : 'center')};
...@@ -85,7 +87,7 @@ export default function Modal({ ...@@ -85,7 +87,7 @@ export default function Modal({
isOpen, isOpen,
onDismiss, onDismiss,
minHeight = false, minHeight = false,
maxHeight = 50, maxHeight = 90,
initialFocusRef, initialFocusRef,
children children
}: ModalProps) { }: ModalProps) {
......
import React, { useContext } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { AutoColumn, ColumnCenter } from '../Column'
import styled, { ThemeContext } from 'styled-components'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon, CustomLightSpinner } from '../../theme'
import { ArrowUpCircle } from 'react-feather'
import Circle from '../../assets/images/blue-loader.svg'
import { getEtherscanLink } from '../../utils'
import { ExternalLink } from '../../theme/components'
const ConfirmOrLoadingWrapper = styled.div`
width: 100%;
padding: 24px;
`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
`
export function LoadingView({ children, onDismiss }: { children: any; onDismiss: () => void }) {
return (
<ConfirmOrLoadingWrapper>
<RowBetween>
<div />
<CloseIcon onClick={onDismiss} />
</RowBetween>
<ConfirmedIcon>
<CustomLightSpinner src={Circle} alt="loader" size={'90px'} />
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
{children}
<TYPE.subHeader>Confirm this transaction in your wallet</TYPE.subHeader>
</AutoColumn>
</ConfirmOrLoadingWrapper>
)
}
export function SubmittedView({
children,
onDismiss,
hash
}: {
children: any
onDismiss: () => void
hash: string | undefined
}) {
const theme = useContext(ThemeContext)
const { chainId } = useActiveWeb3React()
return (
<ConfirmOrLoadingWrapper>
<RowBetween>
<div />
<CloseIcon onClick={onDismiss} />
</RowBetween>
<ConfirmedIcon>
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.primary1} />
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
{children}
{chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
</ExternalLink>
)}
</AutoColumn>
</ConfirmOrLoadingWrapper>
)
}
...@@ -55,7 +55,7 @@ const StyledArrowLeft = styled(ArrowLeft)` ...@@ -55,7 +55,7 @@ const StyledArrowLeft = styled(ArrowLeft)`
export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) { export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<Tabs style={{ marginBottom: '20px' }}> <Tabs style={{ marginBottom: '20px', display: 'none' }}>
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}> <StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}>
{t('swap')} {t('swap')}
</StyledNavLink> </StyledNavLink>
...@@ -80,14 +80,14 @@ export function FindPoolTabs() { ...@@ -80,14 +80,14 @@ export function FindPoolTabs() {
) )
} }
export function AddRemoveTabs({ adding }: { adding: boolean }) { export function AddRemoveTabs({ adding, creating }: { adding: boolean; creating: boolean }) {
return ( return (
<Tabs> <Tabs>
<RowBetween style={{ padding: '1rem' }}> <RowBetween style={{ padding: '1rem' }}>
<HistoryLink to="/pool"> <HistoryLink to="/pool">
<StyledArrowLeft /> <StyledArrowLeft />
</HistoryLink> </HistoryLink>
<ActiveText>{adding ? 'Add' : 'Remove'} Liquidity</ActiveText> <ActiveText>{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}</ActiveText>
<QuestionHelper <QuestionHelper
text={ text={
adding adding
......
import { TokenAmount } from '@uniswap/sdk'
import React, { useEffect } from 'react'
import { X } from 'react-feather'
import styled, { keyframes } from 'styled-components'
import tokenLogo from '../../assets/images/token-logo.png'
import { ButtonPrimary } from '../../components/Button'
import { useActiveWeb3React } from '../../hooks'
import { ApplicationModal } from '../../state/application/actions'
import {
useModalOpen,
useShowClaimPopup,
useToggleSelfClaimModal,
useToggleShowClaimPopup
} from '../../state/application/hooks'
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { TYPE } from '../../theme'
import { AutoColumn } from '../Column'
import { CardBGImage, CardNoise } from '../earn/styled'
const ClaimPopup = styled(AutoColumn)`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #021d43 100%);
border-radius: 20px;
padding: 1.5rem;
overflow: hidden;
position: relative;
max-width: 360px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
`
const StyledClose = styled(X)`
position: absolute;
right: 10px;
top: 10px;
:hover {
cursor: pointer;
}
`
const rotate = keyframes`
0% {
transform: perspective(1000px) rotateY(0deg);
}
100% {
transform: perspective(1000px) rotateY(360deg);
}
`
const UniToken = styled.img`
animation: ${rotate} 5s cubic-bezier(0.83, 0, 0.17, 1) infinite;
`
export default function ClaimPopup() {
const { account } = useActiveWeb3React()
// dont store these in persisted state yet
const showClaimPopup: boolean = useShowClaimPopup()
const toggleShowClaimPopup = useToggleShowClaimPopup()
// toggle for showing this modal
const showClaimModal = useModalOpen(ApplicationModal.SELF_CLAIM)
const toggleSelfClaimModal = useToggleSelfClaimModal()
// const userHasAvailableclaim = useUserHasAvailableClaim()
const userHasAvailableclaim: boolean = useUserHasAvailableClaim(account)
const unclaimedAmount: TokenAmount | undefined = useUserUnclaimedAmount(account)
// listen for available claim and show popup if needed
useEffect(() => {
if (userHasAvailableclaim) {
toggleShowClaimPopup()
}
// the toggleShowClaimPopup function changes every time the popup changes, so this will cause an infinite loop.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userHasAvailableclaim])
return (
<>
{showClaimPopup && !showClaimModal && (
<ClaimPopup gap="md">
<CardBGImage />
<CardNoise />
<StyledClose stroke="white" onClick={toggleShowClaimPopup} />
<AutoColumn style={{ padding: '2rem 0', zIndex: 10 }} justify="center">
<UniToken width="48px" src={tokenLogo} />{' '}
<TYPE.white style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
</TYPE.white>
<TYPE.white style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
<span role="img" aria-label="party">
🎉
</span>{' '}
UNI has arrived{' '}
<span role="img" aria-label="party">
🎉
</span>
</TYPE.white>
<TYPE.subHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
{`Thanks for being part of the Uniswap community <3`}
</TYPE.subHeader>
</AutoColumn>
<AutoColumn style={{ zIndex: 10 }} justify="center">
<ButtonPrimary padding="8px" borderRadius="8px" width={'fit-content'} onClick={toggleSelfClaimModal}>
Claim your UNI tokens
</ButtonPrimary>
</AutoColumn>
</ClaimPopup>
)}
</>
)
}
...@@ -3,6 +3,8 @@ import styled from 'styled-components' ...@@ -3,6 +3,8 @@ import styled from 'styled-components'
import { useActivePopups } from '../../state/application/hooks' import { useActivePopups } from '../../state/application/hooks'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
import PopupItem from './PopupItem' import PopupItem from './PopupItem'
import ClaimPopup from './ClaimPopup'
import { useURLWarningVisible } from '../../state/user/hooks'
const MobilePopupWrapper = styled.div<{ height: string | number }>` const MobilePopupWrapper = styled.div<{ height: string | number }>`
position: relative; position: relative;
...@@ -29,13 +31,13 @@ const MobilePopupInner = styled.div` ...@@ -29,13 +31,13 @@ const MobilePopupInner = styled.div`
} }
` `
const FixedPopupColumn = styled(AutoColumn)` const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
position: fixed; position: fixed;
top: 64px; top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')};
right: 1rem; right: 1rem;
max-width: 355px !important; max-width: 355px !important;
width: 100%; width: 100%;
z-index: 2; z-index: 3;
${({ theme }) => theme.mediaWidth.upToSmall` ${({ theme }) => theme.mediaWidth.upToSmall`
display: none; display: none;
...@@ -46,9 +48,12 @@ export default function Popups() { ...@@ -46,9 +48,12 @@ export default function Popups() {
// get all popups // get all popups
const activePopups = useActivePopups() const activePopups = useActivePopups()
const urlWarningActive = useURLWarningVisible()
return ( return (
<> <>
<FixedPopupColumn gap="20px"> <FixedPopupColumn gap="20px" extraPadding={urlWarningActive}>
<ClaimPopup />
{activePopups.map(item => ( {activePopups.map(item => (
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} /> <PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
))} ))}
......
...@@ -9,16 +9,20 @@ import { useTotalSupply } from '../../data/TotalSupply' ...@@ -9,16 +9,20 @@ import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { useTokenBalance } from '../../state/wallet/hooks' import { useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink } from '../../theme' import { ExternalLink, TYPE } from '../../theme'
import { currencyId } from '../../utils/currencyId' import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/wrappedCurrency' import { unwrappedToken } from '../../utils/wrappedCurrency'
import { ButtonSecondary } from '../Button' import { ButtonPrimary, ButtonSecondary, ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'
import Card, { GreyCard } from '../Card' import { useColor } from '../../hooks/useColor'
import Card, { GreyCard, LightCard } from '../Card'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
import CurrencyLogo from '../CurrencyLogo' import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo' import DoubleCurrencyLogo from '../DoubleLogo'
import { AutoRow, RowBetween, RowFixed } from '../Row' import { RowBetween, RowFixed } from '../Row'
import { Dots } from '../swap/styleds' import { Dots } from '../swap/styleds'
export const FixedHeightRow = styled(RowBetween)` export const FixedHeightRow = styled(RowBetween)`
...@@ -26,11 +30,18 @@ export const FixedHeightRow = styled(RowBetween)` ...@@ -26,11 +30,18 @@ export const FixedHeightRow = styled(RowBetween)`
` `
export const HoverCard = styled(Card)` export const HoverCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.bg2}; border: 1px solid transparent;
:hover { :hover {
border: 1px solid ${({ theme }) => darken(0.06, theme.bg2)}; border: 1px solid ${({ theme }) => darken(0.06, theme.bg2)};
} }
` `
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
border: none;
background: ${({ theme, bgColor }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${transparentize(0.8, bgColor)} 0%, ${theme.bg3} 100%) `};
position: relative;
overflow: hidden;
`
interface PositionCardProps { interface PositionCardProps {
pair: Pair pair: Pair
...@@ -49,6 +60,11 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos ...@@ -49,6 +60,11 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
const userPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken) const userPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
const totalPoolTokens = useTotalSupply(pair.liquidityToken) const totalPoolTokens = useTotalSupply(pair.liquidityToken)
const poolTokenPercentage =
!!userPoolBalance && !!totalPoolTokens && JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
: undefined
const [token0Deposited, token1Deposited] = const [token0Deposited, token1Deposited] =
!!pair && !!pair &&
!!totalPoolTokens && !!totalPoolTokens &&
...@@ -63,7 +79,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos ...@@ -63,7 +79,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
return ( return (
<> <>
{userPoolBalance && ( {userPoolBalance && JSBI.greaterThan(userPoolBalance.raw, JSBI.BigInt(0)) ? (
<GreyCard border={border}> <GreyCard border={border}>
<AutoColumn gap="12px"> <AutoColumn gap="12px">
<FixedHeightRow> <FixedHeightRow>
...@@ -88,12 +104,20 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos ...@@ -88,12 +104,20 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
</FixedHeightRow> </FixedHeightRow>
<AutoColumn gap="4px"> <AutoColumn gap="4px">
<FixedHeightRow> <FixedHeightRow>
<Text color="#888D9B" fontSize={16} fontWeight={500}> <Text fontSize={16} fontWeight={500}>
Your pool share:
</Text>
<Text fontSize={16} fontWeight={500}>
{poolTokenPercentage ? poolTokenPercentage.toFixed(6) + '%' : '-'}
</Text>
</FixedHeightRow>
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
{currency0.symbol}: {currency0.symbol}:
</Text> </Text>
{token0Deposited ? ( {token0Deposited ? (
<RowFixed> <RowFixed>
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}> <Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toSignificant(6)} {token0Deposited?.toSignificant(6)}
</Text> </Text>
</RowFixed> </RowFixed>
...@@ -102,12 +126,12 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos ...@@ -102,12 +126,12 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
)} )}
</FixedHeightRow> </FixedHeightRow>
<FixedHeightRow> <FixedHeightRow>
<Text color="#888D9B" fontSize={16} fontWeight={500}> <Text fontSize={16} fontWeight={500}>
{currency1.symbol}: {currency1.symbol}:
</Text> </Text>
{token1Deposited ? ( {token1Deposited ? (
<RowFixed> <RowFixed>
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}> <Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toSignificant(6)} {token1Deposited?.toSignificant(6)}
</Text> </Text>
</RowFixed> </RowFixed>
...@@ -118,6 +142,16 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos ...@@ -118,6 +142,16 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
</AutoColumn> </AutoColumn>
</AutoColumn> </AutoColumn>
</GreyCard> </GreyCard>
) : (
<LightCard>
<TYPE.subHeader style={{ textAlign: 'center' }}>
<span role="img" aria-label="wizard-icon">
⭐️
</span>{' '}
By adding liquidity you&apos;ll earn 0.3% of all trades on this pair proportional to your share of the pool.
Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity.
</TYPE.subHeader>
</LightCard>
)} )}
</> </>
) )
...@@ -151,26 +185,53 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) { ...@@ -151,26 +185,53 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
] ]
: [undefined, undefined] : [undefined, undefined]
const backgroundColor = useColor(pair?.token0)
return ( return (
<HoverCard border={border}> <StyledPositionCard border={border} bgColor={backgroundColor}>
<CardNoise />
<AutoColumn gap="12px"> <AutoColumn gap="12px">
<FixedHeightRow onClick={() => setShowMore(!showMore)} style={{ cursor: 'pointer' }}> <FixedHeightRow>
<RowFixed> <RowFixed>
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} margin={true} size={20} /> <DoubleCurrencyLogo currency0={currency0} currency1={currency1} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}> <Text fontWeight={500} fontSize={20}>
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`} {!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
</Text> </Text>
</RowFixed> </RowFixed>
<RowFixed>
{showMore ? ( <RowFixed gap="8px">
<ChevronUp size="20" style={{ marginLeft: '10px' }} /> <ButtonEmpty
) : ( padding="6px 8px"
<ChevronDown size="20" style={{ marginLeft: '10px' }} /> borderRadius="12px"
)} width="fit-content"
onClick={() => setShowMore(!showMore)}
>
{showMore ? (
<>
{' '}
Manage
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
</>
) : (
<>
Manage
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
</>
)}
</ButtonEmpty>
</RowFixed> </RowFixed>
</FixedHeightRow> </FixedHeightRow>
{showMore && ( {showMore && (
<AutoColumn gap="8px"> <AutoColumn gap="8px">
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Your pool tokens:
</Text>
<Text fontSize={16} fontWeight={500}>
{userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
</Text>
</FixedHeightRow>
<FixedHeightRow> <FixedHeightRow>
<RowFixed> <RowFixed>
<Text fontSize={16} fontWeight={500}> <Text fontSize={16} fontWeight={500}>
...@@ -206,14 +267,7 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) { ...@@ -206,14 +267,7 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
'-' '-'
)} )}
</FixedHeightRow> </FixedHeightRow>
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Your pool tokens:
</Text>
<Text fontSize={16} fontWeight={500}>
{userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
</Text>
</FixedHeightRow>
<FixedHeightRow> <FixedHeightRow>
<Text fontSize={16} fontWeight={500}> <Text fontSize={16} fontWeight={500}>
Your pool share: Your pool share:
...@@ -223,22 +277,37 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) { ...@@ -223,22 +277,37 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
</Text> </Text>
</FixedHeightRow> </FixedHeightRow>
<AutoRow justify="center" marginTop={'10px'}> <ButtonSecondary padding="8px" borderRadius="8px">
<ExternalLink href={`https://uniswap.info/pair/${pair.liquidityToken.address}`}> <ExternalLink
View pool information ↗ style={{ width: '100%', textAlign: 'center' }}
href={`https://uniswap.info/account/${account}`}
>
View accrued fees and analytics<span style={{ fontSize: '11px' }}></span>
</ExternalLink> </ExternalLink>
</AutoRow> </ButtonSecondary>
<RowBetween marginTop="10px"> <RowBetween marginTop="10px">
<ButtonSecondary as={Link} to={`/add/${currencyId(currency0)}/${currencyId(currency1)}`} width="48%"> <ButtonPrimary
padding="8px"
borderRadius="8px"
as={Link}
to={`/add/${currencyId(currency0)}/${currencyId(currency1)}`}
width="48%"
>
Add Add
</ButtonSecondary> </ButtonPrimary>
<ButtonSecondary as={Link} width="48%" to={`/remove/${currencyId(currency0)}/${currencyId(currency1)}`}> <ButtonPrimary
padding="8px"
borderRadius="8px"
as={Link}
width="48%"
to={`/remove/${currencyId(currency0)}/${currencyId(currency1)}`}
>
Remove Remove
</ButtonSecondary> </ButtonPrimary>
</RowBetween> </RowBetween>
</AutoColumn> </AutoColumn>
)} )}
</AutoColumn> </AutoColumn>
</HoverCard> </StyledPositionCard>
) )
} }
...@@ -4,9 +4,7 @@ import { RowBetween } from '../Row' ...@@ -4,9 +4,7 @@ import { RowBetween } from '../Row'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
import { transparentize } from 'polished' import { transparentize } from 'polished'
const Wrapper = styled(AutoColumn)` const Wrapper = styled(AutoColumn)``
margin-top: 1.25rem;
`
const Grouping = styled(RowBetween)` const Grouping = styled(RowBetween)`
width: 50%; width: 50%;
...@@ -32,20 +30,23 @@ const CircleRow = styled.div` ...@@ -32,20 +30,23 @@ const CircleRow = styled.div`
align-items: center; align-items: center;
` `
const Connector = styled.div<{ prevConfirmed?: boolean }>` const Connector = styled.div<{ prevConfirmed?: boolean; disabled?: boolean }>`
width: 100%; width: 100%;
height: 2px; height: 2px;
background-color: ; background-color: ;
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
${({ theme, prevConfirmed }) => transparentize(0.5, prevConfirmed ? theme.green1 : theme.primary1)} 0%, ${({ theme, prevConfirmed, disabled }) =>
${({ theme, prevConfirmed }) => (prevConfirmed ? theme.primary1 : theme.bg4)} 80% disabled ? theme.bg4 : transparentize(0.5, prevConfirmed ? theme.green1 : theme.primary1)}
0%,
${({ theme, prevConfirmed, disabled }) => (disabled ? theme.bg4 : prevConfirmed ? theme.primary1 : theme.bg4)} 80%
); );
opacity: 0.6; opacity: 0.6;
` `
interface ProgressCirclesProps { interface ProgressCirclesProps {
steps: boolean[] steps: boolean[]
disabled?: boolean
} }
/** /**
...@@ -58,21 +59,21 @@ interface ProgressCirclesProps { ...@@ -58,21 +59,21 @@ interface ProgressCirclesProps {
* *
* @param steps array of booleans where true means step is complete * @param steps array of booleans where true means step is complete
*/ */
export default function ProgressCircles({ steps }: ProgressCirclesProps) { export default function ProgressCircles({ steps, disabled = false, ...rest }: ProgressCirclesProps) {
return ( return (
<Wrapper justify={'center'}> <Wrapper justify={'center'} {...rest}>
<Grouping> <Grouping>
{steps.map((step, i) => { {steps.map((step, i) => {
return ( return (
<CircleRow key={i}> <CircleRow key={i}>
<Circle confirmed={step} disabled={!steps[i - 1] && i !== 0}> <Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
{step ? '' : i + 1} {step ? '' : i + 1}
</Circle> </Circle>
<Connector prevConfirmed={step} /> <Connector prevConfirmed={step} disabled={disabled} />
</CircleRow> </CircleRow>
) )
})} })}
<Circle disabled={!steps[steps.length - 1]}>{steps.length + 1}</Circle> <Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1}</Circle>
</Grouping> </Grouping>
</Wrapper> </Wrapper>
) )
......
...@@ -22,6 +22,31 @@ const QuestionWrapper = styled.div` ...@@ -22,6 +22,31 @@ const QuestionWrapper = styled.div`
} }
` `
const LightQuestionWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 0.2rem;
border: none;
background: none;
outline: none;
cursor: default;
border-radius: 36px;
width: 24px;
height: 24px;
background-color: rgba(255, 255, 255, 0.1);
color: ${({ theme }) => theme.white};
:hover,
:focus {
opacity: 0.7;
}
`
const QuestionMark = styled.span`
font-size: 1rem;
`
export default function QuestionHelper({ text }: { text: string }) { export default function QuestionHelper({ text }: { text: string }) {
const [show, setShow] = useState<boolean>(false) const [show, setShow] = useState<boolean>(false)
...@@ -38,3 +63,20 @@ export default function QuestionHelper({ text }: { text: string }) { ...@@ -38,3 +63,20 @@ export default function QuestionHelper({ text }: { text: string }) {
</span> </span>
) )
} }
export function LightQuestionHelper({ text }: { text: string }) {
const [show, setShow] = useState<boolean>(false)
const open = useCallback(() => setShow(true), [setShow])
const close = useCallback(() => setShow(false), [setShow])
return (
<span style={{ marginLeft: 4 }}>
<Tooltip text={text} show={show}>
<LightQuestionWrapper onClick={open} onMouseEnter={open} onMouseLeave={close}>
<QuestionMark>?</QuestionMark>
</LightQuestionWrapper>
</Tooltip>
</span>
)
}
...@@ -2,10 +2,8 @@ import { Currency } from '@uniswap/sdk' ...@@ -2,10 +2,8 @@ import { Currency } from '@uniswap/sdk'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import useLast from '../../hooks/useLast' import useLast from '../../hooks/useLast'
import { useSelectedListUrl } from '../../state/lists/hooks'
import Modal from '../Modal' import Modal from '../Modal'
import { CurrencySearch } from './CurrencySearch' import { CurrencySearch } from './CurrencySearch'
import ListIntroduction from './ListIntroduction'
import { ListSelect } from './ListSelect' import { ListSelect } from './ListSelect'
interface CurrencySearchModalProps { interface CurrencySearchModalProps {
...@@ -56,19 +54,11 @@ export default function CurrencySearchModal({ ...@@ -56,19 +54,11 @@ export default function CurrencySearchModal({
}) })
setListView(false) setListView(false)
}, []) }, [])
const handleSelectListIntroduction = useCallback(() => {
setListView(true)
}, [])
const selectedListUrl = useSelectedListUrl()
const noListSelected = !selectedListUrl
return ( return (
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={90} minHeight={listView ? 40 : noListSelected ? 0 : 80}> <Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={80} minHeight={listView ? 40 : 80}>
{listView ? ( {listView ? (
<ListSelect onDismiss={onDismiss} onBack={handleClickBack} /> <ListSelect onDismiss={onDismiss} onBack={handleClickBack} />
) : noListSelected ? (
<ListIntroduction onSelectList={handleSelectListIntroduction} />
) : ( ) : (
<CurrencySearch <CurrencySearch
isOpen={isOpen} isOpen={isOpen}
......
import React from 'react'
import { Text } from 'rebass'
import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
import { OutlineCard } from '../Card'
import Column, { AutoColumn } from '../Column'
import { PaddedColumn } from './styleds'
import { useDarkModeManager } from '../../state/user/hooks'
import listLight from '../../assets/images/token-list/lists-light.png'
import listDark from '../../assets/images/token-list/lists-dark.png'
export default function ListIntroduction({ onSelectList }: { onSelectList: () => void }) {
const [isDark] = useDarkModeManager()
return (
<Column style={{ width: '100%', flex: '1 1' }}>
<PaddedColumn>
<AutoColumn gap="14px">
<img
style={{ width: '120px', margin: '0 auto' }}
src={isDark ? listDark : listLight}
alt="token-list-preview"
/>
<img
style={{ width: '100%', borderRadius: '12px' }}
src="https://cloudflare-ipfs.com/ipfs/QmRf1rAJcZjV3pwKTHfPdJh4RxR8yvRHkdLjZCsmp7T6hA"
alt="token-list-preview"
/>
<Text style={{ marginBottom: '8px', textAlign: 'center' }}>
Uniswap now supports token lists. You can add your own custom lists via IPFS, HTTPS and ENS.{' '}
</Text>
<ButtonPrimary onClick={onSelectList} id="list-introduction-choose-a-list">
Choose a list
</ButtonPrimary>
<OutlineCard style={{ marginBottom: '8px', padding: '1rem' }}>
<Text fontWeight={400} fontSize={14} style={{ textAlign: 'center' }}>
Token lists are an{' '}
<ExternalLink href="https://github.com/uniswap/token-lists">open specification</ExternalLink>. Check out{' '}
<ExternalLink href="https://tokenlists.org">tokenlists.org</ExternalLink> to learn more.
</Text>
</OutlineCard>
</AutoColumn>
</PaddedColumn>
</Column>
)
}
...@@ -236,7 +236,6 @@ const ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; on ...@@ -236,7 +236,6 @@ const ListRow = memo(function ListRow({ listUrl, onBack }: { listUrl: string; on
}) })
const AddListButton = styled(ButtonSecondary)` const AddListButton = styled(ButtonSecondary)`
/* height: 1.8rem; */
max-width: 4rem; max-width: 4rem;
margin-left: 1rem; margin-left: 1rem;
border-radius: 12px; border-radius: 12px;
......
import React, { useRef, useContext, useState } from 'react' import React, { useContext, useRef, useState } from 'react'
import { Settings, X } from 'react-feather' import { Settings, X } from 'react-feather'
import styled from 'styled-components' import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components'
import { useOnClickOutside } from '../../hooks/useOnClickOutside' import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks'
import { import {
useUserSlippageTolerance, useDarkModeManager,
useExpertModeManager, useExpertModeManager,
useUserDeadline, useUserTransactionTTL,
useDarkModeManager useUserSlippageTolerance
} from '../../state/user/hooks' } from '../../state/user/hooks'
import TransactionSettings from '../TransactionSettings'
import { RowFixed, RowBetween } from '../Row'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import QuestionHelper from '../QuestionHelper'
import Toggle from '../Toggle'
import { ThemeContext } from 'styled-components'
import { AutoColumn } from '../Column'
import { ButtonError } from '../Button' import { ButtonError } from '../Button'
import { useSettingsMenuOpen, useToggleSettingsMenu } from '../../state/application/hooks' import { AutoColumn } from '../Column'
import { Text } from 'rebass'
import Modal from '../Modal' import Modal from '../Modal'
import QuestionHelper from '../QuestionHelper'
import { RowBetween, RowFixed } from '../Row'
import Toggle from '../Toggle'
import TransactionSettings from '../TransactionSettings'
const StyledMenuIcon = styled(Settings)` const StyledMenuIcon = styled(Settings)`
height: 20px; height: 20px;
...@@ -85,18 +85,15 @@ const StyledMenu = styled.div` ...@@ -85,18 +85,15 @@ const StyledMenu = styled.div`
const MenuFlyout = styled.span` const MenuFlyout = styled.span`
min-width: 20.125rem; min-width: 20.125rem;
background-color: ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.bg2};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 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.01); 0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 12px;
border: 1px solid ${({ theme }) => theme.bg3};
border-radius: 0.5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: 1rem; font-size: 1rem;
position: absolute; position: absolute;
top: 3rem; top: 4rem;
right: 0rem; right: 0rem;
z-index: 100; z-index: 100;
...@@ -104,6 +101,11 @@ const MenuFlyout = styled.span` ...@@ -104,6 +101,11 @@ const MenuFlyout = styled.span`
min-width: 18.125rem; min-width: 18.125rem;
right: -46px; right: -46px;
`}; `};
${({ theme }) => theme.mediaWidth.upToMedium`
min-width: 18.125rem;
top: -22rem;
`};
` `
const Break = styled.div` const Break = styled.div`
...@@ -123,13 +125,13 @@ const ModalContentWrapper = styled.div` ...@@ -123,13 +125,13 @@ const ModalContentWrapper = styled.div`
export default function SettingsTab() { export default function SettingsTab() {
const node = useRef<HTMLDivElement>() const node = useRef<HTMLDivElement>()
const open = useSettingsMenuOpen() const open = useModalOpen(ApplicationModal.SETTINGS)
const toggle = useToggleSettingsMenu() const toggle = useToggleSettingsMenu()
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
const [userSlippageTolerance, setUserslippageTolerance] = useUserSlippageTolerance() const [userSlippageTolerance, setUserslippageTolerance] = useUserSlippageTolerance()
const [deadline, setDeadline] = useUserDeadline() const [ttl, setTtl] = useUserTransactionTTL()
const [expertMode, toggleExpertMode] = useExpertModeManager() const [expertMode, toggleExpertMode] = useExpertModeManager()
...@@ -182,13 +184,13 @@ export default function SettingsTab() { ...@@ -182,13 +184,13 @@ export default function SettingsTab() {
</Modal> </Modal>
<StyledMenuButton onClick={toggle} id="open-settings-dialog-button"> <StyledMenuButton onClick={toggle} id="open-settings-dialog-button">
<StyledMenuIcon /> <StyledMenuIcon />
{expertMode && ( {expertMode ? (
<EmojiWrapper> <EmojiWrapper>
<span role="img" aria-label="wizard-icon"> <span role="img" aria-label="wizard-icon">
🧙 🧙
</span> </span>
</EmojiWrapper> </EmojiWrapper>
)} ) : null}
</StyledMenuButton> </StyledMenuButton>
{open && ( {open && (
<MenuFlyout> <MenuFlyout>
...@@ -199,8 +201,8 @@ export default function SettingsTab() { ...@@ -199,8 +201,8 @@ export default function SettingsTab() {
<TransactionSettings <TransactionSettings
rawSlippage={userSlippageTolerance} rawSlippage={userSlippageTolerance}
setRawSlippage={setUserslippageTolerance} setRawSlippage={setUserslippageTolerance}
deadline={deadline} deadline={ttl}
setDeadline={setDeadline} setDeadline={setTtl}
/> />
<Text fontWeight={600} fontSize={14}> <Text fontWeight={600} fontSize={14}>
Interface Settings Interface Settings
......
...@@ -6,19 +6,34 @@ const ToggleElement = styled.span<{ isActive?: boolean; isOnSwitch?: boolean }>` ...@@ -6,19 +6,34 @@ const ToggleElement = styled.span<{ isActive?: boolean; isOnSwitch?: boolean }>`
border-radius: 14px; border-radius: 14px;
background: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.primary1 : theme.text4) : 'none')}; background: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.primary1 : theme.text4) : 'none')};
color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text2) : theme.text3)}; color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text2) : theme.text3)};
font-size: 0.825rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
padding: 0.35rem 0.6rem;
border-radius: 12px;
background: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.primary1 : theme.text4) : 'none')};
color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text2) : theme.text2)};
font-size: 1rem;
font-weight: ${({ isOnSwitch }) => (isOnSwitch ? '500' : '400')};
:hover {
user-select: ${({ isOnSwitch }) => (isOnSwitch ? 'none' : 'initial')};
background: ${({ theme, isActive, isOnSwitch }) =>
isActive ? (isOnSwitch ? theme.primary1 : theme.text3) : 'none'};
color: ${({ theme, isActive, isOnSwitch }) => (isActive ? (isOnSwitch ? theme.white : theme.text2) : theme.text3)};
}
` `
const StyledToggle = styled.button<{ isActive?: boolean; activeElement?: boolean }>` const StyledToggle = styled.button<{ isActive?: boolean; activeElement?: boolean }>`
border-radius: 16px; border-radius: 12px;
border: 1px solid ${({ theme, isActive }) => (isActive ? theme.primary5 : theme.text4)}; border: none;
/* border: 1px solid ${({ theme, isActive }) => (isActive ? theme.primary5 : theme.text4)}; */
background: ${({ theme }) => theme.bg3};
display: flex; display: flex;
width: fit-content; width: fit-content;
cursor: pointer; cursor: pointer;
outline: none; outline: none;
padding: 0; padding: 0;
background-color: transparent; /* background-color: transparent; */
` `
export interface ToggleProps { export interface ToggleProps {
......
...@@ -4,7 +4,7 @@ import styled, { ThemeContext } from 'styled-components' ...@@ -4,7 +4,7 @@ import styled, { ThemeContext } from 'styled-components'
import Modal from '../Modal' import Modal from '../Modal'
import { ExternalLink } from '../../theme' import { ExternalLink } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
import { CloseIcon, Spinner } from '../../theme/components' import { CloseIcon, CustomLightSpinner } from '../../theme/components'
import { RowBetween } from '../Row' import { RowBetween } from '../Row'
import { AlertTriangle, ArrowUpCircle } from 'react-feather' import { AlertTriangle, ArrowUpCircle } from 'react-feather'
import { ButtonPrimary } from '../Button' import { ButtonPrimary } from '../Button'
...@@ -31,11 +31,6 @@ const ConfirmedIcon = styled(ColumnCenter)` ...@@ -31,11 +31,6 @@ const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0; padding: 60px 0;
` `
const CustomLightSpinner = styled(Spinner)<{ size: string }>`
height: ${({ size }) => size};
width: ${({ size }) => size};
`
function ConfirmationPendingContent({ onDismiss, pendingText }: { onDismiss: () => void; pendingText: string }) { function ConfirmationPendingContent({ onDismiss, pendingText }: { onDismiss: () => void; pendingText: string }) {
return ( return (
<Wrapper> <Wrapper>
...@@ -90,7 +85,6 @@ function TransactionSubmittedContent({ ...@@ -90,7 +85,6 @@ function TransactionSubmittedContent({
<Text fontWeight={500} fontSize={20}> <Text fontWeight={500} fontSize={20}>
Transaction Submitted Transaction Submitted
</Text> </Text>
{chainId && hash && ( {chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}> <ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>
<Text fontWeight={500} fontSize={14} color={theme.primary1}> <Text fontWeight={500} fontSize={14} color={theme.primary1}>
......
...@@ -23,9 +23,9 @@ const FancyButton = styled.button` ...@@ -23,9 +23,9 @@ const FancyButton = styled.button`
align-items: center; align-items: center;
height: 2rem; height: 2rem;
border-radius: 36px; border-radius: 36px;
font-size: 12px; font-size: 1rem;
width: auto; width: auto;
min-width: 3rem; min-width: 3.5rem;
border: 1px solid ${({ theme }) => theme.bg3}; border: 1px solid ${({ theme }) => theme.bg3};
outline: none; outline: none;
background: ${({ theme }) => theme.bg1}; background: ${({ theme }) => theme.bg1};
......
import React, { useState, useEffect } from 'react' import { AbstractConnector } from '@web3-react/abstract-connector'
import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
import React, { useEffect, useState } from 'react'
import { isMobile } from 'react-device-detect'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import styled from 'styled-components' import styled from 'styled-components'
import { isMobile } from 'react-device-detect' import MetamaskIcon from '../../assets/images/metamask.png'
import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core' import { ReactComponent as Close } from '../../assets/images/x.svg'
import { fortmatic, injected, portis } from '../../connectors'
import { OVERLAY_READY } from '../../connectors/Fortmatic'
import { SUPPORTED_WALLETS } from '../../constants'
import usePrevious from '../../hooks/usePrevious' import usePrevious from '../../hooks/usePrevious'
import { useWalletModalOpen, useWalletModalToggle } from '../../state/application/hooks' import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useWalletModalToggle } from '../../state/application/hooks'
import { ExternalLink } from '../../theme'
import AccountDetails from '../AccountDetails'
import Modal from '../Modal' import Modal from '../Modal'
import AccountDetails from '../AccountDetails'
import PendingView from './PendingView'
import Option from './Option' import Option from './Option'
import { SUPPORTED_WALLETS } from '../../constants' import PendingView from './PendingView'
import { ExternalLink } from '../../theme'
import MetamaskIcon from '../../assets/images/metamask.png'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { injected, fortmatic, portis } from '../../connectors'
import { OVERLAY_READY } from '../../connectors/Fortmatic'
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
import { AbstractConnector } from '@web3-react/abstract-connector'
const CloseIcon = styled.div` const CloseIcon = styled.div`
position: absolute; position: absolute;
...@@ -133,7 +134,7 @@ export default function WalletModal({ ...@@ -133,7 +134,7 @@ export default function WalletModal({
const [pendingError, setPendingError] = useState<boolean>() const [pendingError, setPendingError] = useState<boolean>()
const walletModalOpen = useWalletModalOpen() const walletModalOpen = useModalOpen(ApplicationModal.WALLET)
const toggleWalletModal = useWalletModalToggle() const toggleWalletModal = useWalletModalToggle()
const previousAccount = usePrevious(account) const previousAccount = usePrevious(account)
......
import React, { useState } from 'react'
import Modal from '../Modal'
import { AutoColumn, ColumnCenter } from '../Column'
import styled from 'styled-components'
import { DataCard, CardSection, Break } from '../earn/styled'
import { RowBetween } from '../Row'
import { TYPE, ExternalLink, CloseIcon, CustomLightSpinner, UniTokenAnimated } from '../../theme'
import { ButtonPrimary } from '../Button'
import { useClaimCallback, useUserUnclaimedAmount, useUserHasAvailableClaim } from '../../state/claim/hooks'
import tokenLogo from '../../assets/images/token-logo.png'
import Circle from '../../assets/images/blue-loader.svg'
import { Text } from 'rebass'
import AddressInputPanel from '../AddressInputPanel'
import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks'
import { isAddress } from 'ethers/lib/utils'
import Confetti from '../Confetti'
import { CardNoise, CardBGImage, CardBGImageSmaller } from '../earn/styled'
import { useIsTransactionPending } from '../../state/transactions/hooks'
import { TokenAmount } from '@uniswap/sdk'
import { getEtherscanLink, shortenAddress } from '../../utils'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
`
const ModalUpper = styled(DataCard)`
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #021d43 100%);
`
const ConfirmOrLoadingWrapper = styled.div<{ activeBG: boolean }>`
width: 100%;
padding: 24px;
position: relative;
background: ${({ activeBG }) =>
activeBG &&
'radial-gradient(76.02% 75.41% at 1.84% 0%, rgba(255, 0, 122, 0.2) 0%, rgba(33, 114, 229, 0.2) 100%), #FFFFFF;'};
`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
`
export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boolean; onDismiss: () => void }) {
const { chainId } = useActiveWeb3React()
// state for smart contract input
const [typed, setTyped] = useState('')
function handleRecipientType(val: string) {
setTyped(val)
}
// monitor for third party recipient of claim
const { address: parsedAddress } = useENS(typed)
// used for UI loading states
const [attempting, setAttempting] = useState<boolean>(false)
// monitor the status of the claim from contracts and txns
const { claimCallback } = useClaimCallback(parsedAddress)
const unclaimedAmount: TokenAmount | undefined = useUserUnclaimedAmount(parsedAddress)
// check if the user has something available
const hasAvailableClaim = useUserHasAvailableClaim(parsedAddress)
const [hash, setHash] = useState<string | undefined>()
// monitor the status of the claim from contracts and txns
const claimPending = useIsTransactionPending(hash ?? '')
const claimConfirmed = hash && !claimPending
// use the hash to monitor this txn
function onClaim() {
setAttempting(true)
claimCallback()
.then(hash => {
setHash(hash)
})
// reset modal and log error
.catch(error => {
setAttempting(false)
console.log(error)
})
}
function wrappedOnDismiss() {
setAttempting(false)
setHash(undefined)
setTyped('')
onDismiss()
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOnDismiss} maxHeight={90}>
<Confetti start={Boolean(isOpen && claimConfirmed && attempting)} />
{!attempting && (
<ContentWrapper gap="lg">
<ModalUpper>
<CardBGImage />
<CardNoise />
<CardSection gap="md">
<RowBetween>
<TYPE.white fontWeight={500}>Claim UNI Token</TYPE.white>
<CloseIcon onClick={wrappedOnDismiss} style={{ zIndex: 99 }} stroke="white" />
</RowBetween>
<TYPE.white fontWeight={700} fontSize={36}>
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
</TYPE.white>
</CardSection>
<Break />
</ModalUpper>
<AutoColumn gap="md" style={{ padding: '1rem', paddingTop: '0' }} justify="center">
<TYPE.subHeader fontWeight={500}>
Enter an address to trigger a UNI claim. If the address has any claimable UNI it will be sent to them on
submission.
</TYPE.subHeader>
<AddressInputPanel value={typed} onChange={handleRecipientType} />
{parsedAddress && !hasAvailableClaim && (
<TYPE.error error={true}>Address has no available claim</TYPE.error>
)}
<ButtonPrimary
disabled={!isAddress(parsedAddress ?? '') || !hasAvailableClaim}
padding="16px 16px"
width="100%"
borderRadius="12px"
mt="1rem"
onClick={onClaim}
>
Claim UNI
</ButtonPrimary>
</AutoColumn>
</ContentWrapper>
)}
{(attempting || claimConfirmed) && (
<ConfirmOrLoadingWrapper activeBG={true}>
<CardNoise />
<CardBGImageSmaller desaturate />
<RowBetween>
<div />
<CloseIcon onClick={wrappedOnDismiss} style={{ zIndex: 99 }} stroke="black" />
</RowBetween>
<ConfirmedIcon>
{!claimConfirmed ? (
<CustomLightSpinner src={Circle} alt="loader" size={'90px'} />
) : (
<UniTokenAnimated width="72px" src={tokenLogo} />
)}
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader fontWeight={600} color="black">
{claimConfirmed ? 'Claimed' : 'Claiming'}
</TYPE.largeHeader>
{!claimConfirmed && (
<Text fontSize={36} color={'#ff007a'} fontWeight={800}>
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
</Text>
)}
{parsedAddress && (
<TYPE.largeHeader fontWeight={600} color="black">
for {shortenAddress(parsedAddress)}
</TYPE.largeHeader>
)}
</AutoColumn>
{claimConfirmed && (
<>
<TYPE.subHeader fontWeight={500} color="black">
<span role="img" aria-label="party-hat">
🎉{' '}
</span>
Welcome to team Unicorn :){' '}
<span role="img" aria-label="party-hat">
🎉
</span>
</TYPE.subHeader>
</>
)}
{attempting && !hash && (
<TYPE.subHeader color="black">Confirm this transaction in your wallet</TYPE.subHeader>
)}
{attempting && hash && !claimConfirmed && chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ zIndex: 99 }}>
View transaction on Etherscan
</ExternalLink>
)}
</AutoColumn>
</ConfirmOrLoadingWrapper>
)}
</Modal>
)
}
import { JSBI, TokenAmount } from '@uniswap/sdk'
import { isAddress } from 'ethers/lib/utils'
import React, { useEffect, useState } from 'react'
import { Text } from 'rebass'
import styled from 'styled-components'
import Circle from '../../assets/images/blue-loader.svg'
import tokenLogo from '../../assets/images/token-logo.png'
import { useActiveWeb3React } from '../../hooks'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleSelfClaimModal } from '../../state/application/hooks'
import { useClaimCallback, useUserClaimData, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { CloseIcon, CustomLightSpinner, ExternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { getEtherscanLink } from '../../utils'
import { ButtonPrimary } from '../Button'
import { AutoColumn, ColumnCenter } from '../Column'
import Confetti from '../Confetti'
import { Break, CardBGImage, CardBGImageSmaller, CardNoise, CardSection, DataCard } from '../earn/styled'
import Modal from '../Modal'
import { RowBetween } from '../Row'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
`
const ModalUpper = styled(DataCard)`
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #021d43 100%);
`
const ConfirmOrLoadingWrapper = styled.div<{ activeBG: boolean }>`
width: 100%;
padding: 24px;
position: relative;
background: ${({ activeBG }) =>
activeBG &&
'radial-gradient(76.02% 75.41% at 1.84% 0%, rgba(255, 0, 122, 0.2) 0%, rgba(33, 114, 229, 0.2) 100%), #FFFFFF;'};
`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
`
const SOCKS_AMOUNT = 1000
const USER_AMOUNT = 400
export default function ClaimModal() {
const isOpen = useModalOpen(ApplicationModal.SELF_CLAIM)
const toggleClaimModal = useToggleSelfClaimModal()
const { account, chainId } = useActiveWeb3React()
// used for UI loading states
const [attempting, setAttempting] = useState<boolean>(false)
// get user claim data
const userClaimData = useUserClaimData(account)
// monitor the status of the claim from contracts and txns
const { claimCallback } = useClaimCallback(account)
const unclaimedAmount: TokenAmount | undefined = useUserUnclaimedAmount(account)
const { claimSubmitted, claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
const claimConfirmed = Boolean(claimTxn?.receipt)
function onClaim() {
setAttempting(true)
claimCallback()
// reset modal and log error
.catch(error => {
setAttempting(false)
console.log(error)
})
}
// once confirmed txn is found, if modal is closed open, mark as not attempting regradless
useEffect(() => {
if (claimConfirmed && claimSubmitted && attempting) {
setAttempting(false)
if (!isOpen) {
toggleClaimModal()
}
}
}, [attempting, claimConfirmed, claimSubmitted, isOpen, toggleClaimModal])
const nonLPAmount = JSBI.multiply(
JSBI.BigInt((userClaimData?.flags?.isSOCKS ? SOCKS_AMOUNT : 0) + (userClaimData?.flags?.isUser ? USER_AMOUNT : 0)),
JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
)
return (
<Modal isOpen={isOpen} onDismiss={toggleClaimModal} maxHeight={90}>
<Confetti start={Boolean(isOpen && claimConfirmed)} />
{!attempting && !claimConfirmed && (
<ContentWrapper gap="lg">
<ModalUpper>
<CardBGImage />
<CardNoise />
<CardSection gap="md">
<RowBetween>
<TYPE.white fontWeight={500}>Claim UNI</TYPE.white>
<CloseIcon onClick={toggleClaimModal} style={{ zIndex: 99 }} color="white" />
</RowBetween>
<TYPE.white fontWeight={700} fontSize={36}>
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
</TYPE.white>
</CardSection>
<Break />
<CardSection gap="sm">
{userClaimData?.flags?.isSOCKS && (
<RowBetween>
<TYPE.subHeader color="white">SOCKS</TYPE.subHeader>
<TYPE.subHeader color="white">{SOCKS_AMOUNT} UNI</TYPE.subHeader>
</RowBetween>
)}
{userClaimData?.flags?.isLP &&
unclaimedAmount &&
JSBI.greaterThanOrEqual(unclaimedAmount.raw, nonLPAmount) && (
<RowBetween>
<TYPE.subHeader color="white">Liquidity</TYPE.subHeader>
<TYPE.subHeader color="white">
{unclaimedAmount
.subtract(new TokenAmount(unclaimedAmount.token, nonLPAmount))
.toFixed(0, { groupSeparator: ',' })}{' '}
UNI
</TYPE.subHeader>
</RowBetween>
)}
{userClaimData?.flags?.isUser && (
<RowBetween>
<TYPE.subHeader color="white">User</TYPE.subHeader>
<TYPE.subHeader color="white">{USER_AMOUNT} UNI</TYPE.subHeader>
</RowBetween>
)}
</CardSection>
</ModalUpper>
<AutoColumn gap="md" style={{ padding: '1rem', paddingTop: '0' }} justify="center">
<TYPE.subHeader fontWeight={500}>
As a member of the Uniswap community you may claim UNI to be used for voting and governance. <br /> <br />
<ExternalLink href="https://uniswap.org/blog/uni">Read more about UNI</ExternalLink>
</TYPE.subHeader>
<ButtonPrimary
disabled={!isAddress(account ?? '')}
padding="16px 16px"
width="100%"
borderRadius="12px"
mt="1rem"
onClick={onClaim}
>
Claim UNI
</ButtonPrimary>
</AutoColumn>
</ContentWrapper>
)}
{(attempting || claimConfirmed) && (
<ConfirmOrLoadingWrapper activeBG={true}>
<CardNoise />
<CardBGImageSmaller desaturate />
<RowBetween>
<div />
<CloseIcon onClick={toggleClaimModal} style={{ zIndex: 99 }} stroke="black" />
</RowBetween>
<ConfirmedIcon>
{!claimConfirmed ? (
<CustomLightSpinner src={Circle} alt="loader" size={'90px'} />
) : (
<UniTokenAnimated width="72px" src={tokenLogo} />
)}
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader fontWeight={600} color="black">
{claimConfirmed ? 'Claimed!' : 'Claiming'}
</TYPE.largeHeader>
{!claimConfirmed && (
<Text fontSize={36} color={'#ff007a'} fontWeight={800}>
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
</Text>
)}
</AutoColumn>
{claimConfirmed && (
<>
<TYPE.subHeader fontWeight={500} color="black">
<span role="img" aria-label="party-hat">
🎉{' '}
</span>
Welcome to team Unicorn :){' '}
<span role="img" aria-label="party-hat">
🎉
</span>
</TYPE.subHeader>
</>
)}
{attempting && !claimSubmitted && (
<TYPE.subHeader color="black">Confirm this transaction in your wallet</TYPE.subHeader>
)}
{attempting && claimSubmitted && !claimConfirmed && chainId && claimTxn?.hash && (
<ExternalLink href={getEtherscanLink(chainId, claimTxn?.hash, 'transaction')} style={{ zIndex: 99 }}>
View transaction on Etherscan
</ExternalLink>
)}
</AutoColumn>
</ConfirmOrLoadingWrapper>
)}
</Modal>
)
}
import React, { useState } from 'react'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon } from '../../theme'
import { ButtonError } from '../Button'
import { StakingInfo } from '../../state/stake/hooks'
import { useStakingContract } from '../../hooks/useContract'
import { SubmittedView, LoadingView } from '../ModalViews'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useActiveWeb3React } from '../../hooks'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 1rem;
`
interface StakingModalProps {
isOpen: boolean
onDismiss: () => void
stakingInfo: StakingInfo
}
export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) {
const { account } = useActiveWeb3React()
// monitor call to help UI loading state
const addTransaction = useTransactionAdder()
const [hash, setHash] = useState<string | undefined>()
const [attempting, setAttempting] = useState(false)
function wrappedOnDismiss() {
setHash(undefined)
setAttempting(false)
onDismiss()
}
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onClaimReward() {
if (stakingContract && stakingInfo?.stakedAmount) {
setAttempting(true)
await stakingContract
.getReward({ gasLimit: 350000 })
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: `Claim accumulated UNI rewards`
})
setHash(response.hash)
})
.catch((error: any) => {
setAttempting(false)
console.log(error)
})
}
}
let error: string | undefined
if (!account) {
error = 'Connect Wallet'
}
if (!stakingInfo?.stakedAmount) {
error = error ?? 'Enter an amount'
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOnDismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<TYPE.mediumHeader>Claim</TYPE.mediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
{stakingInfo?.earnedAmount && (
<AutoColumn justify="center" gap="md">
<TYPE.body fontWeight={600} fontSize={36}>
{stakingInfo?.earnedAmount?.toSignificant(6)}
</TYPE.body>
<TYPE.body>Unclaimed UNI</TYPE.body>
</AutoColumn>
)}
<TYPE.subHeader style={{ textAlign: 'center' }}>
When you claim without withdrawing your liquidity remains in the mining pool.
</TYPE.subHeader>
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onClaimReward}>
{error ?? 'Claim'}
</ButtonError>
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.body fontSize={20}>Claiming {stakingInfo?.earnedAmount?.toSignificant(6)} UNI</TYPE.body>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Transaction Submitted</TYPE.largeHeader>
<TYPE.body fontSize={20}>Claimed UNI!</TYPE.body>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
import React from 'react'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import styled from 'styled-components'
import { TYPE, StyledInternalLink } from '../../theme'
import DoubleCurrencyLogo from '../DoubleLogo'
import { ETHER, JSBI, TokenAmount } from '@uniswap/sdk'
import { ButtonPrimary } from '../Button'
import { StakingInfo } from '../../state/stake/hooks'
import { useColor } from '../../hooks/useColor'
import { currencyId } from '../../utils/currencyId'
import { Break, CardNoise, CardBGImage } from './styled'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { useTotalSupply } from '../../data/TotalSupply'
import { usePair } from '../../data/Reserves'
import useUSDCPrice from '../../utils/useUSDCPrice'
const StatContainer = styled.div`
display: flex;
justify-content: space-between;
flex-direction: column;
gap: 12px;
margin-bottom: 1rem;
margin-right: 1rem;
margin-left: 1rem;
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
`
const Wrapper = styled(AutoColumn)<{ showBackground: boolean; bgColor: any }>`
border-radius: 12px;
width: 100%;
overflow: hidden;
position: relative;
opacity: ${({ showBackground }) => (showBackground ? '1' : '1')};
background: ${({ theme, bgColor, showBackground }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${showBackground ? theme.black : theme.bg5} 100%) `};
color: ${({ theme, showBackground }) => (showBackground ? theme.white : theme.text1)} !important;
${({ showBackground }) =>
showBackground &&
` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 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.01);`}
`
const TopSection = styled.div`
display: grid;
grid-template-columns: 48px 1fr 120px;
grid-gap: 0px;
align-items: center;
padding: 1rem;
z-index: 1;
${({ theme }) => theme.mediaWidth.upToSmall`
grid-template-columns: 48px 1fr 96px;
`};
`
// const APR = styled.div`
// display: flex;
// justify-content: flex-end;
// `
const BottomSection = styled.div<{ showBackground: boolean }>`
padding: 12px 16px;
opacity: ${({ showBackground }) => (showBackground ? '1' : '0.4')};
border-radius: 0 0 12px 12px;
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: space-between;
z-index: 1;
`
export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo }) {
const token0 = stakingInfo.tokens[0]
const token1 = stakingInfo.tokens[1]
const currency0 = unwrappedToken(token0)
const currency1 = unwrappedToken(token1)
const isStaking = Boolean(stakingInfo.stakedAmount.greaterThan('0'))
// get the color of the token
const token = currency0 === ETHER ? token1 : token0
const WETH = currency0 === ETHER ? token0 : token1
const backgroundColor = useColor(token)
const totalSupplyOfStakingToken = useTotalSupply(stakingInfo.stakedAmount.token)
const [, stakingTokenPair] = usePair(...stakingInfo.tokens)
// let returnOverMonth: Percent = new Percent('0')
let valueOfTotalStakedAmountInWETH: TokenAmount | undefined
if (totalSupplyOfStakingToken && stakingTokenPair) {
// take the total amount of LP tokens staked, multiply by ETH value of all LP tokens, divide by all LP tokens
valueOfTotalStakedAmountInWETH = new TokenAmount(
WETH,
JSBI.divide(
JSBI.multiply(
JSBI.multiply(stakingInfo.totalStakedAmount.raw, stakingTokenPair.reserveOf(WETH).raw),
JSBI.BigInt(2) // this is b/c the value of LP shares are ~double the value of the WETH they entitle owner to
),
totalSupplyOfStakingToken.raw
)
)
}
// get the USD value of staked WETH
const USDPrice = useUSDCPrice(WETH)
const valueOfTotalStakedAmountInUSDC =
valueOfTotalStakedAmountInWETH && USDPrice?.quote(valueOfTotalStakedAmountInWETH)
return (
<Wrapper showBackground={isStaking} bgColor={backgroundColor}>
<CardBGImage desaturate />
<CardNoise />
<TopSection>
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={24} />
<TYPE.white fontWeight={600} fontSize={24} style={{ marginLeft: '8px' }}>
{currency0.symbol}-{currency1.symbol}
</TYPE.white>
<StyledInternalLink to={`/uni/${currencyId(currency0)}/${currencyId(currency1)}`} style={{ width: '100%' }}>
<ButtonPrimary padding="8px" borderRadius="8px">
{isStaking ? 'Manage' : 'Deposit'}
</ButtonPrimary>
</StyledInternalLink>
</TopSection>
<StatContainer>
<RowBetween>
<TYPE.white> Total deposited</TYPE.white>
<TYPE.white>
{valueOfTotalStakedAmountInUSDC
? `$${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })}`
: `${valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH`}
</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white> Pool rate </TYPE.white>
<TYPE.white>{`${stakingInfo.totalRewardRate
?.multiply(`${60 * 60 * 24 * 7}`)
?.toFixed(0, { groupSeparator: ',' })} UNI / week`}</TYPE.white>
</RowBetween>
</StatContainer>
{isStaking && (
<>
<Break />
<BottomSection showBackground={true}>
<TYPE.black color={'white'} fontWeight={500}>
<span>Your rate</span>
</TYPE.black>
<TYPE.black style={{ textAlign: 'right' }} color={'white'} fontWeight={500}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '0.5rem' }}>
</span>
{`${stakingInfo.rewardRate
?.multiply(`${60 * 60 * 24 * 7}`)
?.toSignificant(4, { groupSeparator: ',' })} UNI / week`}
</TYPE.black>
</BottomSection>
</>
)}
</Wrapper>
)
}
import React, { useState, useCallback } from 'react'
import useIsArgentWallet from '../../hooks/useIsArgentWallet'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon } from '../../theme'
import { ButtonConfirmed, ButtonError } from '../Button'
import ProgressCircles from '../ProgressSteps'
import CurrencyInputPanel from '../CurrencyInputPanel'
import { TokenAmount, Pair } from '@uniswap/sdk'
import { useActiveWeb3React } from '../../hooks'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { usePairContract, useStakingContract } from '../../hooks/useContract'
import { useApproveCallback, ApprovalState } from '../../hooks/useApproveCallback'
import { splitSignature } from 'ethers/lib/utils'
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
import { wrappedCurrencyAmount } from '../../utils/wrappedCurrency'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { LoadingView, SubmittedView } from '../ModalViews'
const HypotheticalRewardRate = styled.div<{ dim: boolean }>`
display: flex;
justify-content: space-between;
padding-right: 20px;
padding-left: 20px;
opacity: ${({ dim }) => (dim ? 0.5 : 1)};
`
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 1rem;
`
interface StakingModalProps {
isOpen: boolean
onDismiss: () => void
stakingInfo: StakingInfo
userLiquidityUnstaked: TokenAmount | undefined
}
export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiquidityUnstaked }: StakingModalProps) {
const { account, chainId, library } = useActiveWeb3React()
// track and parse user input
const [typedValue, setTypedValue] = useState('')
const { parsedAmount, error } = useDerivedStakeInfo(typedValue, stakingInfo.stakedAmount.token, userLiquidityUnstaked)
const parsedAmountWrapped = wrappedCurrencyAmount(parsedAmount, chainId)
let hypotheticalRewardRate: TokenAmount = new TokenAmount(stakingInfo.rewardRate.token, '0')
if (parsedAmountWrapped?.greaterThan('0')) {
hypotheticalRewardRate = stakingInfo.getHypotheticalRewardRate(
stakingInfo.stakedAmount.add(parsedAmountWrapped),
stakingInfo.totalStakedAmount.add(parsedAmountWrapped),
stakingInfo.totalRewardRate
)
}
// state for pending and submitted txn views
const addTransaction = useTransactionAdder()
const [attempting, setAttempting] = useState<boolean>(false)
const [hash, setHash] = useState<string | undefined>()
const wrappedOnDismiss = useCallback(() => {
setHash(undefined)
setAttempting(false)
onDismiss()
}, [onDismiss])
// pair contract for this token to be staked
const dummyPair = new Pair(new TokenAmount(stakingInfo.tokens[0], '0'), new TokenAmount(stakingInfo.tokens[1], '0'))
const pairContract = usePairContract(dummyPair.liquidityToken.address)
// approval data for stake
const deadline = useTransactionDeadline()
const [signatureData, setSignatureData] = useState<{ v: number; r: string; s: string; deadline: number } | null>(null)
const [approval, approveCallback] = useApproveCallback(parsedAmount, stakingInfo.stakingRewardAddress)
const isArgentWallet = useIsArgentWallet()
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onStake() {
setAttempting(true)
if (stakingContract && parsedAmount && deadline) {
if (approval === ApprovalState.APPROVED) {
await stakingContract.stake(`0x${parsedAmount.raw.toString(16)}`, { gasLimit: 350000 })
} else if (signatureData) {
stakingContract
.stakeWithPermit(
`0x${parsedAmount.raw.toString(16)}`,
signatureData.deadline,
signatureData.v,
signatureData.r,
signatureData.s,
{ gasLimit: 350000 }
)
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: `Deposit liquidity`
})
setHash(response.hash)
})
.catch((error: any) => {
setAttempting(false)
console.log(error)
})
} else {
setAttempting(false)
throw new Error('Attempting to stake without approval or a signature. Please contact support.')
}
}
}
// wrapped onUserInput to clear signatures
const onUserInput = useCallback((typedValue: string) => {
setSignatureData(null)
setTypedValue(typedValue)
}, [])
// used for max input button
const maxAmountInput = maxAmountSpend(userLiquidityUnstaked)
const atMaxAmount = Boolean(maxAmountInput && parsedAmount?.equalTo(maxAmountInput))
const handleMax = useCallback(() => {
maxAmountInput && onUserInput(maxAmountInput.toExact())
}, [maxAmountInput, onUserInput])
async function onAttemptToApprove() {
if (!pairContract || !library || !deadline) throw new Error('missing dependencies')
const liquidityAmount = parsedAmount
if (!liquidityAmount) throw new Error('missing liquidity amount')
if (isArgentWallet) {
return approveCallback()
}
// try to gather a signature for permission
const nonce = await pairContract.nonces(account)
const EIP712Domain = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
]
const domain = {
name: 'Uniswap V2',
version: '1',
chainId: chainId,
verifyingContract: pairContract.address
}
const Permit = [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
const message = {
owner: account,
spender: stakingInfo.stakingRewardAddress,
value: liquidityAmount.raw.toString(),
nonce: nonce.toHexString(),
deadline: deadline.toNumber()
}
const data = JSON.stringify({
types: {
EIP712Domain,
Permit
},
domain,
primaryType: 'Permit',
message
})
library
.send('eth_signTypedData_v4', [account, data])
.then(splitSignature)
.then(signature => {
setSignatureData({
v: signature.v,
r: signature.r,
s: signature.s,
deadline: deadline.toNumber()
})
})
.catch(error => {
// for all errors other than 4001 (EIP-1193 user rejected request), fall back to manual approve
if (error?.code !== 4001) {
approveCallback()
}
})
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOnDismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<TYPE.mediumHeader>Deposit</TYPE.mediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
<CurrencyInputPanel
value={typedValue}
onUserInput={onUserInput}
onMax={handleMax}
showMaxButton={!atMaxAmount}
currency={stakingInfo.stakedAmount.token}
pair={dummyPair}
label={''}
disableCurrencySelect={true}
customBalanceText={'Available to deposit: '}
id="stake-liquidity-token"
/>
<HypotheticalRewardRate dim={!hypotheticalRewardRate.greaterThan('0')}>
<div>
<TYPE.black fontWeight={600}>Weekly Rewards</TYPE.black>
</div>
<TYPE.black>
{hypotheticalRewardRate.multiply((60 * 60 * 24 * 7).toString()).toSignificant(4, { groupSeparator: ',' })}{' '}
UNI / week
</TYPE.black>
</HypotheticalRewardRate>
<RowBetween>
<ButtonConfirmed
mr="0.5rem"
onClick={onAttemptToApprove}
confirmed={approval === ApprovalState.APPROVED || signatureData !== null}
disabled={approval !== ApprovalState.NOT_APPROVED || signatureData !== null}
>
Approve
</ButtonConfirmed>
<ButtonError
disabled={!!error || (signatureData === null && approval !== ApprovalState.APPROVED)}
error={!!error && !!parsedAmount}
onClick={onStake}
>
{error ?? 'Deposit'}
</ButtonError>
</RowBetween>
<ProgressCircles steps={[approval === ApprovalState.APPROVED || signatureData !== null]} disabled={true} />
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Depositing Liquidity</TYPE.largeHeader>
<TYPE.body fontSize={20}>{parsedAmount?.toSignificant(4)} UNI-V2</TYPE.body>
</AutoColumn>
</LoadingView>
)}
{attempting && hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Transaction Submitted</TYPE.largeHeader>
<TYPE.body fontSize={20}>Deposited {parsedAmount?.toSignificant(4)} UNI-V2</TYPE.body>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
import React, { useState } from 'react'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon } from '../../theme'
import { ButtonError } from '../Button'
import { StakingInfo } from '../../state/stake/hooks'
import { useStakingContract } from '../../hooks/useContract'
import { SubmittedView, LoadingView } from '../ModalViews'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import FormattedCurrencyAmount from '../FormattedCurrencyAmount'
import { useActiveWeb3React } from '../../hooks'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 1rem;
`
interface StakingModalProps {
isOpen: boolean
onDismiss: () => void
stakingInfo: StakingInfo
}
export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) {
const { account } = useActiveWeb3React()
// monitor call to help UI loading state
const addTransaction = useTransactionAdder()
const [hash, setHash] = useState<string | undefined>()
const [attempting, setAttempting] = useState(false)
function wrappedOndismiss() {
setHash(undefined)
setAttempting(false)
onDismiss()
}
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onWithdraw() {
if (stakingContract && stakingInfo?.stakedAmount) {
setAttempting(true)
await stakingContract
.exit({ gasLimit: 300000 })
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: `Withdraw deposited liquidity`
})
setHash(response.hash)
})
.catch((error: any) => {
setAttempting(false)
console.log(error)
})
}
}
let error: string | undefined
if (!account) {
error = 'Connect Wallet'
}
if (!stakingInfo?.stakedAmount) {
error = error ?? 'Enter an amount'
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOndismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<TYPE.mediumHeader>Withdraw</TYPE.mediumHeader>
<CloseIcon onClick={wrappedOndismiss} />
</RowBetween>
{stakingInfo?.stakedAmount && (
<AutoColumn justify="center" gap="md">
<TYPE.body fontWeight={600} fontSize={36}>
{<FormattedCurrencyAmount currencyAmount={stakingInfo.stakedAmount} />}
</TYPE.body>
<TYPE.body>Deposited liquidity:</TYPE.body>
</AutoColumn>
)}
{stakingInfo?.earnedAmount && (
<AutoColumn justify="center" gap="md">
<TYPE.body fontWeight={600} fontSize={36}>
{<FormattedCurrencyAmount currencyAmount={stakingInfo?.earnedAmount} />}
</TYPE.body>
<TYPE.body>Unclaimed UNI</TYPE.body>
</AutoColumn>
)}
<TYPE.subHeader style={{ textAlign: 'center' }}>
When you withdraw, your UNI is claimed and your liquidity is removed from the mining pool.
</TYPE.subHeader>
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onWithdraw}>
{error ?? 'Withdraw & Claim'}
</ButtonError>
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOndismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.body fontSize={20}>Withdrawing {stakingInfo?.stakedAmount?.toSignificant(4)} UNI-V2</TYPE.body>
<TYPE.body fontSize={20}>Claiming {stakingInfo?.earnedAmount?.toSignificant(4)} UNI</TYPE.body>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOndismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Transaction Submitted</TYPE.largeHeader>
<TYPE.body fontSize={20}>Withdrew UNI-V2!</TYPE.body>
<TYPE.body fontSize={20}>Claimed UNI!</TYPE.body>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
import styled from 'styled-components'
import { AutoColumn } from '../Column'
import uImage from '../../assets/images/big_unicorn.png'
import xlUnicorn from '../../assets/images/xl_uni.png'
import noise from '../../assets/images/noise.png'
export const TextBox = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 4px 12px;
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 20px;
width: fit-content;
justify-self: flex-end;
`
export const DataCard = styled(AutoColumn)<{ disabled?: boolean }>`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #2172e5 100%);
border-radius: 12px;
width: 100%;
position: relative;
overflow: hidden;
`
export const CardBGImage = styled.span<{ desaturate?: boolean }>`
background: url(${uImage});
width: 1000px;
height: 600px;
position: absolute;
border-radius: 12px;
opacity: 0.4;
top: -100px;
left: -100px;
transform: rotate(-15deg);
user-select: none;
${({ desaturate }) => desaturate && `filter: saturate(0)`}
`
export const CardBGImageSmaller = styled.span<{ desaturate?: boolean }>`
background: url(${xlUnicorn});
width: 1200px;
height: 1200px;
position: absolute;
border-radius: 12px;
top: -300px;
left: -300px;
opacity: 0.4;
user-select: none;
${({ desaturate }) => desaturate && `filter: saturate(0)`}
`
export const CardNoise = styled.span`
background: url(${noise});
background-size: cover;
mix-blend-mode: overlay;
border-radius: 12px;
width: 100%;
height: 100%;
opacity: 0.15;
position: absolute;
top: 0;
left: 0;
user-select: none;
`
export const CardSection = styled(AutoColumn)<{ disabled?: boolean }>`
padding: 1rem;
z-index: 1;
opacity: ${({ disabled }) => disabled && '0.4'};
`
export const Break = styled.div`
width: 100%;
background-color: rgba(255, 255, 255, 0.2);
height: 1px;
`
import { Trade, TradeType } from '@uniswap/sdk' import { Trade, TradeType } from '@uniswap/sdk'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { ThemeContext } from 'styled-components' import styled, { ThemeContext } from 'styled-components'
import { Field } from '../../state/swap/actions' import { Field } from '../../state/swap/actions'
import { useUserSlippageTolerance } from '../../state/user/hooks' import { useUserSlippageTolerance } from '../../state/user/hooks'
import { TYPE } from '../../theme' import { TYPE, ExternalLink } from '../../theme'
import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown } from '../../utils/prices' import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown } from '../../utils/prices'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
import QuestionHelper from '../QuestionHelper' import QuestionHelper from '../QuestionHelper'
...@@ -12,6 +12,16 @@ import FormattedPriceImpact from './FormattedPriceImpact' ...@@ -12,6 +12,16 @@ import FormattedPriceImpact from './FormattedPriceImpact'
import { SectionBreak } from './styleds' import { SectionBreak } from './styleds'
import SwapRoute from './SwapRoute' import SwapRoute from './SwapRoute'
const InfoLink = styled(ExternalLink)`
width: 100%;
border: 1px solid ${({ theme }) => theme.bg3};
padding: 6px 6px;
border-radius: 8px;
text-align: center;
font-size: 14px;
color: ${({ theme }) => theme.text1};
`
function TradeSummary({ trade, allowedSlippage }: { trade: Trade; allowedSlippage: number }) { function TradeSummary({ trade, allowedSlippage }: { trade: Trade; allowedSlippage: number }) {
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade) const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade)
...@@ -94,6 +104,11 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) { ...@@ -94,6 +104,11 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
</AutoColumn> </AutoColumn>
</> </>
)} )}
<AutoColumn style={{ padding: '0 24px' }}>
<InfoLink href={'https://uniswap.info/pair/' + trade.route.pairs[0].liquidityToken.address} target="_blank">
View pair analytics ↗
</InfoLink>
</AutoColumn>
</> </>
)} )}
</AutoColumn> </AutoColumn>
......
...@@ -4,14 +4,27 @@ import { useLocation } from 'react-router' ...@@ -4,14 +4,27 @@ import { useLocation } from 'react-router'
import { Text } from 'rebass' import { Text } from 'rebass'
import { ThemeContext } from 'styled-components' import { ThemeContext } from 'styled-components'
import useParsedQueryString from '../../hooks/useParsedQueryString' import useParsedQueryString from '../../hooks/useParsedQueryString'
import { DEFAULT_VERSION, Version } from '../../hooks/useToggledVersion' import useToggledVersion, { DEFAULT_VERSION, Version } from '../../hooks/useToggledVersion'
import { StyledInternalLink } from '../../theme' import { StyledInternalLink } from '../../theme'
import { YellowCard } from '../Card' import { YellowCard } from '../Card'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
export default function BetterTradeLink({ version }: { version: Version }) { function VersionLinkContainer({ children }: { children: React.ReactNode }) {
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
return (
<YellowCard style={{ marginTop: '12px', padding: '0.5rem 0.5rem' }}>
<AutoColumn gap="sm" justify="center" style={{ alignItems: 'center', textAlign: 'center' }}>
<Text lineHeight="145.23%;" fontSize={14} fontWeight={400} color={theme.text1}>
{children}
</Text>
</AutoColumn>
</YellowCard>
)
}
export default function BetterTradeLink({ version }: { version: Version }) {
const location = useLocation() const location = useLocation()
const search = useParsedQueryString() const search = useParsedQueryString()
...@@ -26,15 +39,36 @@ export default function BetterTradeLink({ version }: { version: Version }) { ...@@ -26,15 +39,36 @@ export default function BetterTradeLink({ version }: { version: Version }) {
}, [location, search, version]) }, [location, search, version])
return ( return (
<YellowCard style={{ marginTop: '12px', padding: '8px 4px' }}> <VersionLinkContainer>
<AutoColumn gap="sm" justify="center" style={{ alignItems: 'center', textAlign: 'center' }}> There is a better price for this trade on{' '}
<Text lineHeight="145.23%;" fontSize={14} fontWeight={400} color={theme.text1}> <StyledInternalLink to={linkDestination}>
There is a better price for this trade on{' '} <b>Uniswap {version.toUpperCase()}</b>
<StyledInternalLink to={linkDestination}> </StyledInternalLink>
<b>Uniswap {version.toUpperCase()}</b> </VersionLinkContainer>
</StyledInternalLink> )
</Text> }
</AutoColumn>
</YellowCard> export function DefaultVersionLink() {
const location = useLocation()
const search = useParsedQueryString()
const version = useToggledVersion()
const linkDestination = useMemo(() => {
return {
...location,
search: `?${stringify({
...search,
use: DEFAULT_VERSION
})}`
}
}, [location, search])
return (
<VersionLinkContainer>
Showing {version.toUpperCase()} price.{' '}
<StyledInternalLink to={linkDestination}>
<b>Switch to Uniswap {DEFAULT_VERSION.toUpperCase()}</b>
</StyledInternalLink>
</VersionLinkContainer>
) )
} }
import React, { useState } from 'react'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
import styled from 'styled-components'
import { RowBetween } from '../Row'
import { TYPE } from '../../theme'
import { X } from 'react-feather'
import { ButtonPrimary } from '../Button'
import { useActiveWeb3React } from '../../hooks'
import AddressInputPanel from '../AddressInputPanel'
import { isAddress } from 'ethers/lib/utils'
import useENS from '../../hooks/useENS'
import { useDelegateCallback } from '../../state/governance/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { UNI } from '../../constants'
import { LoadingView, SubmittedView } from '../ModalViews'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 24px;
`
const StyledClosed = styled(X)`
:hover {
cursor: pointer;
}
`
const TextButton = styled.div`
:hover {
cursor: pointer;
}
`
interface VoteModalProps {
isOpen: boolean
onDismiss: () => void
title: string
}
export default function DelegateModal({ isOpen, onDismiss, title }: VoteModalProps) {
const { account, chainId } = useActiveWeb3React()
// state for delegate input
const [usingDelegate, setUsingDelegate] = useState(false)
const [typed, setTyped] = useState('')
function handleRecipientType(val: string) {
setTyped(val)
}
// monitor for self delegation or input for third part delegate
// default is self delegation
const activeDelegate = usingDelegate ? typed : account
const { address: parsedAddress } = useENS(activeDelegate)
// get the number of votes available to delegate
const uniBalance = useTokenBalance(account ?? undefined, chainId ? UNI[chainId] : undefined)
const delegateCallback = useDelegateCallback()
// monitor call to help UI loading state
const [hash, setHash] = useState<string | undefined>()
const [attempting, setAttempting] = useState(false)
// wrapper to reset state on modal close
function wrappedOndismiss() {
setHash(undefined)
setAttempting(false)
onDismiss()
}
async function onDelegate() {
setAttempting(true)
// if callback not returned properly ignore
if (!delegateCallback) return
// try delegation and store hash
const hash = await delegateCallback(parsedAddress ?? undefined)?.catch(error => {
setAttempting(false)
console.log(error)
})
if (hash) {
setHash(hash)
}
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOndismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<AutoColumn gap="lg" justify="center">
<RowBetween>
<TYPE.mediumHeader fontWeight={500}>{title}</TYPE.mediumHeader>
<StyledClosed stroke="black" onClick={wrappedOndismiss} />
</RowBetween>
<TYPE.body>Earned UNI tokens represent voting shares in Uniswap governance.</TYPE.body>
<TYPE.body>
You can either vote on each proposal yourself or delegate your votes to a third party.
</TYPE.body>
{usingDelegate && <AddressInputPanel value={typed} onChange={handleRecipientType} />}
<ButtonPrimary disabled={!isAddress(parsedAddress ?? '')} onClick={onDelegate}>
<TYPE.mediumHeader color="white">{usingDelegate ? 'Delegate Votes' : 'Self Delegate'}</TYPE.mediumHeader>
</ButtonPrimary>
<TextButton onClick={() => setUsingDelegate(!usingDelegate)}>
<TYPE.blue>
{usingDelegate ? 'Remove' : 'Add'} Delegate {!usingDelegate && '+'}
</TYPE.blue>
</TextButton>
</AutoColumn>
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOndismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>{usingDelegate ? 'Delegating votes' : 'Unlocking Votes'}</TYPE.largeHeader>
<TYPE.main fontSize={36}>{uniBalance?.toSignificant(4)}</TYPE.main>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOndismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Transaction Submitted</TYPE.largeHeader>
<TYPE.main fontSize={36}>{uniBalance?.toSignificant(4)}</TYPE.main>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
import React, { useState, useContext } from 'react'
import { useActiveWeb3React } from '../../hooks'
import Modal from '../Modal'
import { AutoColumn, ColumnCenter } from '../Column'
import styled, { ThemeContext } from 'styled-components'
import { RowBetween } from '../Row'
import { TYPE, CustomLightSpinner } from '../../theme'
import { X, ArrowUpCircle } from 'react-feather'
import { ButtonPrimary } from '../Button'
import Circle from '../../assets/images/blue-loader.svg'
import { useVoteCallback, useUserVotes } from '../../state/governance/hooks'
import { getEtherscanLink } from '../../utils'
import { ExternalLink } from '../../theme/components'
import { TokenAmount } from '@uniswap/sdk'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 24px;
`
const StyledClosed = styled(X)`
:hover {
cursor: pointer;
}
`
const ConfirmOrLoadingWrapper = styled.div`
width: 100%;
padding: 24px;
`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
`
interface VoteModalProps {
isOpen: boolean
onDismiss: () => void
support: boolean // if user is for or against proposal
proposalId: string | undefined // id for the proposal to vote on
}
export default function VoteModal({ isOpen, onDismiss, proposalId, support }: VoteModalProps) {
const { chainId } = useActiveWeb3React()
const {
voteCallback
}: {
voteCallback: (proposalId: string | undefined, support: boolean) => Promise<string> | undefined
} = useVoteCallback()
const availableVotes: TokenAmount | undefined = useUserVotes()
// monitor call to help UI loading state
const [hash, setHash] = useState<string | undefined>()
const [attempting, setAttempting] = useState<boolean>(false)
// get theme for colors
const theme = useContext(ThemeContext)
// wrapper to reset state on modal close
function wrappedOndismiss() {
setHash(undefined)
setAttempting(false)
onDismiss()
}
async function onVote() {
setAttempting(true)
// if callback not returned properly ignore
if (!voteCallback) return
// try delegation and store hash
const hash = await voteCallback(proposalId, support)?.catch(error => {
setAttempting(false)
console.log(error)
})
if (hash) {
setHash(hash)
}
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOndismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<AutoColumn gap="lg" justify="center">
<RowBetween>
<TYPE.mediumHeader fontWeight={500}>{`Vote ${
support ? 'for ' : 'against'
} proposal ${proposalId}`}</TYPE.mediumHeader>
<StyledClosed stroke="black" onClick={wrappedOndismiss} />
</RowBetween>
<TYPE.largeHeader>{availableVotes?.toSignificant(4)} Votes</TYPE.largeHeader>
<ButtonPrimary onClick={onVote}>
<TYPE.mediumHeader color="white">{`Vote ${
support ? 'for ' : 'against'
} proposal ${proposalId}`}</TYPE.mediumHeader>
</ButtonPrimary>
</AutoColumn>
</ContentWrapper>
)}
{attempting && !hash && (
<ConfirmOrLoadingWrapper>
<RowBetween>
<div />
<StyledClosed onClick={wrappedOndismiss} />
</RowBetween>
<ConfirmedIcon>
<CustomLightSpinner src={Circle} alt="loader" size={'90px'} />
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Submitting Vote</TYPE.largeHeader>
</AutoColumn>
<TYPE.subHeader>Confirm this transaction in your wallet</TYPE.subHeader>
</AutoColumn>
</ConfirmOrLoadingWrapper>
)}
{hash && (
<ConfirmOrLoadingWrapper>
<RowBetween>
<div />
<StyledClosed onClick={wrappedOndismiss} />
</RowBetween>
<ConfirmedIcon>
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.primary1} />
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>Transaction Submitted</TYPE.largeHeader>
</AutoColumn>
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
</ExternalLink>
)}
</AutoColumn>
</ConfirmOrLoadingWrapper>
)}
</Modal>
)
}
[
{
"inputs": [
{ "internalType": "bytes32[]", "name": "_codes", "type": "bytes32[]" },
{ "internalType": "address[]", "name": "_implementations", "type": "address[]" }
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "bytes32", "name": "code", "type": "bytes32" }],
"name": "CodeAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "address", "name": "implementation", "type": "address" }],
"name": "ImplementationAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "address", "name": "_newOwner", "type": "address" }],
"name": "OwnerChanged",
"type": "event"
},
{
"inputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
"name": "acceptedCodes",
"outputs": [
{ "internalType": "bool", "name": "exists", "type": "bool" },
{ "internalType": "uint128", "name": "index", "type": "uint128" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "", "type": "address" }],
"name": "acceptedImplementations",
"outputs": [
{ "internalType": "bool", "name": "exists", "type": "bool" },
{ "internalType": "uint128", "name": "index", "type": "uint128" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "bytes32", "name": "_code", "type": "bytes32" }],
"name": "addCode",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "_argentWallet", "type": "address" }],
"name": "addCodeAndImplementationFromWallet",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "_impl", "type": "address" }],
"name": "addImplementation",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "_newOwner", "type": "address" }],
"name": "changeOwner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getCodes",
"outputs": [{ "internalType": "bytes32[]", "name": "", "type": "bytes32[]" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getImplementations",
"outputs": [{ "internalType": "address[]", "name": "", "type": "address[]" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }],
"name": "isArgentWallet",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
}
]
import ARGENT_WALLET_DETECTOR_ABI from './argent-wallet-detector.json'
const ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS = '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8'
export { ARGENT_WALLET_DETECTOR_ABI, ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS }
...@@ -813,4 +813,4 @@ ...@@ -813,4 +813,4 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
} }
] ]
\ No newline at end of file
...@@ -419,4 +419,4 @@ ...@@ -419,4 +419,4 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
} }
] ]
\ No newline at end of file
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
}, },
{ {
"constant": false, "constant": false,
"inputs": [{ "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" }], "inputs": [
{ "name": "_spender", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "approve", "name": "approve",
"outputs": [{ "name": "", "type": "bool" }], "outputs": [{ "name": "", "type": "bool" }],
"payable": false, "payable": false,
...@@ -68,7 +71,10 @@ ...@@ -68,7 +71,10 @@
}, },
{ {
"constant": false, "constant": false,
"inputs": [{ "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" }], "inputs": [
{ "name": "_to", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "transfer", "name": "transfer",
"outputs": [{ "name": "", "type": "bool" }], "outputs": [{ "name": "", "type": "bool" }],
"payable": false, "payable": false,
...@@ -77,7 +83,10 @@ ...@@ -77,7 +83,10 @@
}, },
{ {
"constant": true, "constant": true,
"inputs": [{ "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" }], "inputs": [
{ "name": "_owner", "type": "address" },
{ "name": "_spender", "type": "address" }
],
"name": "allowance", "name": "allowance",
"outputs": [{ "name": "", "type": "uint256" }], "outputs": [{ "name": "", "type": "uint256" }],
"payable": false, "payable": false,
......
...@@ -52,4 +52,4 @@ ...@@ -52,4 +52,4 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "receive" "type": "receive"
} }
] ]
\ No newline at end of file
import { Interface } from '@ethersproject/abi'
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { abi as STAKING_REWARDS_FACTORY_ABI } from '@uniswap/liquidity-staker/build/StakingRewardsFactory.json'
const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
const STAKING_REWARDS_FACTORY_INTERFACE = new Interface(STAKING_REWARDS_FACTORY_ABI)
export { STAKING_REWARDS_FACTORY_INTERFACE, STAKING_REWARDS_INTERFACE }
...@@ -468,4 +468,4 @@ ...@@ -468,4 +468,4 @@
"type": "function", "type": "function",
"gas": 1246 "gas": 1246
} }
] ]
\ No newline at end of file
...@@ -276,4 +276,4 @@ ...@@ -276,4 +276,4 @@
"name": "Withdrawal", "name": "Withdrawal",
"type": "event" "type": "event"
} }
] ]
\ No newline at end of file
...@@ -5,6 +5,8 @@ import { fortmatic, injected, portis, walletconnect, walletlink } from '../conne ...@@ -5,6 +5,8 @@ import { fortmatic, injected, portis, walletconnect, walletlink } from '../conne
export const ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' export const ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
// a list of tokens by chain // a list of tokens by chain
type ChainTokenList = { type ChainTokenList = {
readonly [chainId in ChainId]: Token[] readonly [chainId in ChainId]: Token[]
...@@ -16,6 +18,26 @@ export const USDT = new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597 ...@@ -16,6 +18,26 @@ export const USDT = new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597
export const COMP = new Token(ChainId.MAINNET, '0xc00e94Cb662C3520282E6f5717214004A7f26888', 18, 'COMP', 'Compound') export const COMP = new Token(ChainId.MAINNET, '0xc00e94Cb662C3520282E6f5717214004A7f26888', 18, 'COMP', 'Compound')
export const MKR = new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker') export const MKR = new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker')
export const AMPL = new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth') export const AMPL = new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth')
export const WBTC = new Token(ChainId.MAINNET, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 18, 'WBTC', 'Wrapped BTC')
// TODO this is only approximate, it's actually based on blocks
export const PROPOSAL_LENGTH_IN_DAYS = 7
export const GOVERNANCE_ADDRESS = '0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F'
const UNI_ADDRESS = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'
export const UNI: { [chainId in ChainId]: Token } = {
[ChainId.MAINNET]: new Token(ChainId.MAINNET, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.RINKEBY]: new Token(ChainId.RINKEBY, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.ROPSTEN]: new Token(ChainId.ROPSTEN, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.GÖRLI]: new Token(ChainId.GÖRLI, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.KOVAN]: new Token(ChainId.KOVAN, UNI_ADDRESS, 18, 'UNI', 'Uniswap')
}
// TODO: specify merkle distributor for mainnet
export const MERKLE_DISTRIBUTOR_ADDRESS: { [chainId in ChainId]?: string } = {
[ChainId.MAINNET]: '0x090D4613473dEE047c3f2706764f49E0821D256e'
}
const WETH_ONLY: ChainTokenList = { const WETH_ONLY: ChainTokenList = {
[ChainId.MAINNET]: [WETH[ChainId.MAINNET]], [ChainId.MAINNET]: [WETH[ChainId.MAINNET]],
...@@ -147,6 +169,8 @@ export const INITIAL_ALLOWED_SLIPPAGE = 50 ...@@ -147,6 +169,8 @@ export const INITIAL_ALLOWED_SLIPPAGE = 50
// 20 minutes, denominated in seconds // 20 minutes, denominated in seconds
export const DEFAULT_DEADLINE_FROM_NOW = 60 * 20 export const DEFAULT_DEADLINE_FROM_NOW = 60 * 20
export const BIG_INT_ZERO = JSBI.BigInt(0)
// one basis point // one basis point
export const ONE_BIPS = new Percent(JSBI.BigInt(1), JSBI.BigInt(10000)) export const ONE_BIPS = new Percent(JSBI.BigInt(1), JSBI.BigInt(10000))
export const BIPS_BASE = JSBI.BigInt(10000) export const BIPS_BASE = JSBI.BigInt(10000)
......
...@@ -140,4 +140,4 @@ ...@@ -140,4 +140,4 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
} }
] ]
\ No newline at end of file
...@@ -968,4 +968,4 @@ ...@@ -968,4 +968,4 @@
"payable": false, "payable": false,
"type": "function" "type": "function"
} }
] ]
\ No newline at end of file
This diff is collapsed.
import { useState, useLayoutEffect } from 'react'
import { shade } from 'polished'
import Vibrant from 'node-vibrant'
import { hex } from 'wcag-contrast'
import { Token, ChainId } from '@uniswap/sdk'
async function getColorFromToken(token: Token): Promise<string | null> {
if (token.chainId === ChainId.RINKEBY && token.address === '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735') {
return Promise.resolve('#FAAB14')
}
const path = `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${token.address}/logo.png`
return Vibrant.from(path)
.getPalette()
.then(palette => {
if (palette?.Vibrant) {
let detectedHex = palette.Vibrant.hex
let AAscore = hex(detectedHex, '#FFF')
while (AAscore < 3) {
detectedHex = shade(0.005, detectedHex)
AAscore = hex(detectedHex, '#FFF')
}
return detectedHex
}
return null
})
.catch(() => null)
}
export function useColor(token?: Token) {
const [color, setColor] = useState('#2172E5')
useLayoutEffect(() => {
let stale = false
if (token) {
getColorFromToken(token).then(tokenColor => {
if (!stale && tokenColor !== null) {
setColor(tokenColor)
}
})
}
return () => {
stale = true
setColor('#2172E5')
}
}, [token])
return color
}
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { abi as MERKLE_DISTRIBUTOR_ABI } from '@uniswap/merkle-distributor/build/MerkleDistributor.json'
import { ChainId, WETH } from '@uniswap/sdk' import { ChainId, WETH } from '@uniswap/sdk'
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json' import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import { useMemo } from 'react' import { useMemo } from 'react'
import ENS_ABI from '../constants/abis/ens-registrar.json' import { GOVERNANCE_ADDRESS, MERKLE_DISTRIBUTOR_ADDRESS, UNI } from '../constants'
import {
ARGENT_WALLET_DETECTOR_ABI,
ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS
} from '../constants/abis/argent-wallet-detector'
import ENS_PUBLIC_RESOLVER_ABI from '../constants/abis/ens-public-resolver.json' import ENS_PUBLIC_RESOLVER_ABI from '../constants/abis/ens-public-resolver.json'
import ENS_ABI from '../constants/abis/ens-registrar.json'
import { ERC20_BYTES32_ABI } from '../constants/abis/erc20' import { ERC20_BYTES32_ABI } from '../constants/abis/erc20'
import ERC20_ABI from '../constants/abis/erc20.json' import ERC20_ABI from '../constants/abis/erc20.json'
import { MIGRATOR_ABI, MIGRATOR_ADDRESS } from '../constants/abis/migrator' import { MIGRATOR_ABI, MIGRATOR_ADDRESS } from '../constants/abis/migrator'
...@@ -51,6 +60,15 @@ export function useWETHContract(withSignerIfPossible?: boolean): Contract | null ...@@ -51,6 +60,15 @@ export function useWETHContract(withSignerIfPossible?: boolean): Contract | null
return useContract(chainId ? WETH[chainId].address : undefined, WETH_ABI, withSignerIfPossible) return useContract(chainId ? WETH[chainId].address : undefined, WETH_ABI, withSignerIfPossible)
} }
export function useArgentWalletDetectorContract(): Contract | null {
const { chainId } = useActiveWeb3React()
return useContract(
chainId === ChainId.MAINNET ? ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS : undefined,
ARGENT_WALLET_DETECTOR_ABI,
false
)
}
export function useENSRegistrarContract(withSignerIfPossible?: boolean): Contract | null { export function useENSRegistrarContract(withSignerIfPossible?: boolean): Contract | null {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
let address: string | undefined let address: string | undefined
...@@ -84,6 +102,24 @@ export function useMulticallContract(): Contract | null { ...@@ -84,6 +102,24 @@ export function useMulticallContract(): Contract | null {
return useContract(chainId && MULTICALL_NETWORKS[chainId], MULTICALL_ABI, false) return useContract(chainId && MULTICALL_NETWORKS[chainId], MULTICALL_ABI, false)
} }
export function useMerkleDistributorContract(): Contract | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId ? MERKLE_DISTRIBUTOR_ADDRESS[chainId] : undefined, MERKLE_DISTRIBUTOR_ABI, true)
}
export function useGovernanceContract(): Contract | null {
return useContract(GOVERNANCE_ADDRESS, GOVERNANCE_ABI, true)
}
export function useUniContract(): Contract | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId ? UNI[chainId].address : undefined, UNI_ABI, true)
}
export function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean): Contract | null {
return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
}
export function useSocksController(): Contract | null { export function useSocksController(): Contract | null {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
return useContract( return useContract(
......
import { BigNumber } from 'ethers'
import { useSingleCallResult } from '../state/multicall/hooks'
import { useMulticallContract } from './useContract'
// gets the current timestamp from the blockchain
export default function useCurrentBlockTimestamp(): BigNumber | undefined {
const multicall = useMulticallContract()
return useSingleCallResult(multicall, 'getCurrentBlockTimestamp')?.result?.[0]
}
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
import { useActiveWeb3React } from './index'
import { useArgentWalletDetectorContract } from './useContract'
export default function useIsArgentWallet(): boolean {
const { account } = useActiveWeb3React()
const argentWalletDetector = useArgentWalletDetectorContract()
const call = useSingleCallResult(argentWalletDetector, 'isArgentWallet', [account ?? undefined], NEVER_RELOAD)
return call?.result?.[0] ?? false
}
...@@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber' ...@@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber'
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { JSBI, Percent, Router, SwapParameters, Trade, TradeType } from '@uniswap/sdk' import { JSBI, Percent, Router, SwapParameters, Trade, TradeType } from '@uniswap/sdk'
import { useMemo } from 'react' import { useMemo } from 'react'
import { BIPS_BASE, DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../constants' import { BIPS_BASE, INITIAL_ALLOWED_SLIPPAGE } from '../constants'
import { getTradeVersion, useV1TradeExchangeAddress } from '../data/V1' import { getTradeVersion, useV1TradeExchangeAddress } from '../data/V1'
import { useTransactionAdder } from '../state/transactions/hooks' import { useTransactionAdder } from '../state/transactions/hooks'
import { calculateGasMargin, getRouterContract, isAddress, shortenAddress } from '../utils' import { calculateGasMargin, getRouterContract, isAddress, shortenAddress } from '../utils'
...@@ -10,6 +10,7 @@ import isZero from '../utils/isZero' ...@@ -10,6 +10,7 @@ import isZero from '../utils/isZero'
import v1SwapArguments from '../utils/v1SwapArguments' import v1SwapArguments from '../utils/v1SwapArguments'
import { useActiveWeb3React } from './index' import { useActiveWeb3React } from './index'
import { useV1ExchangeContract } from './useContract' import { useV1ExchangeContract } from './useContract'
import useTransactionDeadline from './useTransactionDeadline'
import useENS from './useENS' import useENS from './useENS'
import { Version } from './useToggledVersion' import { Version } from './useToggledVersion'
...@@ -40,25 +41,24 @@ type EstimatedSwapCall = SuccessfulCall | FailedCall ...@@ -40,25 +41,24 @@ type EstimatedSwapCall = SuccessfulCall | FailedCall
* Returns the swap calls that can be used to make the trade * Returns the swap calls that can be used to make the trade
* @param trade trade to execute * @param trade trade to execute
* @param allowedSlippage user allowed slippage * @param allowedSlippage user allowed slippage
* @param deadline the deadline for the trade
* @param recipientAddressOrName * @param recipientAddressOrName
*/ */
function useSwapCallArguments( function useSwapCallArguments(
trade: Trade | undefined, // trade to execute, required trade: Trade | undefined, // trade to execute, required
allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips
deadline: number = DEFAULT_DEADLINE_FROM_NOW, // in seconds from now
recipientAddressOrName: string | null // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender recipientAddressOrName: string | null // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
): SwapCall[] { ): SwapCall[] {
const { account, chainId, library } = useActiveWeb3React() const { account, chainId, library } = useActiveWeb3React()
const { address: recipientAddress } = useENS(recipientAddressOrName) const { address: recipientAddress } = useENS(recipientAddressOrName)
const recipient = recipientAddressOrName === null ? account : recipientAddress const recipient = recipientAddressOrName === null ? account : recipientAddress
const deadline = useTransactionDeadline()
const v1Exchange = useV1ExchangeContract(useV1TradeExchangeAddress(trade), true) const v1Exchange = useV1ExchangeContract(useV1TradeExchangeAddress(trade), true)
return useMemo(() => { return useMemo(() => {
const tradeVersion = getTradeVersion(trade) const tradeVersion = getTradeVersion(trade)
if (!trade || !recipient || !library || !account || !tradeVersion || !chainId) return [] if (!trade || !recipient || !library || !account || !tradeVersion || !chainId || !deadline) return []
const contract: Contract | null = const contract: Contract | null =
tradeVersion === Version.v2 ? getRouterContract(chainId, library, account) : v1Exchange tradeVersion === Version.v2 ? getRouterContract(chainId, library, account) : v1Exchange
...@@ -75,7 +75,7 @@ function useSwapCallArguments( ...@@ -75,7 +75,7 @@ function useSwapCallArguments(
feeOnTransfer: false, feeOnTransfer: false,
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE), allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
recipient, recipient,
ttl: deadline deadline: deadline.toNumber()
}) })
) )
...@@ -85,7 +85,7 @@ function useSwapCallArguments( ...@@ -85,7 +85,7 @@ function useSwapCallArguments(
feeOnTransfer: true, feeOnTransfer: true,
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE), allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
recipient, recipient,
ttl: deadline deadline: deadline.toNumber()
}) })
) )
} }
...@@ -95,7 +95,7 @@ function useSwapCallArguments( ...@@ -95,7 +95,7 @@ function useSwapCallArguments(
v1SwapArguments(trade, { v1SwapArguments(trade, {
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE), allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
recipient, recipient,
ttl: deadline deadline: deadline.toNumber()
}) })
) )
break break
...@@ -109,12 +109,11 @@ function useSwapCallArguments( ...@@ -109,12 +109,11 @@ function useSwapCallArguments(
export function useSwapCallback( export function useSwapCallback(
trade: Trade | undefined, // trade to execute, required trade: Trade | undefined, // trade to execute, required
allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips
deadline: number = DEFAULT_DEADLINE_FROM_NOW, // in seconds from now
recipientAddressOrName: string | null // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender recipientAddressOrName: string | null // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
): { state: SwapCallbackState; callback: null | (() => Promise<string>); error: string | null } { ): { state: SwapCallbackState; callback: null | (() => Promise<string>); error: string | null } {
const { account, chainId, library } = useActiveWeb3React() const { account, chainId, library } = useActiveWeb3React()
const swapCalls = useSwapCallArguments(trade, allowedSlippage, deadline, recipientAddressOrName) const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName)
const addTransaction = useTransactionAdder() const addTransaction = useTransactionAdder()
......
import { useActiveWeb3React } from '.'
import { useState, useEffect } from 'react'
export function useTimestampFromBlock(block: number | undefined): number | undefined {
const { library } = useActiveWeb3React()
const [timestamp, setTimestamp] = useState<number>()
useEffect(() => {
async function fetchTimestamp() {
if (block) {
const blockData = await library?.getBlock(block)
blockData && setTimestamp(blockData.timestamp)
}
}
if (!timestamp) {
fetchTimestamp()
}
}, [block, library, timestamp])
return timestamp
}
import { BigNumber } from 'ethers'
import { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { AppState } from '../state'
import useCurrentBlockTimestamp from './useCurrentBlockTimestamp'
// combines the block timestamp with the user setting to give the deadline that should be used for any submitted transaction
export default function useTransactionDeadline(): BigNumber | undefined {
const ttl = useSelector<AppState, number>(state => state.user.userDeadline)
const blockTimestamp = useCurrentBlockTimestamp()
return useMemo(() => {
if (blockTimestamp && ttl) return blockTimestamp.add(ttl)
return undefined
}, [blockTimestamp, ttl])
}
import { useEffect, useState } from 'react'
const isClient = typeof window === 'object'
function getSize() {
return {
width: isClient ? window.innerWidth : undefined,
height: isClient ? window.innerHeight : undefined
}
}
// https://usehooks.com/useWindowSize/
export function useWindowSize() {
const [windowSize, setWindowSize] = useState(getSize)
useEffect(() => {
function handleResize() {
setWindowSize(getSize())
}
if (isClient) {
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}
return undefined
}, [])
return windowSize
}
...@@ -5,6 +5,7 @@ import { isMobile } from 'react-device-detect' ...@@ -5,6 +5,7 @@ import { isMobile } from 'react-device-detect'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { HashRouter } from 'react-router-dom'
import { NetworkContextName } from './constants' import { NetworkContextName } from './constants'
import './i18n' import './i18n'
import App from './pages/App' import App from './pages/App'
...@@ -61,7 +62,9 @@ ReactDOM.render( ...@@ -61,7 +62,9 @@ ReactDOM.render(
<Updaters /> <Updaters />
<ThemeProvider> <ThemeProvider>
<ThemedGlobalStyle /> <ThemedGlobalStyle />
<App /> <HashRouter>
<App />
</HashRouter>
</ThemeProvider> </ThemeProvider>
</Provider> </Provider>
</Web3ProviderNetwork> </Web3ProviderNetwork>
......
...@@ -8,7 +8,7 @@ import { RouteComponentProps } from 'react-router-dom' ...@@ -8,7 +8,7 @@ import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass' import { Text } from 'rebass'
import { ThemeContext } from 'styled-components' import { ThemeContext } from 'styled-components'
import { ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button' import { ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
import { BlueCard, GreyCard, LightCard } from '../../components/Card' import { BlueCard, LightCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal' import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
...@@ -22,12 +22,13 @@ import { PairState } from '../../data/Reserves' ...@@ -22,12 +22,13 @@ import { PairState } from '../../data/Reserves'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { useCurrency } from '../../hooks/Tokens' import { useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { useWalletModalToggle } from '../../state/application/hooks' import { useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/mint/actions' import { Field } from '../../state/mint/actions'
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks' import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { useIsExpertMode, useUserDeadline, useUserSlippageTolerance } from '../../state/user/hooks' import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils' import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
import { maxAmountSpend } from '../../utils/maxAmountSpend' import { maxAmountSpend } from '../../utils/maxAmountSpend'
...@@ -84,7 +85,7 @@ export default function AddLiquidity({ ...@@ -84,7 +85,7 @@ export default function AddLiquidity({
const [attemptingTxn, setAttemptingTxn] = useState<boolean>(false) // clicked confirm const [attemptingTxn, setAttemptingTxn] = useState<boolean>(false) // clicked confirm
// txn values // txn values
const [deadline] = useUserDeadline() // custom from users settings const deadline = useTransactionDeadline() // custom from users settings
const [allowedSlippage] = useUserSlippageTolerance() // custom from users const [allowedSlippage] = useUserSlippageTolerance() // custom from users
const [txHash, setTxHash] = useState<string>('') const [txHash, setTxHash] = useState<string>('')
...@@ -126,7 +127,7 @@ export default function AddLiquidity({ ...@@ -126,7 +127,7 @@ export default function AddLiquidity({
const router = getRouterContract(chainId, library, account) const router = getRouterContract(chainId, library, account)
const { [Field.CURRENCY_A]: parsedAmountA, [Field.CURRENCY_B]: parsedAmountB } = parsedAmounts const { [Field.CURRENCY_A]: parsedAmountA, [Field.CURRENCY_B]: parsedAmountB } = parsedAmounts
if (!parsedAmountA || !parsedAmountB || !currencyA || !currencyB) { if (!parsedAmountA || !parsedAmountB || !currencyA || !currencyB || !deadline) {
return return
} }
...@@ -135,8 +136,6 @@ export default function AddLiquidity({ ...@@ -135,8 +136,6 @@ export default function AddLiquidity({
[Field.CURRENCY_B]: calculateSlippageAmount(parsedAmountB, noLiquidity ? 0 : allowedSlippage)[0] [Field.CURRENCY_B]: calculateSlippageAmount(parsedAmountB, noLiquidity ? 0 : allowedSlippage)[0]
} }
const deadlineFromNow = Math.ceil(Date.now() / 1000) + deadline
let estimate, let estimate,
method: (...args: any) => Promise<TransactionResponse>, method: (...args: any) => Promise<TransactionResponse>,
args: Array<string | string[] | number>, args: Array<string | string[] | number>,
...@@ -151,7 +150,7 @@ export default function AddLiquidity({ ...@@ -151,7 +150,7 @@ export default function AddLiquidity({
amountsMin[tokenBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), // token min amountsMin[tokenBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), // token min
amountsMin[tokenBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), // eth min amountsMin[tokenBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), // eth min
account, account,
deadlineFromNow deadline.toHexString()
] ]
value = BigNumber.from((tokenBIsETH ? parsedAmountB : parsedAmountA).raw.toString()) value = BigNumber.from((tokenBIsETH ? parsedAmountB : parsedAmountA).raw.toString())
} else { } else {
...@@ -165,7 +164,7 @@ export default function AddLiquidity({ ...@@ -165,7 +164,7 @@ export default function AddLiquidity({
amountsMin[Field.CURRENCY_A].toString(), amountsMin[Field.CURRENCY_A].toString(),
amountsMin[Field.CURRENCY_B].toString(), amountsMin[Field.CURRENCY_B].toString(),
account, account,
deadlineFromNow deadline.toHexString()
] ]
value = null value = null
} }
...@@ -303,10 +302,12 @@ export default function AddLiquidity({ ...@@ -303,10 +302,12 @@ export default function AddLiquidity({
setTxHash('') setTxHash('')
}, [onFieldAInput, txHash]) }, [onFieldAInput, txHash])
const isCreate = history.location.pathname.includes('/create')
return ( return (
<> <>
<AppBody> <AppBody>
<AddRemoveTabs adding={true} /> <AddRemoveTabs creating={isCreate} adding={true} />
<Wrapper> <Wrapper>
<TransactionConfirmationModal <TransactionConfirmationModal
isOpen={showConfirm} isOpen={showConfirm}
...@@ -324,23 +325,24 @@ export default function AddLiquidity({ ...@@ -324,23 +325,24 @@ export default function AddLiquidity({
pendingText={pendingText} pendingText={pendingText}
/> />
<AutoColumn gap="20px"> <AutoColumn gap="20px">
{noLiquidity && ( {noLiquidity ||
<ColumnCenter> (isCreate && (
<BlueCard> <ColumnCenter>
<AutoColumn gap="10px"> <BlueCard>
<TYPE.link fontWeight={600} color={'primaryText1'}> <AutoColumn gap="10px">
You are the first liquidity provider. <TYPE.link fontWeight={600} color={'primaryText1'}>
</TYPE.link> You are the first liquidity provider.
<TYPE.link fontWeight={400} color={'primaryText1'}> </TYPE.link>
The ratio of tokens you add will set the price of this pool. <TYPE.link fontWeight={400} color={'primaryText1'}>
</TYPE.link> The ratio of tokens you add will set the price of this pool.
<TYPE.link fontWeight={400} color={'primaryText1'}> </TYPE.link>
Once you are happy with the rate click supply to review. <TYPE.link fontWeight={400} color={'primaryText1'}>
</TYPE.link> Once you are happy with the rate click supply to review.
</AutoColumn> </TYPE.link>
</BlueCard> </AutoColumn>
</ColumnCenter> </BlueCard>
)} </ColumnCenter>
))}
<CurrencyInputPanel <CurrencyInputPanel
value={formattedAmounts[Field.CURRENCY_A]} value={formattedAmounts[Field.CURRENCY_A]}
onUserInput={onFieldAInput} onUserInput={onFieldAInput}
...@@ -370,7 +372,7 @@ export default function AddLiquidity({ ...@@ -370,7 +372,7 @@ export default function AddLiquidity({
/> />
{currencies[Field.CURRENCY_A] && currencies[Field.CURRENCY_B] && pairState !== PairState.INVALID && ( {currencies[Field.CURRENCY_A] && currencies[Field.CURRENCY_B] && pairState !== PairState.INVALID && (
<> <>
<GreyCard padding="0px" borderRadius={'20px'}> <LightCard padding="0px" borderRadius={'20px'}>
<RowBetween padding="1rem"> <RowBetween padding="1rem">
<TYPE.subHeader fontWeight={500} fontSize={14}> <TYPE.subHeader fontWeight={500} fontSize={14}>
{noLiquidity ? 'Initial prices' : 'Prices'} and pool share {noLiquidity ? 'Initial prices' : 'Prices'} and pool share
...@@ -384,7 +386,7 @@ export default function AddLiquidity({ ...@@ -384,7 +386,7 @@ export default function AddLiquidity({
price={price} price={price}
/> />
</LightCard> </LightCard>
</GreyCard> </LightCard>
</> </>
)} )}
...@@ -444,7 +446,7 @@ export default function AddLiquidity({ ...@@ -444,7 +446,7 @@ export default function AddLiquidity({
</AppBody> </AppBody>
{pair && !noLiquidity && pairState !== PairState.INVALID ? ( {pair && !noLiquidity && pairState !== PairState.INVALID ? (
<AutoColumn style={{ minWidth: '20rem', marginTop: '1rem' }}> <AutoColumn style={{ minWidth: '20rem', width: '100%', maxWidth: '400px', marginTop: '1rem' }}>
<MinimalPositionCard showUnwrapped={oneCurrencyIsWETH} pair={pair} /> <MinimalPositionCard showUnwrapped={oneCurrencyIsWETH} pair={pair} />
</AutoColumn> </AutoColumn>
) : null} ) : null}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -131,6 +131,9 @@ export default function PoolFinder() { ...@@ -131,6 +131,9 @@ export default function PoolFinder() {
<Text textAlign="center" fontWeight={500}> <Text textAlign="center" fontWeight={500}>
Pool Found! Pool Found!
</Text> </Text>
<StyledInternalLink to={`/pool`}>
<Text textAlign="center">Manage this pool.</Text>
</StyledInternalLink>
</ColumnCenter> </ColumnCenter>
)} )}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment