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 "
} }
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.3463 13.7365C14.9529 14.8091 13.5964 15.1416 12.9598 15.2028C11.9952 15.2969 11.7955 14.7385 11.9839 14.0369C12.0457 13.7857 12.183 13.5597 12.377 13.3896C12.571 13.2194 12.8125 13.1133 13.0686 13.0857C13.3209 13.0625 13.5747 13.1112 13.8007 13.2263C14.0267 13.3414 14.2158 13.5183 14.3463 13.7365Z" fill="black"/> <g style="mix-blend-mode:darken">
<path d="M18.1331 11.752C17.4281 17.1689 26.9322 16.0416 26.7513 20.3529C27.6888 19.1287 28.0919 15.8024 25.3319 14.0903C22.8729 12.5637 19.6687 13.3991 18.1331 11.752Z" fill="black"/> <path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="black"/>
<path d="M23.6074 9.70952C23.5455 9.65207 23.4818 9.59557 23.4199 9.53906C23.4827 9.59651 23.5455 9.65678 23.6074 9.70952Z" fill="black"/> <path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="black"/>
<path d="M25.8167 13.6578L25.8111 13.6493C25.7253 13.4892 25.6275 13.3359 25.5186 13.1906C25.1981 12.7546 24.7385 12.4421 24.2164 12.3054C23.8689 12.2153 23.5135 12.1592 23.1552 12.1378C22.7924 12.1114 22.4221 12.0963 22.048 12.0766C21.2981 12.0351 20.5303 11.9579 19.7803 11.7432C19.5928 11.6895 19.4053 11.6311 19.2244 11.5605C19.1306 11.5266 19.0434 11.4899 18.9506 11.4503C18.8578 11.4107 18.7631 11.3655 18.6694 11.3175C18.3167 11.1235 17.9901 10.8852 17.6972 10.6084C17.1197 10.0688 16.666 9.45662 16.2113 8.86332C15.7827 8.26774 15.3227 7.69559 14.8333 7.14934C14.3504 6.6142 13.7796 6.16633 13.1458 5.82524C12.4896 5.4932 11.7768 5.28871 11.0449 5.22253C11.8041 5.13971 12.5721 5.23381 13.2892 5.49752C14.0129 5.77972 14.6744 6.20174 15.2364 6.73968C15.6033 7.08534 15.9477 7.45433 16.2676 7.84435C18.6488 7.37347 20.58 7.79161 22.064 8.6034L22.0977 8.62035C22.5701 8.8777 23.0127 9.18658 23.4177 9.54138C23.4824 9.59788 23.5461 9.65439 23.6052 9.71183C23.9213 10.0036 24.2155 10.3183 24.4855 10.6536L24.5061 10.6809C25.3826 11.7959 25.8157 12.9552 25.8167 13.6578Z" fill="black"/> <path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="black"/>
<path d="M25.8161 13.6577L25.8105 13.6465L25.8161 13.6577Z" fill="black"/> <path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="black"/>
<path d="M11.1654 5.91992C11.7729 6.00845 12.3925 6.25142 12.7919 6.71476C13.1913 7.1781 13.3384 7.78082 13.4538 8.35434C13.5475 8.79885 13.6216 9.25183 13.7959 9.67279C13.8803 9.87809 14.004 10.058 14.1072 10.252C14.1925 10.413 14.3481 10.558 14.4081 10.7304C14.4188 10.7551 14.4228 10.7823 14.4197 10.8091C14.4166 10.8359 14.4064 10.8614 14.3903 10.8829C14.1784 11.1184 13.6066 10.8566 13.3909 10.7511C13.0182 10.5655 12.6843 10.3097 12.4075 9.99769C11.5423 9.0324 11.0951 7.64332 11.1223 6.37855C11.1283 6.225 11.1427 6.07189 11.1654 5.91992Z" fill="black"/> <path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="black"/>
<path d="M21.326 16.8066C20.0136 20.486 25.9665 22.9544 23.7363 26.695C26.0246 25.7457 27.1112 22.8781 26.1615 20.6028C25.3309 18.6054 22.8747 17.8774 21.326 16.8066Z" fill="black"/> <path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="black"/>
<path d="M13.4531 21.6165C13.8108 21.3457 14.2018 21.1226 14.6165 20.9526C15.0361 20.7839 15.4714 20.6577 15.9159 20.5759C16.798 20.4073 17.6708 20.3659 18.4039 20.0693C18.7661 19.9272 19.0994 19.7197 19.3873 19.4571C19.666 19.197 19.8793 18.8743 20.0098 18.5154C20.1435 18.1367 20.1977 17.7343 20.1692 17.3335C20.1361 16.903 20.0391 16.4798 19.8814 16.0781C20.2082 16.4195 20.4461 16.8366 20.5741 17.2925C20.7022 17.7483 20.7163 18.2288 20.6154 18.6915C20.5007 19.1812 20.2531 19.6295 19.9001 19.9864C19.5507 20.3322 19.1294 20.5959 18.6664 20.7586C18.2246 20.9143 17.7639 21.0093 17.2968 21.0411C16.8468 21.0788 16.4118 21.0892 15.9843 21.1146C15.12 21.151 14.2664 21.3203 13.4531 21.6165Z" fill="black"/> <path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="black"/>
<path d="M21.8492 28.0849C21.7171 28.1904 21.5849 28.3025 21.4396 28.4004C21.2934 28.4972 21.1401 28.5828 20.9811 28.6566C20.6502 28.8188 20.2863 28.9016 19.9181 28.8986C18.9206 28.8798 18.2156 28.1339 17.8031 27.291C17.5219 26.7166 17.3278 26.0959 16.9941 25.5488C16.5169 24.7662 15.7004 24.1362 14.7441 24.2529C14.3542 24.3019 13.9885 24.478 13.772 24.818C13.202 25.7061 14.0204 26.9501 15.0638 26.774C15.1526 26.7604 15.2394 26.7364 15.3226 26.7024C15.4054 26.667 15.4828 26.6197 15.5522 26.5621C15.698 26.4402 15.8079 26.2806 15.87 26.1007C15.9386 25.913 15.9538 25.7098 15.9141 25.5139C15.8713 25.3092 15.751 25.1292 15.5785 25.012C15.779 25.1063 15.9353 25.2751 16.0144 25.4829C16.0965 25.6966 16.1176 25.9292 16.0754 26.1543C16.0344 26.3889 15.9314 26.6082 15.7772 26.7891C15.6953 26.8821 15.6006 26.963 15.496 27.0292C15.3923 27.0947 15.2812 27.1475 15.1651 27.1865C14.9296 27.2673 14.6789 27.2934 14.432 27.2628C14.0852 27.2132 13.7582 27.0707 13.4851 26.8503C13.1626 26.5941 12.9226 26.2589 12.6311 25.9707C12.2969 25.6184 11.9025 25.3291 11.4667 25.1165C11.1662 24.9839 10.8517 24.886 10.5293 24.8246C10.3671 24.7907 10.203 24.7662 10.039 24.7455C9.96397 24.7379 9.59648 24.656 9.54492 24.704C10.052 24.2351 10.6048 23.8187 11.1949 23.4609C11.8008 23.0997 12.4513 22.8201 13.1298 22.6294C13.8333 22.4305 14.569 22.3738 15.2944 22.4627C15.6678 22.5077 16.0336 22.6028 16.3819 22.7452C16.7469 22.8916 17.0838 23.1007 17.3775 23.363C17.6682 23.6381 17.9032 23.9672 18.0694 24.3321C18.2194 24.6736 18.3313 25.0308 18.4031 25.3972C18.6168 26.4924 18.5381 28.1904 19.9659 28.4409C20.0403 28.4554 20.1154 28.4661 20.1909 28.4729L20.4243 28.4786C20.5848 28.4671 20.7442 28.4441 20.9015 28.4098C21.2272 28.3329 21.5446 28.224 21.8492 28.0849Z" fill="black"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="black"/>
<path d="M13.5531 26.8907L13.5156 26.8613L13.5531 26.8907Z" fill="black"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="black"/>
<path d="M12.2721 11.4336C12.2201 11.6343 12.1293 11.8228 12.0049 11.9883C11.7733 12.2902 11.4669 12.5256 11.1162 12.6711C10.8007 12.8076 10.4667 12.8958 10.1253 12.9329C10.0512 12.9423 9.97433 12.9479 9.90027 12.9536C9.684 12.9618 9.47603 13.0394 9.30683 13.175C9.13763 13.3106 9.01608 13.497 8.95998 13.707C8.93429 13.8115 8.91519 13.9175 8.90279 14.0244C8.86905 14.3012 8.86342 14.5894 8.83342 14.9378C8.75997 15.5308 8.58922 16.1074 8.32812 16.6443C7.98876 17.3619 7.60815 17.9401 7.69627 18.767C7.75439 19.3038 8.02813 19.6635 8.39187 20.0346C9.0481 20.7089 10.5171 21.0102 10.189 22.6715C9.9912 23.6631 8.35437 24.7038 6.05382 25.0673C6.28256 25.0324 5.76039 24.1481 5.72757 24.0916C5.48102 23.7036 5.21103 23.3382 5.0151 22.9173C4.63073 22.1008 4.45261 21.1562 4.61011 20.2615C4.77604 19.3198 5.46977 18.5984 6.04632 17.8789C6.73255 17.0229 7.45252 15.9013 7.61189 14.79C7.64939 14.5207 7.67564 14.1835 7.73564 13.8482C7.79284 13.4768 7.90867 13.1169 8.07875 12.7822C8.19485 12.5626 8.3477 12.3648 8.53062 12.1974C8.62598 12.1085 8.6889 11.99 8.7093 11.8609C8.72969 11.7318 8.70639 11.5996 8.64311 11.4854L4.97572 4.85926L10.2434 11.3893C10.3034 11.465 10.3792 11.5265 10.4655 11.5694C10.5518 11.6123 10.6464 11.6357 10.7427 11.6377C10.839 11.6398 10.9345 11.6206 11.0225 11.5814C11.1106 11.5422 11.189 11.484 11.2521 11.411C11.3188 11.3328 11.3567 11.2339 11.3592 11.131C11.3618 11.0281 11.3289 10.9274 11.2662 10.846C10.9221 10.4043 10.5584 9.95412 10.2059 9.5115L8.8803 7.86344L6.21975 4.57297L2.5918 0L6.56099 4.27821L9.39309 7.42364L10.8059 9.00013C11.2746 9.53034 11.7433 10.0436 12.2121 10.6011L12.2889 10.6953L12.3058 10.8412C12.3287 11.0392 12.3173 11.2396 12.2721 11.4336Z" fill="black"/> </g>
<path d="M13.4856 26.8466C13.231 26.6491 13.0046 26.4174 12.8125 26.1582C13.0186 26.405 13.2436 26.6352 13.4856 26.8466Z" fill="black"/>
</svg> </svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.3078 13.7367C14.9143 14.8093 13.5578 15.1418 12.9212 15.203C11.9566 15.2972 11.7569 14.7387 11.9453 14.0371C12.0072 13.7859 12.1444 13.5599 12.3384 13.3898C12.5325 13.2196 12.7739 13.1135 13.03 13.0859C13.2823 13.0627 13.5361 13.1114 13.7621 13.2265C13.9881 13.3417 14.1773 13.5185 14.3078 13.7367V13.7367Z" fill="#FF007A"/> <g style="mix-blend-mode:darken">
<path d="M18.094 11.7521C17.389 17.169 26.8931 16.0418 26.7122 20.3531C27.6497 19.1288 28.0528 15.8026 25.2929 14.0905C22.8339 12.5639 19.6296 13.3992 18.094 11.7521Z" fill="#FF007A"/> <path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2967 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="#FF007A"/>
<path d="M23.5684 9.7093C23.5065 9.65186 23.4427 9.59535 23.3809 9.53885C23.4437 9.59629 23.5065 9.65657 23.5684 9.7093Z" fill="#FF007A"/> <path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="#FF007A"/>
<path d="M25.7771 13.6581L25.7715 13.6496C25.6857 13.4895 25.5879 13.3362 25.479 13.191C25.1586 12.7549 24.699 12.4424 24.1769 12.3057C23.8293 12.2156 23.4739 12.1595 23.1156 12.1381C22.7528 12.1117 22.3825 12.0966 22.0085 12.0769C21.2585 12.0354 20.4907 11.9582 19.7407 11.7435C19.5532 11.6898 19.3658 11.6314 19.1848 11.5608C19.0911 11.5269 19.0039 11.4902 18.9111 11.4506C18.8183 11.4111 18.7236 11.3658 18.6298 11.3178C18.2772 11.1238 17.9505 10.8855 17.6577 10.6087C17.0802 10.0691 16.6265 9.45692 16.1718 8.86362C15.7432 8.26804 15.2831 7.6959 14.7937 7.14964C14.3108 6.61451 13.74 6.16664 13.1063 5.82555C12.45 5.49351 11.7372 5.28902 11.0054 5.22283C11.7646 5.14002 12.5326 5.23412 13.2497 5.49782C13.9733 5.78002 14.6348 6.20204 15.1968 6.73999C15.5637 7.08565 15.9082 7.45463 16.228 7.84465C18.6092 7.37378 20.5404 7.79192 22.0244 8.6037L22.0582 8.62065C22.5305 8.87801 22.9732 9.18689 23.3781 9.54168C23.4428 9.59819 23.5066 9.65469 23.5656 9.71214C23.8817 10.0039 24.1759 10.3187 24.4459 10.6539L24.4665 10.6812C25.3431 11.7962 25.7762 12.9555 25.7771 13.6581Z" fill="#FF007A"/> <path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="#FF007A"/>
<path d="M25.776 13.657L25.7705 13.6458L25.776 13.657Z" fill="#FF007A"/> <path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="#FF007A"/>
<path d="M11.1263 5.92056C11.7338 6.00909 12.3535 6.25206 12.7528 6.7154C13.1522 7.17874 13.2994 7.78146 13.4147 8.35498C13.5084 8.79949 13.5825 9.25247 13.7569 9.67343C13.8412 9.87873 13.965 10.0586 14.0681 10.2526C14.1534 10.4136 14.309 10.5587 14.369 10.731C14.3798 10.7558 14.3838 10.7829 14.3806 10.8097C14.3775 10.8365 14.3674 10.862 14.3512 10.8836C14.1394 11.119 13.5675 10.8572 13.3519 10.7517C12.9791 10.5661 12.6453 10.3104 12.3685 9.99834C11.5032 9.03304 11.056 7.64396 11.0832 6.37919C11.0892 6.22564 11.1036 6.07253 11.1263 5.92056Z" fill="#FF007A"/> <path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91234 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27859C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="#FF007A"/>
<path d="M21.2865 16.8057C19.974 20.4851 25.927 22.9535 23.6967 26.6941C25.9851 25.7448 27.0716 22.8772 26.122 20.6019C25.2914 18.6045 22.8352 17.8765 21.2865 16.8057Z" fill="#FF007A"/> <path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79008 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="#FF007A"/>
<path d="M13.4131 21.6174C13.7707 21.3466 14.1618 21.1234 14.5765 20.9535C14.996 20.7848 15.4313 20.6586 15.8758 20.5768C16.758 20.4082 17.6308 20.3668 18.3639 20.0701C18.7261 19.928 19.0594 19.7206 19.3473 19.458C19.626 19.1979 19.8393 18.8751 19.9698 18.5162C20.1034 18.1375 20.1577 17.7351 20.1291 17.3343C20.096 16.9038 19.999 16.4807 19.8413 16.079C20.1681 16.4204 20.4061 16.8375 20.5341 17.2933C20.6621 17.7492 20.6763 18.2297 20.5754 18.6923C20.4607 19.1821 20.2131 19.6303 19.8601 19.9872C19.5107 20.333 19.0893 20.5967 18.6264 20.7595C18.1846 20.9151 17.7238 21.0102 17.2567 21.042C16.8067 21.0797 16.3717 21.09 15.9443 21.1154C15.08 21.1519 14.2263 21.3211 13.4131 21.6174V21.6174Z" fill="#FF007A"/> <path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7563 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87091 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="#FF007A"/>
<path d="M21.8102 28.0852C21.678 28.1907 21.5458 28.3028 21.4005 28.4007C21.2543 28.4975 21.101 28.5831 20.9421 28.6569C20.6111 28.8191 20.2472 28.9019 19.879 28.8989C18.8815 28.8801 18.1765 28.1342 17.7641 27.2913C17.4828 26.7169 17.2888 26.0962 16.955 25.5491C16.4778 24.7665 15.6613 24.1365 14.7051 24.2532C14.3151 24.3022 13.9495 24.4783 13.7329 24.8183C13.1629 25.7064 13.9814 26.9504 15.0248 26.7743C15.1135 26.7607 15.2003 26.7367 15.2835 26.7027C15.3664 26.6673 15.4437 26.62 15.5132 26.5624C15.6589 26.4405 15.7688 26.281 15.831 26.101C15.8995 25.9133 15.9148 25.7101 15.875 25.5142C15.8322 25.3095 15.7119 25.1295 15.5394 25.0123C15.74 25.1066 15.8962 25.2754 15.9754 25.4832C16.0574 25.6969 16.0785 25.9295 16.0363 26.1546C15.9953 26.3892 15.8924 26.6085 15.7382 26.7894C15.6563 26.8824 15.5616 26.9633 15.4569 27.0295C15.3532 27.095 15.2421 27.1478 15.126 27.1868C14.8905 27.2677 14.6399 27.2937 14.3929 27.2631C14.0461 27.2135 13.7191 27.071 13.4461 26.8506C13.1236 26.5944 12.8836 26.2592 12.592 25.971C12.2578 25.6187 11.8635 25.3294 11.4277 25.1168C11.1272 24.9842 10.8127 24.8863 10.4902 24.8249C10.328 24.791 10.164 24.7665 9.99991 24.7458C9.92491 24.7383 9.55742 24.6563 9.50586 24.7043C10.0129 24.2354 10.5657 23.819 11.1558 23.4612C11.7617 23.1 12.4123 22.8205 13.0908 22.6297C13.7942 22.4308 14.53 22.3741 15.2554 22.463C15.6288 22.508 15.9945 22.6031 16.3428 22.7455C16.7078 22.892 17.0447 23.101 17.3384 23.3633C17.6292 23.6384 17.8641 23.9675 18.0303 24.3324C18.1803 24.6739 18.2923 25.0311 18.364 25.3975C18.5778 26.4927 18.499 28.1907 19.9268 28.4412C20.0012 28.4557 20.0763 28.4664 20.1518 28.4732L20.3852 28.4789C20.5457 28.4674 20.7052 28.4444 20.8624 28.4101C21.1881 28.3332 21.5056 28.2243 21.8102 28.0852V28.0852Z" fill="#FF007A"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926352 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="#FF007A"/>
<path d="M13.514 26.8916L13.4766 26.8622L13.514 26.8916Z" fill="#FF007A"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="#FF007A"/>
<path d="M12.2325 11.4336C12.1805 11.6343 12.0897 11.8228 11.9653 11.9883C11.7338 12.2902 11.4273 12.5256 11.0766 12.6711C10.7612 12.8076 10.4272 12.8958 10.0857 12.9329C10.0116 12.9423 9.93478 12.9479 9.86072 12.9536C9.64444 12.9618 9.43648 13.0394 9.26728 13.175C9.09808 13.3106 8.97653 13.497 8.92043 13.707C8.89474 13.8115 8.87564 13.9175 8.86324 14.0244C8.82949 14.3012 8.82387 14.5894 8.79387 14.9378C8.72041 15.5308 8.54967 16.1074 8.28857 16.6443C7.94921 17.3619 7.5686 17.9401 7.65672 18.767C7.71484 19.3038 7.98858 19.6635 8.35232 20.0346C9.00855 20.7089 10.4776 21.0102 10.1495 22.6715C9.95165 23.6631 8.31482 24.7038 6.01426 25.0673C6.24301 25.0324 5.72083 24.1481 5.68802 24.0916C5.44147 23.7036 5.17148 23.3382 4.97555 22.9173C4.59118 22.1008 4.41306 21.1562 4.57056 20.2615C4.73649 19.3198 5.43022 18.5984 6.00677 17.8789C6.693 17.0229 7.41297 15.9013 7.57234 14.79C7.60984 14.5207 7.63609 14.1835 7.69609 13.8482C7.75329 13.4768 7.86912 13.1169 8.0392 12.7822C8.1553 12.5626 8.30815 12.3648 8.49106 12.1974C8.58643 12.1085 8.64935 11.99 8.66975 11.8609C8.69014 11.7318 8.66684 11.5996 8.60356 11.4854L4.93617 4.85926L10.2038 11.3893C10.2638 11.465 10.3396 11.5265 10.4259 11.5694C10.5122 11.6123 10.6069 11.6357 10.7031 11.6377C10.7994 11.6398 10.8949 11.6206 10.983 11.5814C11.071 11.5422 11.1494 11.484 11.2126 11.411C11.2793 11.3328 11.3171 11.2339 11.3197 11.131C11.3222 11.0281 11.2894 10.9274 11.2266 10.846C10.8826 10.4043 10.5188 9.95412 10.1663 9.5115L8.84075 7.86344L6.1802 4.57297L2.55225 0L6.52144 4.27821L9.35354 7.42364L10.7663 9.00013C11.235 9.53034 11.7038 10.0436 12.1725 10.6011L12.2494 10.6953L12.2663 10.8412C12.2891 11.0392 12.2777 11.2396 12.2325 11.4336Z" fill="#FF007A"/> </g>
<path d="M13.447 26.8463C13.1925 26.6488 12.966 26.4171 12.7739 26.1579C12.98 26.4047 13.205 26.6349 13.447 26.8463V26.8463Z" fill="#FF007A"/>
</svg> </svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.3078 13.7367C14.9143 14.8093 13.5578 15.1418 12.9212 15.203C11.9566 15.2972 11.7569 14.7387 11.9453 14.0371C12.0072 13.7859 12.1444 13.5599 12.3384 13.3898C12.5325 13.2196 12.7739 13.1135 13.03 13.0859C13.2823 13.0627 13.5361 13.1114 13.7621 13.2265C13.9881 13.3417 14.1773 13.5185 14.3078 13.7367V13.7367Z" fill="white"/> <path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2967 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
<path d="M18.094 11.7521C17.389 17.169 26.8931 16.0418 26.7122 20.3531C27.6497 19.1288 28.0528 15.8026 25.2929 14.0905C22.8339 12.5639 19.6296 13.3992 18.094 11.7521Z" fill="white"/> <path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="white"/>
<path d="M23.5684 9.7093C23.5065 9.65186 23.4427 9.59535 23.3809 9.53885C23.4437 9.59629 23.5065 9.65657 23.5684 9.7093Z" fill="white"/> <path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="white"/>
<path d="M25.7771 13.6581L25.7715 13.6496C25.6857 13.4895 25.5879 13.3362 25.479 13.191C25.1586 12.7549 24.699 12.4424 24.1769 12.3057C23.8293 12.2156 23.4739 12.1595 23.1156 12.1381C22.7528 12.1117 22.3825 12.0966 22.0085 12.0769C21.2585 12.0354 20.4907 11.9582 19.7407 11.7435C19.5532 11.6898 19.3658 11.6314 19.1848 11.5608C19.0911 11.5269 19.0039 11.4902 18.9111 11.4506C18.8183 11.4111 18.7236 11.3658 18.6298 11.3178C18.2772 11.1238 17.9505 10.8855 17.6577 10.6087C17.0802 10.0691 16.6265 9.45692 16.1718 8.86362C15.7432 8.26804 15.2831 7.6959 14.7937 7.14964C14.3108 6.61451 13.74 6.16664 13.1063 5.82555C12.45 5.49351 11.7372 5.28902 11.0054 5.22283C11.7646 5.14002 12.5326 5.23412 13.2497 5.49782C13.9733 5.78002 14.6348 6.20204 15.1968 6.73999C15.5637 7.08565 15.9082 7.45463 16.228 7.84465C18.6092 7.37378 20.5404 7.79192 22.0244 8.6037L22.0582 8.62065C22.5305 8.87801 22.9732 9.18689 23.3781 9.54168C23.4428 9.59819 23.5066 9.65469 23.5656 9.71214C23.8817 10.0039 24.1759 10.3187 24.4459 10.6539L24.4665 10.6812C25.3431 11.7962 25.7762 12.9555 25.7771 13.6581Z" fill="white"/> <path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="white"/>
<path d="M25.776 13.657L25.7705 13.6458L25.776 13.657Z" fill="white"/> <path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91234 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27859C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
<path d="M11.1263 5.92056C11.7338 6.00909 12.3535 6.25206 12.7528 6.7154C13.1522 7.17874 13.2994 7.78146 13.4147 8.35498C13.5084 8.79949 13.5825 9.25247 13.7569 9.67343C13.8412 9.87873 13.965 10.0586 14.0681 10.2526C14.1534 10.4136 14.309 10.5587 14.369 10.731C14.3798 10.7558 14.3838 10.7829 14.3806 10.8097C14.3775 10.8365 14.3674 10.862 14.3512 10.8836C14.1394 11.119 13.5675 10.8572 13.3519 10.7517C12.9791 10.5661 12.6453 10.3104 12.3685 9.99834C11.5032 9.03304 11.056 7.64396 11.0832 6.37919C11.0892 6.22564 11.1036 6.07253 11.1263 5.92056Z" fill="white"/> <path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79008 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
<path d="M21.2865 16.8057C19.974 20.4851 25.927 22.9535 23.6967 26.6941C25.9851 25.7448 27.0716 22.8772 26.122 20.6019C25.2914 18.6045 22.8352 17.8765 21.2865 16.8057Z" fill="white"/> <path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7563 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87091 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
<path d="M13.4131 21.6174C13.7707 21.3466 14.1618 21.1234 14.5765 20.9535C14.996 20.7848 15.4313 20.6586 15.8758 20.5768C16.758 20.4082 17.6308 20.3668 18.3639 20.0701C18.7261 19.928 19.0594 19.7206 19.3473 19.458C19.626 19.1979 19.8393 18.8751 19.9698 18.5162C20.1034 18.1375 20.1577 17.7351 20.1291 17.3343C20.096 16.9038 19.999 16.4807 19.8413 16.079C20.1681 16.4204 20.4061 16.8375 20.5341 17.2933C20.6621 17.7492 20.6763 18.2297 20.5754 18.6923C20.4607 19.1821 20.2131 19.6303 19.8601 19.9872C19.5107 20.333 19.0893 20.5967 18.6264 20.7595C18.1846 20.9151 17.7238 21.0102 17.2567 21.042C16.8067 21.0797 16.3717 21.09 15.9443 21.1154C15.08 21.1519 14.2263 21.3211 13.4131 21.6174V21.6174Z" fill="white"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926352 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
<path d="M21.8102 28.0852C21.678 28.1907 21.5458 28.3028 21.4005 28.4007C21.2543 28.4975 21.101 28.5831 20.9421 28.6569C20.6111 28.8191 20.2472 28.9019 19.879 28.8989C18.8815 28.8801 18.1765 28.1342 17.7641 27.2913C17.4828 26.7169 17.2888 26.0962 16.955 25.5491C16.4778 24.7665 15.6613 24.1365 14.7051 24.2532C14.3151 24.3022 13.9495 24.4783 13.7329 24.8183C13.1629 25.7064 13.9814 26.9504 15.0248 26.7743C15.1135 26.7607 15.2003 26.7367 15.2835 26.7027C15.3664 26.6673 15.4437 26.62 15.5132 26.5624C15.6589 26.4405 15.7688 26.281 15.831 26.101C15.8995 25.9133 15.9148 25.7101 15.875 25.5142C15.8322 25.3095 15.7119 25.1295 15.5394 25.0123C15.74 25.1066 15.8962 25.2754 15.9754 25.4832C16.0574 25.6969 16.0785 25.9295 16.0363 26.1546C15.9953 26.3892 15.8924 26.6085 15.7382 26.7894C15.6563 26.8824 15.5616 26.9633 15.4569 27.0295C15.3532 27.095 15.2421 27.1478 15.126 27.1868C14.8905 27.2677 14.6399 27.2937 14.3929 27.2631C14.0461 27.2135 13.7191 27.071 13.4461 26.8506C13.1236 26.5944 12.8836 26.2592 12.592 25.971C12.2578 25.6187 11.8635 25.3294 11.4277 25.1168C11.1272 24.9842 10.8127 24.8863 10.4902 24.8249C10.328 24.791 10.164 24.7665 9.99991 24.7458C9.92491 24.7383 9.55742 24.6563 9.50586 24.7043C10.0129 24.2354 10.5657 23.819 11.1558 23.4612C11.7617 23.1 12.4123 22.8205 13.0908 22.6297C13.7942 22.4308 14.53 22.3741 15.2554 22.463C15.6288 22.508 15.9945 22.6031 16.3428 22.7455C16.7078 22.892 17.0447 23.101 17.3384 23.3633C17.6292 23.6384 17.8641 23.9675 18.0303 24.3324C18.1803 24.6739 18.2923 25.0311 18.364 25.3975C18.5778 26.4927 18.499 28.1907 19.9268 28.4412C20.0012 28.4557 20.0763 28.4664 20.1518 28.4732L20.3852 28.4789C20.5457 28.4674 20.7052 28.4444 20.8624 28.4101C21.1881 28.3332 21.5056 28.2243 21.8102 28.0852V28.0852Z" fill="white"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="white"/>
<path d="M13.514 26.8916L13.4766 26.8622L13.514 26.8916Z" fill="white"/>
<path d="M12.2325 11.4336C12.1805 11.6343 12.0897 11.8228 11.9653 11.9883C11.7338 12.2902 11.4273 12.5256 11.0766 12.6711C10.7612 12.8076 10.4272 12.8958 10.0857 12.9329C10.0116 12.9423 9.93478 12.9479 9.86072 12.9536C9.64444 12.9618 9.43648 13.0394 9.26728 13.175C9.09808 13.3106 8.97653 13.497 8.92043 13.707C8.89474 13.8115 8.87564 13.9175 8.86324 14.0244C8.82949 14.3012 8.82387 14.5894 8.79387 14.9378C8.72041 15.5308 8.54967 16.1074 8.28857 16.6443C7.94921 17.3619 7.5686 17.9401 7.65672 18.767C7.71484 19.3038 7.98858 19.6635 8.35232 20.0346C9.00855 20.7089 10.4776 21.0102 10.1495 22.6715C9.95165 23.6631 8.31482 24.7038 6.01426 25.0673C6.24301 25.0324 5.72083 24.1481 5.68802 24.0916C5.44147 23.7036 5.17148 23.3382 4.97555 22.9173C4.59118 22.1008 4.41306 21.1562 4.57056 20.2615C4.73649 19.3198 5.43022 18.5984 6.00677 17.8789C6.693 17.0229 7.41297 15.9013 7.57234 14.79C7.60984 14.5207 7.63609 14.1835 7.69609 13.8482C7.75329 13.4768 7.86912 13.1169 8.0392 12.7822C8.1553 12.5626 8.30815 12.3648 8.49106 12.1974C8.58643 12.1085 8.64935 11.99 8.66975 11.8609C8.69014 11.7318 8.66684 11.5996 8.60356 11.4854L4.93617 4.85926L10.2038 11.3893C10.2638 11.465 10.3396 11.5265 10.4259 11.5694C10.5122 11.6123 10.6069 11.6357 10.7031 11.6377C10.7994 11.6398 10.8949 11.6206 10.983 11.5814C11.071 11.5422 11.1494 11.484 11.2126 11.411C11.2793 11.3328 11.3171 11.2339 11.3197 11.131C11.3222 11.0281 11.2894 10.9274 11.2266 10.846C10.8826 10.4043 10.5188 9.95412 10.1663 9.5115L8.84075 7.86344L6.1802 4.57297L2.55225 0L6.52144 4.27821L9.35354 7.42364L10.7663 9.00013C11.235 9.53034 11.7038 10.0436 12.1725 10.6011L12.2494 10.6953L12.2663 10.8412C12.2891 11.0392 12.2777 11.2396 12.2325 11.4336Z" fill="white"/>
<path d="M13.447 26.8463C13.1925 26.6488 12.966 26.4171 12.7739 26.1579C12.98 26.4047 13.205 26.6349 13.447 26.8463V26.8463Z" fill="white"/>
</svg> </svg>
...@@ -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>
)
}
import { ChainId } from '@uniswap/sdk' import { ChainId, TokenAmount, JSBI } from '@uniswap/sdk'
import React from 'react' import React, { useState } from 'react'
import { isMobile } from 'react-device-detect'
import { Text } from 'rebass' import { Text } from 'rebass'
import { NavLink, withRouter } from 'react-router-dom'
import { darken } from 'polished'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import Logo from '../../assets/svg/logo.svg' import Logo from '../../assets/svg/logo.svg'
import LogoDark from '../../assets/svg/logo_white.svg' import LogoDark from '../../assets/svg/logo_white.svg'
import Wordmark from '../../assets/svg/wordmark.svg'
import WordmarkDark from '../../assets/svg/wordmark_white.svg'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { useDarkModeManager } from '../../state/user/hooks' import { useDarkModeManager } from '../../state/user/hooks'
import { useETHBalances } from '../../state/wallet/hooks' import { useETHBalances, useAggregateUniBalance } from '../../state/wallet/hooks'
import { CardNoise } from '../earn/styled'
import { CountUp } from 'use-count-up'
import { TYPE, ExternalLink } from '../../theme'
import { YellowCard } from '../Card' import { YellowCard } from '../Card'
import Settings from '../Settings' import Settings from '../Settings'
import Menu from '../Menu' import Menu from '../Menu'
import Row, { RowBetween } from '../Row' import Row, { RowFixed } from '../Row'
import Web3Status from '../Web3Status' import Web3Status from '../Web3Status'
import VersionSwitch from './VersionSwitch' import ClaimModal from '../claim/ClaimModal'
import { useToggleSelfClaimModal, useShowClaimPopup } from '../../state/application/hooks'
import { useUserHasAvailableClaim } from '../../state/claim/hooks'
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { Dots } from '../swap/styleds'
import Modal from '../Modal'
import UniBalanceContent from './UniBalanceContent'
import usePrevious from '../../hooks/usePrevious'
const HeaderFrame = styled.div` const HeaderFrame = styled.div`
display: flex; display: grid;
grid-template-columns: 1fr 120px;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-direction: column; align-items: center;
flex-direction: row;
width: 100%; width: 100%;
top: 0; top: 0;
position: absolute; position: relative;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 1rem;
z-index: 2; z-index: 2;
${({ theme }) => theme.mediaWidth.upToExtraSmall` ${({ theme }) => theme.mediaWidth.upToMedium`
padding: 12px 0 0 0; grid-template-columns: 1fr;
padding: 0 1rem;
width: calc(100%); width: calc(100%);
position: relative; position: relative;
`}; `};
${({ theme }) => theme.mediaWidth.upToExtraSmall`
padding: 0.5rem 1rem;
`}
` `
const HeaderElement = styled.div` const HeaderControls = styled.div`
display: flex; display: flex;
flex-direction: row;
align-items: center; align-items: center;
justify-self: flex-end;
${({ theme }) => theme.mediaWidth.upToMedium`
flex-direction: row;
justify-content: space-between;
justify-self: center;
width: 100%;
max-width: 960px;
padding: 1rem;
position: fixed;
bottom: 0px;
left: 0px;
width: 100%;
z-index: 99;
height: 72px;
border-radius: 12px 12px 0 0;
background-color: ${({ theme }) => theme.bg1};
`};
` `
const HeaderElementWrap = styled.div` const HeaderElement = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px;
${({ theme }) => theme.mediaWidth.upToSmall` ${({ theme }) => theme.mediaWidth.upToMedium`
margin-top: 0.5rem; flex-direction: row-reverse;
`}; align-items: center;
`};
` `
const Title = styled.a` const HeaderElementWrap = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
pointer-events: auto;
:hover {
cursor: pointer;
}
` `
const TitleText = styled(Row)` const HeaderRow = styled(RowFixed)`
width: fit-content; ${({ theme }) => theme.mediaWidth.upToMedium`
white-space: nowrap; width: 100%;
${({ theme }) => theme.mediaWidth.upToExtraSmall`
display: none;
`}; `};
` `
const HeaderLinks = styled(Row)`
justify-content: center;
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem 0 1rem 1rem;
justify-content: flex-end;
`};
`
const AccountElement = styled.div<{ active: boolean }>` const AccountElement = styled.div<{ active: boolean }>`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
...@@ -77,24 +118,76 @@ const AccountElement = styled.div<{ active: boolean }>` ...@@ -77,24 +118,76 @@ const AccountElement = styled.div<{ active: boolean }>`
border-radius: 12px; border-radius: 12px;
white-space: nowrap; white-space: nowrap;
width: 100%; width: 100%;
cursor: pointer;
:focus { :focus {
border: 1px solid blue; border: 1px solid blue;
} }
/* :hover {
background-color: ${({ theme, active }) => (!active ? theme.bg2 : theme.bg4)};
} */
` `
const TestnetWrapper = styled.div` const UNIAmount = styled(AccountElement)`
white-space: nowrap; color: white;
padding: 4px 8px;
height: 36px;
font-weight: 500;
background-color: ${({ theme }) => theme.bg3};
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
`
const UNIWrapper = styled.span`
width: fit-content; width: fit-content;
margin-left: 10px; position: relative;
pointer-events: auto; cursor: pointer;
:hover {
opacity: 0.8;
}
:active {
opacity: 0.9;
}
`
const HideSmall = styled.span`
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
` `
const NetworkCard = styled(YellowCard)` const NetworkCard = styled(YellowCard)`
width: fit-content;
margin-right: 10px;
border-radius: 12px; border-radius: 12px;
padding: 8px 12px; padding: 8px 12px;
${({ theme }) => theme.mediaWidth.upToSmall`
margin: 0;
margin-right: 0.5rem;
width: initial;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
`};
`
const BalanceText = styled(Text)`
${({ theme }) => theme.mediaWidth.upToExtraSmall`
display: none;
`};
`
const Title = styled.a`
display: flex;
align-items: center;
pointer-events: auto;
justify-self: flex-start;
margin-right: 12px;
${({ theme }) => theme.mediaWidth.upToSmall`
justify-self: center;
`};
:hover {
cursor: pointer;
}
` `
const UniIcon = styled.div` const UniIcon = styled.div`
...@@ -102,78 +195,195 @@ const UniIcon = styled.div` ...@@ -102,78 +195,195 @@ const UniIcon = styled.div`
:hover { :hover {
transform: rotate(-5deg); transform: rotate(-5deg);
} }
${({ theme }) => theme.mediaWidth.upToSmall`
img {
width: 4.5rem;
}
`};
` `
const HeaderControls = styled.div` const activeClassName = 'ACTIVE'
display: flex;
flex-direction: row;
align-items: center;
${({ theme }) => theme.mediaWidth.upToSmall` const StyledNavLink = styled(NavLink).attrs({
flex-direction: column; activeClassName
align-items: flex-end; })`
`}; ${({ theme }) => theme.flexRowNoWrap}
align-items: left;
border-radius: 3rem;
outline: none;
cursor: pointer;
text-decoration: none;
color: ${({ theme }) => theme.text2};
font-size: 1rem;
width: fit-content;
margin: 0 12px;
font-weight: 500;
&.${activeClassName} {
border-radius: 12px;
font-weight: 600;
color: ${({ theme }) => theme.text1};
}
:hover,
:focus {
color: ${({ theme }) => darken(0.1, theme.text1)};
}
` `
const BalanceText = styled(Text)` const StyledExternalLink = styled(ExternalLink).attrs({
activeClassName
})<{ isActive?: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: left;
border-radius: 3rem;
outline: none;
cursor: pointer;
text-decoration: none;
color: ${({ theme }) => theme.text2};
font-size: 1rem;
width: fit-content;
margin: 0 12px;
font-weight: 500;
&.${activeClassName} {
border-radius: 12px;
font-weight: 600;
color: ${({ theme }) => theme.text1};
}
:hover,
:focus {
color: ${({ theme }) => darken(0.1, theme.text1)};
}
${({ theme }) => theme.mediaWidth.upToExtraSmall` ${({ theme }) => theme.mediaWidth.upToExtraSmall`
display: none; display: none;
`}; `}
` `
const NETWORK_LABELS: { [chainId in ChainId]: string | null } = { const NETWORK_LABELS: { [chainId in ChainId]?: string } = {
[ChainId.MAINNET]: null,
[ChainId.RINKEBY]: 'Rinkeby', [ChainId.RINKEBY]: 'Rinkeby',
[ChainId.ROPSTEN]: 'Ropsten', [ChainId.ROPSTEN]: 'Ropsten',
[ChainId.GÖRLI]: 'Görli', [ChainId.GÖRLI]: 'Görli',
[ChainId.KOVAN]: 'Kovan' [ChainId.KOVAN]: 'Kovan'
} }
export default function Header() { function Header({ history }: { history: any }) {
const { account, chainId } = useActiveWeb3React() const { account, chainId } = useActiveWeb3React()
const { t } = useTranslation()
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
const [isDark] = useDarkModeManager() const [isDark] = useDarkModeManager()
const toggleClaimModal = useToggleSelfClaimModal()
const availableClaim: boolean = useUserHasAvailableClaim(account)
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance()
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
const showClaimPopup = useShowClaimPopup()
const countUpValue = aggregateBalance?.toFixed(0) ?? '0'
const countUpValuePrevious = usePrevious(countUpValue) ?? '0'
return ( return (
<HeaderFrame> <HeaderFrame>
<RowBetween style={{ alignItems: 'flex-start' }} padding="1rem 1rem 0 1rem"> <ClaimModal />
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
</Modal>
<HeaderRow>
<Title href="." style={{}}>
<UniIcon>
<img width={'24px'} src={isDark ? LogoDark : Logo} alt="logo" />
</UniIcon>
</Title>
<HeaderLinks>
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => history.location.pathname.includes('/swap')}>
{t('swap')}
</StyledNavLink>
<StyledNavLink
id={`pool-nav-link`}
to={'/pool'}
isActive={() =>
history.location.pathname.includes('/pool') ||
history.location.pathname.includes('/add') ||
history.location.pathname.includes('/remove')
}
>
{t('pool')}
</StyledNavLink>
<StyledNavLink id={`stake-nav-link`} to={'/uni'} isActive={() => history.location.pathname.includes('/uni')}>
UNI
</StyledNavLink>
<StyledNavLink
id={`stake-nav-link`}
to={'/vote'}
isActive={() => history.location.pathname.includes('/vote')}
>
Vote
</StyledNavLink>
<StyledExternalLink id={`stake-nav-link`} href={'https://uniswap.info'}>
Charts <span style={{ fontSize: '11px' }}></span>
</StyledExternalLink>
</HeaderLinks>
</HeaderRow>
<HeaderControls>
<HeaderElement> <HeaderElement>
<Title href="."> <HideSmall>
<UniIcon> {chainId && NETWORK_LABELS[chainId] && (
<img src={isDark ? LogoDark : Logo} alt="logo" /> <NetworkCard title={NETWORK_LABELS[chainId]}>{NETWORK_LABELS[chainId]}</NetworkCard>
</UniIcon> )}
<TitleText> </HideSmall>
<img style={{ marginLeft: '4px', marginTop: '4px' }} src={isDark ? WordmarkDark : Wordmark} alt="logo" /> {availableClaim && !showClaimPopup && (
</TitleText> <UNIWrapper onClick={toggleClaimModal}>
</Title> <UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
<TYPE.white padding="0 2px">
{claimTxn && !claimTxn?.receipt ? <Dots>Claiming UNI</Dots> : 'Claim UNI'}
</TYPE.white>
</UNIAmount>
<CardNoise />
</UNIWrapper>
)}
{!availableClaim && aggregateBalance && JSBI.greaterThan(aggregateBalance.raw, JSBI.BigInt(0)) && (
<UNIWrapper onClick={() => setShowUniBalanceModal(true)}>
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
<HideSmall>
<TYPE.white
style={{
paddingRight: '.4rem'
}}
>
<CountUp
key={countUpValue}
isCounting
start={parseFloat(countUpValuePrevious)}
end={parseFloat(countUpValue)}
thousandsSeparator={','}
duration={1}
/>
</TYPE.white>
</HideSmall>
{'UNI'}
</UNIAmount>
<CardNoise />
</UNIWrapper>
)}
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
{account && userEthBalance ? (
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
{userEthBalance?.toSignificant(4)} ETH
</BalanceText>
) : null}
<Web3Status />
</AccountElement>
</HeaderElement> </HeaderElement>
<HeaderControls> <HeaderElementWrap>
<HeaderElement> <Settings />
<TestnetWrapper> <Menu />
{!isMobile && chainId && NETWORK_LABELS[chainId] && <NetworkCard>{NETWORK_LABELS[chainId]}</NetworkCard>} </HeaderElementWrap>
</TestnetWrapper> </HeaderControls>
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
{account && userEthBalance ? (
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
{userEthBalance?.toSignificant(4)} ETH
</BalanceText>
) : null}
<Web3Status />
</AccountElement>
</HeaderElement>
<HeaderElementWrap>
<VersionSwitch />
<Settings />
<Menu />
</HeaderElementWrap>
</HeaderControls>
</RowBetween>
</HeaderFrame> </HeaderFrame>
) )
} }
export default withRouter(Header)
...@@ -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 source diff could not be displayed because it is too large. You can view the blob instead.
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}
......
import React, { Suspense } from 'react' import React, { Suspense } from 'react'
import { HashRouter, Route, Switch } from 'react-router-dom' import { Route, Switch } from 'react-router-dom'
import styled from 'styled-components' import styled from 'styled-components'
import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter' import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter'
import AddressClaimModal from '../components/claim/AddressClaimModal'
import Header from '../components/Header' import Header from '../components/Header'
import Polling from '../components/Header/Polling'
import URLWarning from '../components/Header/URLWarning'
import Popups from '../components/Popups' import Popups from '../components/Popups'
import Web3ReactManager from '../components/Web3ReactManager' import Web3ReactManager from '../components/Web3ReactManager'
import { ApplicationModal } from '../state/application/actions'
import { useModalOpen, useToggleModal } from '../state/application/hooks'
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader' import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
import AddLiquidity from './AddLiquidity' import AddLiquidity from './AddLiquidity'
import { import {
...@@ -12,6 +17,8 @@ import { ...@@ -12,6 +17,8 @@ import {
RedirectOldAddLiquidityPathStructure, RedirectOldAddLiquidityPathStructure,
RedirectToAddLiquidity RedirectToAddLiquidity
} from './AddLiquidity/redirects' } from './AddLiquidity/redirects'
import Earn from './Earn'
import Manage from './Earn/Manage'
import MigrateV1 from './MigrateV1' import MigrateV1 from './MigrateV1'
import MigrateV1Exchange from './MigrateV1/MigrateV1Exchange' import MigrateV1Exchange from './MigrateV1/MigrateV1Exchange'
import RemoveV1Exchange from './MigrateV1/RemoveV1Exchange' import RemoveV1Exchange from './MigrateV1/RemoveV1Exchange'
...@@ -20,7 +27,10 @@ import PoolFinder from './PoolFinder' ...@@ -20,7 +27,10 @@ import PoolFinder from './PoolFinder'
import RemoveLiquidity from './RemoveLiquidity' import RemoveLiquidity from './RemoveLiquidity'
import { RedirectOldRemoveLiquidityPathStructure } from './RemoveLiquidity/redirects' import { RedirectOldRemoveLiquidityPathStructure } from './RemoveLiquidity/redirects'
import Swap from './Swap' import Swap from './Swap'
import { RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects' import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
import Vote from './Vote'
import VotePage from './Vote/VotePage'
const AppWrapper = styled.div` const AppWrapper = styled.div`
display: flex; display: flex;
...@@ -39,15 +49,16 @@ const BodyWrapper = styled.div` ...@@ -39,15 +49,16 @@ const BodyWrapper = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
padding-top: 160px; padding-top: 100px;
align-items: center; align-items: center;
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
z-index: 10; z-index: 10;
${({ theme }) => theme.mediaWidth.upToExtraSmall` ${({ theme }) => theme.mediaWidth.upToSmall`
padding: 16px; padding: 16px;
padding-top: 2rem;
`}; `};
z-index: 1; z-index: 1;
...@@ -57,41 +68,56 @@ const Marginer = styled.div` ...@@ -57,41 +68,56 @@ const Marginer = styled.div`
margin-top: 5rem; margin-top: 5rem;
` `
function TopLevelModals() {
const open = useModalOpen(ApplicationModal.ADDRESS_CLAIM)
const toggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
return <AddressClaimModal isOpen={open} onDismiss={toggle} />
}
export default function App() { export default function App() {
return ( return (
<Suspense fallback={null}> <Suspense fallback={null}>
<HashRouter> <Route component={GoogleAnalyticsReporter} />
<Route component={GoogleAnalyticsReporter} /> <Route component={DarkModeQueryParamReader} />
<Route component={DarkModeQueryParamReader} /> <AppWrapper>
<AppWrapper> <URLWarning />
<HeaderWrapper> <HeaderWrapper>
<Header /> <Header />
</HeaderWrapper> </HeaderWrapper>
<BodyWrapper> <BodyWrapper>
<Popups /> <Popups />
<Web3ReactManager> <Polling />
<Switch> <TopLevelModals />
<Route exact strict path="/swap" component={Swap} /> <Web3ReactManager>
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} /> <Switch>
<Route exact strict path="/send" component={RedirectPathToSwapOnly} /> <Route exact strict path="/swap" component={Swap} />
<Route exact strict path="/find" component={PoolFinder} /> <Route exact strict path="/claim" component={OpenClaimAddressModalAndRedirectToSwap} />
<Route exact strict path="/pool" component={Pool} /> <Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
<Route exact strict path="/create" component={RedirectToAddLiquidity} /> <Route exact strict path="/send" component={RedirectPathToSwapOnly} />
<Route exact path="/add" component={AddLiquidity} /> <Route exact strict path="/find" component={PoolFinder} />
<Route exact path="/add/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} /> <Route exact strict path="/pool" component={Pool} />
<Route exact path="/add/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} /> <Route exact strict path="/uni" component={Earn} />
<Route exact strict path="/remove/v1/:address" component={RemoveV1Exchange} /> <Route exact strict path="/vote" component={Vote} />
<Route exact strict path="/remove/:tokens" component={RedirectOldRemoveLiquidityPathStructure} /> <Route exact strict path="/create" component={RedirectToAddLiquidity} />
<Route exact strict path="/remove/:currencyIdA/:currencyIdB" component={RemoveLiquidity} /> <Route exact path="/add" component={AddLiquidity} />
<Route exact strict path="/migrate/v1" component={MigrateV1} /> <Route exact path="/add/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
<Route exact strict path="/migrate/v1/:address" component={MigrateV1Exchange} /> <Route exact path="/add/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
<Route component={RedirectPathToSwapOnly} /> <Route exact path="/create" component={AddLiquidity} />
</Switch> <Route exact path="/create/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
</Web3ReactManager> <Route exact path="/create/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
<Marginer /> <Route exact strict path="/remove/v1/:address" component={RemoveV1Exchange} />
</BodyWrapper> <Route exact strict path="/remove/:tokens" component={RedirectOldRemoveLiquidityPathStructure} />
</AppWrapper> <Route exact strict path="/remove/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
</HashRouter> <Route exact strict path="/migrate/v1" component={MigrateV1} />
<Route exact strict path="/migrate/v1/:address" component={MigrateV1Exchange} />
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
<Route exact strict path="/vote/:id" component={VotePage} />
<Route component={RedirectPathToSwapOnly} />
</Switch>
</Web3ReactManager>
<Marginer />
</BodyWrapper>
</AppWrapper>
</Suspense> </Suspense>
) )
} }
import React, { useEffect, useMemo, useState } from 'react'
import { STAKING_GENESIS, REWARDS_DURATION_DAYS } from '../../state/stake/hooks'
import { TYPE } from '../../theme'
const MINUTE = 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const REWARDS_DURATION = DAY * REWARDS_DURATION_DAYS
export function Countdown({ exactEnd }: { exactEnd?: Date }) {
// get end/beginning times
const end = useMemo(() => (exactEnd ? Math.floor(exactEnd.getTime() / 1000) : STAKING_GENESIS + REWARDS_DURATION), [
exactEnd
])
const begin = useMemo(() => end - REWARDS_DURATION, [end])
// get current time
const [time, setTime] = useState(() => Math.floor(Date.now() / 1000))
useEffect((): (() => void) | void => {
// we only need to tick if rewards haven't ended yet
if (time <= end) {
const timeout = setTimeout(() => setTime(Math.floor(Date.now() / 1000)), 1000)
return () => {
clearTimeout(timeout)
}
}
}, [time, end])
const timeUntilGenesis = begin - time
const timeUntilEnd = end - time
let timeRemaining: number
let message: string
if (timeUntilGenesis >= 0) {
message = 'Rewards begin in'
timeRemaining = timeUntilGenesis
} else {
const ongoing = timeUntilEnd >= 0
if (ongoing) {
message = 'Rewards end in'
timeRemaining = timeUntilEnd
} else {
message = 'Rewards have ended!'
timeRemaining = Infinity
}
}
const days = (timeRemaining - (timeRemaining % DAY)) / DAY
timeRemaining -= days * DAY
const hours = (timeRemaining - (timeRemaining % HOUR)) / HOUR
timeRemaining -= hours * HOUR
const minutes = (timeRemaining - (timeRemaining % MINUTE)) / MINUTE
timeRemaining -= minutes * MINUTE
const seconds = timeRemaining
return (
<TYPE.black fontWeight={400}>
{message}{' '}
{Number.isFinite(timeRemaining) && (
<code>
{`${days}:${hours.toString().padStart(2, '0')}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`}
</code>
)}
</TYPE.black>
)
}
import React, { useCallback, useState } from 'react'
import { AutoColumn } from '../../components/Column'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
import { JSBI, TokenAmount, ETHER } from '@uniswap/sdk'
import { RouteComponentProps } from 'react-router-dom'
import DoubleCurrencyLogo from '../../components/DoubleLogo'
import { useCurrency } from '../../hooks/Tokens'
import { useWalletModalToggle } from '../../state/application/hooks'
import { TYPE } from '../../theme'
import { RowBetween } from '../../components/Row'
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
import { ButtonPrimary, ButtonEmpty } from '../../components/Button'
import StakingModal from '../../components/earn/StakingModal'
import { useStakingInfo } from '../../state/stake/hooks'
import UnstakingModal from '../../components/earn/UnstakingModal'
import ClaimRewardModal from '../../components/earn/ClaimRewardModal'
import { useTokenBalance } from '../../state/wallet/hooks'
import { useActiveWeb3React } from '../../hooks'
import { useColor } from '../../hooks/useColor'
import { CountUp } from 'use-count-up'
import { wrappedCurrency } from '../../utils/wrappedCurrency'
import { currencyId } from '../../utils/currencyId'
import { useTotalSupply } from '../../data/TotalSupply'
import { usePair } from '../../data/Reserves'
import usePrevious from '../../hooks/usePrevious'
import useUSDCPrice from '../../utils/useUSDCPrice'
import { BIG_INT_ZERO } from '../../constants'
const PageWrapper = styled(AutoColumn)`
max-width: 640px;
width: 100%;
`
const PositionInfo = styled(AutoColumn)<{ dim: any }>`
position: relative;
max-width: 640px;
width: 100%;
opacity: ${({ dim }) => (dim ? 0.6 : 1)};
`
const BottomSection = styled(AutoColumn)`
border-radius: 12px;
width: 100%;
position: relative;
`
const StyledDataCard = styled(DataCard)<{ bgColor?: any; showBackground?: any }>`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #1e1a31 0%, #3d51a5 100%);
z-index: 2;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
background: ${({ theme, bgColor, showBackground }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${showBackground ? theme.black : theme.bg5} 100%) `};
`
const StyledBottomCard = styled(DataCard)<{ dim: any }>`
background: ${({ theme }) => theme.bg3};
opacity: ${({ dim }) => (dim ? 0.4 : 1)};
margin-top: -40px;
padding: 0 1.25rem 1rem 1.25rem;
padding-top: 32px;
z-index: 1;
`
const PoolData = styled(DataCard)`
background: none;
border: 1px solid ${({ theme }) => theme.bg4};
padding: 1rem;
z-index: 1;
`
const VoteCard = styled(DataCard)`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%);
overflow: hidden;
`
const DataRow = styled(RowBetween)`
justify-content: center;
gap: 12px;
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
gap: 12px;
`};
`
export default function Manage({
match: {
params: { currencyIdA, currencyIdB }
}
}: RouteComponentProps<{ currencyIdA: string; currencyIdB: string }>) {
const { account, chainId } = useActiveWeb3React()
// get currencies and pair
const [currencyA, currencyB] = [useCurrency(currencyIdA), useCurrency(currencyIdB)]
const tokenA = wrappedCurrency(currencyA ?? undefined, chainId)
const tokenB = wrappedCurrency(currencyB ?? undefined, chainId)
const [, stakingTokenPair] = usePair(tokenA, tokenB)
const stakingInfo = useStakingInfo(stakingTokenPair)?.[0]
// detect existing unstaked LP position to show add button if none found
const userLiquidityUnstaked = useTokenBalance(account ?? undefined, stakingInfo?.stakedAmount?.token)
const showAddLiquidityButton = Boolean(stakingInfo?.stakedAmount?.equalTo('0') && userLiquidityUnstaked?.equalTo('0'))
// toggle for staking modal and unstaking modal
const [showStakingModal, setShowStakingModal] = useState(false)
const [showUnstakingModal, setShowUnstakingModal] = useState(false)
const [showClaimRewardModal, setShowClaimRewardModal] = useState(false)
// fade cards if nothing staked or nothing earned yet
const disableTop = !stakingInfo?.stakedAmount || stakingInfo.stakedAmount.equalTo(JSBI.BigInt(0))
const token = currencyA === ETHER ? tokenB : tokenA
const WETH = currencyA === ETHER ? tokenA : tokenB
const backgroundColor = useColor(token)
// get WETH value of staked LP tokens
const totalSupplyOfStakingToken = useTotalSupply(stakingInfo?.stakedAmount?.token)
let valueOfTotalStakedAmountInWETH: TokenAmount | undefined
if (totalSupplyOfStakingToken && stakingTokenPair && stakingInfo && WETH) {
// 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
)
)
}
const countUpAmount = stakingInfo?.earnedAmount?.toFixed(6) ?? '0'
const countUpAmountPrevious = usePrevious(countUpAmount) ?? '0'
// get the USD value of staked WETH
const USDPrice = useUSDCPrice(WETH)
const valueOfTotalStakedAmountInUSDC =
valueOfTotalStakedAmountInWETH && USDPrice?.quote(valueOfTotalStakedAmountInWETH)
const toggleWalletModal = useWalletModalToggle()
const handleDepositClick = useCallback(() => {
if (account) {
setShowStakingModal(true)
} else {
toggleWalletModal()
}
}, [account, toggleWalletModal])
return (
<PageWrapper gap="lg" justify="center">
<RowBetween style={{ gap: '24px' }}>
<TYPE.mediumHeader style={{ margin: 0 }}>
{currencyA?.symbol}-{currencyB?.symbol} Liquidity Mining
</TYPE.mediumHeader>
<DoubleCurrencyLogo currency0={currencyA ?? undefined} currency1={currencyB ?? undefined} size={24} />
</RowBetween>
<DataRow style={{ gap: '24px' }}>
<PoolData>
<AutoColumn gap="sm">
<TYPE.body style={{ margin: 0 }}>Total deposits</TYPE.body>
<TYPE.body fontSize={24} fontWeight={500}>
{valueOfTotalStakedAmountInUSDC
? `$${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })}`
: `${valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH`}
</TYPE.body>
</AutoColumn>
</PoolData>
<PoolData>
<AutoColumn gap="sm">
<TYPE.body style={{ margin: 0 }}>Pool Rate</TYPE.body>
<TYPE.body fontSize={24} fontWeight={500}>
{stakingInfo?.totalRewardRate
?.multiply((60 * 60 * 24 * 7).toString())
?.toFixed(0, { groupSeparator: ',' }) ?? '-'}
{' UNI / week'}
</TYPE.body>
</AutoColumn>
</PoolData>
</DataRow>
{showAddLiquidityButton && (
<VoteCard>
<CardBGImage />
<CardNoise />
<CardSection>
<AutoColumn gap="md">
<RowBetween>
<TYPE.white fontWeight={600}>Step 1. Get UNI-V2 Liquidity tokens</TYPE.white>
</RowBetween>
<RowBetween style={{ marginBottom: '1rem' }}>
<TYPE.white fontSize={14}>
{`UNI-V2 LP tokens are required. Once you've added liquidity to the ${currencyA?.symbol}-${currencyB?.symbol} pool you can stake your liquidity tokens on this page.`}
</TYPE.white>
</RowBetween>
<ButtonPrimary
padding="8px"
borderRadius="8px"
width={'fit-content'}
as={Link}
to={`/add/${currencyA && currencyId(currencyA)}/${currencyB && currencyId(currencyB)}`}
>
{`Add ${currencyA?.symbol}-${currencyB?.symbol} liquidity`}
</ButtonPrimary>
</AutoColumn>
</CardSection>
<CardBGImage />
<CardNoise />
</VoteCard>
)}
{stakingInfo && (
<>
<StakingModal
isOpen={showStakingModal}
onDismiss={() => setShowStakingModal(false)}
stakingInfo={stakingInfo}
userLiquidityUnstaked={userLiquidityUnstaked}
/>
<UnstakingModal
isOpen={showUnstakingModal}
onDismiss={() => setShowUnstakingModal(false)}
stakingInfo={stakingInfo}
/>
<ClaimRewardModal
isOpen={showClaimRewardModal}
onDismiss={() => setShowClaimRewardModal(false)}
stakingInfo={stakingInfo}
/>
</>
)}
<PositionInfo gap="lg" justify="center" dim={showAddLiquidityButton}>
<BottomSection gap="lg" justify="center">
<StyledDataCard disabled={disableTop} bgColor={backgroundColor} showBackground={!showAddLiquidityButton}>
<CardSection>
<CardBGImage desaturate />
<CardNoise />
<AutoColumn gap="md">
<RowBetween>
<TYPE.white fontWeight={600}>Your liquidity deposits</TYPE.white>
</RowBetween>
<RowBetween style={{ alignItems: 'baseline' }}>
<TYPE.white fontSize={36} fontWeight={600}>
{stakingInfo?.stakedAmount?.toSignificant(6) ?? '-'}
</TYPE.white>
<TYPE.white>
UNI-V2 {currencyA?.symbol}-{currencyB?.symbol}
</TYPE.white>
</RowBetween>
</AutoColumn>
</CardSection>
</StyledDataCard>
<StyledBottomCard dim={stakingInfo?.stakedAmount?.equalTo(JSBI.BigInt(0))}>
<CardBGImage desaturate />
<CardNoise />
<AutoColumn gap="sm">
<RowBetween>
<div>
<TYPE.black>Your unclaimed UNI</TYPE.black>
</div>
{stakingInfo?.earnedAmount && JSBI.notEqual(BIG_INT_ZERO, stakingInfo?.earnedAmount?.raw) && (
<ButtonEmpty
padding="8px"
borderRadius="8px"
width="fit-content"
onClick={() => setShowClaimRewardModal(true)}
>
Claim
</ButtonEmpty>
)}
</RowBetween>
<RowBetween style={{ alignItems: 'baseline' }}>
<TYPE.largeHeader fontSize={36} fontWeight={600}>
<CountUp
key={countUpAmount}
isCounting
decimalPlaces={4}
start={parseFloat(countUpAmountPrevious)}
end={parseFloat(countUpAmount)}
thousandsSeparator={','}
duration={1}
/>
</TYPE.largeHeader>
<TYPE.black fontSize={16} fontWeight={500}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '8px ' }}>
</span>
{stakingInfo?.rewardRate
?.multiply((60 * 60 * 24 * 7).toString())
?.toSignificant(4, { groupSeparator: ',' }) ?? '-'}
{' UNI / week'}
</TYPE.black>
</RowBetween>
</AutoColumn>
</StyledBottomCard>
</BottomSection>
<TYPE.main style={{ textAlign: 'center' }} fontSize={14}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '8px' }}>
⭐️
</span>
When you withdraw, the contract will automagically claim UNI on your behalf!
</TYPE.main>
{!showAddLiquidityButton && (
<DataRow style={{ marginBottom: '1rem' }}>
<ButtonPrimary padding="8px" borderRadius="8px" width="160px" onClick={handleDepositClick}>
{stakingInfo?.stakedAmount?.greaterThan(JSBI.BigInt(0)) ? 'Deposit' : 'Deposit UNI-V2 LP Tokens'}
</ButtonPrimary>
{stakingInfo?.stakedAmount?.greaterThan(JSBI.BigInt(0)) && (
<>
<ButtonPrimary
padding="8px"
borderRadius="8px"
width="160px"
onClick={() => setShowUnstakingModal(true)}
>
Withdraw
</ButtonPrimary>
</>
)}
</DataRow>
)}
{!userLiquidityUnstaked ? null : userLiquidityUnstaked.equalTo('0') ? null : (
<TYPE.main>{userLiquidityUnstaked.toSignificant(6)} UNI-V2 LP tokens available</TYPE.main>
)}
</PositionInfo>
</PageWrapper>
)
}
import React from 'react'
import { AutoColumn } from '../../components/Column'
import styled from 'styled-components'
import { STAKING_REWARDS_INFO, useStakingInfo } from '../../state/stake/hooks'
import { TYPE, ExternalLink } from '../../theme'
import PoolCard from '../../components/earn/PoolCard'
import { RowBetween } from '../../components/Row'
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
import { Countdown } from './Countdown'
import Loader from '../../components/Loader'
import { useActiveWeb3React } from '../../hooks'
const PageWrapper = styled(AutoColumn)`
max-width: 640px;
width: 100%;
`
const TopSection = styled(AutoColumn)`
max-width: 720px;
width: 100%;
`
const PoolSection = styled.div`
display: grid;
grid-template-columns: 1fr;
column-gap: 10px;
row-gap: 15px;
width: 100%;
justify-self: center;
`
export default function Earn() {
const { chainId } = useActiveWeb3React()
const stakingInfos = useStakingInfo()
const DataRow = styled(RowBetween)`
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
`};
`
const stakingRewardsExist = Boolean(typeof chainId === 'number' && (STAKING_REWARDS_INFO[chainId]?.length ?? 0) > 0)
return (
<PageWrapper gap="lg" justify="center">
<TopSection gap="md">
<DataCard>
<CardBGImage />
<CardNoise />
<CardSection>
<AutoColumn gap="md">
<RowBetween>
<TYPE.white fontWeight={600}>Uniswap liquidity mining</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white fontSize={14}>
Deposit your Liquidity Provider tokens to receive UNI, the Uniswap protocol governance token.
</TYPE.white>
</RowBetween>{' '}
<ExternalLink
style={{ color: 'white', textDecoration: 'underline' }}
href="https://uniswap.org/blog/uni/"
target="_blank"
>
<TYPE.white fontSize={14}>Read more about UNI</TYPE.white>
</ExternalLink>
</AutoColumn>
</CardSection>
<CardBGImage />
<CardNoise />
</DataCard>
</TopSection>
<AutoColumn gap="lg" style={{ width: '100%', maxWidth: '720px' }}>
<DataRow style={{ alignItems: 'baseline' }}>
<TYPE.mediumHeader style={{ marginTop: '0.5rem' }}>Participating pools</TYPE.mediumHeader>
<Countdown exactEnd={stakingInfos?.[0]?.periodFinish} />
</DataRow>
<PoolSection>
{stakingRewardsExist && stakingInfos?.length === 0 ? (
<Loader style={{ margin: 'auto' }} />
) : !stakingRewardsExist ? (
'No active rewards'
) : (
stakingInfos?.map(stakingInfo => {
// need to sort by added liquidity here
return <PoolCard key={stakingInfo.stakingRewardAddress} stakingInfo={stakingInfo} />
})
)}
</PoolSection>
</AutoColumn>
</PageWrapper>
)
}
...@@ -9,6 +9,7 @@ import { ButtonConfirmed } from '../../components/Button' ...@@ -9,6 +9,7 @@ import { ButtonConfirmed } from '../../components/Button'
import { LightCard, PinkCard, YellowCard } from '../../components/Card' import { LightCard, PinkCard, YellowCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column' import { AutoColumn } from '../../components/Column'
import CurrencyLogo from '../../components/CurrencyLogo' import CurrencyLogo from '../../components/CurrencyLogo'
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
import QuestionHelper from '../../components/QuestionHelper' import QuestionHelper from '../../components/QuestionHelper'
import { AutoRow, RowBetween, RowFixed } from '../../components/Row' import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { Dots } from '../../components/swap/styleds' import { Dots } from '../../components/swap/styleds'
...@@ -28,25 +29,12 @@ import { getEtherscanLink, isAddress } from '../../utils' ...@@ -28,25 +29,12 @@ import { getEtherscanLink, isAddress } from '../../utils'
import { BodyWrapper } from '../AppBody' import { BodyWrapper } from '../AppBody'
import { EmptyState } from './EmptyState' import { EmptyState } from './EmptyState'
const POOL_CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))
const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
const ZERO = JSBI.BigInt(0) const ZERO = JSBI.BigInt(0)
const ONE = JSBI.BigInt(1) const ONE = JSBI.BigInt(1)
const ZERO_FRACTION = new Fraction(ZERO, ONE) const ZERO_FRACTION = new Fraction(ZERO, ONE)
const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000)) const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000))
function FormattedPoolCurrencyAmount({ currencyAmount }: { currencyAmount: CurrencyAmount }) {
return (
<>
{currencyAmount.equalTo(JSBI.BigInt(0))
? '0'
: currencyAmount.greaterThan(POOL_CURRENCY_AMOUNT_MIN)
? currencyAmount.toSignificant(4)
: `<${POOL_CURRENCY_AMOUNT_MIN.toSignificant(1)}`}
</>
)
}
export function V1LiquidityInfo({ export function V1LiquidityInfo({
token, token,
liquidityTokenAmount, liquidityTokenAmount,
...@@ -66,7 +54,7 @@ export function V1LiquidityInfo({ ...@@ -66,7 +54,7 @@ export function V1LiquidityInfo({
<CurrencyLogo size="24px" currency={token} /> <CurrencyLogo size="24px" currency={token} />
<div style={{ marginLeft: '.75rem' }}> <div style={{ marginLeft: '.75rem' }}>
<TYPE.mediumHeader> <TYPE.mediumHeader>
{<FormattedPoolCurrencyAmount currencyAmount={liquidityTokenAmount} />}{' '} {<FormattedCurrencyAmount currencyAmount={liquidityTokenAmount} />}{' '}
{chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH {chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH
</TYPE.mediumHeader> </TYPE.mediumHeader>
</div> </div>
...@@ -89,7 +77,7 @@ export function V1LiquidityInfo({ ...@@ -89,7 +77,7 @@ export function V1LiquidityInfo({
</Text> </Text>
<RowFixed> <RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}> <Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
<FormattedPoolCurrencyAmount currencyAmount={ethWorth} /> <FormattedCurrencyAmount currencyAmount={ethWorth} />
</Text> </Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={Currency.ETHER} /> <CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={Currency.ETHER} />
</RowFixed> </RowFixed>
......
import React, { useContext, useMemo } from 'react' import React, { useContext, useMemo } from 'react'
import { ThemeContext } from 'styled-components' import styled, { ThemeContext } from 'styled-components'
import { Pair } from '@uniswap/sdk' import { Pair } from '@uniswap/sdk'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { SwapPoolTabs } from '../../components/NavigationTabs' import { SwapPoolTabs } from '../../components/NavigationTabs'
import Question from '../../components/QuestionHelper'
import FullPositionCard from '../../components/PositionCard' import FullPositionCard from '../../components/PositionCard'
import { useUserHasLiquidityInAllTokens } from '../../data/V1' import { useUserHasLiquidityInAllTokens } from '../../data/V1'
import { useTokenBalancesWithLoadingIndicator } from '../../state/wallet/hooks' import { useTokenBalancesWithLoadingIndicator } from '../../state/wallet/hooks'
import { StyledInternalLink, TYPE } from '../../theme' import { StyledInternalLink, ExternalLink, TYPE, HideSmall } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
import { LightCard } from '../../components/Card' import Card from '../../components/Card'
import { RowBetween } from '../../components/Row' import { RowBetween, RowFixed } from '../../components/Row'
import { ButtonPrimary, ButtonSecondary } from '../../components/Button' import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
import { AutoColumn } from '../../components/Column' import { AutoColumn } from '../../components/Column'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { usePairs } from '../../data/Reserves' import { usePairs } from '../../data/Reserves'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks' import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
import AppBody from '../AppBody'
import { Dots } from '../../components/swap/styleds' import { Dots } from '../../components/swap/styleds'
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
const PageWrapper = styled(AutoColumn)`
max-width: 640px;
width: 100%;
`
const VoteCard = styled(DataCard)`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%);
overflow: hidden;
`
const TitleRow = styled(RowBetween)`
${({ theme }) => theme.mediaWidth.upToSmall`
flex-wrap: wrap;
gap: 12px;
width: 100%;
flex-direction: column-reverse;
`};
`
const ButtonRow = styled(RowFixed)`
gap: 8px;
${({ theme }) => theme.mediaWidth.upToSmall`
width: 100%;
flex-direction: row-reverse;
justify-content: space-between;
`};
`
const ResponsiveButtonPrimary = styled(ButtonPrimary)`
width: fit-content;
${({ theme }) => theme.mediaWidth.upToSmall`
width: 48%;
`};
`
const ResponsiveButtonSecondary = styled(ButtonSecondary)`
width: fit-content;
${({ theme }) => theme.mediaWidth.upToSmall`
width: 48%;
`};
`
const EmptyProposals = styled.div`
border: 1px solid ${({ theme }) => theme.text4};
padding: 16px 12px;
border-radius: 12px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`
export default function Pool() { export default function Pool() {
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
...@@ -58,66 +109,101 @@ export default function Pool() { ...@@ -58,66 +109,101 @@ export default function Pool() {
return ( return (
<> <>
<AppBody> <PageWrapper>
<SwapPoolTabs active={'pool'} /> <SwapPoolTabs active={'pool'} />
<VoteCard>
<CardBGImage />
<CardNoise />
<CardSection>
<AutoColumn gap="md">
<RowBetween>
<TYPE.white fontWeight={600}>Liquidity provider rewards</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white fontSize={14}>
{`Liquidity providers earn a 0.3% fee on all trades proportional to their share of the pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity.`}
</TYPE.white>
</RowBetween>
<ExternalLink
style={{ color: 'white', textDecoration: 'underline' }}
target="_blank"
href="https://uniswap.org/docs/v2/core-concepts/pools/"
>
<TYPE.white fontSize={14}>Read more about providing liquidity</TYPE.white>
</ExternalLink>
</AutoColumn>
</CardSection>
<CardBGImage />
<CardNoise />
</VoteCard>
<AutoColumn gap="lg" justify="center"> <AutoColumn gap="lg" justify="center">
<ButtonPrimary id="join-pool-button" as={Link} style={{ padding: 16 }} to="/add/ETH"> <AutoColumn gap="lg" style={{ width: '100%' }}>
<Text fontWeight={500} fontSize={20}> <TitleRow style={{ marginTop: '1rem' }} padding={'0'}>
Add Liquidity <HideSmall>
</Text> <TYPE.mediumHeader style={{ marginTop: '0.5rem', justifySelf: 'flex-start' }}>
</ButtonPrimary> Your liquidity
</TYPE.mediumHeader>
<AutoColumn gap="12px" style={{ width: '100%' }}> </HideSmall>
<RowBetween padding={'0 8px'}> <ButtonRow>
<Text color={theme.text1} fontWeight={500}> <ResponsiveButtonSecondary as={Link} padding="6px 8px" to="/create/ETH">
Your Liquidity Create a pair
</Text> </ResponsiveButtonSecondary>
<Question text="When you add liquidity, you are given pool tokens that represent your share. If you don’t see a pool you joined in this list, try importing a pool below." /> <ResponsiveButtonPrimary id="join-pool-button" as={Link} padding="6px 8px" to="/add/ETH">
</RowBetween> <Text fontWeight={500} fontSize={16}>
Add Liquidity
</Text>
</ResponsiveButtonPrimary>
</ButtonRow>
</TitleRow>
{!account ? ( {!account ? (
<LightCard padding="40px"> <Card padding="40px">
<TYPE.body color={theme.text3} textAlign="center"> <TYPE.body color={theme.text3} textAlign="center">
Connect to a wallet to view your liquidity. Connect to a wallet to view your liquidity.
</TYPE.body> </TYPE.body>
</LightCard> </Card>
) : v2IsLoading ? ( ) : v2IsLoading ? (
<LightCard padding="40px"> <EmptyProposals>
<TYPE.body color={theme.text3} textAlign="center"> <TYPE.body color={theme.text3} textAlign="center">
<Dots>Loading</Dots> <Dots>Loading</Dots>
</TYPE.body> </TYPE.body>
</LightCard> </EmptyProposals>
) : allV2PairsWithLiquidity?.length > 0 ? ( ) : allV2PairsWithLiquidity?.length > 0 ? (
<> <>
<ButtonSecondary>
<RowBetween>
<ExternalLink href={'https://uniswap.info/account/' + account}>
Account analytics and accrued fees
</ExternalLink>
<span></span>
</RowBetween>
</ButtonSecondary>
{allV2PairsWithLiquidity.map(v2Pair => ( {allV2PairsWithLiquidity.map(v2Pair => (
<FullPositionCard key={v2Pair.liquidityToken.address} pair={v2Pair} /> <FullPositionCard key={v2Pair.liquidityToken.address} pair={v2Pair} />
))} ))}
</> </>
) : ( ) : (
<LightCard padding="40px"> <EmptyProposals>
<TYPE.body color={theme.text3} textAlign="center"> <TYPE.body color={theme.text3} textAlign="center">
No liquidity found. No liquidity found.
</TYPE.body> </TYPE.body>
</LightCard> </EmptyProposals>
)} )}
<div> <AutoColumn justify={'center'} gap="md">
<Text textAlign="center" fontSize={14} style={{ padding: '.5rem 0 .5rem 0' }}> <Text textAlign="center" fontSize={14} style={{ padding: '.5rem 0 .5rem 0' }}>
{hasV1Liquidity ? 'Uniswap V1 liquidity found!' : "Don't see a pool you joined?"}{' '} {hasV1Liquidity ? 'Uniswap V1 liquidity found!' : "Don't see a pool you joined?"}{' '}
<StyledInternalLink id="import-pool-link" to={hasV1Liquidity ? '/migrate/v1' : '/find'}> <StyledInternalLink id="import-pool-link" to={hasV1Liquidity ? '/migrate/v1' : '/find'}>
{hasV1Liquidity ? 'Migrate now.' : 'Import it.'} {hasV1Liquidity ? 'Migrate now.' : 'Import it.'}
</StyledInternalLink> </StyledInternalLink>
</Text> </Text>
</div> </AutoColumn>
</AutoColumn> </AutoColumn>
</AutoColumn> </AutoColumn>
</AppBody> </PageWrapper>
<div style={{ display: 'flex', alignItems: 'center', marginTop: '1.5rem' }}>
<ButtonSecondary as={Link} style={{ width: 'initial' }} to="/migrate/v1">
Migrate V1 Liquidity
</ButtonSecondary>
</div>
</> </>
) )
} }
...@@ -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>
)} )}
......
...@@ -24,6 +24,8 @@ import { ROUTER_ADDRESS } from '../../constants' ...@@ -24,6 +24,8 @@ import { ROUTER_ADDRESS } from '../../constants'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { useCurrency } from '../../hooks/Tokens' import { useCurrency } from '../../hooks/Tokens'
import { usePairContract } from '../../hooks/useContract' import { usePairContract } from '../../hooks/useContract'
import useIsArgentWallet from '../../hooks/useIsArgentWallet'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { useTransactionAdder } from '../../state/transactions/hooks' import { useTransactionAdder } from '../../state/transactions/hooks'
import { StyledInternalLink, TYPE } from '../../theme' import { StyledInternalLink, TYPE } from '../../theme'
...@@ -39,7 +41,7 @@ import { useBurnActionHandlers } from '../../state/burn/hooks' ...@@ -39,7 +41,7 @@ import { useBurnActionHandlers } from '../../state/burn/hooks'
import { useDerivedBurnInfo, useBurnState } from '../../state/burn/hooks' import { useDerivedBurnInfo, useBurnState } from '../../state/burn/hooks'
import { Field } from '../../state/burn/actions' import { Field } from '../../state/burn/actions'
import { useWalletModalToggle } from '../../state/application/hooks' import { useWalletModalToggle } from '../../state/application/hooks'
import { useUserDeadline, useUserSlippageTolerance } from '../../state/user/hooks' import { useUserSlippageTolerance } from '../../state/user/hooks'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
export default function RemoveLiquidity({ export default function RemoveLiquidity({
...@@ -74,7 +76,7 @@ export default function RemoveLiquidity({ ...@@ -74,7 +76,7 @@ export default function RemoveLiquidity({
// txn values // txn values
const [txHash, setTxHash] = useState<string>('') const [txHash, setTxHash] = useState<string>('')
const [deadline] = useUserDeadline() const deadline = useTransactionDeadline()
const [allowedSlippage] = useUserSlippageTolerance() const [allowedSlippage] = useUserSlippageTolerance()
const formattedAmounts = { const formattedAmounts = {
...@@ -99,15 +101,21 @@ export default function RemoveLiquidity({ ...@@ -99,15 +101,21 @@ export default function RemoveLiquidity({
// allowance handling // allowance handling
const [signatureData, setSignatureData] = useState<{ v: number; r: string; s: string; deadline: number } | null>(null) const [signatureData, setSignatureData] = useState<{ v: number; r: string; s: string; deadline: number } | null>(null)
const [approval, approveCallback] = useApproveCallback(parsedAmounts[Field.LIQUIDITY], ROUTER_ADDRESS) const [approval, approveCallback] = useApproveCallback(parsedAmounts[Field.LIQUIDITY], ROUTER_ADDRESS)
const isArgentWallet = useIsArgentWallet()
async function onAttemptToApprove() { async function onAttemptToApprove() {
if (!pairContract || !pair || !library) throw new Error('missing dependencies') if (!pairContract || !pair || !library || !deadline) throw new Error('missing dependencies')
const liquidityAmount = parsedAmounts[Field.LIQUIDITY] const liquidityAmount = parsedAmounts[Field.LIQUIDITY]
if (!liquidityAmount) throw new Error('missing liquidity amount') if (!liquidityAmount) throw new Error('missing liquidity amount')
if (isArgentWallet) {
return approveCallback()
}
// try to gather a signature for permission // try to gather a signature for permission
const nonce = await pairContract.nonces(account) const nonce = await pairContract.nonces(account)
const deadlineForSignature: number = Math.ceil(Date.now() / 1000) + deadline
const EIP712Domain = [ const EIP712Domain = [
{ name: 'name', type: 'string' }, { name: 'name', type: 'string' },
{ name: 'version', type: 'string' }, { name: 'version', type: 'string' },
...@@ -132,7 +140,7 @@ export default function RemoveLiquidity({ ...@@ -132,7 +140,7 @@ export default function RemoveLiquidity({
spender: ROUTER_ADDRESS, spender: ROUTER_ADDRESS,
value: liquidityAmount.raw.toString(), value: liquidityAmount.raw.toString(),
nonce: nonce.toHexString(), nonce: nonce.toHexString(),
deadline: deadlineForSignature deadline: deadline.toNumber()
} }
const data = JSON.stringify({ const data = JSON.stringify({
types: { types: {
...@@ -152,7 +160,7 @@ export default function RemoveLiquidity({ ...@@ -152,7 +160,7 @@ export default function RemoveLiquidity({
v: signature.v, v: signature.v,
r: signature.r, r: signature.r,
s: signature.s, s: signature.s,
deadline: deadlineForSignature deadline: deadline.toNumber()
}) })
}) })
.catch(error => { .catch(error => {
...@@ -185,7 +193,7 @@ export default function RemoveLiquidity({ ...@@ -185,7 +193,7 @@ export default function RemoveLiquidity({
// tx sending // tx sending
const addTransaction = useTransactionAdder() const addTransaction = useTransactionAdder()
async function onRemove() { async function onRemove() {
if (!chainId || !library || !account) throw new Error('missing dependencies') if (!chainId || !library || !account || !deadline) throw new Error('missing dependencies')
const { [Field.CURRENCY_A]: currencyAmountA, [Field.CURRENCY_B]: currencyAmountB } = parsedAmounts const { [Field.CURRENCY_A]: currencyAmountA, [Field.CURRENCY_B]: currencyAmountB } = parsedAmounts
if (!currencyAmountA || !currencyAmountB) { if (!currencyAmountA || !currencyAmountB) {
throw new Error('missing currency amounts') throw new Error('missing currency amounts')
...@@ -203,7 +211,6 @@ export default function RemoveLiquidity({ ...@@ -203,7 +211,6 @@ export default function RemoveLiquidity({
const currencyBIsETH = currencyB === ETHER const currencyBIsETH = currencyB === ETHER
const oneCurrencyIsETH = currencyA === ETHER || currencyBIsETH const oneCurrencyIsETH = currencyA === ETHER || currencyBIsETH
const deadlineFromNow = Math.ceil(Date.now() / 1000) + deadline
if (!tokenA || !tokenB) throw new Error('could not wrap') if (!tokenA || !tokenB) throw new Error('could not wrap')
...@@ -219,7 +226,7 @@ export default function RemoveLiquidity({ ...@@ -219,7 +226,7 @@ export default function RemoveLiquidity({
amountsMin[currencyBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), amountsMin[currencyBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(),
amountsMin[currencyBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), amountsMin[currencyBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(),
account, account,
deadlineFromNow deadline.toHexString()
] ]
} }
// removeLiquidity // removeLiquidity
...@@ -232,7 +239,7 @@ export default function RemoveLiquidity({ ...@@ -232,7 +239,7 @@ export default function RemoveLiquidity({
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()
] ]
} }
} }
...@@ -467,7 +474,7 @@ export default function RemoveLiquidity({ ...@@ -467,7 +474,7 @@ export default function RemoveLiquidity({
return ( return (
<> <>
<AppBody> <AppBody>
<AddRemoveTabs adding={false} /> <AddRemoveTabs creating={false} adding={false} />
<Wrapper> <Wrapper>
<TransactionConfirmationModal <TransactionConfirmationModal
isOpen={showConfirm} isOpen={showConfirm}
...@@ -679,7 +686,7 @@ export default function RemoveLiquidity({ ...@@ -679,7 +686,7 @@ export default function RemoveLiquidity({
</AppBody> </AppBody>
{pair ? ( {pair ? (
<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}
......
...@@ -7,13 +7,13 @@ import { ThemeContext } from 'styled-components' ...@@ -7,13 +7,13 @@ import { ThemeContext } from 'styled-components'
import AddressInputPanel from '../../components/AddressInputPanel' import AddressInputPanel from '../../components/AddressInputPanel'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonConfirmed } from '../../components/Button' import { ButtonError, ButtonLight, ButtonPrimary, ButtonConfirmed } from '../../components/Button'
import Card, { GreyCard } from '../../components/Card' import Card, { GreyCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column' import Column, { AutoColumn } from '../../components/Column'
import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal' import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import { SwapPoolTabs } from '../../components/NavigationTabs' import { SwapPoolTabs } from '../../components/NavigationTabs'
import { AutoRow, RowBetween } from '../../components/Row' import { AutoRow, RowBetween } from '../../components/Row'
import AdvancedSwapDetailsDropdown from '../../components/swap/AdvancedSwapDetailsDropdown' import AdvancedSwapDetailsDropdown from '../../components/swap/AdvancedSwapDetailsDropdown'
import BetterTradeLink from '../../components/swap/BetterTradeLink' import BetterTradeLink, { DefaultVersionLink } from '../../components/swap/BetterTradeLink'
import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee' import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee'
import { ArrowWrapper, BottomGrouping, SwapCallbackError, Wrapper } from '../../components/swap/styleds' import { ArrowWrapper, BottomGrouping, SwapCallbackError, Wrapper } from '../../components/swap/styleds'
import TradePrice from '../../components/swap/TradePrice' import TradePrice from '../../components/swap/TradePrice'
...@@ -27,7 +27,7 @@ import { useCurrency } from '../../hooks/Tokens' ...@@ -27,7 +27,7 @@ import { useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback' import { ApprovalState, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback'
import useENSAddress from '../../hooks/useENSAddress' import useENSAddress from '../../hooks/useENSAddress'
import { useSwapCallback } from '../../hooks/useSwapCallback' import { useSwapCallback } from '../../hooks/useSwapCallback'
import useToggledVersion, { Version } from '../../hooks/useToggledVersion' import useToggledVersion, { DEFAULT_VERSION, Version } from '../../hooks/useToggledVersion'
import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback' import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback'
import { useToggleSettingsMenu, useWalletModalToggle } from '../../state/application/hooks' import { useToggleSettingsMenu, useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/swap/actions' import { Field } from '../../state/swap/actions'
...@@ -37,7 +37,7 @@ import { ...@@ -37,7 +37,7 @@ import {
useSwapActionHandlers, useSwapActionHandlers,
useSwapState useSwapState
} from '../../state/swap/hooks' } from '../../state/swap/hooks'
import { useExpertModeManager, useUserDeadline, useUserSlippageTolerance } from '../../state/user/hooks' import { useExpertModeManager, useUserSlippageTolerance } from '../../state/user/hooks'
import { LinkStyledButton, TYPE } from '../../theme' import { LinkStyledButton, TYPE } from '../../theme'
import { maxAmountSpend } from '../../utils/maxAmountSpend' import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices' import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
...@@ -73,7 +73,6 @@ export default function Swap() { ...@@ -73,7 +73,6 @@ export default function Swap() {
const [isExpertMode] = useExpertModeManager() const [isExpertMode] = useExpertModeManager()
// get custom setting values for user // get custom setting values for user
const [deadline] = useUserDeadline()
const [allowedSlippage] = useUserSlippageTolerance() const [allowedSlippage] = useUserSlippageTolerance()
// swap state // swap state
...@@ -94,12 +93,12 @@ export default function Swap() { ...@@ -94,12 +93,12 @@ export default function Swap() {
const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE
const { address: recipientAddress } = useENSAddress(recipient) const { address: recipientAddress } = useENSAddress(recipient)
const toggledVersion = useToggledVersion() const toggledVersion = useToggledVersion()
const trade = showWrap const tradesByVersion = {
? undefined [Version.v1]: v1Trade,
: { [Version.v2]: v2Trade
[Version.v1]: v1Trade, }
[Version.v2]: v2Trade const trade = showWrap ? undefined : tradesByVersion[toggledVersion]
}[toggledVersion] const defaultTrade = showWrap ? undefined : tradesByVersion[DEFAULT_VERSION]
const betterTradeLinkVersion: Version | undefined = const betterTradeLinkVersion: Version | undefined =
toggledVersion === Version.v2 && isTradeBetter(v2Trade, v1Trade, BETTER_TRADE_LINK_THRESHOLD) toggledVersion === Version.v2 && isTradeBetter(v2Trade, v1Trade, BETTER_TRADE_LINK_THRESHOLD)
...@@ -180,12 +179,7 @@ export default function Swap() { ...@@ -180,12 +179,7 @@ export default function Swap() {
const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput)) const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput))
// the callback to execute the swap // the callback to execute the swap
const { callback: swapCallback, error: swapCallbackError } = useSwapCallback( const { callback: swapCallback, error: swapCallbackError } = useSwapCallback(trade, allowedSlippage, recipient)
trade,
allowedSlippage,
deadline,
recipient
)
const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade) const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade)
...@@ -466,9 +460,17 @@ export default function Swap() { ...@@ -466,9 +460,17 @@ export default function Swap() {
</Text> </Text>
</ButtonError> </ButtonError>
)} )}
{showApproveFlow && <ProgressSteps steps={[approval === ApprovalState.APPROVED]} />} {showApproveFlow && (
<Column style={{ marginTop: '1rem' }}>
<ProgressSteps steps={[approval === ApprovalState.APPROVED]} />
</Column>
)}
{isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null} {isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null}
{betterTradeLinkVersion && <BetterTradeLink version={betterTradeLinkVersion} />} {betterTradeLinkVersion ? (
<BetterTradeLink version={betterTradeLinkVersion} />
) : toggledVersion !== DEFAULT_VERSION && defaultTrade ? (
<DefaultVersionLink />
) : null}
</BottomGrouping> </BottomGrouping>
</Wrapper> </Wrapper>
</AppBody> </AppBody>
......
import React from 'react' import React, { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { Redirect, RouteComponentProps } from 'react-router-dom' import { Redirect, RouteComponentProps } from 'react-router-dom'
import { AppDispatch } from '../../state'
import { ApplicationModal, setOpenModal } from '../../state/application/actions'
// Redirects to swap but only replace the pathname // Redirects to swap but only replace the pathname
export function RedirectPathToSwapOnly({ location }: RouteComponentProps) { export function RedirectPathToSwapOnly({ location }: RouteComponentProps) {
...@@ -28,3 +31,11 @@ export function RedirectToSwap(props: RouteComponentProps<{ outputCurrency: stri ...@@ -28,3 +31,11 @@ export function RedirectToSwap(props: RouteComponentProps<{ outputCurrency: stri
/> />
) )
} }
export function OpenClaimAddressModalAndRedirectToSwap(props: RouteComponentProps) {
const dispatch = useDispatch<AppDispatch>()
useEffect(() => {
dispatch(setOpenModal(ApplicationModal.ADDRESS_CLAIM))
}, [dispatch])
return <RedirectPathToSwapOnly {...props} />
}
import React, { useState } from 'react'
import { AutoColumn } from '../../components/Column'
import styled from 'styled-components'
import { RouteComponentProps } from 'react-router-dom'
import { TYPE, StyledInternalLink, ExternalLink } from '../../theme'
import { RowFixed, RowBetween } from '../../components/Row'
import { CardSection, DataCard } from '../../components/earn/styled'
import { ArrowLeft } from 'react-feather'
import { ButtonPrimary } from '../../components/Button'
import { ProposalStatus } from './styled'
import { useProposalData, useUserVotes, useUserDelegatee, ProposalData } from '../../state/governance/hooks'
import { useTimestampFromBlock } from '../../hooks/useTimestampFromBlock'
import { DateTime } from 'luxon'
import ReactMarkdown from 'react-markdown'
import VoteModal from '../../components/vote/VoteModal'
import { TokenAmount, JSBI } from '@uniswap/sdk'
import { useTokenBalance } from '../../state/wallet/hooks'
import { useActiveWeb3React } from '../../hooks'
import { UNI, ZERO_ADDRESS, PROPOSAL_LENGTH_IN_DAYS } from '../../constants'
import { isAddress, getEtherscanLink } from '../../utils'
const PageWrapper = styled(AutoColumn)`
width: 100%;
`
const ProposalInfo = styled(AutoColumn)`
border: 1px solid ${({ theme }) => theme.bg4};
border-radius: 12px;
padding: 1.5rem;
position: relative;
max-width: 640px;
width: 100%;
`
const ArrowWrapper = styled(StyledInternalLink)`
display: flex;
align-items: center;
gap: 8px;
height: 24px;
color: ${({ theme }) => theme.text1};
a {
color: ${({ theme }) => theme.text1};
text-decoration: none;
}
:hover {
text-decoration: none;
}
`
const CardWrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
width: 100%;
`
const StyledDataCard = styled(DataCard)`
width: 100%;
background: none;
background-color: ${({ theme }) => theme.bg1};
height: fit-content;
z-index: 2;
`
const ProgressWrapper = styled.div`
width: 100%;
margin-top: 1rem;
height: 4px;
border-radius: 4px;
background-color: ${({ theme }) => theme.bg3};
position: relative;
`
const Progress = styled.div<{ status: 'for' | 'against'; percentageString?: string }>`
height: 4px;
border-radius: 4px;
background-color: ${({ theme, status }) => (status === 'for' ? theme.green1 : theme.red1)};
width: ${({ percentageString }) => percentageString};
`
const MarkDownWrapper = styled.div`
max-width: 640px;
overflow: hidden;
`
const WrapSmall = styled(RowBetween)`
${({ theme }) => theme.mediaWidth.upToSmall`
align-items: flex-start;
flex-direction: column;
`};
`
const DetailText = styled.div`
word-break: break-all;
`
export default function VotePage({
match: {
params: { id }
}
}: RouteComponentProps<{ id: string }>) {
const { account, chainId } = useActiveWeb3React()
// get data for this specific proposal
const proposalData: ProposalData | undefined = useProposalData(id)
// update support based on button interactions
const [support, setSupport] = useState<boolean>(true)
// modal for casting votes
const [showModal, setShowModal] = useState<boolean>(false)
// get and format date from data
const startTimestamp: number | undefined = useTimestampFromBlock(proposalData?.startBlock)
const endDate: DateTime | undefined = startTimestamp
? DateTime.fromSeconds(startTimestamp).plus({ days: PROPOSAL_LENGTH_IN_DAYS })
: undefined
const now: DateTime = DateTime.local()
// get total votes and format percentages for UI
const totalVotes: number | undefined = proposalData ? proposalData.forCount + proposalData.againstCount : undefined
const forPercentage: string =
proposalData && totalVotes ? ((proposalData.forCount * 100) / totalVotes).toFixed(0) + '%' : '0%'
const againstPercentage: string =
proposalData && totalVotes ? ((proposalData.againstCount * 100) / totalVotes).toFixed(0) + '%' : '0%'
// show delegation option if they have have a balance, have not delegated
const availableVotes: TokenAmount | undefined = useUserVotes()
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, chainId ? UNI[chainId] : undefined)
const userDelegatee: string | undefined = useUserDelegatee()
const showUnlockVoting = Boolean(
uniBalance && JSBI.notEqual(uniBalance.raw, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS
)
// show links in propsoal details if content is an address
const linkIfAddress = (content: string) => {
if (isAddress(content) && chainId) {
return <ExternalLink href={getEtherscanLink(chainId, content, 'address')}>{content}</ExternalLink>
}
return <span>{content}</span>
}
return (
<PageWrapper gap="lg" justify="center">
<VoteModal
isOpen={showModal}
onDismiss={() => setShowModal(false)}
proposalId={proposalData?.id}
support={support}
/>
<ProposalInfo gap="lg" justify="start">
<RowBetween style={{ width: '100%' }}>
<ArrowWrapper to="/vote">
<ArrowLeft size={20} /> All Proposals
</ArrowWrapper>
{proposalData && <ProposalStatus status={proposalData?.status ?? ''}>{proposalData?.status}</ProposalStatus>}
</RowBetween>
<AutoColumn gap="10px" style={{ width: '100%' }}>
<TYPE.largeHeader style={{ marginBottom: '.5rem' }}>{proposalData?.title}</TYPE.largeHeader>
<RowBetween>
<TYPE.main>
{endDate && endDate < now
? 'Voting ended ' + (endDate && endDate.toLocaleString(DateTime.DATETIME_FULL))
: proposalData
? 'Voting ends approximately' + (endDate && endDate.toLocaleString(DateTime.DATETIME_FULL))
: ''}
</TYPE.main>
{showUnlockVoting && endDate && endDate > now && (
<ButtonPrimary
style={{ width: 'fit-content' }}
padding="8px"
borderRadius="8px"
onClick={() => setShowModal(true)}
>
Unlock Voting
</ButtonPrimary>
)}
</RowBetween>
</AutoColumn>
{!showUnlockVoting &&
availableVotes &&
JSBI.greaterThan(availableVotes?.raw, JSBI.BigInt(0)) &&
endDate &&
endDate > now ? (
<RowFixed style={{ width: '100%', gap: '12px' }}>
<ButtonPrimary
padding="8px"
borderRadius="8px"
onClick={() => {
setSupport(true)
setShowModal(true)
}}
>
Vote For
</ButtonPrimary>
<ButtonPrimary
padding="8px"
borderRadius="8px"
onClick={() => {
setSupport(false)
setShowModal(true)
}}
>
Vote Against
</ButtonPrimary>
</RowFixed>
) : (
''
)}
<CardWrapper>
<StyledDataCard>
<CardSection>
<AutoColumn gap="md">
<WrapSmall>
<TYPE.black fontWeight={600}>For</TYPE.black>
<TYPE.black fontWeight={600}>
{' '}
{proposalData?.forCount.toLocaleString(undefined, { maximumFractionDigits: 0 })}
</TYPE.black>
</WrapSmall>
</AutoColumn>
<ProgressWrapper>
<Progress status={'for'} percentageString={forPercentage} />
</ProgressWrapper>
</CardSection>
</StyledDataCard>
<StyledDataCard>
<CardSection>
<AutoColumn gap="md">
<WrapSmall>
<TYPE.black fontWeight={600}>Against</TYPE.black>
<TYPE.black fontWeight={600}>
{proposalData?.againstCount.toLocaleString(undefined, { maximumFractionDigits: 0 })}
</TYPE.black>
</WrapSmall>
</AutoColumn>
<ProgressWrapper>
<Progress status={'against'} percentageString={againstPercentage} />
</ProgressWrapper>
</CardSection>
</StyledDataCard>
</CardWrapper>
<AutoColumn gap="md">
<TYPE.mediumHeader fontWeight={600}>Details</TYPE.mediumHeader>
{proposalData?.details?.map((d, i) => {
return (
<DetailText key={i}>
{i + 1}: {linkIfAddress(d.target)}.{d.functionSig}(
{d.callData.split(',').map((content, i) => {
return (
<span key={i}>
{linkIfAddress(content)}
{d.callData.split(',').length - 1 === i ? '' : ','}
</span>
)
})}
)
</DetailText>
)
})}
</AutoColumn>
<AutoColumn gap="md">
<TYPE.mediumHeader fontWeight={600}>Overview</TYPE.mediumHeader>
<MarkDownWrapper>
<ReactMarkdown source={proposalData?.description} />
</MarkDownWrapper>
</AutoColumn>
<AutoColumn gap="md">
<TYPE.mediumHeader fontWeight={600}>Proposer</TYPE.mediumHeader>
<ExternalLink
href={proposalData?.proposer && chainId ? getEtherscanLink(chainId, proposalData?.proposer, 'address') : ''}
>
<ReactMarkdown source={proposalData?.proposer} />
</ExternalLink>
</AutoColumn>
</ProposalInfo>
</PageWrapper>
)
}
import React, { useState } from 'react'
import { AutoColumn } from '../../components/Column'
import styled from 'styled-components'
import { TYPE, ExternalLink } from '../../theme'
import { RowBetween, RowFixed } from '../../components/Row'
import { Link } from 'react-router-dom'
import { ProposalStatus } from './styled'
import { ButtonPrimary } from '../../components/Button'
import { Button } from 'rebass/styled-components'
import { darken } from 'polished'
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
import { useAllProposalData, ProposalData, useUserVotes, useUserDelegatee } from '../../state/governance/hooks'
import DelegateModal from '../../components/vote/DelegateModal'
import { useTokenBalance } from '../../state/wallet/hooks'
import { useActiveWeb3React } from '../../hooks'
import { UNI, ZERO_ADDRESS } from '../../constants'
import { JSBI, TokenAmount, ChainId } from '@uniswap/sdk'
import { shortenAddress, getEtherscanLink } from '../../utils'
import Loader from '../../components/Loader'
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
const PageWrapper = styled(AutoColumn)``
const TopSection = styled(AutoColumn)`
max-width: 640px;
width: 100%;
`
const Proposal = styled(Button)`
padding: 0.75rem 1rem;
width: 100%;
margin-top: 1rem;
border-radius: 12px;
display: grid;
grid-template-columns: 48px 1fr 120px;
align-items: center;
text-align: left;
outline: none;
cursor: pointer;
color: ${({ theme }) => theme.text1};
text-decoration: none;
background-color: ${({ theme }) => theme.bg1};
&:focus {
background-color: ${({ theme }) => darken(0.05, theme.bg1)};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, theme.bg1)};
}
`
const ProposalNumber = styled.span`
opacity: 0.6;
`
const ProposalTitle = styled.span`
font-weight: 600;
`
const VoteCard = styled(DataCard)`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%);
overflow: hidden;
`
const WrapSmall = styled(RowBetween)`
${({ theme }) => theme.mediaWidth.upToSmall`
flex-wrap: wrap;
`};
`
const TextButton = styled(TYPE.main)`
color: ${({ theme }) => theme.primary1};
:hover {
cursor: pointer;
text-decoration: underline;
}
`
const AddressButton = styled.div`
border: 1px solid ${({ theme }) => theme.bg3};
padding: 2px 4px;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
`
const StyledExternalLink = styled(ExternalLink)`
color: ${({ theme }) => theme.text1};
`
const EmptyProposals = styled.div`
border: 1px solid ${({ theme }) => theme.text4};
padding: 16px 12px;
border-radius: 12px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`
export default function Vote() {
const { account, chainId } = useActiveWeb3React()
const [showModal, setShowModal] = useState<boolean>(false)
// get data to list all proposals
const allProposals: ProposalData[] = useAllProposalData()
// user data
const availableVotes: TokenAmount | undefined = useUserVotes()
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, chainId ? UNI[chainId] : undefined)
const userDelegatee: string | undefined = useUserDelegatee()
// show delegation option if they have have a balance, but have not delegated
const showUnlockVoting = Boolean(
uniBalance && JSBI.notEqual(uniBalance.raw, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS
)
return (
<PageWrapper gap="lg" justify="center">
<DelegateModal
isOpen={showModal}
onDismiss={() => setShowModal(false)}
title={showUnlockVoting ? 'Unlock Votes' : 'Update Delegation'}
/>
<TopSection gap="md">
<VoteCard>
<CardBGImage />
<CardNoise />
<CardSection>
<AutoColumn gap="md">
<RowBetween>
<TYPE.white fontWeight={600}>Uniswap Governance</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white fontSize={14}>
UNI tokens represent voting shares in Uniswap governance. You can vote on each proposal yourself or
delegate your votes to a third party.
</TYPE.white>
</RowBetween>
<ExternalLink
style={{ color: 'white', textDecoration: 'underline' }}
href="https://uniswap.org/blog/uni"
target="_blank"
>
<TYPE.white fontSize={14}>Read more about Uniswap governance</TYPE.white>
</ExternalLink>
</AutoColumn>
</CardSection>
<CardBGImage />
<CardNoise />
</VoteCard>
</TopSection>
<TopSection gap="2px">
<WrapSmall>
<TYPE.mediumHeader style={{ margin: '0.5rem 0' }}>Proposals</TYPE.mediumHeader>
{(!allProposals || allProposals.length === 0) && !availableVotes && <Loader />}
{showUnlockVoting ? (
<ButtonPrimary
style={{ width: 'fit-content' }}
padding="8px"
borderRadius="8px"
onClick={() => setShowModal(true)}
>
Unlock Voting
</ButtonPrimary>
) : availableVotes && JSBI.notEqual(JSBI.BigInt(0), availableVotes?.raw) ? (
<TYPE.body fontWeight={500} mr="6px">
<FormattedCurrencyAmount currencyAmount={availableVotes} /> Votes
</TYPE.body>
) : uniBalance &&
userDelegatee &&
userDelegatee !== ZERO_ADDRESS &&
JSBI.notEqual(JSBI.BigInt(0), uniBalance?.raw) ? (
<TYPE.body fontWeight={500} mr="6px">
<FormattedCurrencyAmount currencyAmount={uniBalance} /> Votes
</TYPE.body>
) : (
''
)}
</WrapSmall>
{!showUnlockVoting && (
<RowBetween>
<div />
{userDelegatee && userDelegatee !== ZERO_ADDRESS ? (
<RowFixed>
<TYPE.body fontWeight={500} mr="4px">
Delegated to:
</TYPE.body>
<AddressButton>
<StyledExternalLink
href={getEtherscanLink(ChainId.MAINNET, userDelegatee, 'address')}
style={{ margin: '0 4px' }}
>
{userDelegatee === account ? 'Self' : shortenAddress(userDelegatee)}
</StyledExternalLink>
<TextButton onClick={() => setShowModal(true)} style={{ marginLeft: '4px' }}>
(edit)
</TextButton>
</AddressButton>
</RowFixed>
) : (
''
)}
</RowBetween>
)}
{allProposals?.length === 0 && (
<EmptyProposals>
<TYPE.body style={{ marginBottom: '8px' }}>No proposals found.</TYPE.body>
<TYPE.subHeader>
<i>Proposals submitted by community members will appear here.</i>
</TYPE.subHeader>
</EmptyProposals>
)}
{allProposals?.map((p: ProposalData, i) => {
return (
<Proposal as={Link} to={'/vote/' + p.id} key={i}>
<ProposalNumber>{p.id}</ProposalNumber>
<ProposalTitle>{p.title}</ProposalTitle>
<ProposalStatus status={p.status}>{p.status}</ProposalStatus>
</Proposal>
)
})}
</TopSection>
<TYPE.subHeader color="text3">
A minimum threshhold of 1% of the total UNI supply is required to submit proposals
</TYPE.subHeader>
</PageWrapper>
)
}
import styled from 'styled-components'
const handleColorType = (status?: any, theme?: any) => {
switch (status) {
case 'pending':
return theme.blue1
case 'active':
return theme.blue1
case 'succeeded':
return theme.green1
case 'defeated':
return theme.red1
case 'queued':
return theme.text3
case 'executed':
return theme.green1
case 'canceled':
return theme.text3
case 'expired':
return theme.text3
default:
return theme.text3
}
}
export const ProposalStatus = styled.span<{ status: string }>`
font-size: 0.825rem;
font-weight: 600;
padding: 0.5rem;
border-radius: 8px;
color: ${({ status, theme }) => handleColorType(status, theme)};
border: 1px solid ${({ status, theme }) => handleColorType(status, theme)};
width: fit-content;
justify-self: flex-end;
text-transform: uppercase;
`
...@@ -18,10 +18,18 @@ export type PopupContent = ...@@ -18,10 +18,18 @@ export type PopupContent =
} }
} }
export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('app/updateBlockNumber') export enum ApplicationModal {
export const toggleWalletModal = createAction<void>('app/toggleWalletModal') WALLET,
export const toggleSettingsMenu = createAction<void>('app/toggleSettingsMenu') SETTINGS,
SELF_CLAIM,
ADDRESS_CLAIM,
CLAIM_POPUP,
MENU
}
export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('application/updateBlockNumber')
export const setOpenModal = createAction<ApplicationModal | null>('application/setOpenModal')
export const addPopup = createAction<{ key?: string; removeAfterMs?: number | null; content: PopupContent }>( export const addPopup = createAction<{ key?: string; removeAfterMs?: number | null; content: PopupContent }>(
'app/addPopup' 'application/addPopup'
) )
export const removePopup = createAction<{ key: string }>('app/removePopup') export const removePopup = createAction<{ key: string }>('application/removePopup')
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { addPopup, PopupContent, removePopup, toggleWalletModal, toggleSettingsMenu } from './actions' import { AppDispatch, AppState } from '../index'
import { useSelector, useDispatch } from 'react-redux' import { addPopup, ApplicationModal, PopupContent, removePopup, setOpenModal } from './actions'
import { AppState } from '../index'
export function useBlockNumber(): number | undefined { export function useBlockNumber(): number | undefined {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
...@@ -10,22 +10,45 @@ export function useBlockNumber(): number | undefined { ...@@ -10,22 +10,45 @@ export function useBlockNumber(): number | undefined {
return useSelector((state: AppState) => state.application.blockNumber[chainId ?? -1]) return useSelector((state: AppState) => state.application.blockNumber[chainId ?? -1])
} }
export function useWalletModalOpen(): boolean { export function useModalOpen(modal: ApplicationModal): boolean {
return useSelector((state: AppState) => state.application.walletModalOpen) const openModal = useSelector((state: AppState) => state.application.openModal)
return openModal === modal
} }
export function useWalletModalToggle(): () => void { export function useToggleModal(modal: ApplicationModal): () => void {
const dispatch = useDispatch() const open = useModalOpen(modal)
return useCallback(() => dispatch(toggleWalletModal()), [dispatch]) const dispatch = useDispatch<AppDispatch>()
return useCallback(() => dispatch(setOpenModal(open ? null : modal)), [dispatch, modal, open])
}
export function useOpenModal(modal: ApplicationModal): () => void {
const dispatch = useDispatch<AppDispatch>()
return useCallback(() => dispatch(setOpenModal(modal)), [dispatch, modal])
} }
export function useSettingsMenuOpen(): boolean { export function useCloseModals(): () => void {
return useSelector((state: AppState) => state.application.settingsMenuOpen) const dispatch = useDispatch<AppDispatch>()
return useCallback(() => dispatch(setOpenModal(null)), [dispatch])
}
export function useWalletModalToggle(): () => void {
return useToggleModal(ApplicationModal.WALLET)
} }
export function useToggleSettingsMenu(): () => void { export function useToggleSettingsMenu(): () => void {
const dispatch = useDispatch() return useToggleModal(ApplicationModal.SETTINGS)
return useCallback(() => dispatch(toggleSettingsMenu()), [dispatch]) }
export function useShowClaimPopup(): boolean {
return useModalOpen(ApplicationModal.CLAIM_POPUP)
}
export function useToggleShowClaimPopup(): () => void {
return useToggleModal(ApplicationModal.CLAIM_POPUP)
}
export function useToggleSelfClaimModal(): () => void {
return useToggleModal(ApplicationModal.SELF_CLAIM)
} }
// returns a function that allows adding a popup // returns a function that allows adding a popup
......
import { ChainId } from '@uniswap/sdk' import { ChainId } from '@uniswap/sdk'
import { createStore, Store } from 'redux' import { createStore, Store } from 'redux'
import { addPopup, removePopup, toggleSettingsMenu, toggleWalletModal, updateBlockNumber } from './actions' import { addPopup, ApplicationModal, removePopup, setOpenModal, updateBlockNumber } from './actions'
import reducer, { ApplicationState } from './reducer' import reducer, { ApplicationState } from './reducer'
describe('application reducer', () => { describe('application reducer', () => {
...@@ -9,11 +9,10 @@ describe('application reducer', () => { ...@@ -9,11 +9,10 @@ describe('application reducer', () => {
beforeEach(() => { beforeEach(() => {
store = createStore(reducer, { store = createStore(reducer, {
popupList: [], popupList: [],
walletModalOpen: false,
settingsMenuOpen: false,
blockNumber: { blockNumber: {
[ChainId.MAINNET]: 3 [ChainId.MAINNET]: 3
} },
openModal: null
}) })
}) })
...@@ -40,25 +39,16 @@ describe('application reducer', () => { ...@@ -40,25 +39,16 @@ describe('application reducer', () => {
}) })
}) })
describe('toggleWalletModal', () => { describe('setOpenModal', () => {
it('toggles wallet modal', () => { it('set wallet modal', () => {
store.dispatch(toggleWalletModal()) store.dispatch(setOpenModal(ApplicationModal.WALLET))
expect(store.getState().walletModalOpen).toEqual(true) expect(store.getState().openModal).toEqual(ApplicationModal.WALLET)
store.dispatch(toggleWalletModal()) store.dispatch(setOpenModal(ApplicationModal.WALLET))
expect(store.getState().walletModalOpen).toEqual(false) expect(store.getState().openModal).toEqual(ApplicationModal.WALLET)
store.dispatch(toggleWalletModal()) store.dispatch(setOpenModal(ApplicationModal.CLAIM_POPUP))
expect(store.getState().walletModalOpen).toEqual(true) expect(store.getState().openModal).toEqual(ApplicationModal.CLAIM_POPUP)
}) store.dispatch(setOpenModal(null))
}) expect(store.getState().openModal).toEqual(null)
describe('settingsMenuOpen', () => {
it('toggles settings menu', () => {
store.dispatch(toggleSettingsMenu())
expect(store.getState().settingsMenuOpen).toEqual(true)
store.dispatch(toggleSettingsMenu())
expect(store.getState().settingsMenuOpen).toEqual(false)
store.dispatch(toggleSettingsMenu())
expect(store.getState().settingsMenuOpen).toEqual(true)
}) })
}) })
......
import { createReducer, nanoid } from '@reduxjs/toolkit' import { createReducer, nanoid } from '@reduxjs/toolkit'
import { import { addPopup, PopupContent, removePopup, updateBlockNumber, ApplicationModal, setOpenModal } from './actions'
addPopup,
PopupContent,
removePopup,
toggleWalletModal,
toggleSettingsMenu,
updateBlockNumber
} from './actions'
type PopupList = Array<{ key: string; show: boolean; content: PopupContent; removeAfterMs: number | null }> type PopupList = Array<{ key: string; show: boolean; content: PopupContent; removeAfterMs: number | null }>
export interface ApplicationState { export interface ApplicationState {
blockNumber: { [chainId: number]: number } readonly blockNumber: { readonly [chainId: number]: number }
popupList: PopupList readonly popupList: PopupList
walletModalOpen: boolean readonly openModal: ApplicationModal | null
settingsMenuOpen: boolean
} }
const initialState: ApplicationState = { const initialState: ApplicationState = {
blockNumber: {}, blockNumber: {},
popupList: [], popupList: [],
walletModalOpen: false, openModal: null
settingsMenuOpen: false
} }
export default createReducer(initialState, builder => export default createReducer(initialState, builder =>
...@@ -34,11 +25,8 @@ export default createReducer(initialState, builder => ...@@ -34,11 +25,8 @@ export default createReducer(initialState, builder =>
state.blockNumber[chainId] = Math.max(blockNumber, state.blockNumber[chainId]) state.blockNumber[chainId] = Math.max(blockNumber, state.blockNumber[chainId])
} }
}) })
.addCase(toggleWalletModal, state => { .addCase(setOpenModal, (state, action) => {
state.walletModalOpen = !state.walletModalOpen state.openModal = action.payload
})
.addCase(toggleSettingsMenu, state => {
state.settingsMenuOpen = !state.settingsMenuOpen
}) })
.addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 15000 } }) => { .addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 15000 } }) => {
state.popupList = (key ? state.popupList.filter(popup => popup.key !== key) : state.popupList).concat([ state.popupList = (key ? state.popupList.filter(popup => popup.key !== key) : state.popupList).concat([
......
import { UNI } from './../../constants/index'
import { TokenAmount, JSBI, ChainId } from '@uniswap/sdk'
import { TransactionResponse } from '@ethersproject/providers'
import { useEffect, useState } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { useMerkleDistributorContract } from '../../hooks/useContract'
import { useSingleCallResult } from '../multicall/hooks'
import { calculateGasMargin, isAddress } from '../../utils'
import { useTransactionAdder } from '../transactions/hooks'
interface UserClaimData {
index: number
amount: string
proof: string[]
flags?: {
isSOCKS: boolean
isLP: boolean
isUser: boolean
}
}
const CLAIM_PROMISES: { [key: string]: Promise<UserClaimData | null> } = {}
// returns the claim for the given address, or null if not valid
function fetchClaim(account: string, chainId: ChainId): Promise<UserClaimData | null> {
const formatted = isAddress(account)
if (!formatted) return Promise.reject(new Error('Invalid address'))
const key = `${chainId}:${account}`
return (CLAIM_PROMISES[key] =
CLAIM_PROMISES[key] ??
fetch(`https://gentle-frost-9e74.uniswap.workers.dev/${chainId}/${formatted}`)
.then(res => {
if (res.status === 200) {
return res.json()
} else {
console.debug(`No claim for account ${formatted} on chain ID ${chainId}`)
return null
}
})
.catch(error => {
console.error('Failed to get claim data', error)
}))
}
// parse distributorContract blob and detect if user has claim data
// null means we know it does not
export function useUserClaimData(account: string | null | undefined): UserClaimData | null | undefined {
const { chainId } = useActiveWeb3React()
const key = `${chainId}:${account}`
const [claimInfo, setClaimInfo] = useState<{ [key: string]: UserClaimData | null }>({})
useEffect(() => {
if (!account || !chainId) return
fetchClaim(account, chainId).then(accountClaimInfo =>
setClaimInfo(claimInfo => {
return {
...claimInfo,
[key]: accountClaimInfo
}
})
)
}, [account, chainId, key])
return account && chainId ? claimInfo[key] : undefined
}
// check if user is in blob and has not yet claimed UNI
export function useUserHasAvailableClaim(account: string | null | undefined): boolean {
const userClaimData = useUserClaimData(account)
const distributorContract = useMerkleDistributorContract()
const isClaimedResult = useSingleCallResult(distributorContract, 'isClaimed', [userClaimData?.index])
// user is in blob and contract marks as unclaimed
return Boolean(userClaimData && !isClaimedResult.loading && isClaimedResult.result?.[0] === false)
}
export function useUserUnclaimedAmount(account: string | null | undefined): TokenAmount | undefined {
const { chainId } = useActiveWeb3React()
const userClaimData = useUserClaimData(account)
const canClaim = useUserHasAvailableClaim(account)
const uni = chainId ? UNI[chainId] : undefined
if (!uni) return undefined
if (!canClaim || !userClaimData) {
return new TokenAmount(uni, JSBI.BigInt(0))
}
return new TokenAmount(uni, JSBI.BigInt(userClaimData.amount))
}
export function useClaimCallback(
account: string | null | undefined
): {
claimCallback: () => Promise<string>
} {
// get claim data for this account
const { library, chainId } = useActiveWeb3React()
const claimData = useUserClaimData(account)
// used for popup summary
const unClaimedAmount: TokenAmount | undefined = useUserUnclaimedAmount(account)
const addTransaction = useTransactionAdder()
const distributorContract = useMerkleDistributorContract()
const claimCallback = async function() {
if (!claimData || !account || !library || !chainId || !distributorContract) return
const args = [claimData.index, account, claimData.amount, claimData.proof]
return distributorContract.estimateGas['claim'](...args, {}).then(estimatedGasLimit => {
return distributorContract
.claim(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: `Claimed ${unClaimedAmount?.toSignificant(4)} UNI`,
claim: { recipient: account }
})
return response.hash
})
})
}
return { claimCallback }
}
import { UNI } from './../../constants/index'
import { TokenAmount } from '@uniswap/sdk'
import { isAddress } from 'ethers/lib/utils'
import { useGovernanceContract, useUniContract } from '../../hooks/useContract'
import { useSingleCallResult, useSingleContractMultipleData } from '../multicall/hooks'
import { useActiveWeb3React } from '../../hooks'
import { ethers, utils } from 'ethers'
import { calculateGasMargin } from '../../utils'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../transactions/hooks'
import { useState, useEffect, useCallback } from 'react'
import { abi as GOV_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
interface ProposalDetail {
target: string
functionSig: string
callData: string
}
export interface ProposalData {
id: string
title: string
description: string
proposer: string
status: string
forCount: number
againstCount: number
startBlock: number
endBlock: number
details: ProposalDetail[]
}
const enumerateProposalState = (state: number) => {
const proposalStates = ['pending', 'active', 'canceled', 'defeated', 'succeeded', 'queued', 'expired', 'executed']
return proposalStates[state]
}
// get count of all proposals made
export function useProposalCount(): number | undefined {
const gov = useGovernanceContract()
const res = useSingleCallResult(gov, 'proposalCount')
if (res.result && !res.loading) {
return parseInt(res.result[0])
}
return undefined
}
/**
* Need proposal events to get description data emitted from
* new proposal event.
*/
export function useDataFromEventLogs() {
const { library } = useActiveWeb3React()
const [formattedEvents, setFormattedEvents] = useState<any>()
const govContract = useGovernanceContract()
// create filter for these specific events
const filter = { ...govContract?.filters?.['ProposalCreated'](), fromBlock: 0, toBlock: 'latest' }
const eventParser = new ethers.utils.Interface(GOV_ABI)
useEffect(() => {
async function fetchData() {
const pastEvents = await library?.getLogs(filter)
// reverse events to get them from newest to odlest
const formattedEventData = pastEvents
?.map(event => {
const eventParsed = eventParser.parseLog(event).args
return {
description: eventParsed.description,
details: eventParsed.targets.map((target: string, i: number) => {
const signature = eventParsed.signatures[i]
const [name, types] = signature.substr(0, signature.length - 1).split('(')
const calldata = eventParsed.calldatas[i]
const decoded = utils.defaultAbiCoder.decode(types.split(','), calldata)
return {
target,
functionSig: name,
callData: decoded.join(', ')
}
})
}
})
.reverse()
setFormattedEvents(formattedEventData)
}
if (!formattedEvents) {
fetchData()
}
}, [eventParser, filter, library, formattedEvents])
return formattedEvents
}
// get data for all past and active proposals
export function useAllProposalData() {
const proposalCount = useProposalCount()
const govContract = useGovernanceContract()
const proposalIndexes = []
for (let i = 1; i <= (proposalCount ?? 0); i++) {
proposalIndexes.push([i])
}
// get metadata from past events
const formattedEvents = useDataFromEventLogs()
// get all proposal entities
const allProposals = useSingleContractMultipleData(govContract, 'proposals', proposalIndexes)
// get all proposal states
const allProposalStates = useSingleContractMultipleData(govContract, 'state', proposalIndexes)
if (formattedEvents && allProposals && allProposalStates) {
allProposals.reverse()
allProposalStates.reverse()
return allProposals
.filter((p, i) => {
return Boolean(p.result) && Boolean(allProposalStates[i]?.result) && Boolean(formattedEvents[i])
})
.map((p, i) => {
const formattedProposal: ProposalData = {
id: allProposals[i]?.result?.id.toString(),
title: formattedEvents[i].description?.split(/# |\n/g)[1] || 'Untitled',
description: formattedEvents[i].description?.split(/# /)[1] || 'No description.',
proposer: allProposals[i]?.result?.proposer,
status: enumerateProposalState(allProposalStates[i]?.result?.[0]) ?? 'Undetermined',
forCount: parseFloat(ethers.utils.formatUnits(allProposals[i]?.result?.forVotes.toString(), 18)),
againstCount: parseFloat(ethers.utils.formatUnits(allProposals[i]?.result?.againstVotes.toString(), 18)),
startBlock: parseInt(allProposals[i]?.result?.startBlock?.toString()),
endBlock: parseInt(allProposals[i]?.result?.endBlock?.toString()),
details: formattedEvents[i].details
}
return formattedProposal
})
} else {
return []
}
}
export function useProposalData(id: string): ProposalData | undefined {
const allProposalData = useAllProposalData()
return allProposalData?.find(p => p.id === id)
}
// get the users delegatee if it exists
export function useUserDelegatee(): string {
const { account } = useActiveWeb3React()
const uniContract = useUniContract()
const { result } = useSingleCallResult(uniContract, 'delegates', [account ?? undefined])
return result?.[0] ?? undefined
}
export function useUserVotes(): TokenAmount | undefined {
const { account, chainId } = useActiveWeb3React()
const uniContract = useUniContract()
// check for available votes
const uni = chainId ? UNI[chainId] : undefined
const votes = useSingleCallResult(uniContract, 'getCurrentVotes', [account ?? undefined])?.result?.[0]
return votes && uni ? new TokenAmount(uni, votes) : undefined
}
export function useDelegateCallback(): (delegatee: string | undefined) => undefined | Promise<string> {
const { account, chainId, library } = useActiveWeb3React()
const addTransaction = useTransactionAdder()
const uniContract = useUniContract()
return useCallback(
(delegatee: string | undefined) => {
if (!library || !chainId || !account || !isAddress(delegatee ?? '')) return undefined
const args = [delegatee]
if (!uniContract) throw new Error('No UNI Contract!')
return uniContract.estimateGas.delegate(...args, {}).then(estimatedGasLimit => {
return uniContract
.delegate(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: `Delegated votes`
})
return response.hash
})
})
},
[account, addTransaction, chainId, library, uniContract]
)
}
export function useVoteCallback(): {
voteCallback: (proposalId: string | undefined, support: boolean) => undefined | Promise<string>
} {
const { account } = useActiveWeb3React()
const govContract = useGovernanceContract()
const addTransaction = useTransactionAdder()
const voteCallback = useCallback(
(proposalId: string | undefined, support: boolean) => {
if (!account || !govContract || !proposalId) return
const args = [proposalId, support]
return govContract.estimateGas.castVote(...args, {}).then(estimatedGasLimit => {
return govContract
.castVote(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
summary: `Voted ${support ? 'for ' : 'against'} proposal ${proposalId}`
})
return response.hash
})
})
},
[account, addTransaction, govContract]
)
return { voteCallback }
}
...@@ -3,7 +3,6 @@ import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/l ...@@ -3,7 +3,6 @@ import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/l
import { updateVersion } from '../global/actions' import { updateVersion } from '../global/actions'
import { fetchTokenList, acceptListUpdate, addList, removeList, selectList } from './actions' import { fetchTokenList, acceptListUpdate, addList, removeList, selectList } from './actions'
import reducer, { ListsState } from './reducer' import reducer, { ListsState } from './reducer'
import UNISWAP_DEFAULT_TOKEN_LIST from '@uniswap/default-token-list'
const STUB_TOKEN_LIST = { const STUB_TOKEN_LIST = {
name: '', name: '',
...@@ -306,7 +305,7 @@ describe('list reducer', () => { ...@@ -306,7 +305,7 @@ describe('list reducer', () => {
selectedListUrl: undefined selectedListUrl: undefined
}) })
}) })
it('unselects the list if selected', () => { it('selects the default list if removed list was selected', () => {
store = createStore(reducer, { store = createStore(reducer, {
byUrl: { byUrl: {
'fake-url': { 'fake-url': {
...@@ -321,7 +320,7 @@ describe('list reducer', () => { ...@@ -321,7 +320,7 @@ describe('list reducer', () => {
store.dispatch(removeList('fake-url')) store.dispatch(removeList('fake-url'))
expect(store.getState()).toEqual({ expect(store.getState()).toEqual({
byUrl: {}, byUrl: {},
selectedListUrl: undefined selectedListUrl: 'tokens.uniswap.eth'
}) })
}) })
}) })
...@@ -449,7 +448,7 @@ describe('list reducer', () => { ...@@ -449,7 +448,7 @@ describe('list reducer', () => {
if (url === DEFAULT_TOKEN_LIST_URL) { if (url === DEFAULT_TOKEN_LIST_URL) {
expect(s.byUrl[url]).toEqual({ expect(s.byUrl[url]).toEqual({
error: null, error: null,
current: UNISWAP_DEFAULT_TOKEN_LIST, current: null,
loadingRequestId: null, loadingRequestId: null,
pendingUpdate: null pendingUpdate: null
}) })
...@@ -466,6 +465,17 @@ describe('list reducer', () => { ...@@ -466,6 +465,17 @@ describe('list reducer', () => {
it('sets initialized lists', () => { it('sets initialized lists', () => {
expect(store.getState().lastInitializedDefaultListOfLists).toEqual(DEFAULT_LIST_OF_LISTS) expect(store.getState().lastInitializedDefaultListOfLists).toEqual(DEFAULT_LIST_OF_LISTS)
}) })
it('sets selected list', () => {
expect(store.getState().selectedListUrl).toEqual(DEFAULT_TOKEN_LIST_URL)
})
it('default list is initialized', () => {
expect(store.getState().byUrl[DEFAULT_TOKEN_LIST_URL]).toEqual({
error: null,
current: null,
loadingRequestId: null,
pendingUpdate: null
})
})
}) })
describe('initialized with a different set of lists', () => { describe('initialized with a different set of lists', () => {
beforeEach(() => { beforeEach(() => {
...@@ -527,6 +537,17 @@ describe('list reducer', () => { ...@@ -527,6 +537,17 @@ describe('list reducer', () => {
it('sets initialized lists', () => { it('sets initialized lists', () => {
expect(store.getState().lastInitializedDefaultListOfLists).toEqual(DEFAULT_LIST_OF_LISTS) expect(store.getState().lastInitializedDefaultListOfLists).toEqual(DEFAULT_LIST_OF_LISTS)
}) })
it('sets default list to selected list', () => {
expect(store.getState().selectedListUrl).toEqual(DEFAULT_TOKEN_LIST_URL)
})
it('default list is initialized', () => {
expect(store.getState().byUrl[DEFAULT_TOKEN_LIST_URL]).toEqual({
error: null,
current: null,
loadingRequestId: null,
pendingUpdate: null
})
})
}) })
}) })
}) })
...@@ -4,7 +4,6 @@ import { TokenList } from '@uniswap/token-lists/dist/types' ...@@ -4,7 +4,6 @@ import { TokenList } from '@uniswap/token-lists/dist/types'
import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/lists' import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/lists'
import { updateVersion } from '../global/actions' import { updateVersion } from '../global/actions'
import { acceptListUpdate, addList, fetchTokenList, removeList, selectList } from './actions' import { acceptListUpdate, addList, fetchTokenList, removeList, selectList } from './actions'
import UNISWAP_DEFAULT_LIST from '@uniswap/default-token-list'
export interface ListsState { export interface ListsState {
readonly byUrl: { readonly byUrl: {
...@@ -20,7 +19,9 @@ export interface ListsState { ...@@ -20,7 +19,9 @@ export interface ListsState {
readonly selectedListUrl: string | undefined readonly selectedListUrl: string | undefined
} }
const NEW_LIST_STATE: ListsState['byUrl'][string] = { type ListState = ListsState['byUrl'][string]
const NEW_LIST_STATE: ListState = {
error: null, error: null,
current: null, current: null,
loadingRequestId: null, loadingRequestId: null,
...@@ -35,15 +36,9 @@ const initialState: ListsState = { ...@@ -35,15 +36,9 @@ const initialState: ListsState = {
...DEFAULT_LIST_OF_LISTS.reduce<Mutable<ListsState['byUrl']>>((memo, listUrl) => { ...DEFAULT_LIST_OF_LISTS.reduce<Mutable<ListsState['byUrl']>>((memo, listUrl) => {
memo[listUrl] = NEW_LIST_STATE memo[listUrl] = NEW_LIST_STATE
return memo return memo
}, {}), }, {})
[DEFAULT_TOKEN_LIST_URL]: {
error: null,
current: UNISWAP_DEFAULT_LIST,
loadingRequestId: null,
pendingUpdate: null
}
}, },
selectedListUrl: undefined selectedListUrl: DEFAULT_TOKEN_LIST_URL
} }
export default createReducer(initialState, builder => export default createReducer(initialState, builder =>
...@@ -115,7 +110,7 @@ export default createReducer(initialState, builder => ...@@ -115,7 +110,7 @@ export default createReducer(initialState, builder =>
delete state.byUrl[url] delete state.byUrl[url]
} }
if (state.selectedListUrl === url) { if (state.selectedListUrl === url) {
state.selectedListUrl = Object.keys(state.byUrl)[0] state.selectedListUrl = url === DEFAULT_TOKEN_LIST_URL ? Object.keys(state.byUrl)[0] : DEFAULT_TOKEN_LIST_URL
} }
}) })
.addCase(acceptListUpdate, (state, { payload: url }) => { .addCase(acceptListUpdate, (state, { payload: url }) => {
...@@ -132,7 +127,7 @@ export default createReducer(initialState, builder => ...@@ -132,7 +127,7 @@ export default createReducer(initialState, builder =>
// state loaded from localStorage, but new lists have never been initialized // state loaded from localStorage, but new lists have never been initialized
if (!state.lastInitializedDefaultListOfLists) { if (!state.lastInitializedDefaultListOfLists) {
state.byUrl = initialState.byUrl state.byUrl = initialState.byUrl
state.selectedListUrl = undefined state.selectedListUrl = DEFAULT_TOKEN_LIST_URL
} else if (state.lastInitializedDefaultListOfLists) { } else if (state.lastInitializedDefaultListOfLists) {
const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce<Set<string>>( const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce<Set<string>>(
(s, l) => s.add(l), (s, l) => s.add(l),
...@@ -154,5 +149,12 @@ export default createReducer(initialState, builder => ...@@ -154,5 +149,12 @@ export default createReducer(initialState, builder =>
} }
state.lastInitializedDefaultListOfLists = DEFAULT_LIST_OF_LISTS state.lastInitializedDefaultListOfLists = DEFAULT_LIST_OF_LISTS
if (!state.selectedListUrl) {
state.selectedListUrl = DEFAULT_TOKEN_LIST_URL
if (!state.byUrl[DEFAULT_TOKEN_LIST_URL]) {
state.byUrl[DEFAULT_TOKEN_LIST_URL] = NEW_LIST_STATE
}
}
}) })
) )
import { ChainId, CurrencyAmount, JSBI, Token, TokenAmount, WETH, Pair } from '@uniswap/sdk'
import { useMemo } from 'react'
import { DAI, UNI, USDC, USDT, WBTC } from '../../constants'
import { STAKING_REWARDS_INTERFACE } from '../../constants/abis/staking-rewards'
import { useActiveWeb3React } from '../../hooks'
import { NEVER_RELOAD, useMultipleContractSingleData } from '../multicall/hooks'
import { tryParseAmount } from '../swap/hooks'
export const STAKING_GENESIS = 1600387200
export const REWARDS_DURATION_DAYS = 60
// TODO add staking rewards addresses here
export const STAKING_REWARDS_INFO: {
[chainId in ChainId]?: {
tokens: [Token, Token]
stakingRewardAddress: string
}[]
} = {
[ChainId.MAINNET]: [
{
tokens: [WETH[ChainId.MAINNET], DAI],
stakingRewardAddress: '0xa1484C3aa22a66C62b77E0AE78E15258bd0cB711'
},
{
tokens: [WETH[ChainId.MAINNET], USDC],
stakingRewardAddress: '0x7FBa4B8Dc5E7616e59622806932DBea72537A56b'
},
{
tokens: [WETH[ChainId.MAINNET], USDT],
stakingRewardAddress: '0x6C3e4cb2E96B01F4b866965A91ed4437839A121a'
},
{
tokens: [WETH[ChainId.MAINNET], WBTC],
stakingRewardAddress: '0xCA35e32e7926b96A9988f61d510E038108d8068e'
}
]
}
export interface StakingInfo {
// the address of the reward contract
stakingRewardAddress: string
// the tokens involved in this pair
tokens: [Token, Token]
// the amount of token currently staked, or undefined if no account
stakedAmount: TokenAmount
// the amount of reward token earned by the active account, or undefined if no account
earnedAmount: TokenAmount
// the total amount of token staked in the contract
totalStakedAmount: TokenAmount
// the amount of token distributed per second to all LPs, constant
totalRewardRate: TokenAmount
// the current amount of token distributed to the active account per second.
// equivalent to percent of total supply * reward rate
rewardRate: TokenAmount
// when the period ends
periodFinish: Date | undefined
// calculates a hypothetical amount of token distributed to the active account per second.
getHypotheticalRewardRate: (
stakedAmount: TokenAmount,
totalStakedAmount: TokenAmount,
totalRewardRate: TokenAmount
) => TokenAmount
}
// gets the staking info from the network for the active chain id
export function useStakingInfo(pairToFilterBy?: Pair | null): StakingInfo[] {
const { chainId, account } = useActiveWeb3React()
const info = useMemo(
() =>
chainId
? STAKING_REWARDS_INFO[chainId]?.filter(stakingRewardInfo =>
pairToFilterBy === undefined
? true
: pairToFilterBy === null
? false
: pairToFilterBy.involvesToken(stakingRewardInfo.tokens[0]) &&
pairToFilterBy.involvesToken(stakingRewardInfo.tokens[1])
) ?? []
: [],
[chainId, pairToFilterBy]
)
const uni = chainId ? UNI[chainId] : undefined
const rewardsAddresses = useMemo(() => info.map(({ stakingRewardAddress }) => stakingRewardAddress), [info])
const accountArg = useMemo(() => [account ?? undefined], [account])
// get all the info from the staking rewards contracts
const balances = useMultipleContractSingleData(rewardsAddresses, STAKING_REWARDS_INTERFACE, 'balanceOf', accountArg)
const earnedAmounts = useMultipleContractSingleData(rewardsAddresses, STAKING_REWARDS_INTERFACE, 'earned', accountArg)
const totalSupplies = useMultipleContractSingleData(rewardsAddresses, STAKING_REWARDS_INTERFACE, 'totalSupply')
// tokens per second, constants
const rewardRates = useMultipleContractSingleData(
rewardsAddresses,
STAKING_REWARDS_INTERFACE,
'rewardRate',
undefined,
NEVER_RELOAD
)
const periodFinishes = useMultipleContractSingleData(
rewardsAddresses,
STAKING_REWARDS_INTERFACE,
'periodFinish',
undefined,
NEVER_RELOAD
)
return useMemo(() => {
if (!chainId || !uni) return []
return rewardsAddresses.reduce<StakingInfo[]>((memo, rewardsAddress, index) => {
// these two are dependent on account
const balanceState = balances[index]
const earnedAmountState = earnedAmounts[index]
// these get fetched regardless of account
const totalSupplyState = totalSupplies[index]
const rewardRateState = rewardRates[index]
const periodFinishState = periodFinishes[index]
if (
// these may be undefined if not logged in
!balanceState?.loading &&
!earnedAmountState?.loading &&
// always need these
totalSupplyState &&
!totalSupplyState.loading &&
rewardRateState &&
!rewardRateState.loading &&
periodFinishState &&
!periodFinishState.loading
) {
if (
balanceState?.error ||
earnedAmountState?.error ||
totalSupplyState.error ||
rewardRateState.error ||
periodFinishState.error
) {
console.error('Failed to load staking rewards info')
return memo
}
// get the LP token
const tokens = info[index].tokens
const dummyPair = new Pair(new TokenAmount(tokens[0], '0'), new TokenAmount(tokens[1], '0'))
// check for account, if no account set to 0
const stakedAmount = new TokenAmount(dummyPair.liquidityToken, JSBI.BigInt(balanceState?.result?.[0] ?? 0))
const totalStakedAmount = new TokenAmount(dummyPair.liquidityToken, JSBI.BigInt(totalSupplyState.result?.[0]))
const totalRewardRate = new TokenAmount(uni, JSBI.BigInt(rewardRateState.result?.[0]))
const getHypotheticalRewardRate = (
stakedAmount: TokenAmount,
totalStakedAmount: TokenAmount,
totalRewardRate: TokenAmount
): TokenAmount => {
return new TokenAmount(
uni,
JSBI.greaterThan(totalStakedAmount.raw, JSBI.BigInt(0))
? JSBI.divide(JSBI.multiply(totalRewardRate.raw, stakedAmount.raw), totalStakedAmount.raw)
: JSBI.BigInt(0)
)
}
const individualRewardRate = getHypotheticalRewardRate(stakedAmount, totalStakedAmount, totalRewardRate)
const periodFinishMs = periodFinishState.result?.[0]?.mul(1000)?.toNumber()
memo.push({
stakingRewardAddress: rewardsAddress,
tokens: info[index].tokens,
periodFinish: periodFinishMs > 0 ? new Date(periodFinishMs) : undefined,
earnedAmount: new TokenAmount(uni, JSBI.BigInt(earnedAmountState?.result?.[0] ?? 0)),
rewardRate: individualRewardRate,
totalRewardRate: totalRewardRate,
stakedAmount: stakedAmount,
totalStakedAmount: totalStakedAmount,
getHypotheticalRewardRate
})
}
return memo
}, [])
}, [balances, chainId, earnedAmounts, info, periodFinishes, rewardRates, rewardsAddresses, totalSupplies, uni])
}
export function useTotalUniEarned(): TokenAmount | undefined {
const { chainId } = useActiveWeb3React()
const uni = chainId ? UNI[chainId] : undefined
const stakingInfos = useStakingInfo()
return useMemo(() => {
if (!uni) return undefined
return (
stakingInfos?.reduce(
(accumulator, stakingInfo) => accumulator.add(stakingInfo.earnedAmount),
new TokenAmount(uni, '0')
) ?? new TokenAmount(uni, '0')
)
}, [stakingInfos, uni])
}
// based on typed value
export function useDerivedStakeInfo(
typedValue: string,
stakingToken: Token,
userLiquidityUnstaked: TokenAmount | undefined
): {
parsedAmount?: CurrencyAmount
error?: string
} {
const { account } = useActiveWeb3React()
const parsedInput: CurrencyAmount | undefined = tryParseAmount(typedValue, stakingToken)
const parsedAmount =
parsedInput && userLiquidityUnstaked && JSBI.lessThanOrEqual(parsedInput.raw, userLiquidityUnstaked.raw)
? parsedInput
: undefined
let error: string | undefined
if (!account) {
error = 'Connect Wallet'
}
if (!parsedAmount) {
error = error ?? 'Enter an amount'
}
return {
parsedAmount,
error
}
}
// based on typed value
export function useDerivedUnstakeInfo(
typedValue: string,
stakingAmount: TokenAmount
): {
parsedAmount?: CurrencyAmount
error?: string
} {
const { account } = useActiveWeb3React()
const parsedInput: CurrencyAmount | undefined = tryParseAmount(typedValue, stakingAmount.token)
const parsedAmount = parsedInput && JSBI.lessThanOrEqual(parsedInput.raw, stakingAmount.raw) ? parsedInput : undefined
let error: string | undefined
if (!account) {
error = 'Connect Wallet'
}
if (!parsedAmount) {
error = error ?? 'Enter an amount'
}
return {
parsedAmount,
error
}
}
...@@ -17,6 +17,7 @@ export const addTransaction = createAction<{ ...@@ -17,6 +17,7 @@ export const addTransaction = createAction<{
hash: string hash: string
from: string from: string
approval?: { tokenAddress: string; spender: string } approval?: { tokenAddress: string; spender: string }
claim?: { recipient: string }
summary?: string summary?: string
}>('transactions/addTransaction') }>('transactions/addTransaction')
export const clearAllTransactions = createAction<{ chainId: ChainId }>('transactions/clearAllTransactions') export const clearAllTransactions = createAction<{ chainId: ChainId }>('transactions/clearAllTransactions')
......
...@@ -10,7 +10,7 @@ import { TransactionDetails } from './reducer' ...@@ -10,7 +10,7 @@ import { TransactionDetails } from './reducer'
// helper that can take a ethers library transaction response and add it to the list of transactions // helper that can take a ethers library transaction response and add it to the list of transactions
export function useTransactionAdder(): ( export function useTransactionAdder(): (
response: TransactionResponse, response: TransactionResponse,
customData?: { summary?: string; approval?: { tokenAddress: string; spender: string } } customData?: { summary?: string; approval?: { tokenAddress: string; spender: string }; claim?: { recipient: string } }
) => void { ) => void {
const { chainId, account } = useActiveWeb3React() const { chainId, account } = useActiveWeb3React()
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
...@@ -18,7 +18,11 @@ export function useTransactionAdder(): ( ...@@ -18,7 +18,11 @@ export function useTransactionAdder(): (
return useCallback( return useCallback(
( (
response: TransactionResponse, response: TransactionResponse,
{ summary, approval }: { summary?: string; approval?: { tokenAddress: string; spender: string } } = {} {
summary,
approval,
claim
}: { summary?: string; claim?: { recipient: string }; approval?: { tokenAddress: string; spender: string } } = {}
) => { ) => {
if (!account) return if (!account) return
if (!chainId) return if (!chainId) return
...@@ -27,7 +31,7 @@ export function useTransactionAdder(): ( ...@@ -27,7 +31,7 @@ export function useTransactionAdder(): (
if (!hash) { if (!hash) {
throw Error('No transaction hash found.') throw Error('No transaction hash found.')
} }
dispatch(addTransaction({ hash, from: account, chainId, approval, summary })) dispatch(addTransaction({ hash, from: account, chainId, approval, summary, claim }))
}, },
[dispatch, chainId, account] [dispatch, chainId, account]
) )
...@@ -79,3 +83,22 @@ export function useHasPendingApproval(tokenAddress: string | undefined, spender: ...@@ -79,3 +83,22 @@ export function useHasPendingApproval(tokenAddress: string | undefined, spender:
[allTransactions, spender, tokenAddress] [allTransactions, spender, tokenAddress]
) )
} }
// watch for submissions to claim
// return null if not done loading, return undefined if not found
export function useUserHasSubmittedClaim(
account?: string
): { claimSubmitted: boolean; claimTxn: TransactionDetails | undefined } {
const allTransactions = useAllTransactions()
// get the txn if it has been submitted
const claimTxn = useMemo(() => {
const txnIndex = Object.keys(allTransactions).find(hash => {
const tx = allTransactions[hash]
return tx.claim && tx.claim.recipient === account
})
return txnIndex && allTransactions[txnIndex] ? allTransactions[txnIndex] : undefined
}, [account, allTransactions])
return { claimSubmitted: Boolean(claimTxn), claimTxn }
}
...@@ -13,6 +13,7 @@ export interface TransactionDetails { ...@@ -13,6 +13,7 @@ export interface TransactionDetails {
hash: string hash: string
approval?: { tokenAddress: string; spender: string } approval?: { tokenAddress: string; spender: string }
summary?: string summary?: string
claim?: { recipient: string }
receipt?: SerializableTransactionReceipt receipt?: SerializableTransactionReceipt
lastCheckedBlockNumber?: number lastCheckedBlockNumber?: number
addedTime: number addedTime: number
...@@ -30,12 +31,12 @@ export const initialState: TransactionState = {} ...@@ -30,12 +31,12 @@ export const initialState: TransactionState = {}
export default createReducer(initialState, builder => export default createReducer(initialState, builder =>
builder builder
.addCase(addTransaction, (transactions, { payload: { chainId, from, hash, approval, summary } }) => { .addCase(addTransaction, (transactions, { payload: { chainId, from, hash, approval, summary, claim } }) => {
if (transactions[chainId]?.[hash]) { if (transactions[chainId]?.[hash]) {
throw Error('Attempted to add existing transaction.') throw Error('Attempted to add existing transaction.')
} }
const txs = transactions[chainId] ?? {} const txs = transactions[chainId] ?? {}
txs[hash] = { hash, approval, summary, from, addedTime: now() } txs[hash] = { hash, approval, summary, claim, from, addedTime: now() }
transactions[chainId] = txs transactions[chainId] = txs
}) })
.addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => { .addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => {
......
...@@ -26,3 +26,4 @@ export const addSerializedPair = createAction<{ serializedPair: SerializedPair } ...@@ -26,3 +26,4 @@ export const addSerializedPair = createAction<{ serializedPair: SerializedPair }
export const removeSerializedPair = createAction<{ chainId: number; tokenAAddress: string; tokenBAddress: string }>( export const removeSerializedPair = createAction<{ chainId: number; tokenAAddress: string; tokenBAddress: string }>(
'user/removeSerializedPair' 'user/removeSerializedPair'
) )
export const toggleURLWarning = createAction<void>('app/toggleURLWarning')
...@@ -16,7 +16,8 @@ import { ...@@ -16,7 +16,8 @@ import {
updateUserDarkMode, updateUserDarkMode,
updateUserDeadline, updateUserDeadline,
updateUserExpertMode, updateUserExpertMode,
updateUserSlippageTolerance updateUserSlippageTolerance,
toggleURLWarning
} from './actions' } from './actions'
function serializeToken(token: Token): SerializedToken { function serializeToken(token: Token): SerializedToken {
...@@ -96,7 +97,7 @@ export function useUserSlippageTolerance(): [number, (slippage: number) => void] ...@@ -96,7 +97,7 @@ export function useUserSlippageTolerance(): [number, (slippage: number) => void]
return [userSlippageTolerance, setUserSlippageTolerance] return [userSlippageTolerance, setUserSlippageTolerance]
} }
export function useUserDeadline(): [number, (slippage: number) => void] { export function useUserTransactionTTL(): [number, (slippage: number) => void] {
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const userDeadline = useSelector<AppState, AppState['user']['userDeadline']>(state => { const userDeadline = useSelector<AppState, AppState['user']['userDeadline']>(state => {
return state.user.userDeadline return state.user.userDeadline
...@@ -160,6 +161,15 @@ export function usePairAdder(): (pair: Pair) => void { ...@@ -160,6 +161,15 @@ export function usePairAdder(): (pair: Pair) => void {
) )
} }
export function useURLWarningVisible(): boolean {
return useSelector((state: AppState) => state.user.URLWarningVisible)
}
export function useURLWarningToggle(): () => void {
const dispatch = useDispatch()
return useCallback(() => dispatch(toggleURLWarning()), [dispatch])
}
/** /**
* Given two tokens return the liquidity token that represents its liquidity shares * Given two tokens return the liquidity token that represents its liquidity shares
* @param tokenA one of the two tokens * @param tokenA one of the two tokens
......
...@@ -12,7 +12,8 @@ import { ...@@ -12,7 +12,8 @@ import {
updateUserDarkMode, updateUserDarkMode,
updateUserExpertMode, updateUserExpertMode,
updateUserSlippageTolerance, updateUserSlippageTolerance,
updateUserDeadline updateUserDeadline,
toggleURLWarning
} from './actions' } from './actions'
const currentTimestamp = () => new Date().getTime() const currentTimestamp = () => new Date().getTime()
...@@ -46,6 +47,7 @@ export interface UserState { ...@@ -46,6 +47,7 @@ export interface UserState {
} }
timestamp: number timestamp: number
URLWarningVisible: boolean
} }
function pairKey(token0Address: string, token1Address: string) { function pairKey(token0Address: string, token1Address: string) {
...@@ -60,7 +62,8 @@ export const initialState: UserState = { ...@@ -60,7 +62,8 @@ export const initialState: UserState = {
userDeadline: DEFAULT_DEADLINE_FROM_NOW, userDeadline: DEFAULT_DEADLINE_FROM_NOW,
tokens: {}, tokens: {},
pairs: {}, pairs: {},
timestamp: currentTimestamp() timestamp: currentTimestamp(),
URLWarningVisible: true
} }
export default createReducer(initialState, builder => export default createReducer(initialState, builder =>
...@@ -129,4 +132,7 @@ export default createReducer(initialState, builder => ...@@ -129,4 +132,7 @@ export default createReducer(initialState, builder =>
} }
state.timestamp = currentTimestamp() state.timestamp = currentTimestamp()
}) })
.addCase(toggleURLWarning, state => {
state.URLWarningVisible = !state.URLWarningVisible
})
) )
import { UNI } from './../../constants/index'
import { Currency, CurrencyAmount, ETHER, JSBI, Token, TokenAmount } from '@uniswap/sdk' import { Currency, CurrencyAmount, ETHER, JSBI, Token, TokenAmount } from '@uniswap/sdk'
import { useMemo } from 'react' import { useMemo } from 'react'
import ERC20_INTERFACE from '../../constants/abis/erc20' import ERC20_INTERFACE from '../../constants/abis/erc20'
...@@ -6,6 +7,8 @@ import { useActiveWeb3React } from '../../hooks' ...@@ -6,6 +7,8 @@ import { useActiveWeb3React } from '../../hooks'
import { useMulticallContract } from '../../hooks/useContract' import { useMulticallContract } from '../../hooks/useContract'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { useSingleContractMultipleData, useMultipleContractSingleData } from '../multicall/hooks' import { useSingleContractMultipleData, useMultipleContractSingleData } from '../multicall/hooks'
import { useUserUnclaimedAmount } from '../claim/hooks'
import { useTotalUniEarned } from '../stake/hooks'
/** /**
* Returns a map of the given addresses to their eventually consistent ETH balances. * Returns a map of the given addresses to their eventually consistent ETH balances.
...@@ -130,3 +133,24 @@ export function useAllTokenBalances(): { [tokenAddress: string]: TokenAmount | u ...@@ -130,3 +133,24 @@ export function useAllTokenBalances(): { [tokenAddress: string]: TokenAmount | u
const balances = useTokenBalances(account ?? undefined, allTokensArray) const balances = useTokenBalances(account ?? undefined, allTokensArray)
return balances ?? {} return balances ?? {}
} }
// get the total owned, unclaimed, and unharvested UNI for account
export function useAggregateUniBalance(): TokenAmount | undefined {
const { account, chainId } = useActiveWeb3React()
const uni = chainId ? UNI[chainId] : undefined
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, uni)
const uniUnclaimed: TokenAmount | undefined = useUserUnclaimedAmount(account)
const uniUnHarvested: TokenAmount | undefined = useTotalUniEarned()
if (!uni) return undefined
return new TokenAmount(
uni,
JSBI.add(
JSBI.add(uniBalance?.raw ?? JSBI.BigInt(0), uniUnclaimed?.raw ?? JSBI.BigInt(0)),
uniUnHarvested?.raw ?? JSBI.BigInt(0)
)
)
}
...@@ -104,6 +104,22 @@ const StyledLink = styled.a` ...@@ -104,6 +104,22 @@ const StyledLink = styled.a`
} }
` `
const rotateImg = keyframes`
0% {
transform: perspective(1000px) rotateY(0deg);
}
100% {
transform: perspective(1000px) rotateY(360deg);
}
`
export const UniTokenAnimated = styled.img`
animation: ${rotateImg} 5s cubic-bezier(0.83, 0, 0.17, 1) infinite;
padding: 2rem 0 0 0;
filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.15));
`
/** /**
* Outbound link that handles firing google analytics events * Outbound link that handles firing google analytics events
*/ */
...@@ -158,3 +174,14 @@ export function BackArrow({ to }: { to: string }) { ...@@ -158,3 +174,14 @@ export function BackArrow({ to }: { to: string }) {
</BackArrowLink> </BackArrowLink>
) )
} }
export const CustomLightSpinner = styled(Spinner)<{ size: string }>`
height: ${({ size }) => size};
width: ${({ size }) => size};
`
export const HideSmall = styled.span`
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
`
...@@ -14,7 +14,7 @@ export * from './components' ...@@ -14,7 +14,7 @@ export * from './components'
const MEDIA_WIDTHS = { const MEDIA_WIDTHS = {
upToExtraSmall: 500, upToExtraSmall: 500,
upToSmall: 600, upToSmall: 720,
upToMedium: 960, upToMedium: 960,
upToLarge: 1280 upToLarge: 1280
} }
...@@ -78,7 +78,8 @@ export function colors(darkMode: boolean): Colors { ...@@ -78,7 +78,8 @@ export function colors(darkMode: boolean): Colors {
red2: '#F82D3A', red2: '#F82D3A',
green1: '#27AE60', green1: '#27AE60',
yellow1: '#FFE270', yellow1: '#FFE270',
yellow2: '#F3841E' yellow2: '#F3841E',
blue1: '#2172E5'
// dont wanna forget these blue yet // dont wanna forget these blue yet
// blue4: darkMode ? '#153d6f70' : '#C4D9F8', // blue4: darkMode ? '#153d6f70' : '#C4D9F8',
...@@ -136,6 +137,9 @@ export const TYPE = { ...@@ -136,6 +137,9 @@ export const TYPE = {
black(props: TextProps) { black(props: TextProps) {
return <TextWrapper fontWeight={500} color={'text1'} {...props} /> return <TextWrapper fontWeight={500} color={'text1'} {...props} />
}, },
white(props: TextProps) {
return <TextWrapper fontWeight={500} color={'white'} {...props} />
},
body(props: TextProps) { body(props: TextProps) {
return <TextWrapper fontWeight={400} fontSize={16} color={'text1'} {...props} /> return <TextWrapper fontWeight={400} fontSize={16} color={'text1'} {...props} />
}, },
...@@ -148,6 +152,9 @@ export const TYPE = { ...@@ -148,6 +152,9 @@ export const TYPE = {
subHeader(props: TextProps) { subHeader(props: TextProps) {
return <TextWrapper fontWeight={400} fontSize={14} {...props} /> return <TextWrapper fontWeight={400} fontSize={14} {...props} />
}, },
small(props: TextProps) {
return <TextWrapper fontWeight={500} fontSize={11} {...props} />
},
blue(props: TextProps) { blue(props: TextProps) {
return <TextWrapper fontWeight={500} color={'primary1'} {...props} /> return <TextWrapper fontWeight={500} color={'primary1'} {...props} />
}, },
...@@ -171,7 +178,6 @@ export const TYPE = { ...@@ -171,7 +178,6 @@ export const TYPE = {
export const FixedGlobalStyle = createGlobalStyle` export const FixedGlobalStyle = createGlobalStyle`
html, input, textarea, button { html, input, textarea, button {
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
letter-spacing: -0.018em;
font-display: fallback; font-display: fallback;
} }
@supports (font-variation-settings: normal) { @supports (font-variation-settings: normal) {
...@@ -200,6 +206,8 @@ html { ...@@ -200,6 +206,8 @@ html {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
} }
` `
......
...@@ -43,6 +43,7 @@ export interface Colors { ...@@ -43,6 +43,7 @@ export interface Colors {
green1: Color green1: Color
yellow1: Color yellow1: Color
yellow2: Color yellow2: Color
blue1: Color
} }
export interface Grids { export interface Grids {
......
import { JSBI, Token, TokenAmount } from '@uniswap/sdk'
import { BigNumber } from 'ethers'
import { STAKING_GENESIS } from '../state/stake/hooks'
const STAKING_END = STAKING_GENESIS + 60 * 60 * 24 * 60
const TREASURY_VESTING_GENESIS = 1600387200
// 30 days
const TREASURY_VESTING_CLIFF: number = 60 * 60 * 24 * 30
const ONE_YEAR: number = 60 * 60 * 24 * 365
const TREASURY_BEGIN_YEAR_1 = TREASURY_VESTING_GENESIS
const TREASURY_CLIFF_YEAR_1 = TREASURY_BEGIN_YEAR_1 + TREASURY_VESTING_CLIFF
const TREASURY_END_YEAR_1 = TREASURY_BEGIN_YEAR_1 + ONE_YEAR
const TREASURY_BEGIN_YEAR_2 = TREASURY_END_YEAR_1
const TREASURY_END_YEAR_2 = TREASURY_BEGIN_YEAR_2 + ONE_YEAR
const TREASURY_BEGIN_YEAR_3 = TREASURY_END_YEAR_2
const TREASURY_END_YEAR_3 = TREASURY_BEGIN_YEAR_3 + ONE_YEAR
const TREASURY_BEGIN_YEAR_4 = TREASURY_END_YEAR_3
const TREASURY_END_YEAR_4 = TREASURY_BEGIN_YEAR_4 + ONE_YEAR
const USERS_AMOUNT = 150_000_000
const STAKING_REWARDS_AMOUNT = 20_000_000
const TREASURY_YEAR_1_AMOUNT = 172_000_000
const TREASURY_YEAR_2_AMOUNT = 129_000_00
const TREASURY_YEAR_3_AMOUNT = 86_000_00
const TREASURY_YEAR_4_AMOUNT = 43_000_00
const TEAM_YEAR_1_AMOUNT = 160_000_000
const TEAM_YEAR_2_AMOUNT = 120_000_00
const TEAM_YEAR_3_AMOUNT = 80_000_00
const TEAM_YEAR_4_AMOUNT = 40_000_00
function withVesting(before: JSBI, time: BigNumber, amount: number, start: number, end: number, cliff?: number) {
if (time.gt(start)) {
if (time.gte(end)) {
return JSBI.add(before, JSBI.BigInt(amount))
} else if (cliff && time.gte(cliff)) {
return JSBI.add(
before,
JSBI.divide(
JSBI.multiply(JSBI.BigInt(amount), JSBI.BigInt(time.sub(start).toString())),
JSBI.subtract(JSBI.BigInt(end), JSBI.BigInt(start))
)
)
}
}
return before
}
export function computeUniCirculation(uni: Token, blockTimestamp: BigNumber): TokenAmount {
let wholeAmount = JSBI.BigInt(USERS_AMOUNT) // users, 15%
// staking rewards
wholeAmount = withVesting(wholeAmount, blockTimestamp, STAKING_REWARDS_AMOUNT, STAKING_GENESIS, STAKING_END)
// treasury vesting
wholeAmount = withVesting(
wholeAmount,
blockTimestamp,
TREASURY_YEAR_1_AMOUNT,
TREASURY_BEGIN_YEAR_1,
TREASURY_END_YEAR_1,
TREASURY_CLIFF_YEAR_1
)
wholeAmount = withVesting(
wholeAmount,
blockTimestamp,
TREASURY_YEAR_2_AMOUNT,
TREASURY_BEGIN_YEAR_2,
TREASURY_END_YEAR_2
)
wholeAmount = withVesting(
wholeAmount,
blockTimestamp,
TREASURY_YEAR_3_AMOUNT,
TREASURY_BEGIN_YEAR_3,
TREASURY_END_YEAR_3
)
wholeAmount = withVesting(
wholeAmount,
blockTimestamp,
TREASURY_YEAR_4_AMOUNT,
TREASURY_BEGIN_YEAR_4,
TREASURY_END_YEAR_4
)
// team
wholeAmount = withVesting(
wholeAmount,
blockTimestamp,
TEAM_YEAR_1_AMOUNT,
TREASURY_BEGIN_YEAR_1,
TREASURY_END_YEAR_1,
TREASURY_CLIFF_YEAR_1
)
wholeAmount = withVesting(wholeAmount, blockTimestamp, TEAM_YEAR_2_AMOUNT, TREASURY_BEGIN_YEAR_2, TREASURY_END_YEAR_2)
wholeAmount = withVesting(wholeAmount, blockTimestamp, TEAM_YEAR_3_AMOUNT, TREASURY_BEGIN_YEAR_3, TREASURY_END_YEAR_3)
wholeAmount = withVesting(wholeAmount, blockTimestamp, TEAM_YEAR_4_AMOUNT, TREASURY_BEGIN_YEAR_4, TREASURY_END_YEAR_4)
return new TokenAmount(uni, JSBI.multiply(wholeAmount, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))))
}
import { Web3Provider } from '@ethersproject/providers' import { Web3Provider } from '@ethersproject/providers'
export default function getLibrary(provider: any): Web3Provider { export default function getLibrary(provider: any): Web3Provider {
const library = new Web3Provider(provider) const library = new Web3Provider(provider, 'any')
library.pollingInterval = 15000 library.pollingInterval = 15000
return library return library
} }
...@@ -25,7 +25,11 @@ const ETHERSCAN_PREFIXES: { [chainId in ChainId]: string } = { ...@@ -25,7 +25,11 @@ const ETHERSCAN_PREFIXES: { [chainId in ChainId]: string } = {
42: 'kovan.' 42: 'kovan.'
} }
export function getEtherscanLink(chainId: ChainId, data: string, type: 'transaction' | 'token' | 'address'): string { export function getEtherscanLink(
chainId: ChainId,
data: string,
type: 'transaction' | 'token' | 'address' | 'block'
): string {
const prefix = `https://${ETHERSCAN_PREFIXES[chainId] || ETHERSCAN_PREFIXES[1]}etherscan.io` const prefix = `https://${ETHERSCAN_PREFIXES[chainId] || ETHERSCAN_PREFIXES[1]}etherscan.io`
switch (type) { switch (type) {
...@@ -35,6 +39,9 @@ export function getEtherscanLink(chainId: ChainId, data: string, type: 'transact ...@@ -35,6 +39,9 @@ export function getEtherscanLink(chainId: ChainId, data: string, type: 'transact
case 'token': { case 'token': {
return `${prefix}/token/${data}` return `${prefix}/token/${data}`
} }
case 'block': {
return `${prefix}/block/${data}`
}
case 'address': case 'address':
default: { default: {
return `${prefix}/address/${data}` return `${prefix}/address/${data}`
......
...@@ -10,24 +10,15 @@ describe('v1SwapArguments', () => { ...@@ -10,24 +10,15 @@ describe('v1SwapArguments', () => {
// just some random address // just some random address
const TEST_RECIPIENT_ADDRESS = USDC_WETH.liquidityToken.address const TEST_RECIPIENT_ADDRESS = USDC_WETH.liquidityToken.address
function checkDeadline(hex: string | string[], ttl: number) {
if (typeof hex !== 'string') throw new Error('invalid hex')
const now = new Date().getTime() / 1000
expect(parseInt(hex) - now).toBeGreaterThanOrEqual(ttl - 3)
expect(parseInt(hex) - now).toBeLessThanOrEqual(ttl + 3)
}
it('exact eth to token', () => { it('exact eth to token', () => {
const trade = Trade.exactIn(new Route([USDC_WETH], ETHER), CurrencyAmount.ether('100')) const trade = Trade.exactIn(new Route([USDC_WETH], ETHER), CurrencyAmount.ether('100'))
const result = v1SwapArguments(trade, { const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS, recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'), allowedSlippage: new Percent('1', '100'),
ttl: 20 * 60 deadline: 20 * 60
}) })
expect(result.methodName).toEqual('ethToTokenTransferInput') expect(result.methodName).toEqual('ethToTokenTransferInput')
expect(result.args[0]).toEqual('0x62') expect(result.args).toEqual(['0x62', '0x4b0', TEST_RECIPIENT_ADDRESS])
expect(result.args[2]).toEqual(TEST_RECIPIENT_ADDRESS)
checkDeadline(result.args[1], 20 * 60)
expect(result.value).toEqual('0x64') expect(result.value).toEqual('0x64')
}) })
it('exact token to eth', () => { it('exact token to eth', () => {
...@@ -35,12 +26,12 @@ describe('v1SwapArguments', () => { ...@@ -35,12 +26,12 @@ describe('v1SwapArguments', () => {
const result = v1SwapArguments(trade, { const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS, recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'), allowedSlippage: new Percent('1', '100'),
ttl: 20 * 60 deadline: 40 * 60
}) })
expect(result.methodName).toEqual('tokenToEthTransferInput') expect(result.methodName).toEqual('tokenToEthTransferInput')
expect(result.args[0]).toEqual('0x64') expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x62') expect(result.args[1]).toEqual('0x62')
checkDeadline(result.args[2], 20 * 60) expect(result.args[2]).toEqual('0x960')
expect(result.args[3]).toEqual(TEST_RECIPIENT_ADDRESS) expect(result.args[3]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.value).toEqual('0x0') expect(result.value).toEqual('0x0')
}) })
...@@ -49,15 +40,15 @@ describe('v1SwapArguments', () => { ...@@ -49,15 +40,15 @@ describe('v1SwapArguments', () => {
const result = v1SwapArguments(trade, { const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS, recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'), allowedSlippage: new Percent('1', '100'),
ttl: 20 * 60 deadline: 20 * 60
}) })
expect(result.methodName).toEqual('tokenToTokenTransferInput') expect(result.methodName).toEqual('tokenToTokenTransferInput')
expect(result.args[0]).toEqual('0x64') expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x61') expect(result.args[1]).toEqual('0x61')
expect(result.args[2]).toEqual('0x1') expect(result.args[2]).toEqual('0x1')
expect(result.args[3]).toEqual('0x4b0')
expect(result.args[4]).toEqual(TEST_RECIPIENT_ADDRESS) expect(result.args[4]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.args[5]).toEqual(DAI.address) expect(result.args[5]).toEqual(DAI.address)
checkDeadline(result.args[3], 20 * 60)
expect(result.value).toEqual('0x0') expect(result.value).toEqual('0x0')
}) })
it('eth to exact token', () => { it('eth to exact token', () => {
...@@ -65,11 +56,11 @@ describe('v1SwapArguments', () => { ...@@ -65,11 +56,11 @@ describe('v1SwapArguments', () => {
const result = v1SwapArguments(trade, { const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS, recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'), allowedSlippage: new Percent('1', '100'),
ttl: 20 * 60 deadline: 20 * 60
}) })
expect(result.methodName).toEqual('ethToTokenTransferOutput') expect(result.methodName).toEqual('ethToTokenTransferOutput')
expect(result.args[0]).toEqual('0x64') expect(result.args[0]).toEqual('0x64')
checkDeadline(result.args[1], 20 * 60) expect(result.args[1]).toEqual('0x4b0')
expect(result.args[2]).toEqual(TEST_RECIPIENT_ADDRESS) expect(result.args[2]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.value).toEqual('0x66') expect(result.value).toEqual('0x66')
}) })
...@@ -78,12 +69,12 @@ describe('v1SwapArguments', () => { ...@@ -78,12 +69,12 @@ describe('v1SwapArguments', () => {
const result = v1SwapArguments(trade, { const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS, recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'), allowedSlippage: new Percent('1', '100'),
ttl: 20 * 60 deadline: 20 * 60
}) })
expect(result.methodName).toEqual('tokenToEthTransferOutput') expect(result.methodName).toEqual('tokenToEthTransferOutput')
expect(result.args[0]).toEqual('0x64') expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x66') expect(result.args[1]).toEqual('0x66')
checkDeadline(result.args[2], 20 * 60) expect(result.args[2]).toEqual('0x4b0')
expect(result.args[3]).toEqual(TEST_RECIPIENT_ADDRESS) expect(result.args[3]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.value).toEqual('0x0') expect(result.value).toEqual('0x0')
}) })
...@@ -92,13 +83,13 @@ describe('v1SwapArguments', () => { ...@@ -92,13 +83,13 @@ describe('v1SwapArguments', () => {
const result = v1SwapArguments(trade, { const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS, recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'), allowedSlippage: new Percent('1', '100'),
ttl: 20 * 60 deadline: 20 * 60
}) })
expect(result.methodName).toEqual('tokenToTokenTransferOutput') expect(result.methodName).toEqual('tokenToTokenTransferOutput')
expect(result.args[0]).toEqual('0x64') expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x67') expect(result.args[1]).toEqual('0x67')
expect(result.args[2]).toEqual(`0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`) expect(result.args[2]).toEqual(`0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`)
checkDeadline(result.args[3], 20 * 60) expect(result.args[3]).toEqual('0x4b0')
expect(result.args[4]).toEqual(TEST_RECIPIENT_ADDRESS) expect(result.args[4]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.args[5]).toEqual(DAI.address) expect(result.args[5]).toEqual(DAI.address)
expect(result.value).toEqual('0x0') expect(result.value).toEqual('0x0')
......
import { MaxUint256 } from '@ethersproject/constants' import { MaxUint256 } from '@ethersproject/constants'
import { CurrencyAmount, ETHER, SwapParameters, Token, Trade, TradeOptions, TradeType } from '@uniswap/sdk' import { CurrencyAmount, ETHER, SwapParameters, Token, Trade, TradeOptionsDeadline, TradeType } from '@uniswap/sdk'
import { getTradeVersion } from '../data/V1' import { getTradeVersion } from '../data/V1'
import { Version } from '../hooks/useToggledVersion' import { Version } from '../hooks/useToggledVersion'
...@@ -7,16 +7,15 @@ function toHex(currencyAmount: CurrencyAmount): string { ...@@ -7,16 +7,15 @@ function toHex(currencyAmount: CurrencyAmount): string {
return `0x${currencyAmount.raw.toString(16)}` return `0x${currencyAmount.raw.toString(16)}`
} }
function deadlineFromNow(ttl: number): string {
return `0x${(Math.floor(new Date().getTime() / 1000) + ttl).toString(16)}`
}
/** /**
* Get the arguments to make for a swap * Get the arguments to make for a swap
* @param trade trade to get v1 arguments for swapping * @param trade trade to get v1 arguments for swapping
* @param options options for swapping * @param options options for swapping
*/ */
export default function v1SwapArguments(trade: Trade, options: Omit<TradeOptions, 'feeOnTransfer'>): SwapParameters { export default function v1SwapArguments(
trade: Trade,
options: Omit<TradeOptionsDeadline, 'feeOnTransfer'>
): SwapParameters {
if (getTradeVersion(trade) !== Version.v1) { if (getTradeVersion(trade) !== Version.v1) {
throw new Error('invalid trade version') throw new Error('invalid trade version')
} }
...@@ -29,7 +28,7 @@ export default function v1SwapArguments(trade: Trade, options: Omit<TradeOptions ...@@ -29,7 +28,7 @@ export default function v1SwapArguments(trade: Trade, options: Omit<TradeOptions
if (inputETH && outputETH) throw new Error('ETHER to ETHER') if (inputETH && outputETH) throw new Error('ETHER to ETHER')
const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage)) const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage)) const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
const deadline = deadlineFromNow(options.ttl) const deadline = `0x${options.deadline.toString(16)}`
if (isExactIn) { if (isExactIn) {
if (inputETH) { if (inputETH) {
return { return {
......
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
...@@ -26,17 +22,8 @@ ...@@ -26,17 +22,8 @@
"jsx": "preserve", "jsx": "preserve",
"downlevelIteration": true, "downlevelIteration": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"types": [ "types": ["react-spring", "jest"]
"react-spring",
"jest"
]
}, },
"exclude": [ "exclude": ["node_modules", "cypress"],
"node_modules", "include": ["./src/**/*.ts", "./src/**/*.tsx", "src/components/Confetti/index.js"]
"cypress"
],
"include": [
"./src/**/*.ts",
"./src/**/*.tsx"
]
} }
...@@ -1865,6 +1865,113 @@ ...@@ -1865,6 +1865,113 @@
"@types/yargs" "^15.0.0" "@types/yargs" "^15.0.0"
chalk "^3.0.0" chalk "^3.0.0"
"@jimp/bmp@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.9.8.tgz#5933ab8fb359889bec380b0f7802163374933624"
integrity sha512-CZYQPEC3iUBMuaGWrtIG+GKNl93q/PkdudrCKJR/B96dfNngsmoosEm3LuFgJHEcJIfvnJkNqKw74l+zEiqCbg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.8"
bmp-js "^0.1.0"
core-js "^3.4.1"
"@jimp/core@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.9.8.tgz#b2b74263a80559c0ee244e0f2d1052b36a358b85"
integrity sha512-N4GCjcXb0QwR5GBABDK2xQ3cKyaF7LlCYeJEG9mV7G/ynBoRqJe4JA6YKU9Ww9imGkci/4A594nQo8tUIqdcBw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.8"
any-base "^1.1.0"
buffer "^5.2.0"
core-js "^3.4.1"
exif-parser "^0.1.12"
file-type "^9.0.0"
load-bmfont "^1.3.1"
mkdirp "^0.5.1"
phin "^2.9.1"
pixelmatch "^4.0.2"
tinycolor2 "^1.4.1"
"@jimp/custom@^0.9.3":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.9.8.tgz#1e9d904b1b05aa22b00b899baba2be7c0704a5d1"
integrity sha512-1UpJjI7fhX02BWLJ/KEqPwkHH60eNkCNeD6hEd+IZdTwLXfZCfFiM5BVlpgiZYZJSsVoRiAL4ne2Q5mCiKPKyw==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/core" "^0.9.8"
core-js "^3.4.1"
"@jimp/gif@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.9.8.tgz#513aff511634c338d1ab33a7bba1ba3412220b5b"
integrity sha512-LEbfpcO1sBJIQCJHchZjNlyNxzPjZQQ4X32klpQHZJG58n9FvL7Uuh1rpkrJRbqv3cU3P0ENNtTrsBDxsYwcfA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.8"
core-js "^3.4.1"
omggif "^1.0.9"
"@jimp/jpeg@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.9.8.tgz#8c086f69d0e8c46e43a7db9725576edc30925cb1"
integrity sha512-5u29SUzbZ32ZMmOaz3gO0hXatwSCnsvEAXRCKZoPPgbsPoyFAiZKVxjfLzjkeQF6awkvJ8hZni5chM15SNMg+g==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.8"
core-js "^3.4.1"
jpeg-js "^0.3.4"
"@jimp/plugin-resize@^0.9.3":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.9.8.tgz#eef750b77f1cc06e8bcf9b390860c95c489dcc02"
integrity sha512-L80NZ+HKsiKFyeDc6AfneC4+5XACrdL2vnyAVfAAsb3pmamgT/jDInWvvGhyI0Y76vx2w6XikplzEznW/QQvWg==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.8"
core-js "^3.4.1"
"@jimp/png@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.9.8.tgz#f88dacc9b9da1c2ea8e91026a9530d0fb45c4409"
integrity sha512-9CqR8d40zQCDhbnXHqcwkAMnvlV0vk9xSyE6LHjkYHS7x18Unsz5txQdsaEkEcXxCrOQSoWyITfLezlrWXRJAA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/utils" "^0.9.8"
core-js "^3.4.1"
pngjs "^3.3.3"
"@jimp/tiff@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.9.8.tgz#91dc3eab2f222e23414f139e917f3407caa73560"
integrity sha512-eMxcpJivJqMByn2dZxUHLeh6qvVs5J/52kBF3TFa3C922OJ97D9l1C1h0WKUCBqFMWzMYapQQ4vwnLgpJ5tkow==
dependencies:
"@babel/runtime" "^7.7.2"
core-js "^3.4.1"
utif "^2.0.1"
"@jimp/types@^0.9.3":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.9.8.tgz#46980a4a7bfcadf2f0484d187c32b4e7d6d61b8e"
integrity sha512-H5y/uqt0lqJ/ZN8pWqFG+pv8jPAppMKkTMByuC8YBIjWSsornwv44hjiWl93sbYhduLZY8ubz/CbX9jH2X6EwA==
dependencies:
"@babel/runtime" "^7.7.2"
"@jimp/bmp" "^0.9.8"
"@jimp/gif" "^0.9.8"
"@jimp/jpeg" "^0.9.8"
"@jimp/png" "^0.9.8"
"@jimp/tiff" "^0.9.8"
core-js "^3.4.1"
timm "^1.6.1"
"@jimp/utils@^0.9.8":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.9.8.tgz#6a6f47158ec6b424f03df0f55f0baff5b4b5e096"
integrity sha512-UK0Fu0eevQlpRXq5ff4o/71HJlpX9wJMddJjMYg9vUqCCl8ZnumRAljfShHFhGyO+Vc9IzN6dd8Y5JZZTp1KOw==
dependencies:
"@babel/runtime" "^7.7.2"
core-js "^3.4.1"
"@mrmlnc/readdir-enhanced@^2.2.1": "@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
...@@ -2321,6 +2428,16 @@ ...@@ -2321,6 +2428,16 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.158.tgz#b38ea8b6fe799acd076d7a8d7ab71c26ef77f785" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.158.tgz#b38ea8b6fe799acd076d7a8d7ab71c26ef77f785"
integrity sha512-InCEXJNTv/59yO4VSfuvNrZHt7eeNtWQEgnieIA+mIC+MOWM9arOWG2eQ8Vhk6NbOre6/BidiXhkZYeDY9U35w== integrity sha512-InCEXJNTv/59yO4VSfuvNrZHt7eeNtWQEgnieIA+mIC+MOWM9arOWG2eQ8Vhk6NbOre6/BidiXhkZYeDY9U35w==
"@types/lodash@^4.14.53":
version "4.14.161"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18"
integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==
"@types/luxon@^1.24.4":
version "1.24.4"
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.24.4.tgz#5dbd050b89247dbccab6eaa7744874c1fb7cea85"
integrity sha512-ce+P0b4rVq3LNXdD5r7eDqX2ydz2BonmTgVrHrV5yxsNKTt3YpnT5QDrid7xMAThhE/gg1RLAbxqbW1OCdiZog==
"@types/minimatch@*": "@types/minimatch@*":
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
...@@ -2338,6 +2455,11 @@ ...@@ -2338,6 +2455,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.26.tgz#22a3b8a46510da8944b67bfc27df02c34a35331c" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.26.tgz#22a3b8a46510da8944b67bfc27df02c34a35331c"
integrity sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA== integrity sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA==
"@types/node@^10.11.7":
version "10.17.30"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.30.tgz#20556a0d7f62b83e163973a6cd640af636d3dd3b"
integrity sha512-euU8QLX0ipj+5mOYa4ZqZoTv+53BY7yTg9I2ZIhDXgiI3M+0n4mdAt9TQCuvxVAgU179g8OsRLaBt0qEi0T6xA==
"@types/node@^13.13.5": "@types/node@^13.13.5":
version "13.13.15" version "13.13.15"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766"
...@@ -2502,6 +2624,11 @@ ...@@ -2502,6 +2624,11 @@
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI= integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
"@types/wcag-contrast@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/wcag-contrast/-/wcag-contrast-3.0.0.tgz#640a4c36a6b62d54f1dd64a74322595df465148d"
integrity sha512-RDinJW4P1bg+dWA6C0/looxA0zyi24gIDHrewzFW/xyU2v97D38KRzXdxMbiVpqovrjTa/xsX8abj3/hpASklg==
"@types/yargs-parser@*": "@types/yargs-parser@*":
version "15.0.0" version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
...@@ -2564,20 +2691,32 @@ ...@@ -2564,20 +2691,32 @@
semver "^7.3.2" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@uniswap/default-token-list@^1.3.1": "@uniswap/governance@^1.0.2":
version "1.3.1" version "1.0.2"
resolved "https://registry.yarnpkg.com/@uniswap/default-token-list/-/default-token-list-1.3.1.tgz#e856aa8e7d4112fcb39229df3023af0da1727699" resolved "https://registry.yarnpkg.com/@uniswap/governance/-/governance-1.0.2.tgz#7371ab54dea9a5c045275001e2d5325ff2c11a93"
integrity sha512-nLBSzWGxVlbKby6xtf3ph56NltMyHCk8ohFD97Wub7K0I66YyHKuhyV7NZq05unB8TEWWpYDcdghmgztSv6H8Q== integrity sha512-L7BzdI/asAINB2F6EvJuQcg5YhykNeEfZxJrn2RPIVvx+9A9u+opDuqnBK4SYnanrsr3UEX1NwWXwDHmyw2TjQ==
"@uniswap/lib@1.1.1": "@uniswap/lib@1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-1.1.1.tgz#0afd29601846c16e5d082866cbb24a9e0758e6bc" resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-1.1.1.tgz#0afd29601846c16e5d082866cbb24a9e0758e6bc"
integrity sha512-2yK7sLpKIT91TiS5sewHtOa7YuM8IuBXVl4GZv2jZFys4D2sY7K5vZh6MqD25TPA95Od+0YzCVq6cTF2IKrOmg== integrity sha512-2yK7sLpKIT91TiS5sewHtOa7YuM8IuBXVl4GZv2jZFys4D2sY7K5vZh6MqD25TPA95Od+0YzCVq6cTF2IKrOmg==
"@uniswap/sdk@3.0.3-beta.1": "@uniswap/liquidity-staker@^1.0.2":
version "3.0.3-beta.1" version "1.0.2"
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-3.0.3-beta.1.tgz#ef758f45e1f7050e098956fce097aedb87e0ceed" resolved "https://registry.yarnpkg.com/@uniswap/liquidity-staker/-/liquidity-staker-1.0.2.tgz#afed49d51c5f97411432e891a59a24817728dad6"
integrity sha512-8MqfVDXAHHcAnUxBAaoHdeGmLUc8Tu4GZ4n7kLabKlF2b4RUS/jnuUIdzOR9QPMdY/avw3gH/OtTxH2elDNShw== integrity sha512-Rdj+QqapWO3sY1SJdLUQ8dhf26yXg9DC3XU3cpNoh5jrJ0UhAnKqciZss1F6oxBLbNWXzekuigLgSVbVb1BFcw==
dependencies:
openzeppelin-solidity-2.3.0 "npm:openzeppelin-solidity@2.3.0"
"@uniswap/merkle-distributor@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@uniswap/merkle-distributor/-/merkle-distributor-1.0.1.tgz#dc3d911f65a860fc3f0cae074bdcd08ed6a27a4d"
integrity sha512-5gDiTI5hrXIh5UWTrxKYjw30QQDnpl8ckDSpefldNenDlYO1RKkdUYMYpvrqGi2r7YzLYTlO6+TDlNs6O7hDRw==
"@uniswap/sdk@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-3.0.3.tgz#8201c7c72215d0030cb99acc7e661eff895c18a9"
integrity sha512-t4s8bvzaCFSiqD2qfXIm3rWhbdnXp+QjD3/mRaeVDHK7zWevs6RGEb1ohMiNgOCTZANvBayb4j8p+XFdnMBadQ==
dependencies: dependencies:
"@uniswap/v2-core" "^1.0.0" "@uniswap/v2-core" "^1.0.0"
big.js "^5.2.2" big.js "^5.2.2"
...@@ -3128,6 +3267,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: ...@@ -3128,6 +3267,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1" "@types/color-name" "^1.1.1"
color-convert "^2.0.1" color-convert "^2.0.1"
any-base@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe"
integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==
any-observable@^0.3.0: any-observable@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
...@@ -4103,6 +4247,11 @@ backoff@^2.5.0: ...@@ -4103,6 +4247,11 @@ backoff@^2.5.0:
dependencies: dependencies:
precond "0.2" precond "0.2"
bail@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776"
integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
...@@ -4199,6 +4348,11 @@ bluebird@3.7.2, bluebird@^3.5.5: ...@@ -4199,6 +4348,11 @@ bluebird@3.7.2, bluebird@^3.5.5:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bmp-js@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233"
integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM=
bn.js@4.11.8: bn.js@4.11.8:
version "4.11.8" version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
...@@ -4451,6 +4605,11 @@ buffer-crc32@~0.2.3: ...@@ -4451,6 +4605,11 @@ buffer-crc32@~0.2.3:
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-equal@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b"
integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=
buffer-fill@^1.0.0: buffer-fill@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
...@@ -4471,7 +4630,7 @@ buffer-xor@^1.0.3: ...@@ -4471,7 +4630,7 @@ buffer-xor@^1.0.3:
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
buffer@5.6.0, buffer@^5.4.3: buffer@5.6.0, buffer@^5.2.0, buffer@^5.4.3:
version "5.6.0" version "5.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
...@@ -4702,6 +4861,21 @@ chalk@^4.1.0: ...@@ -4702,6 +4861,21 @@ chalk@^4.1.0:
ansi-styles "^4.1.0" ansi-styles "^4.1.0"
supports-color "^7.1.0" supports-color "^7.1.0"
character-entities-legacy@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1"
integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==
character-entities@^1.0.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b"
integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==
character-reference-invalid@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
chardet@^0.7.0: chardet@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
...@@ -4955,6 +5129,11 @@ code-point-at@^1.0.0: ...@@ -4955,6 +5129,11 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
collapse-white-space@^1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287"
integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==
collection-visit@^1.0.0: collection-visit@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
...@@ -5227,7 +5406,7 @@ core-js@^2.4.0, core-js@^2.5.0: ...@@ -5227,7 +5406,7 @@ core-js@^2.4.0, core-js@^2.5.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
core-js@^3.5.0: core-js@^3.4.1, core-js@^3.5.0:
version "3.6.5" version "3.6.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==
...@@ -5963,6 +6142,15 @@ dom-serializer@0: ...@@ -5963,6 +6142,15 @@ dom-serializer@0:
domelementtype "^2.0.1" domelementtype "^2.0.1"
entities "^2.0.0" entities "^2.0.0"
dom-serializer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.0.1.tgz#79695eb49af3cd8abc8d93a73da382deb1ca0795"
integrity sha512-1Aj1Qy3YLbdslkI75QEOfdp9TkQ3o8LRISAzxOibjBs/xWwr1WxZFOQphFkZuepHFGo+kB8e5FVJSS0faAJ4Rw==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.0.0"
entities "^2.0.0"
dom-walk@^0.1.0: dom-walk@^0.1.0:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
...@@ -5997,6 +6185,13 @@ domhandler@^2.3.0: ...@@ -5997,6 +6185,13 @@ domhandler@^2.3.0:
dependencies: dependencies:
domelementtype "1" domelementtype "1"
domhandler@^3.0, domhandler@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9"
integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==
dependencies:
domelementtype "^2.0.1"
domutils@1.5.1: domutils@1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
...@@ -6013,6 +6208,15 @@ domutils@^1.5.1, domutils@^1.7.0: ...@@ -6013,6 +6208,15 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0" dom-serializer "0"
domelementtype "1" domelementtype "1"
domutils@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.2.0.tgz#f3ce1610af5c30280bde1b71f84b018b958f32cf"
integrity sha512-0haAxVr1PR0SqYwCH7mxMpHZUwjih9oPPedqpR/KufsnxPyZ9dyVw1R5093qnJF3WXSbjBkdzRWLw/knJV/fAg==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.0.1"
domhandler "^3.0.0"
dot-case@^3.0.3: dot-case@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa"
...@@ -6493,6 +6697,11 @@ eslint@^6.6.0, eslint@^6.8.0: ...@@ -6493,6 +6697,11 @@ eslint@^6.6.0, eslint@^6.8.0:
text-table "^0.2.0" text-table "^0.2.0"
v8-compile-cache "^2.0.3" v8-compile-cache "^2.0.3"
esm@^3.0.84:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
espree@^6.1.2: espree@^6.1.2:
version "6.2.1" version "6.2.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"
...@@ -7017,6 +7226,11 @@ executable@4.1.1: ...@@ -7017,6 +7226,11 @@ executable@4.1.1:
dependencies: dependencies:
pify "^2.2.0" pify "^2.2.0"
exif-parser@^0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922"
integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI=
exit-hook@^1.0.0: exit-hook@^1.0.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
...@@ -7110,7 +7324,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: ...@@ -7110,7 +7324,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0" assign-symbols "^1.0.0"
is-extendable "^1.0.1" is-extendable "^1.0.1"
extend@~3.0.2: extend@^3.0.0, extend@~3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
...@@ -7291,6 +7505,11 @@ file-loader@4.3.0: ...@@ -7291,6 +7505,11 @@ file-loader@4.3.0:
loader-utils "^1.2.3" loader-utils "^1.2.3"
schema-utils "^2.5.0" schema-utils "^2.5.0"
file-type@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-9.0.0.tgz#a68d5ad07f486414dfb2c8866f73161946714a18"
integrity sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==
file-uri-to-path@1.0.0: file-uri-to-path@1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
...@@ -8014,6 +8233,16 @@ html-parse-stringify2@2.0.1: ...@@ -8014,6 +8233,16 @@ html-parse-stringify2@2.0.1:
dependencies: dependencies:
void-elements "^2.0.1" void-elements "^2.0.1"
html-to-react@^1.3.4:
version "1.4.3"
resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.3.tgz#1430a1cb581ef29533892ec70a2fdc4554b17ffd"
integrity sha512-txe09A3vxW8yEZGJXJ1is5gGDfBEVACmZDSgwDyH5EsfRdOubBwBCg63ZThZP0xBn0UE4FyvMXZXmohusCxDcg==
dependencies:
domhandler "^3.0"
htmlparser2 "^4.1.0"
lodash.camelcase "^4.3.0"
ramda "^0.27"
html-webpack-plugin@4.0.0-beta.11: html-webpack-plugin@4.0.0-beta.11:
version "4.0.0-beta.11" version "4.0.0-beta.11"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715"
...@@ -8038,6 +8267,16 @@ htmlparser2@^3.3.0: ...@@ -8038,6 +8267,16 @@ htmlparser2@^3.3.0:
inherits "^2.0.1" inherits "^2.0.1"
readable-stream "^3.1.1" readable-stream "^3.1.1"
htmlparser2@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78"
integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.0.0"
domutils "^2.0.0"
entities "^2.0.0"
http-deceiver@^1.2.7: http-deceiver@^1.2.7:
version "1.2.7" version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
...@@ -8269,7 +8508,7 @@ inflight@^1.0.4: ...@@ -8269,7 +8508,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
...@@ -8400,6 +8639,19 @@ is-accessor-descriptor@^1.0.0: ...@@ -8400,6 +8639,19 @@ is-accessor-descriptor@^1.0.0:
dependencies: dependencies:
kind-of "^6.0.0" kind-of "^6.0.0"
is-alphabetical@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d"
integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==
is-alphanumerical@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf"
integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==
dependencies:
is-alphabetical "^1.0.0"
is-decimal "^1.0.0"
is-arguments@^1.0.4: is-arguments@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
...@@ -8429,7 +8681,7 @@ is-binary-path@~2.1.0: ...@@ -8429,7 +8681,7 @@ is-binary-path@~2.1.0:
dependencies: dependencies:
binary-extensions "^2.0.0" binary-extensions "^2.0.0"
is-buffer@^1.0.2, is-buffer@^1.1.5: is-buffer@^1.0.2, is-buffer@^1.1.4, is-buffer@^1.1.5:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
...@@ -8482,6 +8734,11 @@ is-date-object@^1.0.1: ...@@ -8482,6 +8734,11 @@ is-date-object@^1.0.1:
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
is-decimal@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5"
integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==
is-descriptor@^0.1.0: is-descriptor@^0.1.0:
version "0.1.6" version "0.1.6"
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
...@@ -8583,6 +8840,11 @@ is-hex-prefixed@1.0.0: ...@@ -8583,6 +8840,11 @@ is-hex-prefixed@1.0.0:
resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= integrity sha1-fY035q135dEnFIkTxXPggtd39VQ=
is-hexadecimal@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"
integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==
is-installed-globally@0.3.2: is-installed-globally@0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
...@@ -8644,7 +8906,7 @@ is-path-inside@^3.0.1: ...@@ -8644,7 +8906,7 @@ is-path-inside@^3.0.1:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017"
integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==
is-plain-obj@^1.0.0: is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
...@@ -8729,11 +8991,21 @@ is-what@^3.3.1: ...@@ -8729,11 +8991,21 @@ is-what@^3.3.1:
resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.10.0.tgz#5fee88ee7105c373c5b7c9324f345ad7e9554327" resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.10.0.tgz#5fee88ee7105c373c5b7c9324f345ad7e9554327"
integrity sha512-U4RYCXNOmATQHlOPlOCHCfXyKEFIPqvyaKDqYRuLbD6EYKcTTfc3YXkAYjzOVxO3zt34L+Wh2feIyWrYiZ7kng== integrity sha512-U4RYCXNOmATQHlOPlOCHCfXyKEFIPqvyaKDqYRuLbD6EYKcTTfc3YXkAYjzOVxO3zt34L+Wh2feIyWrYiZ7kng==
is-whitespace-character@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==
is-windows@^1.0.2: is-windows@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
is-word-character@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230"
integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==
is-wsl@^1.1.0: is-wsl@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
...@@ -9239,6 +9511,11 @@ jest@24.9.0: ...@@ -9239,6 +9511,11 @@ jest@24.9.0:
import-local "^2.0.0" import-local "^2.0.0"
jest-cli "^24.9.0" jest-cli "^24.9.0"
jpeg-js@^0.3.4:
version "0.3.7"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d"
integrity sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==
js-sha3@0.5.7: js-sha3@0.5.7:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
...@@ -9702,6 +9979,20 @@ listr@0.14.3: ...@@ -9702,6 +9979,20 @@ listr@0.14.3:
p-map "^2.0.0" p-map "^2.0.0"
rxjs "^6.3.3" rxjs "^6.3.3"
load-bmfont@^1.3.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.1.tgz#c0f5f4711a1e2ccff725a7b6078087ccfcddd3e9"
integrity sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==
dependencies:
buffer-equal "0.0.1"
mime "^1.3.4"
parse-bmfont-ascii "^1.0.3"
parse-bmfont-binary "^1.0.5"
parse-bmfont-xml "^1.1.4"
phin "^2.9.1"
xhr "^2.0.1"
xtend "^4.0.0"
load-json-file@^2.0.0: load-json-file@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
...@@ -9781,6 +10072,11 @@ lodash._reinterpolate@^3.0.0: ...@@ -9781,6 +10072,11 @@ lodash._reinterpolate@^3.0.0:
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.flatmap@^4.5.0: lodash.flatmap@^4.5.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e"
...@@ -9888,6 +10184,11 @@ ltgt@~2.2.0: ...@@ -9888,6 +10184,11 @@ ltgt@~2.2.0:
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=
luxon@^1.25.0:
version "1.25.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.25.0.tgz#d86219e90bc0102c0eb299d65b2f5e95efe1fe72"
integrity sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ==
make-dir@^2.0.0, make-dir@^2.1.0: make-dir@^2.0.0, make-dir@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
...@@ -9939,6 +10240,11 @@ map-visit@^1.0.0: ...@@ -9939,6 +10240,11 @@ map-visit@^1.0.0:
dependencies: dependencies:
object-visit "^1.0.0" object-visit "^1.0.0"
markdown-escapes@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
md5.js@^1.3.4: md5.js@^1.3.4:
version "1.3.5" version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
...@@ -9948,6 +10254,13 @@ md5.js@^1.3.4: ...@@ -9948,6 +10254,13 @@ md5.js@^1.3.4:
inherits "^2.0.1" inherits "^2.0.1"
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
mdast-add-list-metadata@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf"
integrity sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA==
dependencies:
unist-util-visit-parents "1.1.2"
mdn-data@2.0.4: mdn-data@2.0.4:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
...@@ -10116,7 +10429,7 @@ mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: ...@@ -10116,7 +10429,7 @@ mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
dependencies: dependencies:
mime-db "1.44.0" mime-db "1.44.0"
mime@1.6.0: mime@1.6.0, mime@^1.3.4:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
...@@ -10468,6 +10781,19 @@ node-releases@^1.1.52, node-releases@^1.1.58: ...@@ -10468,6 +10781,19 @@ node-releases@^1.1.52, node-releases@^1.1.58:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084"
integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==
node-vibrant@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/node-vibrant/-/node-vibrant-3.1.5.tgz#8729bf35aabd54cd2eccbfadf22124ab4e1305b0"
integrity sha512-Gk+iyBzPSN1SF5qL818QaBtuA38206Z8iPNa0PcLUPyIbZL4+i14VmYxkGCL0n/5Q1721CRSktqtACgkx7Qodg==
dependencies:
"@jimp/custom" "^0.9.3"
"@jimp/plugin-resize" "^0.9.3"
"@jimp/types" "^0.9.3"
"@types/lodash" "^4.14.53"
"@types/node" "^10.11.7"
lodash "^4.17.4"
url "^0.11.0"
normalize-package-data@^2.3.2: normalize-package-data@^2.3.2:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
...@@ -10682,6 +11008,11 @@ obuf@^1.0.0, obuf@^1.1.2: ...@@ -10682,6 +11008,11 @@ obuf@^1.0.0, obuf@^1.1.2:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
omggif@^1.0.9:
version "1.0.10"
resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19"
integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==
on-finished@~2.3.0: on-finished@~2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
...@@ -10728,6 +11059,11 @@ open@^7.0.2: ...@@ -10728,6 +11059,11 @@ open@^7.0.2:
is-docker "^2.0.0" is-docker "^2.0.0"
is-wsl "^2.1.1" is-wsl "^2.1.1"
"openzeppelin-solidity-2.3.0@npm:openzeppelin-solidity@2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.3.0.tgz#1ab7b4cc3782a5472ed61eb740c56a8bfdd74119"
integrity sha512-QYeiPLvB1oSbDt6lDQvvpx7k8ODczvE474hb2kLXZBPKMsxKT1WxTCHBYrCU7kS7hfAku4DcJ0jqOyL+jvjwQw==
opn@^5.5.0: opn@^5.5.0:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
...@@ -10887,7 +11223,7 @@ p-try@^2.0.0: ...@@ -10887,7 +11223,7 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
pako@~1.0.5: pako@^1.0.5, pako@~1.0.5:
version "1.0.11" version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
...@@ -10928,6 +11264,36 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: ...@@ -10928,6 +11264,36 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
pbkdf2 "^3.0.3" pbkdf2 "^3.0.3"
safe-buffer "^5.1.1" safe-buffer "^5.1.1"
parse-bmfont-ascii@^1.0.3:
version "1.0.6"
resolved "https://registry.yarnpkg.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz#11ac3c3ff58f7c2020ab22769079108d4dfa0285"
integrity sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=
parse-bmfont-binary@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz#d038b476d3e9dd9db1e11a0b0e53a22792b69006"
integrity sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=
parse-bmfont-xml@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz#015319797e3e12f9e739c4d513872cd2fa35f389"
integrity sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==
dependencies:
xml-parse-from-string "^1.0.0"
xml2js "^0.4.5"
parse-entities@^1.1.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50"
integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==
dependencies:
character-entities "^1.0.0"
character-entities-legacy "^1.0.0"
character-reference-invalid "^1.0.0"
is-alphanumerical "^1.0.0"
is-decimal "^1.0.0"
is-hexadecimal "^1.0.0"
parse-headers@^2.0.0: parse-headers@^2.0.0:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515"
...@@ -11107,6 +11473,11 @@ performance-now@^2.1.0: ...@@ -11107,6 +11473,11 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phin@^2.9.1:
version "2.9.3"
resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c"
integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==
picomatch@^2.0.4, picomatch@^2.2.1: picomatch@^2.0.4, picomatch@^2.2.1:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
...@@ -11146,6 +11517,13 @@ pirates@^4.0.1: ...@@ -11146,6 +11517,13 @@ pirates@^4.0.1:
dependencies: dependencies:
node-modules-regexp "^1.0.0" node-modules-regexp "^1.0.0"
pixelmatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854"
integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=
dependencies:
pngjs "^3.0.0"
pkg-dir@^1.0.0: pkg-dir@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
...@@ -11186,7 +11564,7 @@ pn@^1.1.0: ...@@ -11186,7 +11564,7 @@ pn@^1.1.0:
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
pngjs@^3.3.0: pngjs@^3.0.0, pngjs@^3.3.0, pngjs@^3.3.3:
version "3.4.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
...@@ -12169,6 +12547,11 @@ ramda@0.26.1: ...@@ -12169,6 +12547,11 @@ ramda@0.26.1:
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
ramda@^0.27:
version "0.27.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9"
integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==
randombytes@2.1.0, randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: randombytes@2.1.0, randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
...@@ -12240,6 +12623,13 @@ react-clientside-effect@^1.2.2: ...@@ -12240,6 +12623,13 @@ react-clientside-effect@^1.2.2:
dependencies: dependencies:
"@babel/runtime" "^7.0.0" "@babel/runtime" "^7.0.0"
react-confetti@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/react-confetti/-/react-confetti-6.0.0.tgz#d1817dba4ad8d1a8742689db68d8b4bb145ea43e"
integrity sha512-gPuMIiJcpRFtyv3LvWKOvXxwbm1X9fyWT3wQsIMmmM7UhW3u84Te/xqfee4c0gicQXli8zCfIDkOlZyrHBk7pw==
dependencies:
tween-functions "^1.2.0"
react-dev-utils@^10.2.1: react-dev-utils@^10.2.1:
version "10.2.1" version "10.2.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19"
...@@ -12329,11 +12719,25 @@ react-i18next@^10.7.0: ...@@ -12329,11 +12719,25 @@ react-i18next@^10.7.0:
"@babel/runtime" "^7.3.1" "@babel/runtime" "^7.3.1"
html-parse-stringify2 "2.0.1" html-parse-stringify2 "2.0.1"
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0: react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
version "16.13.1" version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-markdown@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-4.3.1.tgz#39f0633b94a027445b86c9811142d05381300f2f"
integrity sha512-HQlWFTbDxTtNY6bjgp3C3uv1h2xcjCSi1zAEzfBW9OwJJvENSYiLXWNXN5hHLsoqai7RnZiiHzcnWdXk2Splzw==
dependencies:
html-to-react "^1.3.4"
mdast-add-list-metadata "1.0.1"
prop-types "^15.7.2"
react-is "^16.8.6"
remark-parse "^5.0.0"
unified "^6.1.5"
unist-util-visit "^1.3.0"
xtend "^4.0.1"
react-popper@^2.2.3: react-popper@^2.2.3:
version "2.2.3" version "2.2.3"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.3.tgz#33d425fa6975d4bd54d9acd64897a89d904b9d97" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.3.tgz#33d425fa6975d4bd54d9acd64897a89d904b9d97"
...@@ -12788,6 +13192,34 @@ relateurl@^0.2.7: ...@@ -12788,6 +13192,34 @@ relateurl@^0.2.7:
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
relative-luminance@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/relative-luminance/-/relative-luminance-2.0.1.tgz#2babddf3a5a59673227d6f02e0f68e13989e3d13"
integrity sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww==
dependencies:
esm "^3.0.84"
remark-parse@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95"
integrity sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==
dependencies:
collapse-white-space "^1.0.2"
is-alphabetical "^1.0.0"
is-decimal "^1.0.0"
is-whitespace-character "^1.0.0"
is-word-character "^1.0.0"
markdown-escapes "^1.0.0"
parse-entities "^1.1.0"
repeat-string "^1.5.4"
state-toggle "^1.0.0"
trim "0.0.1"
trim-trailing-lines "^1.0.0"
unherit "^1.0.4"
unist-util-remove-position "^1.0.0"
vfile-location "^2.0.0"
xtend "^4.0.1"
remove-trailing-separator@^1.0.1: remove-trailing-separator@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
...@@ -12809,7 +13241,7 @@ repeat-element@^1.1.2: ...@@ -12809,7 +13241,7 @@ repeat-element@^1.1.2:
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
repeat-string@^1.6.1: repeat-string@^1.5.4, repeat-string@^1.6.1:
version "1.6.1" version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
...@@ -12821,6 +13253,11 @@ repeating@^2.0.0: ...@@ -12821,6 +13253,11 @@ repeating@^2.0.0:
dependencies: dependencies:
is-finite "^1.0.0" is-finite "^1.0.0"
replace-ext@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=
request-progress@3.0.0: request-progress@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe"
...@@ -13144,7 +13581,7 @@ sass-loader@8.0.2: ...@@ -13144,7 +13581,7 @@ sass-loader@8.0.2:
schema-utils "^2.6.1" schema-utils "^2.6.1"
semver "^6.3.0" semver "^6.3.0"
sax@^1.2.4, sax@~1.2.4: sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
...@@ -13721,6 +14158,11 @@ start-server-and-test@^1.11.0: ...@@ -13721,6 +14158,11 @@ start-server-and-test@^1.11.0:
ps-tree "1.2.0" ps-tree "1.2.0"
wait-on "5.1.0" wait-on "5.1.0"
state-toggle@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe"
integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==
static-extend@^0.1.1: static-extend@^0.1.1:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
...@@ -14224,6 +14666,11 @@ timers-browserify@^2.0.4: ...@@ -14224,6 +14666,11 @@ timers-browserify@^2.0.4:
dependencies: dependencies:
setimmediate "^1.0.4" setimmediate "^1.0.4"
timm@^1.6.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.0.tgz#c538100a58d066a53cb6cadeb8fb6b0dfd66e270"
integrity sha512-oVYHPG5KiUJ3KrbBTmW2kTauIO9E1lDEUM6K92HVuwnPfTt7W8UXZG3vqOo4tVaHRI9AHToVHqhzIUUFkDN6rA==
timsort@^0.3.0: timsort@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
...@@ -14239,6 +14686,11 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3: ...@@ -14239,6 +14686,11 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
tmp@0.1.0: tmp@0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877"
...@@ -14340,6 +14792,21 @@ trim-right@^1.0.1: ...@@ -14340,6 +14792,21 @@ trim-right@^1.0.1:
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
trim-trailing-lines@^1.0.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94"
integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==
trim@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0=
trough@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
ts-pnp@1.1.6: ts-pnp@1.1.6:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a"
...@@ -14379,6 +14846,11 @@ tunnel-agent@^0.6.0: ...@@ -14379,6 +14846,11 @@ tunnel-agent@^0.6.0:
dependencies: dependencies:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
tween-functions@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff"
integrity sha1-GuOlDnxguz3vd06scHrLynO7w/8=
tweetnacl@^0.14.3, tweetnacl@~0.14.0: tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5" version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
...@@ -14457,6 +14929,14 @@ uint8arrays@^1.0.0: ...@@ -14457,6 +14929,14 @@ uint8arrays@^1.0.0:
multibase "^3.0.0" multibase "^3.0.0"
web-encoding "^1.0.2" web-encoding "^1.0.2"
unherit@^1.0.4:
version "1.1.3"
resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22"
integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==
dependencies:
inherits "^2.0.0"
xtend "^4.0.0"
unicode-canonical-property-names-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
...@@ -14480,6 +14960,18 @@ unicode-property-aliases-ecmascript@^1.0.4: ...@@ -14480,6 +14960,18 @@ unicode-property-aliases-ecmascript@^1.0.4:
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
unified@^6.1.5:
version "6.2.0"
resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba"
integrity sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==
dependencies:
bail "^1.0.0"
extend "^3.0.0"
is-plain-obj "^1.1.0"
trough "^1.0.0"
vfile "^2.0.0"
x-is-string "^0.1.0"
union-value@^1.0.0: union-value@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
...@@ -14514,6 +15006,42 @@ unique-slug@^2.0.0: ...@@ -14514,6 +15006,42 @@ unique-slug@^2.0.0:
dependencies: dependencies:
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
unist-util-is@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd"
integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==
unist-util-remove-position@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020"
integrity sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==
dependencies:
unist-util-visit "^1.1.0"
unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6"
integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==
unist-util-visit-parents@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06"
integrity sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q==
unist-util-visit-parents@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9"
integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==
dependencies:
unist-util-is "^3.0.0"
unist-util-visit@^1.1.0, unist-util-visit@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3"
integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==
dependencies:
unist-util-visit-parents "^2.0.0"
universalify@^0.1.0: universalify@^0.1.0:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
...@@ -14597,6 +15125,18 @@ use-callback-ref@^1.2.1, use-callback-ref@^1.2.3: ...@@ -14597,6 +15125,18 @@ use-callback-ref@^1.2.1, use-callback-ref@^1.2.3:
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.4.tgz#d86d1577bfd0b955b6e04aaf5971025f406bea3c" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.4.tgz#d86d1577bfd0b955b6e04aaf5971025f406bea3c"
integrity sha512-rXpsyvOnqdScyied4Uglsp14qzag1JIemLeTWGKbwpotWht57hbP78aNT+Q4wdFKQfQibbUX4fb6Qb4y11aVOQ== integrity sha512-rXpsyvOnqdScyied4Uglsp14qzag1JIemLeTWGKbwpotWht57hbP78aNT+Q4wdFKQfQibbUX4fb6Qb4y11aVOQ==
use-count-up@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/use-count-up/-/use-count-up-2.2.5.tgz#08527addca15f4ce10dc0fd3d6ffb69df71c9920"
integrity sha512-ZSL4ui6gnn0sfzGNHJeH0l3YxJlygobU6s27BSVenF0pcPMGR/7W6pZXNRB+94dMeQhjfoRSfBYkCPpVoePNfA==
dependencies:
use-elapsed-time "^2.1.4"
use-elapsed-time@^2.1.4:
version "2.1.5"
resolved "https://registry.yarnpkg.com/use-elapsed-time/-/use-elapsed-time-2.1.5.tgz#db1e83c9ce5a12c2fb17d80c9b87621ceac50c3f"
integrity sha512-+JMPN3gmT5vPUKlkIB8JK9zQ2y68cnyJb3EH3CJOmO85XEe5wJHdXqF3vv7PwLH6yiS1giHSu8hatYk/LPw2wA==
use-sidecar@^1.0.1: use-sidecar@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.2.tgz#e72f582a75842f7de4ef8becd6235a4720ad8af6" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.2.tgz#e72f582a75842f7de4ef8becd6235a4720ad8af6"
...@@ -14610,6 +15150,13 @@ use@^3.1.0: ...@@ -14610,6 +15150,13 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
utif@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759"
integrity sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==
dependencies:
pako "^1.0.5"
util-deprecate@^1.0.1, util-deprecate@~1.0.1: util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
...@@ -14704,6 +15251,28 @@ verror@1.10.0: ...@@ -14704,6 +15251,28 @@ verror@1.10.0:
core-util-is "1.0.2" core-util-is "1.0.2"
extsprintf "^1.2.0" extsprintf "^1.2.0"
vfile-location@^2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e"
integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==
vfile-message@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.1.1.tgz#5833ae078a1dfa2d96e9647886cd32993ab313e1"
integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==
dependencies:
unist-util-stringify-position "^1.1.1"
vfile@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a"
integrity sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==
dependencies:
is-buffer "^1.1.4"
replace-ext "1.0.0"
unist-util-stringify-position "^1.0.0"
vfile-message "^1.0.0"
vm-browserify@^1.0.1: vm-browserify@^1.0.1:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
...@@ -14791,6 +15360,13 @@ wbuf@^1.1.0, wbuf@^1.7.3: ...@@ -14791,6 +15360,13 @@ wbuf@^1.1.0, wbuf@^1.7.3:
dependencies: dependencies:
minimalistic-assert "^1.0.0" minimalistic-assert "^1.0.0"
wcag-contrast@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/wcag-contrast/-/wcag-contrast-3.0.0.tgz#51c2df43f15162993006454b3362844205889efb"
integrity sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w==
dependencies:
relative-luminance "^2.0.0"
web-encoding@^1.0.2: web-encoding@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.0.2.tgz#e5050d4826597f242eb6e0e5fede05e3b9512ca5" resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.0.2.tgz#e5050d4826597f242eb6e0e5fede05e3b9512ca5"
...@@ -15238,6 +15814,11 @@ ws@^6.1.2, ws@^6.2.1: ...@@ -15238,6 +15814,11 @@ ws@^6.1.2, ws@^6.2.1:
dependencies: dependencies:
async-limiter "~1.0.0" async-limiter "~1.0.0"
x-is-string@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=
xhr2-cookies@1.1.0: xhr2-cookies@1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48"
...@@ -15245,7 +15826,7 @@ xhr2-cookies@1.1.0: ...@@ -15245,7 +15826,7 @@ xhr2-cookies@1.1.0:
dependencies: dependencies:
cookiejar "^2.1.1" cookiejar "^2.1.1"
xhr@^2.2.0: xhr@^2.0.1, xhr@^2.2.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd"
integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==
...@@ -15260,6 +15841,24 @@ xml-name-validator@^3.0.0: ...@@ -15260,6 +15841,24 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
xml-parse-from-string@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28"
integrity sha1-qQKekp09vN7RafPG4oI42VpdWig=
xml2js@^0.4.5:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
xmlchars@^2.1.1: xmlchars@^2.1.1:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
......
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