diff --git a/.env.example b/.env.example
index 51955f24f..381931eb7 100644
--- a/.env.example
+++ b/.env.example
@@ -1,12 +1,15 @@
-REACT_APP_CHAIN_ID=4
-REACT_APP_NETWORK_URL="PUT_YOUR_INFURA_URL"
-REACT_APP_GOOGLE_ANALYTICS_ID="PUT_YOUR_GOOGLE_ANALYTICS_ID"
-REACT_APP_GIT_COMMIT_HASH="PUT_YOUR_LAST_COMMIT_HASH"
-REACT_APP_FORTMATIC_KEY="PUT_YOUR_FORTMATIC_KEY"
-REACT_APP_PORTIS_ID="PUT_YOUR_PORTIS_ID"
+PUBLIC_URL="."
REACT_APP_ADDITIONAL_SERVICES_API_URL_RINKEBY="PUT_YOUR_API_URL"
REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_RINKEBY="PUT_YOUR_API_URL"
REACT_APP_ADDITIONAL_SERVICES_API_URL_MAINNET="PUT_YOUR_API_URL"
REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_MAINNET="PUT_YOUR_API_URL"
REACT_APP_ADDITIONAL_SERVICES_API_URL_XDAI="PUT_YOUR_API_URL"
-REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_XDAI="PUT_YOUR_API_URL"
\ No newline at end of file
+REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_XDAI="PUT_YOUR_API_URL"
+REACT_APP_CHAIN_ID=4
+REACT_APP_NETWORK_URL_RINKEBY="PUT_YOUR_INFURA_URL"
+REACT_APP_NETWORK_URL_MAINNET="PUT_YOUR_INFURA_URL"
+REACT_APP_NETWORK_URL_XDAI="PUT_YOUR_INFURA_URL"
+REACT_APP_FORTMATIC_KEY="PUT_YOUR_FORTMATIC_KEY"
+REACT_APP_PORTIS_ID="PUT_YOUR_PORTIS_ID"
+REACT_APP_GOOGLE_ANALYTICS_ID="PUT_YOUR_GOOGLE_ANALYTICS_ID"
+REACT_APP_GIT_COMMIT_HASH="PUT_YOUR_LAST_COMMIT_HASH"
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 000000000..191ae4cc9
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+*.d.ts
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
index cd6c67489..4ee445e27 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,31 +1,103 @@
module.exports = {
- parser: "@typescript-eslint/parser",
+ parser: '@typescript-eslint/parser',
parserOptions: {
- ecmaVersion: 2020,
- sourceType: "module",
ecmaFeatures: {
- // Allows for the parsing of JSX
jsx: true,
},
+ ecmaVersion: 2020,
+ sourceType: 'module',
},
- ignorePatterns: ["node_modules/**/*"],
+ env: {
+ browser: true,
+ es6: true,
+ },
+ ignorePatterns: ['node_modules/**/*'],
settings: {
react: {
- version: "detect",
+ pragma: 'React',
+ version: 'detect',
},
+ 'import/extensions': ['.js', '.jsx', '.ts', '.tsx'],
},
+ plugins: [
+ 'react',
+ 'react-hooks',
+ '@typescript-eslint',
+ 'import',
+ 'sort-destructure-keys',
+ 'prettier',
+ ],
extends: [
- "plugin:react/recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:react-hooks/recommended",
- "prettier/@typescript-eslint",
- "plugin:prettier/recommended",
+ 'eslint:recommended',
+ 'plugin:react/recommended',
+ 'plugin:react-hooks/recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'prettier/@typescript-eslint',
+ 'prettier/react',
+ 'plugin:prettier/recommended',
+ 'plugin:import/errors',
+ 'plugin:import/warnings',
+ 'plugin:import/typescript',
],
rules: {
- "@typescript-eslint/explicit-function-return-type": "off",
- "prettier/prettier": "error",
- "@typescript-eslint/no-explicit-any": "off",
- "@typescript-eslint/ban-types": "off",
- "@typescript-eslint/explicit-module-boundary-types": "off",
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ 'prettier/prettier': 'error',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-types': 'off',
+ 'react/prop-types': 'off',
+ 'no-console': [
+ 'warn',
+ {
+ allow: ['warn', 'error'],
+ },
+ ],
+ 'sort-imports': [
+ 'error',
+ {
+ ignoreDeclarationSort: true,
+ },
+ ],
+ semi: ['error', 'never'],
+ 'no-warning-comments': 0,
+ 'import/extensions': 0,
+ 'import/no-unresolved': 0,
+ 'import/no-extraneous-dependencies': [
+ 'error',
+ { optionalDependencies: false, peerDependencies: false },
+ ],
+ 'import/order': [
+ 'error',
+ {
+ alphabetize: { order: 'asc' },
+ groups: [
+ ['builtin', 'external'],
+ ['internal', 'parent', 'sibling', 'index'],
+ ],
+ 'newlines-between': 'always',
+ pathGroups: [
+ { group: 'builtin', pattern: 'react', position: 'before' },
+ {
+ group: 'external',
+ pattern:
+ '{uniswap-xdai-sdk,rebass/styled-components,polished,react-feather,react-router,@walletconnect/web3-provider,@testing-library/dom,@testing-library/user-event,@testing-library/react,styled-components,ethers/utils,react-dom,react-router-dom,ethers,ethers/providers,web3-utils}',
+ position: 'before',
+ },
+ ],
+ pathGroupsExcludedImportTypes: ['builtin'],
+ },
+ ],
+ 'import/prefer-default-export': 0,
+ 'sort-destructure-keys/sort-destructure-keys': 2,
+ 'react/jsx-label-has-associated-control': 0,
+ 'react/jsx-sort-props': 2,
+ 'react/jsx-filename-extension': [
+ 1,
+ {
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
+ },
+ ],
+ 'react-hooks/rules-of-hooks': 'error',
+ 'react-hooks/exhaustive-deps': 'error',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
},
-};
+}
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 37d0de1ec..52e21f6e2 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -9,7 +9,6 @@ jobs:
runs-on: ubuntu-latest
env:
REPO_NAME_SLUG: idoux
-
steps:
- name: Checkout code
uses: actions/checkout@v2
diff --git a/.gitignore b/.gitignore
index 9c87e44a6..576492d45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ package-lock.json
cypress/videos
cypress/screenshots
cypress/fixtures/example.json
+.project
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 000000000..cd4efd8e5
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1 @@
+*.d.ts
diff --git a/.prettierrc.js b/.prettierrc.js
index 9fe772025..654c2932f 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,4 +1,7 @@
module.exports = {
+ printWidth: 100,
+ singleQuote: true,
+ semi: false,
bracketSpacing: true,
trailingComma: "all",
overrides: [
diff --git a/package.json b/package.json
index 459da4dbd..3e3d9322a 100644
--- a/package.json
+++ b/package.json
@@ -3,25 +3,39 @@
"description": "Gnosis Auction",
"homepage": ".",
"private": true,
+ "engines": {
+ "node": ">=4.4.7 <=14.15.4"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "cross-env REACT_APP_GIT_COMMIT_HASH=$(git show -s --format=%H) react-scripts build",
+ "ipfs-build": "cross-env PUBLIC_URL=\".\" react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject",
+ "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
+ "lint:fix": "eslint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
+ "prettier:fix": "prettier --write \"./src/**/*.{js,jsx,ts,tsx}\"",
+ "cy:run": "cypress run",
+ "serve:build": "serve -s build -l 3000",
+ "integration-test": "yarn build && start-server-and-test 'yarn run serve:build' http://localhost:3000 cy:run"
+ },
+ "eslintConfig": {
+ "extends": "react-app"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "license": "GPL-3.0-or-later",
"devDependencies": {
- "uniswap-xdai-sdk": "^3.0.2-a3",
- "@amcharts/amcharts4": "^4.10.13",
- "@ethersproject/address": "^5.0.0-beta.134",
- "@ethersproject/bignumber": "^5.0.0-beta.138",
- "@ethersproject/constants": "^5.0.0-beta.133",
- "@ethersproject/contracts": "^5.0.0-beta.151",
- "@ethersproject/experimental": "^5.0.0-beta.141",
- "@ethersproject/providers": "5.0.0-beta.162",
- "@ethersproject/strings": "^5.0.0-beta.136",
- "@ethersproject/units": "^5.0.0-beta.132",
- "@ethersproject/wallet": "^5.0.0-beta.141",
- "@fortawesome/fontawesome-svg-core": "^1.2.32",
- "@fortawesome/free-solid-svg-icons": "^5.15.1",
- "@fortawesome/react-fontawesome": "^0.1.14",
- "@popperjs/core": "^2.4.0",
- "@reach/dialog": "^0.10.3",
- "@reach/portal": "^0.10.3",
- "@reduxjs/toolkit": "^1.3.5",
"@types/jest": "^25.2.1",
"@types/lodash.flatmap": "^4.5.6",
"@types/node": "^13.13.5",
@@ -32,41 +46,71 @@
"@types/react-router-dom": "^5.0.0",
"@types/react-window": "^1.8.2",
"@types/rebass": "^4.0.5",
- "@types/styled-components": "^4.2.0",
+ "@types/styled-components": "^5.1.7",
"@types/testing-library__cypress": "^5.0.5",
"@typescript-eslint/eslint-plugin": "^4.8.2",
"@typescript-eslint/parser": "^4.8.2",
+ "@web3-react/types": "^6.0.7",
+ "eslint": "^7.14.0",
+ "eslint-config-prettier": "^6.15.0",
+ "eslint-plugin-prettier": "^3.1.4",
+ "eslint-plugin-react": "^7.19.0",
+ "eslint-plugin-react-hooks": "^4.0.0",
+ "eslint-plugin-sort-destructure-keys": "^1.3.5",
+ "husky": "^4.3.6",
+ "lint-staged": "^10.5.3",
+ "prettier": "^2.2.1"
+ },
+ "dependencies": {
+ "@amcharts/amcharts4": "^4.10.13",
+ "@babel/helper-define-map": "^7.12.13",
+ "@babel/helper-regex": "^7.10.5",
+ "@ethersproject/address": "^5.0.0-beta.134",
+ "@ethersproject/bignumber": "^5.0.0-beta.138",
+ "@ethersproject/constants": "^5.0.0-beta.133",
+ "@ethersproject/contracts": "^5.0.0-beta.151",
+ "@ethersproject/experimental": "^5.0.0-beta.191",
+ "@ethersproject/providers": "5.0.23",
+ "@ethersproject/strings": "^5.0.0-beta.136",
+ "@ethersproject/units": "^5.0.0-beta.191",
+ "@ethersproject/wallet": "^5.0.0-beta.191",
+ "@fortawesome/fontawesome-svg-core": "^1.2.32",
+ "@fortawesome/free-solid-svg-icons": "^5.15.1",
+ "@fortawesome/react-fontawesome": "^0.1.14",
+ "@popperjs/core": "^2.4.0",
+ "@reach/dialog": "^0.10.3",
+ "@reach/portal": "^0.10.3",
+ "@reduxjs/toolkit": "^1.3.5",
+ "@types/react-router-hash-link": "^1.2.1",
+ "@uniswap/sdk": "^2.0.5",
+ "@uniswap/token-lists": "^1.0.0-beta.19",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "1.0.0-beta.0",
+ "@web3-react/abstract-connector": "^6.0.7",
"@web3-react/core": "^6.1.9",
"@web3-react/fortmatic-connector": "^6.1.6",
"@web3-react/injected-connector": "^6.0.7",
"@web3-react/portis-connector": "^6.1.9",
"@web3-react/walletconnect-connector": "^6.1.9",
"@web3-react/walletlink-connector": "^6.1.9",
+ "ajv": "^6.12.3",
"copy-to-clipboard": "^3.2.0",
"cross-env": "^7.0.2",
"cypress": "^4.5.0",
- "eslint": "^7.14.0",
- "eslint-config-prettier": "^6.15.0",
- "eslint-plugin-prettier": "^3.1.4",
- "eslint-plugin-react": "^7.19.0",
- "eslint-plugin-react-hooks": "^4.0.0",
- "husky": "^4.3.6",
"i18next": "^15.0.9",
"i18next-browser-languagedetector": "^3.0.1",
"i18next-xhr-backend": "^2.0.1",
+ "inter-ui": "^3.15.0",
"isexe": "^2.0.0",
"jazzicon": "^1.5.0",
"levenary": "^1.1.1",
- "lint-staged": "^10.5.3",
"lodash.flatmap": "^4.5.0",
"modali": "^1.2.0",
"polished": "^3.3.2",
- "prettier": "^2.2.1",
"qrcode.react": "^0.9.3",
"qs": "^6.9.4",
"react": "^16.13.1",
+ "react-copy-to-clipboard": "^5.0.3",
"react-device-detect": "^1.6.2",
"react-dom": "^16.13.1",
"react-feather": "^2.0.8",
@@ -75,51 +119,25 @@
"react-popper": "^2.2.3",
"react-redux": "^7.2.0",
"react-router-dom": "^5.0.0",
+ "react-router-hash-link": "^2.4.0",
"react-scripts": "^4.0.1",
"react-spring": "^8.0.27",
+ "react-table": "^7.6.3",
+ "react-tooltip": "^4.2.14",
"react-use-gesture": "^6.0.14",
"react-window": "^1.8.5",
"rebass": "^4.0.7",
+ "redux": "^4.0.5",
"redux-localstorage-simple": "^2.2.0",
+ "sanitize.css": "^12.0.1",
"serve": "^11.3.0",
"start-server-and-test": "^1.11.0",
- "styled-components": "^4.2.0",
+ "styled-components": "^5.2.1",
+ "tiny-invariant": "^1.1.0",
"typescript": "^3.8.3",
+ "uniswap-xdai-sdk": "^3.0.2-a3",
"use-media": "^1.4.0"
},
- "scripts": {
- "start": "react-scripts start",
- "build": "cross-env REACT_APP_GIT_COMMIT_HASH=$(git show -s --format=%H) react-scripts build",
- "ipfs-build": "cross-env PUBLIC_URL=\".\" react-scripts build",
- "test": "react-scripts test --env=jsdom",
- "eject": "react-scripts eject",
- "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
- "lint:fix": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
- "prettier:fix": "prettier --write './src/**/*.{js,jsx,ts,tsx}'",
- "cy:run": "cypress run",
- "serve:build": "serve -s build -l 3000",
- "integration-test": "yarn build && start-server-and-test 'yarn run serve:build' http://localhost:3000 cy:run"
- },
- "eslintConfig": {
- "extends": "react-app"
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- },
- "license": "GPL-3.0-or-later",
- "dependencies": {
- "inter-ui": "^3.15.0",
- "react-table": "^7.6.3"
- },
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx}": [
"yarn run lint:fix",
diff --git a/public/browserconfig.xml b/public/browserconfig.xml
new file mode 100644
index 000000000..70cb989d3
--- /dev/null
+++ b/public/browserconfig.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ #da532c
+
+
+
diff --git a/public/favicon/android-chrome-192x192.png b/public/favicon/android-chrome-192x192.png
new file mode 100644
index 000000000..ae6f53cd5
Binary files /dev/null and b/public/favicon/android-chrome-192x192.png differ
diff --git a/public/favicon/android-chrome-512x512.png b/public/favicon/android-chrome-512x512.png
new file mode 100644
index 000000000..e04e91b4c
Binary files /dev/null and b/public/favicon/android-chrome-512x512.png differ
diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png
new file mode 100644
index 000000000..485e6121a
Binary files /dev/null and b/public/favicon/apple-touch-icon.png differ
diff --git a/public/favicon/favicon-16x16.png b/public/favicon/favicon-16x16.png
new file mode 100644
index 000000000..83f5bbc45
Binary files /dev/null and b/public/favicon/favicon-16x16.png differ
diff --git a/public/favicon/favicon-32x32.png b/public/favicon/favicon-32x32.png
new file mode 100644
index 000000000..87f5c313b
Binary files /dev/null and b/public/favicon/favicon-32x32.png differ
diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico
new file mode 100644
index 000000000..aecaee0e3
Binary files /dev/null and b/public/favicon/favicon.ico differ
diff --git a/public/favicon/mstile-150x150.png b/public/favicon/mstile-150x150.png
new file mode 100644
index 000000000..d185d77f1
Binary files /dev/null and b/public/favicon/mstile-150x150.png differ
diff --git a/public/favicon/safari-pinned-tab.svg b/public/favicon/safari-pinned-tab.svg
new file mode 100644
index 000000000..f52c776c0
--- /dev/null
+++ b/public/favicon/safari-pinned-tab.svg
@@ -0,0 +1,29 @@
+
+
+
diff --git a/public/index.html b/public/index.html
index c8d5e86a5..e24d41b8e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,39 +1,92 @@
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
GnosisAuction
+
+
-
+
diff --git a/public/manifest.json b/public/manifest.json
index 7389c7a66..e9f3b25f3 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -3,20 +3,18 @@
"name": "GnosisAuction",
"icons": [
{
- "src": "./images/192x192_App_Icon.png",
+ "src": "./favicon/android-chrome-192x192.png",
"sizes": "192x192",
- "type": "image/png",
- "purpose": "any maskable"
+ "type": "image/png"
},
{
- "src": "./images/512x512_App_Icon.png",
+ "src": "./favicon/android-chrome-512x512.png",
"sizes": "512x512",
- "type": "image/png",
- "purpose": "any maskable"
+ "type": "image/png"
}
],
- "orientation": "portrait",
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
"display": "standalone",
- "theme_color": "#ff007a",
- "background_color": "#fff"
+ "orientation": "portrait"
}
diff --git a/src/api/AdditionalServicesApi.ts b/src/api/AdditionalServicesApi.ts
index 2fb6324d5..b6228dbd1 100644
--- a/src/api/AdditionalServicesApi.ts
+++ b/src/api/AdditionalServicesApi.ts
@@ -1,47 +1,52 @@
-import { BigNumber } from "@ethersproject/bignumber";
-import { decodeOrder, encodeOrder, Order } from "../hooks/Order";
-import { AuctionInfo } from "../hooks/useAllAuctionInfos";
+import { BigNumber } from '@ethersproject/bignumber'
+
+import { Order, decodeOrder, encodeOrder } from '../hooks/Order'
+import { AuctionInfo } from '../hooks/useAllAuctionInfos'
+import { AuctionInfoDetail } from '../hooks/useAuctionDetails'
+
export interface AdditionalServicesApi {
- getOrderBookUrl(params: OrderBookParams): string;
- getOrderBookData(params: OrderBookParams): Promise;
- getPreviousOrderUrl(params: PreviousOrderParams): string;
- getPreviousOrder(params: PreviousOrderParams): Promise;
- getCurrentUserOrdersUrl(params: UserOrderParams): string;
- getCurrentUserOrders(params: UserOrderParams): Promise;
- getAllUserOrdersUrl(params: UserOrderParams): string;
- getAllUserOrders(params: UserOrderParams): Promise;
- getMostInterestingAuctionDetailsUrl(params: InterestingAuctionParams): string;
- getMostInterestingAuctionDetails(
- params: InterestingAuctionParams,
- ): Promise;
- getAllAuctionDetailsUrl(networkId: number): string;
- getAllAuctionDetails(): Promise;
- getClearingPriceOrderAndVolumeUrl(params: OrderBookParams): string;
- getClearingPriceOrderAndVolume(
- params: OrderBookParams,
- ): Promise;
+ getOrderBookUrl(params: OrderBookParams): string
+ getOrderBookData(params: OrderBookParams): Promise
+ getPreviousOrderUrl(params: PreviousOrderParams): string
+ getPreviousOrder(params: PreviousOrderParams): Promise
+ getCurrentUserOrdersUrl(params: UserOrderParams): string
+ getCurrentUserOrders(params: UserOrderParams): Promise
+ getAllUserOrdersUrl(params: UserOrderParams): string
+ getAllUserOrders(params: UserOrderParams): Promise
+ getMostInterestingAuctionDetailsUrl(params: InterestingAuctionParams): string
+ getMostInterestingAuctionDetails(): Promise
+ getAllAuctionDetailsUrl(networkId: number): string
+ getAllAuctionDetails(): Promise
+ getClearingPriceOrderAndVolumeUrl(params: OrderBookParams): string
+ getClearingPriceOrderAndVolume(params: OrderBookParams): Promise
+ getAuctionDetails(params: AuctionDetailParams): Promise
}
interface OrderBookParams {
- networkId: number;
- auctionId: number;
+ networkId: number
+ auctionId: number
}
interface InterestingAuctionParams {
- networkId: number;
- numberOfAuctions: number;
+ networkId: number
+ numberOfAuctions: number
}
interface PreviousOrderParams {
- networkId: number;
- auctionId: number;
- order: Order;
+ networkId: number
+ auctionId: number
+ order: Order
}
interface UserOrderParams {
- networkId: number;
- auctionId: number;
- user: string;
+ networkId: number
+ auctionId: number
+ user: string
+}
+
+interface AuctionDetailParams {
+ networkId: number
+ auctionId: number
}
/**
@@ -49,226 +54,259 @@ interface UserOrderParams {
* Both price and volume are numbers (floats)
*/
export interface PricePoint {
- price: number;
- volume: number;
+ price: number
+ volume: number
}
/**
* DATA returned from api as JSON
*/
export interface OrderBookData {
- asks: PricePoint[];
- bids: PricePoint[];
+ asks: PricePoint[]
+ bids: PricePoint[]
}
export interface ClearingPriceAndVolumeData {
- clearingOrder: Order;
- volume: BigNumber;
+ clearingOrder: Order
+ volume: BigNumber
}
export interface AdditionalServicesEndpoint {
- networkId: number;
- url_production: string;
- url_develop?: string;
+ networkId: number
+ url_production: string
+ url_develop?: string
}
-function getAdditionalServiceUrl(baseUlr: string): string {
- return `${baseUlr}${baseUlr.endsWith("/") ? "" : "/"}api/v1/`;
+function getAdditionalServiceUrl(baseUrl: string): string {
+ return `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}api/v1/`
}
-export type AdditionalServicesApiParams = AdditionalServicesEndpoint[];
+export type AdditionalServicesApiParams = AdditionalServicesEndpoint[]
export class AdditionalServicesApiImpl implements AdditionalServicesApi {
- private urlsByNetwork: { [networkId: number]: string } = {};
+ private urlsByNetwork: { [networkId: number]: string } = {}
public constructor(params: AdditionalServicesApiParams) {
params.forEach((endpoint) => {
if (endpoint.url_develop || endpoint.url_production) {
this.urlsByNetwork[endpoint.networkId] = getAdditionalServiceUrl(
- process.env.PRICE_ESTIMATOR_URL === "production"
+ process.env.PRICE_ESTIMATOR_URL === 'production'
? endpoint.url_production
: endpoint.url_develop || endpoint.url_production, // fallback on required url_production
- );
+ )
}
- });
+ })
}
public getOrderBookUrl(params: OrderBookParams): string {
- const { networkId, auctionId } = params;
+ const { auctionId, networkId } = params
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_order_book_display_data/${auctionId}`;
- return url;
+ const url = `${baseUrl}get_order_book_display_data/${auctionId}`
+ return url
}
public getClearingPriceOrderAndVolumeUrl(params: OrderBookParams): string {
- const { networkId, auctionId } = params;
+ const { auctionId, networkId } = params
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_clearing_order_and_volume/${auctionId}`;
- return url;
+ const url = `${baseUrl}get_clearing_order_and_volume/${auctionId}`
+ return url
}
public getPreviousOrderUrl(params: PreviousOrderParams): string {
- const { networkId, auctionId, order } = params;
+ const { auctionId, networkId, order } = params
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_previous_order/${auctionId}/${encodeOrder(
- order,
- )}`;
- return url;
+ const url = `${baseUrl}get_previous_order/${auctionId}/${encodeOrder(order)}`
+ return url
}
public getAllUserOrdersUrl(params: UserOrderParams): string {
- const { networkId, auctionId, user } = params;
+ const { auctionId, networkId, user } = params
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_user_orders/${auctionId}/${user}`;
- return url;
+ const url = `${baseUrl}get_user_orders/${auctionId}/${user}`
+ return url
}
- public getMostInterestingAuctionDetailsUrl(
- params: InterestingAuctionParams,
- ): string {
- const { networkId, numberOfAuctions } = params;
+ public getMostInterestingAuctionDetailsUrl(params: InterestingAuctionParams): string {
+ const { networkId, numberOfAuctions } = params
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_details_of_most_interesting_auctions/${numberOfAuctions}`;
- return url;
+ const url = `${baseUrl}get_details_of_most_interesting_auctions/${numberOfAuctions}`
+ return url
}
+
public getAllAuctionDetailsUrl(networkId: number): string {
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
+
+ const url = `${baseUrl}get_all_auction_with_details/`
+ return url
+ }
+
+ public getAuctionDetailsUrl(params: AuctionDetailParams): string {
+ const { auctionId, networkId } = params
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_all_auction_with_details/`;
- return url;
+ return `${baseUrl}get_auction_with_details/${auctionId}`
}
public getCurrentUserOrdersUrl(params: UserOrderParams): string {
- const { networkId, auctionId, user } = params;
+ const { auctionId, networkId, user } = params
- const baseUrl = this._getBaseUrl(networkId);
+ const baseUrl = this._getBaseUrl(networkId)
- const url = `${baseUrl}get_user_orders_without_canceled_or_claimed/${auctionId}/${user}`;
- return url;
+ const url = `${baseUrl}get_user_orders_without_canceled_or_claimed/${auctionId}/${user}`
+ return url
}
- public async getAllAuctionDetails(): Promise {
+ public async getAllAuctionDetails(): Promise> {
try {
- const promises: Promise[] = [];
+ const promises: Promise[] = []
for (const networkId in this.urlsByNetwork) {
- const url = await this.getAllAuctionDetailsUrl(Number(networkId));
+ const url = await this.getAllAuctionDetailsUrl(Number(networkId))
- promises.push(fetch(url));
+ promises.push(fetch(url))
}
- const results = await Promise.all(promises);
- const allAuctions = [];
+ const results = await Promise.all(promises)
+ const allAuctions = []
for (const res of results) {
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- allAuctions.push(await res.json());
+ allAuctions.push(await res.json())
}
- return allAuctions.flat();
+ return allAuctions.flat()
} catch (error) {
- console.error(error);
+ console.error(error)
- throw new Error(`Failed to query all auctions: ${error.message}`);
+ throw new Error(`Failed to query all auctions: ${error.message}`)
}
}
- public async getMostInterestingAuctionDetails(
- params: InterestingAuctionParams,
- ): Promise {
+
+ public async getAuctionDetails(params: AuctionDetailParams): Promise {
try {
- const url = await this.getMostInterestingAuctionDetailsUrl(params);
+ const url = await this.getAuctionDetailsUrl(params)
- const res = await fetch(url);
+ const res = await fetch(url)
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- return await res.json();
+ return res.json()
} catch (error) {
- console.error(error);
+ console.error(error)
- const { networkId } = params;
+ const { auctionId } = params
throw new Error(
- `Failed to query interesting auctions for network ${networkId}: ${error.message}`,
- );
+ `Failed to query auction details for auction id ${auctionId} : ${error.message}`,
+ )
+ }
+ }
+
+ public async getMostInterestingAuctionDetails(): Promise> {
+ try {
+ const promises: Promise[] = []
+ for (const networkId in this.urlsByNetwork) {
+ const url = await this.getMostInterestingAuctionDetailsUrl({
+ networkId: Number(networkId),
+ numberOfAuctions: 3,
+ })
+
+ promises.push(fetch(url))
+ }
+ const results = await Promise.all(promises)
+ const allInterestingAuctions = []
+ for (const res of results) {
+ if (!res.ok) {
+ // backend returns {"message":"invalid url query"}
+ // for bad requests
+ throw await res.json()
+ }
+ allInterestingAuctions.push(await res.json())
+ }
+
+ const allInterestingAuctionsOrdered = allInterestingAuctions.sort(
+ (auctionA, auctionB) => auctionB.interestScore - auctionA.interestScore,
+ )
+ return allInterestingAuctionsOrdered.flat()
+ } catch (error) {
+ console.error(error)
+ throw new Error(`Failed to query interesting auctions: ${error.message}`)
}
}
public async getPreviousOrder(params: PreviousOrderParams): Promise {
try {
- const url = await this.getPreviousOrderUrl(params);
+ const url = await this.getPreviousOrderUrl(params)
- const res = await fetch(url);
+ const res = await fetch(url)
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- return await res.json();
+ return await res.json()
} catch (error) {
- console.error(error);
+ console.error(error)
- const { auctionId, order } = params;
+ const { auctionId, order } = params
throw new Error(
`Failed to query previous order for auction id ${auctionId} and order ${encodeOrder(
order,
)}: ${error.message}`,
- );
+ )
}
}
public async getAllUserOrders(params: UserOrderParams): Promise {
try {
- const url = await this.getAllUserOrdersUrl(params);
+ const url = await this.getAllUserOrdersUrl(params)
- const res = await fetch(url);
+ const res = await fetch(url)
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- return await res.json();
+ return await res.json()
} catch (error) {
- console.error(error);
+ console.error(error)
- const { auctionId, user } = params;
+ const { auctionId, user } = params
throw new Error(
`Failed to query previous order for auction id ${auctionId} and order ${user}: ${error.message}`,
- );
+ )
}
}
- public async getCurrentUserOrders(
- params: UserOrderParams,
- ): Promise {
+ public async getCurrentUserOrders(params: UserOrderParams): Promise {
try {
- const url = await this.getCurrentUserOrdersUrl(params);
+ const url = await this.getCurrentUserOrdersUrl(params)
- const res = await fetch(url);
+ const res = await fetch(url)
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- return await res.json();
+ return await res.json()
} catch (error) {
- console.error(error);
+ console.error(error)
- const { auctionId, user } = params;
+ const { auctionId, user } = params
throw new Error(
`Failed to query previous order for auction id ${auctionId} and order ${user}: ${error.message}`,
- );
+ )
}
}
@@ -276,86 +314,80 @@ export class AdditionalServicesApiImpl implements AdditionalServicesApi {
params: OrderBookParams,
): Promise {
try {
- const url = await this.getClearingPriceOrderAndVolumeUrl(params);
+ const url = await this.getClearingPriceOrderAndVolumeUrl(params)
- const res = await fetch(url);
+ const res = await fetch(url)
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- const result = await res.json();
+ const result = await res.json()
return {
clearingOrder: decodeOrder(result[0]),
volume: BigNumber.from(result[1]),
- };
- return await res.json();
+ }
} catch (error) {
- console.error(error);
+ console.error(error)
- const { auctionId } = params;
+ const { auctionId } = params
throw new Error(
`Failed to query clearing price order for auction id ${auctionId} : ${error.message}`,
- );
+ )
}
}
- public async getOrderBookData(
- params: OrderBookParams,
- ): Promise {
+ public async getOrderBookData(params: OrderBookParams): Promise {
try {
- const url = await this.getOrderBookUrl(params);
+ const url = await this.getOrderBookUrl(params)
- const res = await fetch(url);
+ const res = await fetch(url)
if (!res.ok) {
// backend returns {"message":"invalid url query"}
// for bad requests
- throw await res.json();
+ throw await res.json()
}
- return await res.json();
+ return await res.json()
} catch (error) {
- console.error(error);
+ console.error(error)
- const { auctionId } = params;
+ const { auctionId } = params
throw new Error(
`Failed to query orderbook data for auction id ${auctionId} : ${error.message}`,
- );
+ )
}
}
- private async query(
- networkId: number,
- queryString: string,
- ): Promise {
- const baseUrl = this._getBaseUrl(networkId);
+ private async query(networkId: number, queryString: string): Promise> {
+ const baseUrl = this._getBaseUrl(networkId)
- const url = baseUrl + queryString;
+ const url = baseUrl + queryString
- const response = await fetch(url);
+ const response = await fetch(url)
if (!response.ok) {
- throw new Error(`Request failed: [${response.status}] ${response.body}`);
+ throw new Error(`Request failed: [${response.status}] ${response.body}`)
}
- const body = await response.text();
+ const body = await response.text()
if (!body) {
- return null;
+ return null
}
- return JSON.parse(body);
+ return JSON.parse(body)
}
private _getBaseUrl(networkId: number): string {
- const baseUrl = this.urlsByNetwork[networkId];
- if (typeof baseUrl === "undefined") {
+ const baseUrl = this.urlsByNetwork[networkId]
+ if (typeof baseUrl === 'undefined') {
throw new Error(
`REACT_APP_ADDITIONAL_SERVICES_API_URL must be a defined environment variable for network ${networkId}`,
- );
+ )
}
- return baseUrl;
+ return baseUrl
}
}
diff --git a/src/api/TokenLogosServiceApi.ts b/src/api/TokenLogosServiceApi.ts
new file mode 100644
index 000000000..e326d1647
--- /dev/null
+++ b/src/api/TokenLogosServiceApi.ts
@@ -0,0 +1,61 @@
+import { TokenInfo, TokenList } from '@uniswap/token-lists'
+import schema from '@uniswap/token-lists/src/tokenlist.schema.json'
+import Ajv from 'ajv'
+
+const TOKEN_LIST_RESOURCES = [
+ 'https://tokens.coingecko.com/uniswap/all.json',
+ 'https://raw.githubusercontent.com/gnosis/gp-swap-ui/develop/src/custom/tokens/rinkeby-token-list.json',
+ 'https://tokens.honeyswap.org',
+]
+const tokenListValidator = new Ajv({ allErrors: true }).compile(schema)
+
+export interface TokenLogosServiceApiInterface {
+ getTokensByUrl(url: string): Promise
+ getAllTokens(): Promise
+}
+
+export class TokenLogosServiceApi implements TokenLogosServiceApiInterface {
+ public async getTokensByUrl(url: string): Promise {
+ try {
+ const response = await fetch(url)
+
+ if (!response.ok) {
+ throw new Error('Invalid token list response.')
+ }
+
+ const data = await response.json()
+
+ if (!tokenListValidator(data)) {
+ console.error(tokenListValidator.errors)
+
+ throw new Error('Token list failed validation')
+ }
+
+ return data as TokenList
+ } catch (error) {
+ console.error(error)
+
+ throw new Error(`Failed to fetch token list from URL ${url}`)
+ }
+ }
+
+ public async getAllTokens(): Promise {
+ const tokens: TokenInfo[] = []
+
+ try {
+ const [coingeckoTokenList, gnosisTokenList, honeyswapTokenList] = await Promise.all(
+ TOKEN_LIST_RESOURCES.map((url) => this.getTokensByUrl(url)),
+ )
+
+ tokens.push(...coingeckoTokenList.tokens)
+ tokens.push(...gnosisTokenList.tokens)
+ tokens.push(...honeyswapTokenList.tokens)
+ } catch (error) {
+ console.error(error)
+
+ throw new Error('Failed to get all tokens')
+ }
+
+ return tokens
+ }
+}
diff --git a/src/api/index.tsx b/src/api/index.tsx
index ab42555a7..0ad9bedd7 100644
--- a/src/api/index.tsx
+++ b/src/api/index.tsx
@@ -1,46 +1,42 @@
+import {
+ API_URL_DEVELOP_MAINNET,
+ API_URL_DEVELOP_RINKEBY,
+ API_URL_DEVELOP_XDAI,
+ API_URL_PRODUCTION_MAINNET,
+ API_URL_PRODUCTION_RINKEBY,
+ API_URL_PRODUCTION_XDAI,
+} from '../constants/config'
import {
AdditionalServicesApi,
- AdditionalServicesEndpoint,
AdditionalServicesApiImpl,
-} from "./AdditionalServicesApi";
+ AdditionalServicesEndpoint,
+} from './AdditionalServicesApi'
+import { TokenLogosServiceApi, TokenLogosServiceApiInterface } from './TokenLogosServiceApi'
function createAdditionalServiceApi(): AdditionalServicesApi {
- const url_develop_rinkeby =
- process.env.REACT_APP_ADDITIONAL_SERVICES_API_URL_RINKEBY;
- const url_production_rinkeby =
- process.env.REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_RINKEBY;
-
- const url_develop_mainnet =
- process.env.REACT_APP_ADDITIONAL_SERVICES_API_URL_MAINNET;
- const url_production_mainnet =
- process.env.REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_MAINNET;
-
- const url_develop_xdai =
- process.env.REACT_APP_ADDITIONAL_SERVICES_API_URL_XDAI;
- const url_production_xdai =
- process.env.REACT_APP_ADDITIONAL_SERVICES_API_URL_PROD_XDAI;
const config: AdditionalServicesEndpoint[] = [
{
networkId: 4,
- url_production: url_production_rinkeby,
- url_develop: url_develop_rinkeby,
+ url_production: API_URL_PRODUCTION_RINKEBY,
+ url_develop: API_URL_DEVELOP_RINKEBY,
},
{
networkId: 100,
- url_production: url_production_xdai,
- url_develop: url_develop_xdai,
+ url_production: API_URL_PRODUCTION_XDAI,
+ url_develop: API_URL_DEVELOP_XDAI,
},
{
networkId: 1,
- url_production: url_production_mainnet,
- url_develop: url_develop_mainnet,
+ url_production: API_URL_PRODUCTION_MAINNET,
+ url_develop: API_URL_DEVELOP_MAINNET,
},
- ];
- const dexPriceEstimatorApi = new AdditionalServicesApiImpl(config);
+ ]
+ const dexPriceEstimatorApi = new AdditionalServicesApiImpl(config)
- window["dexPriceEstimatorApi"] = dexPriceEstimatorApi;
- return dexPriceEstimatorApi;
+ window['dexPriceEstimatorApi'] = dexPriceEstimatorApi
+ return dexPriceEstimatorApi
}
// Build APIs
-export const additionalServiceApi: AdditionalServicesApi = createAdditionalServiceApi();
+export const additionalServiceApi: AdditionalServicesApi = createAdditionalServiceApi()
+export const tokenLogosServiceApi: TokenLogosServiceApiInterface = new TokenLogosServiceApi()
diff --git a/src/components/AccountDetails/Copy.tsx b/src/components/AccountDetails/Copy.tsx
index 1532c53e9..2a7782e8d 100644
--- a/src/components/AccountDetails/Copy.tsx
+++ b/src/components/AccountDetails/Copy.tsx
@@ -1,9 +1,9 @@
-import React from "react";
-import styled from "styled-components";
-import useCopyClipboard from "../../hooks/useCopyClipboard";
+import React from 'react'
+import { CheckCircle, Copy } from 'react-feather'
+import styled from 'styled-components'
-import { LinkStyledButton } from "../../theme";
-import { CheckCircle, Copy } from "react-feather";
+import useCopyClipboard from '../../hooks/useCopyClipboard'
+import { LinkStyledButton } from '../../theme'
const CopyIcon = styled(LinkStyledButton)`
color: ${({ theme }) => theme.text4};
@@ -18,32 +18,29 @@ const CopyIcon = styled(LinkStyledButton)`
text-decoration: none;
color: ${({ theme }) => theme.text3};
}
-`;
+`
const TransactionStatusText = styled.span`
margin-left: 0.25rem;
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
-`;
+`
-export default function CopyHelper(props: {
- toCopy: string;
- children?: React.ReactNode;
-}) {
- const [isCopied, setCopied] = useCopyClipboard();
+export default function CopyHelper(props: { toCopy: string; children?: React.ReactNode }) {
+ const [isCopied, setCopied] = useCopyClipboard()
return (
setCopied(props.toCopy)}>
{props.children}
{isCopied ? (
-
+
Copied
) : (
-
+
)}
- );
+ )
}
diff --git a/src/components/AccountDetails/Transaction.tsx b/src/components/AccountDetails/Transaction.tsx
index 9e6207226..408aa8939 100644
--- a/src/components/AccountDetails/Transaction.tsx
+++ b/src/components/AccountDetails/Transaction.tsx
@@ -1,82 +1,53 @@
-import React from "react";
-import styled from "styled-components";
-import { Check, Triangle } from "react-feather";
+import React from 'react'
+import { Check, Triangle } from 'react-feather'
+import styled from 'styled-components'
-import { useActiveWeb3React } from "../../hooks";
-import { getEtherscanLink } from "../../utils";
-import { ExternalLink, Spinner } from "../../theme";
-import Circle from "../../assets/images/circle.svg";
-
-import { transparentize } from "polished";
-import { useAllTransactions } from "../../state/transactions/hooks";
+import Circle from '../../assets/images/circle.svg'
+import { useActiveWeb3React } from '../../hooks'
+import { useAllTransactions } from '../../state/transactions/hooks'
+import { ExternalLink, Spinner } from '../../theme'
+import { getEtherscanLink } from '../../utils'
const TransactionWrapper = styled.div`
margin-top: 0.75rem;
-`;
+`
const TransactionStatusText = styled.div`
margin-right: 0.5rem;
-`;
+`
-const TransactionState = styled(ExternalLink)<{
- pending: boolean;
- success?: boolean;
-}>`
+const TransactionState = styled(ExternalLink)`
+ border-color: #fff;
+ border-radius: 0.5rem;
+ border: 1px solid;
+ color: #fff;
display: flex;
+ font-size: 0.75rem;
+ font-weight: 500;
justify-content: space-between;
- text-decoration: none !important;
-
- border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
- font-weight: 500;
- font-size: 0.75rem;
- border: 1px solid;
-
- color: ${({ pending, success, theme }) =>
- pending ? theme.primary1 : success ? theme.green1 : theme.red1};
-
- border-color: ${({ pending, success, theme }) =>
- pending
- ? transparentize(0.75, theme.primary1)
- : success
- ? transparentize(0.75, theme.green1)
- : transparentize(0.75, theme.red1)};
-
- :hover {
- border-color: ${({ pending, success, theme }) =>
- pending
- ? transparentize(0, theme.primary1)
- : success
- ? transparentize(0, theme.green1)
- : transparentize(0, theme.red1)};
- }
-`;
+ text-decoration: none !important;
+`
const IconWrapper = styled.div`
flex-shrink: 0;
-`;
+`
export default function Transaction({ hash }: { hash: string }) {
- const { chainId } = useActiveWeb3React();
- const allTransactions = useAllTransactions();
+ const { chainId } = useActiveWeb3React()
+ const allTransactions = useAllTransactions()
- const summary = allTransactions?.[hash]?.summary;
- const pending = !allTransactions?.[hash]?.receipt;
+ const summary = allTransactions?.[hash]?.summary
+ const pending = !allTransactions?.[hash]?.receipt
const success =
!pending &&
(allTransactions[hash].receipt.status === 1 ||
- typeof allTransactions[hash].receipt.status === "undefined");
+ typeof allTransactions[hash].receipt.status === 'undefined')
return (
-
-
- {summary ? summary : hash}
-
+
+ {summary ? summary : hash}
{pending ? (
@@ -88,5 +59,5 @@ export default function Transaction({ hash }: { hash: string }) {
- );
+ )
}
diff --git a/src/components/AccountDetails/index.tsx b/src/components/AccountDetails/index.tsx
index 8e8bc7e82..e0a99373d 100644
--- a/src/components/AccountDetails/index.tsx
+++ b/src/components/AccountDetails/index.tsx
@@ -1,44 +1,36 @@
-import React, { useCallback, useContext } from "react";
-import { useDispatch } from "react-redux";
-import styled, { ThemeContext } from "styled-components";
-import { useActiveWeb3React } from "../../hooks";
-import { isMobile } from "react-device-detect";
-import { AppDispatch } from "../../state";
-import { clearAllTransactions } from "../../state/transactions/actions";
-import { AutoRow } from "../Row";
-import Copy from "./Copy";
-import Transaction from "./Transaction";
-
-import { SUPPORTED_WALLETS } from "../../constants";
-import { ReactComponent as Close } from "../../assets/images/x.svg";
-import { getEtherscanLink } from "../../utils";
-import {
- injected,
- walletconnect,
- walletlink,
- fortmatic,
- portis,
-} from "../../connectors";
-import CoinbaseWalletIcon from "../../assets/images/coinbaseWalletIcon.svg";
-import WalletConnectIcon from "../../assets/images/walletConnectIcon.svg";
-import FortmaticIcon from "../../assets/images/fortmaticIcon.png";
-import PortisIcon from "../../assets/images/portisIcon.png";
-import Identicon from "../Identicon";
-
-import { ButtonEmpty } from "../Button";
-
-import { ExternalLink, LinkStyledButton, TYPE } from "../../theme";
+import React, { useCallback, useContext } from 'react'
+import styled, { ThemeContext } from 'styled-components'
+
+import { isMobile } from 'react-device-detect'
+import { useDispatch } from 'react-redux'
+
+import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
+import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
+import PortisIcon from '../../assets/images/portisIcon.png'
+import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
+import { ReactComponent as Close } from '../../assets/images/x.svg'
+import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
+import { SUPPORTED_WALLETS } from '../../constants'
+import { useActiveWeb3React } from '../../hooks'
+import { AppDispatch } from '../../state'
+import { clearAllTransactions } from '../../state/transactions/actions'
+import { ExternalLink, LinkStyledButton, TYPE } from '../../theme'
+import { getEtherscanLink } from '../../utils'
+import { ButtonEmpty } from '../Button'
+import Identicon from '../Identicon'
+import { AutoRow } from '../Row'
+import Copy from './Copy'
+import Transaction from './Transaction'
const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
padding: 1rem 1rem;
font-weight: 500;
- color: ${(props) =>
- props.color === "blue" ? ({ theme }) => theme.primary1 : "inherit"};
+ color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem;
`};
-`;
+`
const UpperSection = styled.div`
position: relative;
@@ -58,13 +50,13 @@ const UpperSection = styled.div`
margin-top: 0;
font-weight: 500;
}
-`;
+`
const InfoCard = styled.div`
padding: 1rem;
background-color: ${({ theme }) => theme.bg2};
border-radius: 20px;
-`;
+`
const AccountGroupingRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
@@ -81,13 +73,13 @@ const AccountGroupingRow = styled.div`
&:first-of-type {
margin-bottom: 8px;
}
-`;
+`
const AccountSection = styled.div`
background-color: ${({ theme }) => theme.bg1};
padding: 0rem 1rem;
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1rem 1rem;`};
-`;
+`
const YourAccount = styled.div`
h5 {
@@ -99,7 +91,7 @@ const YourAccount = styled.div`
margin: 0;
font-weight: 500;
}
-`;
+`
const GreenCircle = styled.div`
${({ theme }) => theme.flexRowNoWrap}
@@ -114,14 +106,14 @@ const GreenCircle = styled.div`
background-color: ${({ theme }) => theme.green1};
border-radius: 50%;
}
-`;
+`
const CircleWrapper = styled.div`
color: ${({ theme }) => theme.green1};
display: flex;
justify-content: center;
align-items: center;
-`;
+`
const LowerSection = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
@@ -137,7 +129,7 @@ const LowerSection = styled.div`
font-weight: 400;
color: ${({ theme }) => theme.text3};
}
-`;
+`
const AccountControl = styled.div<{ hasENS: boolean; isENS: boolean }>`
${({ theme }) => theme.flexRowNoWrap};
@@ -145,8 +137,7 @@ const AccountControl = styled.div<{ hasENS: boolean; isENS: boolean }>`
min-width: 0;
font-weight: ${({ hasENS, isENS }) => (hasENS ? (isENS ? 500 : 400) : 500)};
- font-size: ${({ hasENS, isENS }) =>
- hasENS ? (isENS ? "1rem" : "0.8rem") : "1rem"};
+ font-size: ${({ hasENS, isENS }) => (hasENS ? (isENS ? '1rem' : '0.8rem') : '1rem')};
a:hover {
text-decoration: underline;
@@ -159,19 +150,19 @@ const AccountControl = styled.div<{ hasENS: boolean; isENS: boolean }>`
text-overflow: ellipsis;
white-space: nowrap;
}
-`;
+`
const ConnectButtonRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
justify-content: center;
margin: 10px 0;
-`;
+`
const AddressLink = styled(ExternalLink)<{ hasENS: boolean; isENS: boolean }>`
color: ${({ hasENS, isENS, theme }) =>
hasENS ? (isENS ? theme.primary1 : theme.text3) : theme.primary1};
-`;
+`
const CloseIcon = styled.div`
position: absolute;
@@ -181,18 +172,18 @@ const CloseIcon = styled.div`
cursor: pointer;
opacity: 0.6;
}
-`;
+`
const CloseColor = styled(Close)`
path {
stroke: ${({ theme }) => theme.text4};
}
-`;
+`
const WalletName = styled.div`
padding-left: 0.5rem;
width: initial;
-`;
+`
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
@@ -200,17 +191,17 @@ const IconWrapper = styled.div<{ size?: number }>`
justify-content: center;
& > img,
span {
- height: ${({ size }) => (size ? size + "px" : "32px")};
- width: ${({ size }) => (size ? size + "px" : "32px")};
+ height: ${({ size }) => (size ? size + 'px' : '32px')};
+ width: ${({ size }) => (size ? size + 'px' : '32px')};
}
${({ theme }) => theme.mediaWidth.upToMedium`
align-items: flex-end;
`};
-`;
+`
const TransactionListWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
-`;
+`
const WalletAction = styled.div`
color: ${({ theme }) => theme.text4};
@@ -220,52 +211,52 @@ const WalletAction = styled.div`
cursor: pointer;
text-decoration: underline;
}
-`;
+`
const MainWalletAction = styled(WalletAction)`
color: ${({ theme }) => theme.primary1};
-`;
+`
function renderTransactions(transactions) {
return (
{transactions.map((hash, i) => {
- return ;
+ return
})}
- );
+ )
}
interface AccountDetailsProps {
- toggleWalletModal: () => void;
- pendingTransactions: any[];
- confirmedTransactions: any[];
- ENSName?: string;
- openOptions: () => void;
+ toggleWalletModal: () => void
+ pendingTransactions: any[]
+ confirmedTransactions: any[]
+ ENSName?: string
+ openOptions: () => void
}
export default function AccountDetails({
- toggleWalletModal,
- pendingTransactions,
- confirmedTransactions,
ENSName,
+ confirmedTransactions,
openOptions,
+ pendingTransactions,
+ toggleWalletModal,
}: AccountDetailsProps) {
- const { chainId, account, connector } = useActiveWeb3React();
- const theme = useContext(ThemeContext);
- const dispatch = useDispatch();
+ const { account, chainId, connector } = useActiveWeb3React()
+ const theme = useContext(ThemeContext)
+ const dispatch = useDispatch()
function formatConnectorName() {
- const { ethereum } = window;
- const isMetaMask = !!(ethereum && ethereum.isMetaMask);
+ const { ethereum } = window
+ const isMetaMask = !!(ethereum && ethereum.isMetaMask)
const name = Object.keys(SUPPORTED_WALLETS)
.filter(
(k) =>
SUPPORTED_WALLETS[k].connector === connector &&
- (connector !== injected || isMetaMask === (k === "METAMASK")),
+ (connector !== injected || isMetaMask === (k === 'METAMASK')),
)
- .map((k) => SUPPORTED_WALLETS[k].name)[0];
- return {name};
+ .map((k) => SUPPORTED_WALLETS[k].name)[0]
+ return {name}
}
function getStatusIcon() {
@@ -274,50 +265,50 @@ export default function AccountDetails({
{formatConnectorName()}
- );
+ )
} else if (connector === walletconnect) {
return (
- {formatConnectorName()}
+ {formatConnectorName()}
- );
+ )
} else if (connector === walletlink) {
return (
- {formatConnectorName()}
+ {formatConnectorName()}
- );
+ )
} else if (connector === fortmatic) {
return (
- {formatConnectorName()}
+ {formatConnectorName()}
- );
+ )
} else if (connector === portis) {
return (
<>
- {formatConnectorName()}
+ {formatConnectorName()}
{
- portis.portis.showPortis();
+ portis.portis.showPortis()
}}
>
Show Portis
>
- );
+ )
}
}
const clearAllTransactionsCallback = useCallback(
(event: React.MouseEvent) => {
- event.preventDefault();
- dispatch(clearAllTransactions({ chainId }));
+ event.preventDefault()
+ dispatch(clearAllTransactions({ chainId }))
},
[dispatch, chainId],
- );
+ )
return (
<>
@@ -335,7 +326,7 @@ export default function AccountDetails({
{connector !== injected && connector !== walletlink && (
{
- (connector as any).close();
+ ;(connector as any).close()
}}
>
Disconnect
@@ -369,8 +360,8 @@ export default function AccountDetails({
View on Etherscan ↗
@@ -381,8 +372,8 @@ export default function AccountDetails({
View on Etherscan ↗
@@ -396,12 +387,12 @@ export default function AccountDetails({
{!(isMobile && (window.web3 || window.ethereum)) && (
{
- openOptions();
+ openOptions()
}}
+ padding={'12px'}
+ style={{ fontWeight: 400 }}
+ width={'260px'}
>
Connect to a different wallet
@@ -411,22 +402,18 @@ export default function AccountDetails({
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
-
+
Recent Transactions
-
- (clear all)
-
+ (clear all)
{renderTransactions(pendingTransactions)}
{renderTransactions(confirmedTransactions)}
) : (
-
- Your transactions will appear here...
-
+ Your transactions will appear here...
)}
>
- );
+ )
}
diff --git a/src/components/AddressInputPanel/index.tsx b/src/components/AddressInputPanel/index.tsx
index 54ba7d07d..7f005601f 100644
--- a/src/components/AddressInputPanel/index.tsx
+++ b/src/components/AddressInputPanel/index.tsx
@@ -1,13 +1,12 @@
-import React, { useState, useEffect, useContext } from "react";
-import styled, { ThemeContext } from "styled-components";
-import useDebounce from "../../hooks/useDebounce";
+import React, { useContext, useEffect, useState } from 'react'
+import styled, { ThemeContext } from 'styled-components'
-import { isAddress } from "../../utils";
-import { useActiveWeb3React } from "../../hooks";
-import { ExternalLink, TYPE } from "../../theme";
-import { AutoColumn } from "../Column";
-import { RowBetween } from "../Row";
-import { getEtherscanLink } from "../../utils";
+import { useActiveWeb3React } from '../../hooks'
+import useDebounce from '../../hooks/useDebounce'
+import { ExternalLink, TYPE } from '../../theme'
+import { getEtherscanLink, isAddress } from '../../utils'
+import { AutoColumn } from '../Column'
+import { RowBetween } from '../Row'
const InputPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
@@ -16,7 +15,7 @@ const InputPanel = styled.div`
background-color: ${({ theme }) => theme.bg1};
z-index: 1;
width: 100%;
-`;
+`
const ContainerRow = styled.div<{ error: boolean }>`
display: flex;
@@ -25,12 +24,12 @@ const ContainerRow = styled.div<{ error: boolean }>`
border-radius: 1.25rem;
border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg2)};
background-color: ${({ theme }) => theme.bg1};
-`;
+`
const InputContainer = styled.div`
flex: 1;
padding: 1rem;
-`;
+`
const Input = styled.input<{ error?: boolean }>`
font-size: 1.25rem;
@@ -62,133 +61,129 @@ const Input = styled.input<{ error?: boolean }>`
::placeholder {
color: ${({ theme }) => theme.text4};
}
-`;
+`
export default function AddressInputPanel({
- initialInput = "",
+ initialInput = '',
onChange,
onError,
}: {
- initialInput?: string;
- onChange: (val: { address: string; name?: string }) => void;
- onError: (error: boolean, input: string) => void;
+ initialInput?: string
+ onChange: (val: { address: string; name?: string }) => void
+ onError: (error: boolean, input: string) => void
}) {
- const { chainId, library } = useActiveWeb3React();
- const theme = useContext(ThemeContext);
+ const { chainId, library } = useActiveWeb3React()
+ const theme = useContext(ThemeContext)
- const [input, setInput] = useState(initialInput ? initialInput : "");
- const debouncedInput = useDebounce(input, 200);
+ const [input, setInput] = useState(initialInput ? initialInput : '')
+ const debouncedInput = useDebounce(input, 200)
const [data, setData] = useState<{ address: string; name: string }>({
address: undefined,
name: undefined,
- });
- const [error, setError] = useState(false);
+ })
+ const [error, setError] = useState(false)
// keep data and errors in sync
useEffect(() => {
- onChange({ address: data.address, name: data.name });
- }, [onChange, data.address, data.name]);
+ onChange({ address: data.address, name: data.name })
+ }, [onChange, data.address, data.name])
useEffect(() => {
- onError(error, input);
- }, [onError, error, input]);
+ onError(error, input)
+ }, [onError, error, input])
// run parser on debounced input
useEffect(() => {
- let stale = false;
+ let stale = false
// if the input is an address, try to look up its name
if (isAddress(debouncedInput)) {
library
.lookupAddress(debouncedInput)
.then((name) => {
- if (stale) return;
+ if (stale) return
// if an ENS name exists, set it as the destination
if (name) {
- setInput(name);
+ setInput(name)
} else {
- setData({ address: debouncedInput, name: "" });
- setError(null);
+ setData({ address: debouncedInput, name: '' })
+ setError(null)
}
})
.catch(() => {
- if (stale) return;
- setData({ address: debouncedInput, name: "" });
- setError(null);
- });
+ if (stale) return
+ setData({ address: debouncedInput, name: '' })
+ setError(null)
+ })
}
// otherwise try to look up the address of the input, treated as an ENS name
else {
- if (debouncedInput !== "") {
+ if (debouncedInput !== '') {
library
.resolveName(debouncedInput)
.then((address) => {
- if (stale) return;
+ if (stale) return
// if the debounced input name resolves to an address
if (address) {
- setData({ address: address, name: debouncedInput });
- setError(null);
+ setData({ address: address, name: debouncedInput })
+ setError(null)
} else {
- setError(true);
+ setError(true)
}
})
.catch(() => {
- if (stale) return;
- setError(true);
- });
- } else if (debouncedInput === "") {
- setError(true);
+ if (stale) return
+ setError(true)
+ })
+ } else if (debouncedInput === '') {
+ setError(true)
}
}
return () => {
- stale = true;
- };
- }, [debouncedInput, library]);
+ stale = true
+ }
+ }, [debouncedInput, library])
function onInput(event) {
- setData({ address: undefined, name: undefined });
- setError(false);
- const input = event.target.value;
- const checksummedInput = isAddress(input.replace(/\s/g, "")); // delete whitespace
- setInput(checksummedInput || input);
+ setData({ address: undefined, name: undefined })
+ setError(false)
+ const input = event.target.value
+ const checksummedInput = isAddress(input.replace(/\s/g, '')) // delete whitespace
+ setInput(checksummedInput || input)
}
return (
-
+
-
+
Recipient
{data.address && (
(View on Etherscan)
)}
- );
+ )
}
diff --git a/src/components/AllAuctionsTable/index.tsx b/src/components/AllAuctionsTable/index.tsx
index 980bd7fdb..e3a92f265 100644
--- a/src/components/AllAuctionsTable/index.tsx
+++ b/src/components/AllAuctionsTable/index.tsx
@@ -1,6 +1,7 @@
-import React, { useMemo } from "react";
-import { useTable, useGlobalFilter } from "react-table";
-import styled from "styled-components";
+import React, { useMemo } from 'react'
+import styled from 'styled-components'
+
+import { useGlobalFilter, useTable } from 'react-table'
const Styles = styled.div`
padding: 1rem;
@@ -22,91 +23,88 @@ const Styles = styled.div`
border-bottom: 1px solid black;
}
}
-`;
+`
// value and onChange function
const GlobalFilter = ({ globalFilter, setGlobalFilter }: any) => {
return (
{
- setGlobalFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
+ setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`Search All ...`}
+ value={globalFilter || ''}
/>
- );
-};
+ )
+}
export default function DatatablePage(allAuctions: any[]) {
const columns = useMemo(
() => [
{
- Header: "Pair",
- accessor: "symbol",
+ Header: 'Pair',
+ accessor: 'symbol',
minWidth: 300,
},
{
- Header: "#AuctionId",
- accessor: "auctionId",
+ Header: '#AuctionId',
+ accessor: 'auctionId',
minWidth: 50,
},
{
- Header: "Network",
- accessor: "chainId",
+ Header: 'Network',
+ accessor: 'chainId',
minWidth: 50,
},
{
- Header: "Selling",
- accessor: "selling",
+ Header: 'Selling',
+ accessor: 'selling',
minWidth: 50,
},
{
- Header: "Buying",
- accessor: "buying",
+ Header: 'Buying',
+ accessor: 'buying',
minWidth: 50,
},
{
- Header: "Status",
- accessor: "status",
+ Header: 'Status',
+ accessor: 'status',
minWidth: 100,
},
{
- Header: "End date",
- accessor: "date",
+ Header: 'End date',
+ accessor: 'date',
minWidth: 50,
},
{
- Header: "Link",
- accessor: "link",
+ Header: 'Link',
+ accessor: 'link',
minWidth: 50,
},
],
[],
- );
- const data = useMemo(() => Object.values(allAuctions), [allAuctions]);
+ )
+ const data = useMemo(() => Object.values(allAuctions), [allAuctions])
const {
- getTableProps,
getTableBodyProps,
+ getTableProps,
headerGroups,
- rows,
prepareRow,
- state,
+ rows,
setGlobalFilter,
+ state,
} = useTable(
{
columns,
data,
},
useGlobalFilter,
- );
+ )
return (
<>
-
+
@@ -115,7 +113,7 @@ export default function DatatablePage(allAuctions: any[]) {
{headerGroup.headers.map((column, j) => (
- {column.render("Header")}
+ {column.render('Header')}
|
))}
@@ -123,22 +121,22 @@ export default function DatatablePage(allAuctions: any[]) {
{rows.map((row, i) => {
- prepareRow(row);
+ prepareRow(row)
return (
{row.cells.map((cell, j) => {
return (
- {cell.render("Cell")}
+ {cell.render('Cell')}
|
- );
+ )
})}
- );
+ )
})}
>
- );
+ )
}
diff --git a/src/components/AuctionDetails/index.tsx b/src/components/AuctionDetails/index.tsx
deleted file mode 100644
index f522e3f51..000000000
--- a/src/components/AuctionDetails/index.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-import React, { useMemo } from "react";
-import styled from "styled-components";
-import { ExternalLink } from "../../theme";
-import {
- useDerivedAuctionInfo,
- AuctionState,
- useDerivedAuctionState,
- orderToPrice,
- orderToSellOrder,
-} from "../../state/orderPlacement/hooks";
-
-import { OrderBookBtn } from "../OrderbookBtn";
-import { getEtherscanLink, getTokenDisplay } from "../../utils";
-import { useActiveWeb3React } from "../../hooks";
-import { useClearingPriceInfo } from "../../hooks/useCurrentClearingOrderAndVolumeCallback";
-
-const Wrapper = styled.div`
- position: relative;
- width: calc(60% - 8px);
- background: none;
- box-shadow: none;
- border-radius: 20px;
- padding: 0px;
- flex: 0 1 auto;
- box-sizing: border-box;
- display: flex;
- flex-flow: column wrap;
- align-items: center;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- width: 100%;
- `};
-`;
-
-const Details = styled.div`
- color: ${({ theme }) => theme.text1};
- font-size: 13px;
- font-weight: normal;
- align-items: center;
- margin-right: auto;
- margin-left: auto;
- width: 100%;
- display: flex;
- flex-flow: column wrap;
- padding: 16px;
- margin: 16px 0 0;
- border-radius: 20px;
- border: 1px solid ${({ theme }) => theme.bg2};
-`;
-
-const Row = styled.span`
- flex-flow: row-wrap;
- width: 100%;
- justify-content: space-between;
- align-items: flex;
- margin: 0 0 4px 0;
- font-weight: normal;
- display: grid;
- grid-template-columns: 1fr 1fr;
- grid-template-areas: "label value";
-
- > i {
- color: ${({ theme }) => theme.text3};
- font-style: normal;
- text-align: left;
- }
-
- > a {
- text-align: right;
- }
-
- > p {
- margin: 0;
- padding: 0;
- text-align: right;
- white-space: normal;
- }
-`;
-
-export default function AuctionDetails() {
- const { chainId } = useActiveWeb3React();
- const {
- auctioningToken,
- biddingToken,
- clearingPrice,
- initialAuctionOrder,
- initialPrice,
- } = useDerivedAuctionInfo();
- const { auctionState } = useDerivedAuctionState();
-
- const auctionTokenAddress = useMemo(
- () => getEtherscanLink(chainId, auctioningToken?.address, "address"),
- [chainId, auctioningToken],
- );
-
- const biddingTokenAddress = useMemo(
- () => getEtherscanLink(chainId, biddingToken?.address, "address"),
- [chainId, biddingToken],
- );
-
- const clearingPriceInfo = useClearingPriceInfo();
-
- const biddingTokenDisplay = useMemo(() => getTokenDisplay(biddingToken), [
- biddingToken,
- ]);
-
- const auctioningTokenDisplay = useMemo(
- () => getTokenDisplay(auctioningToken),
- [auctioningToken],
- );
-
- const clearingPriceDisplay = useMemo(() => {
- const clearingPriceInfoAsSellOrder =
- clearingPriceInfo &&
- orderToSellOrder(
- clearingPriceInfo.clearingOrder,
- biddingToken,
- auctioningToken,
- );
- let clearingPriceNumber = orderToPrice(
- clearingPriceInfoAsSellOrder,
- )?.toSignificant(4);
-
- if (clearingPrice) {
- clearingPriceNumber = clearingPrice && clearingPrice.toSignificant(4);
- }
-
- return !!clearingPriceNumber
- ? `${clearingPriceNumber} ${getTokenDisplay(
- biddingToken,
- )} per ${getTokenDisplay(auctioningToken)}`
- : "-";
- }, [auctioningToken, biddingToken, clearingPrice, clearingPriceInfo]);
-
- const titlePrice = useMemo(
- () =>
- auctionState == AuctionState.ORDER_PLACING ||
- auctionState == AuctionState.ORDER_PLACING_AND_CANCELING
- ? "Current price"
- : auctionState == AuctionState.PRICE_SUBMISSION
- ? "Clearing price"
- : "Closing price",
- [auctionState],
- );
-
- return (
-
-
-
-
-
- {titlePrice}
-
- {clearingPriceDisplay}
-
-
- Bidding with
-
- {biddingTokenDisplay} ↗
-
-
-
-
- Total auctioned
-
- {initialAuctionOrder?.sellAmount.toSignificant(2)}{" "}
-
- {auctioningTokenDisplay} ↗
-
-
-
-
-
- Min. sell price
-
- {initialPrice ? `${initialPrice?.toSignificant(2)} ` : " - "}
- {biddingTokenDisplay} per {auctioningTokenDisplay}
-
-
-
-
- );
-}
diff --git a/src/components/AuctionHeader/index.tsx b/src/components/AuctionHeader/index.tsx
deleted file mode 100644
index 2a418c095..000000000
--- a/src/components/AuctionHeader/index.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import React from "react";
-import {
- AuctionState,
- SellOrder,
- useDerivedAuctionInfo,
- useDerivedAuctionState,
-} from "../../state/orderPlacement/hooks";
-import styled from "styled-components";
-import CountdownTimer from "../CountDown";
-import { Token } from "uniswap-xdai-sdk";
-import { getTokenDisplay } from "../../utils";
-
-const Wrapper = styled.div`
- display: flex;
- width: 100%;
- align-content: center;
- text-align: center;
- flex-flow: row nowrap;
- justify-content: space-between;
- margin: 0;
- background: ${({ theme }) => theme.bg2};
- border-radius: 20px;
- padding: 16px;
- box-sizing: border-box;
- margin: 0 0 16px;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- flex-flow: column wrap;
- `};
-
- > h3 {
- flex: 1 1 auto;
- display: flex;
- text-align: center;
- align-items: center;
- margin: 0 auto;
- font-weight: normal;
- }
-
- > h4 {
- flex: 1 1 auto;
- display: flex;
- text-align: center;
- align-items: center;
- margin: 0 auto;
- font-size: 18px;
- font-weight: normal;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- margin: 0;
- text-align: center;
- justify-content: center;
- `};
- }
-
- > h5 {
- width: 100%;
- margin: auto;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- min-height: 150px;
- }
-
- > h4 > b {
- margin: 0 5px;
- }
-`;
-
-const renderAuctionStatus = ({
- auctionState,
- auctioningToken,
- initialAuctionOrder,
-}: {
- auctionState: AuctionState;
- auctioningToken: Token | null;
- initialAuctionOrder: SellOrder | null;
-}) => {
- switch (auctionState) {
- case AuctionState.ORDER_PLACING:
- case AuctionState.ORDER_PLACING_AND_CANCELING:
- return (
-
- Selling
-
- {initialAuctionOrder?.sellAmount.toSignificant(2)}{" "}
- {getTokenDisplay(auctioningToken)}
-
-
- );
-
- case AuctionState.PRICE_SUBMISSION:
- return 🗓 Auction closed. Pending on-chain price-calculation.
;
-
- default:
- return 🏁 Auction is settled
;
- }
-};
-
-export function AuctionHeaderForScheduledAuction() {
- const {
- auctioningToken,
- initialAuctionOrder,
- auctionEndDate,
- } = useDerivedAuctionInfo();
- const { auctionState } = useDerivedAuctionState();
-
- return (
- <>
- {renderAuctionStatus({
- auctioningToken,
- auctionState,
- initialAuctionOrder,
- })}
-
- >
- );
-}
-
-export default function AuctionHeader() {
- const { auctionState } = useDerivedAuctionState();
- return (
-
- {auctionState == undefined ? (
- ⌛ Loading
- ) : auctionState == AuctionState.NOT_YET_STARTED ? (
- ⌛ Auction not yet started
- ) : (
-
- )}
-
- );
-}
diff --git a/src/components/AuctionInfoCard/index.tsx b/src/components/AuctionInfoCard/index.tsx
deleted file mode 100644
index 00732505a..000000000
--- a/src/components/AuctionInfoCard/index.tsx
+++ /dev/null
@@ -1,191 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-import { AuctionInfo } from "../../hooks/useAllAuctionInfos";
-import DoubleLogo from "../DoubleLogo";
-import { ButtonLight } from "../Button";
-import { useHistory } from "react-router-dom";
-import CountdownTimer from "../CountDown";
-
-const HeaderWrapper = styled.div`
- display: flex;
- width: 100%;
- align-content: center;
- text-align: center;
- flex-flow: row nowrap;
- justify-content: space-between;
- margin: 0;
- background: ${({ theme }) => theme.bg2};
- border-radius: 20px;
- padding: 16px;
- box-sizing: border-box;
- margin: 0 0 16px;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- flex-flow: column wrap;
- `};
-
- > h3 {
- flex: 1 1 auto;
- display: flex;
- text-align: center;
- align-items: center;
- margin: 0 auto;
- font-weight: normal;
- }
-
- > h4 {
- flex: 1 1 auto;
- display: flex;
- text-align: center;
- align-items: center;
- margin: 0 auto;
- font-size: 18px;
- font-weight: normal;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- margin: 0;
- text-align: center;
- justify-content: center;
- `};
- }
-
- > h5 {
- width: 100%;
- margin: auto;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- min-height: 150px;
- }
-
- > h4 > b {
- margin: 0 5px;
- }
-`;
-
-const Wrapper = styled.div`
- position: relative;
- width: calc(50% - 8px);
- background: none;
- box-shadow: none;
- border-radius: 20px;
- padding: 0px;
- flex: 0 1 auto;
- box-sizing: border-box;
- display: flex;
- flex-flow: column wrap;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- width: 100%;
- `};
-`;
-
-const ViewBtn = styled(ButtonLight)`
- background: none;
- height: 100%;
- width: 100%;
- color: ${({ theme }) => theme.text3};
-
- &:hover {
- background: none;
- }
-
- > svg {
- margin: 0 0 0 5px;
- }
-`;
-
-const Details = styled.div`
- color: ${({ theme }) => theme.text1};
- background: ${({ theme }) => theme.bg1};
- font-size: 13px;
- width: 100%;
- height: 100%;
- font-weight: normal;
- display: flex;
- flex-flow: column wrap;
- padding: 16px;
- border-radius: 20px;
- border: 1px solid ${({ theme }) => theme.bg2};
-`;
-
-const Row = styled.span`
- flex-flow: row-wrap;
- width: 100%;
- justify-content: space-between;
- align-items: flex;
- margin: 0 0 4px 0;
- font-weight: normal;
- display: grid;
- grid-template-columns: 1fr 1fr;
- grid-template-areas: "label value";
-
- > i {
- color: ${({ theme }) => theme.text3};
- font-style: normal;
- text-align: left;
- }
-
- > a {
- text-align: right;
- }
-
- > p {
- margin: 0;
- padding: 0;
- text-align: right;
- white-space: normal;
- }
-`;
-
-export default function AuctionInfoCard(auctionInfo: AuctionInfo) {
- const history = useHistory();
-
- function handleClick() {
- history.push(
- `/auction?auctionId=${auctionInfo.auctionId}&chainId=${Number(
- auctionInfo.chainId,
- )}`,
- );
- }
-
- return (
-
-
-
-
-
- Selling {auctionInfo.order.volume + ` `}
- {auctionInfo.symbolAuctioningToken}
-
-
-
-
- Ends in
-
-
-
-
- Min. price
-
- {auctionInfo.order.price} {` ` + auctionInfo.symbolBiddingToken}{" "}
- per {auctionInfo.symbolAuctioningToken}
-
-
-
- Id {auctionInfo.auctionId}
-
-
-
-
- );
-}
diff --git a/src/components/BoxTitle/index.tsx b/src/components/BoxTitle/index.tsx
index 077027aa0..71e6652ce 100644
--- a/src/components/BoxTitle/index.tsx
+++ b/src/components/BoxTitle/index.tsx
@@ -1,4 +1,4 @@
-import styled from "styled-components";
+import styled from 'styled-components'
export const BoxTitle = styled.div`
text-align: center;
@@ -10,4 +10,4 @@ export const BoxTitle = styled.div`
text-decoration: none;
color: ${({ theme }) => theme.text1};
font-size: 20px;
-`;
+`
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 74e776a33..34deed026 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -1,18 +1,18 @@
-import React from "react";
-import styled from "styled-components";
-import { darken, lighten } from "polished";
+import { darken, lighten } from 'polished'
+import React from 'react'
+import { ChevronDown } from 'react-feather'
+import { ButtonProps, Button as RebassButton } from 'rebass/styled-components'
+import styled from 'styled-components'
-import { RowBetween } from "../Row";
-import { ChevronDown } from "react-feather";
-import { Button as RebassButton, ButtonProps } from "rebass/styled-components";
+import { RowBetween } from '../Row'
const Base = styled(RebassButton)<{
- padding?: string;
- width?: string;
- borderRadius?: string;
+ padding?: string
+ width?: string
+ borderRadius?: string
}>`
- padding: ${({ padding }) => (padding ? padding : "18px")};
- width: ${({ width }) => (width ? width : "100%")};
+ padding: ${({ padding }) => (padding ? padding : '18px')};
+ width: ${({ width }) => (width ? width : '100%')};
font-weight: 500;
text-align: center;
border-radius: 20px;
@@ -32,14 +32,14 @@ const Base = styled(RebassButton)<{
> * {
user-select: none;
}
-`;
+`
const BaseSmall = styled(RebassButton)<{
- padding?: string;
- width?: string;
- borderRadius?: string;
+ padding?: string
+ width?: string
+ borderRadius?: string
}>`
- padding: ${({ padding }) => (padding ? padding : "5px")};
- width: ${({ width }) => (width ? width : "100%")};
+ padding: ${({ padding }) => (padding ? padding : '5px')};
+ width: ${({ width }) => (width ? width : '100%')};
font-weight: 500;
text-align: center;
border-radius: 5px;
@@ -59,7 +59,7 @@ const BaseSmall = styled(RebassButton)<{
> * {
user-select: none;
}
-`;
+`
export const ButtonPrimary = styled(Base)`
background-image: ${({ theme }) =>
@@ -87,7 +87,7 @@ export const ButtonPrimary = styled(Base)`
outline: none;
opacity: 1;
}
-`;
+`
export const ButtonCancelPrimary = styled(BaseSmall)`
background-image: ${({ theme }) =>
@@ -112,7 +112,7 @@ export const ButtonCancelPrimary = styled(BaseSmall)`
box-shadow: none;
border: 1px solid ${({ theme }) => theme.bg2};
}
-`;
+`
export const ButtonLight = styled(Base)`
background-color: ${({ theme }) => theme.primary5};
@@ -120,22 +120,17 @@ export const ButtonLight = styled(Base)`
font-size: 16px;
font-weight: 500;
&:focus {
- box-shadow: 0 0 0 1pt
- ${({ theme, disabled }) => !disabled && darken(0.03, theme.primary5)};
- background-color: ${({ theme, disabled }) =>
- !disabled && darken(0.03, theme.primary5)};
+ box-shadow: 0 0 0 1pt ${({ disabled, theme }) => !disabled && darken(0.03, theme.primary5)};
+ background-color: ${({ disabled, theme }) => !disabled && darken(0.03, theme.primary5)};
}
&:hover {
- background-color: ${({ theme, disabled }) =>
- !disabled && darken(0.03, theme.primary5)};
+ background-color: ${({ disabled, theme }) => !disabled && darken(0.03, theme.primary5)};
}
&:active {
- box-shadow: 0 0 0 1pt
- ${({ theme, disabled }) => !disabled && darken(0.05, theme.primary5)};
- background-color: ${({ theme, disabled }) =>
- !disabled && darken(0.05, theme.primary5)};
+ box-shadow: 0 0 0 1pt ${({ disabled, theme }) => !disabled && darken(0.05, theme.primary5)};
+ background-color: ${({ disabled, theme }) => !disabled && darken(0.05, theme.primary5)};
}
-`;
+`
export const ButtonGray = styled(Base)`
background-color: ${({ theme }) => theme.bg3};
@@ -143,29 +138,24 @@ export const ButtonGray = styled(Base)`
font-size: 16px;
font-weight: 500;
&:focus {
- box-shadow: 0 0 0 1pt
- ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
- background-color: ${({ theme, disabled }) =>
- !disabled && darken(0.05, theme.bg2)};
+ box-shadow: 0 0 0 1pt ${({ disabled, theme }) => !disabled && darken(0.05, theme.bg2)};
+ background-color: ${({ disabled, theme }) => !disabled && darken(0.05, theme.bg2)};
}
&:hover {
- background-color: ${({ theme, disabled }) =>
- !disabled && darken(0.05, theme.bg2)};
+ background-color: ${({ disabled, theme }) => !disabled && darken(0.05, theme.bg2)};
}
&:active {
- box-shadow: 0 0 0 1pt
- ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
- background-color: ${({ theme, disabled }) =>
- !disabled && darken(0.1, theme.bg2)};
+ box-shadow: 0 0 0 1pt ${({ disabled, theme }) => !disabled && darken(0.1, theme.bg2)};
+ background-color: ${({ disabled, theme }) => !disabled && darken(0.1, theme.bg2)};
}
-`;
+`
export const ButtonSecondary = styled(Base)`
background-color: ${({ theme }) => theme.primary5};
color: ${({ theme }) => theme.primaryText1};
font-size: 16px;
border-radius: 8px;
- padding: ${({ padding }) => (padding ? padding : "10px")};
+ padding: ${({ padding }) => (padding ? padding : '10px')};
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4};
@@ -183,7 +173,7 @@ export const ButtonSecondary = styled(Base)`
opacity: 50%;
cursor: auto;
}
-`;
+`
export const ButtonPink = styled(Base)`
background-color: ${({ theme }) => theme.primary1};
@@ -205,7 +195,7 @@ export const ButtonPink = styled(Base)`
opacity: 50%;
cursor: auto;
}
-`;
+`
export const ButtonOutlined = styled(Base)`
border: 1px solid ${({ theme }) => theme.bg2};
@@ -225,7 +215,7 @@ export const ButtonOutlined = styled(Base)`
opacity: 50%;
cursor: auto;
}
-`;
+`
export const ButtonEmpty = styled(Base)`
background-color: transparent;
@@ -247,7 +237,7 @@ export const ButtonEmpty = styled(Base)`
opacity: 50%;
cursor: auto;
}
-`;
+`
export const ButtonWhite = styled(Base)`
border: 1px solid #edeef2;
@@ -256,19 +246,19 @@ export const ButtonWhite = styled(Base)`
&:focus {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- box-shadow: 0 0 0 1pt ${darken(0.05, "#edeef2")};
+ box-shadow: 0 0 0 1pt ${darken(0.05, '#edeef2')};
}
&:hover {
- box-shadow: 0 0 0 1pt ${darken(0.1, "#edeef2")};
+ box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
}
&:active {
- box-shadow: 0 0 0 1pt ${darken(0.1, "#edeef2")};
+ box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
}
&:disabled {
opacity: 50%;
cursor: auto;
}
-`;
+`
const ButtonConfirmedStyle = styled(Base)`
background-color: ${({ theme }) => lighten(0.5, theme.green1)};
@@ -279,7 +269,7 @@ const ButtonConfirmedStyle = styled(Base)`
opacity: 50%;
cursor: auto;
}
-`;
+`
const ButtonErrorStyle = styled(Base)`
background-color: ${({ theme }) => theme.red1};
@@ -300,7 +290,7 @@ const ButtonErrorStyle = styled(Base)`
opacity: 50%;
cursor: auto;
}
-`;
+`
const ButtonCancelErrorStyle = styled(BaseSmall)`
background-color: ${({ theme }) => theme.red1};
border: 0.5px solid ${({ theme }) => theme.red1};
@@ -320,78 +310,66 @@ const ButtonCancelErrorStyle = styled(BaseSmall)`
opacity: 50%;
cursor: auto;
}
-`;
+`
-export function ButtonConfirmed({
- confirmed,
- ...rest
-}: { confirmed?: boolean } & ButtonProps) {
+export function ButtonConfirmed({ confirmed, ...rest }: { confirmed?: boolean } & ButtonProps) {
if (confirmed) {
- return ;
+ return
} else {
- return ;
+ return
}
}
-export function ButtonError({
- error,
- ...rest
-}: { error?: boolean } & ButtonProps) {
+export function ButtonError({ error, ...rest }: { error?: boolean } & ButtonProps) {
if (error) {
- return ;
+ return
} else {
- return ;
+ return
}
}
-export function ButtonCancel({
- error,
- ...rest
-}: { error?: boolean } & ButtonProps) {
+export function ButtonCancel({ error, ...rest }: { error?: boolean } & ButtonProps) {
if (error) {
- return ;
+ return
} else {
- return ;
+ return
}
}
export function ButtonDropwdown({
- disabled = false,
children,
+ disabled = false,
...rest
}: { disabled?: boolean } & ButtonProps) {
return (
- {children}
+ {children}
- );
+ )
}
export function ButtonDropwdownLight({
- disabled = false,
children,
+ disabled = false,
...rest
}: { disabled?: boolean } & ButtonProps) {
return (
- {children}
+ {children}
- );
+ )
}
-export function ButtonRadio({
- active,
- ...rest
-}: { active?: boolean } & ButtonProps) {
+export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonProps) {
if (!active) {
- return ;
+ return
} else {
- return ;
+ return
}
}
diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx
index 615403078..624316c23 100644
--- a/src/components/Card/index.tsx
+++ b/src/components/Card/index.tsx
@@ -1,12 +1,13 @@
-import React from "react";
-import styled from "styled-components";
-import { CardProps, Text } from "rebass";
-import { Box } from "rebass/styled-components";
+import React from 'react'
+import { Box } from 'rebass/styled-components'
+import styled from 'styled-components'
+
+import { CardProps, Text } from 'rebass'
const Card = styled(Box)<{
- padding?: string;
- border?: string;
- borderRadius?: string;
+ padding?: string
+ border?: string
+ borderRadius?: string
}>`
width: 100%;
border-radius: 16px;
@@ -14,47 +15,47 @@ const Card = styled(Box)<{
padding: ${({ padding }) => padding};
border: ${({ border }) => border};
border-radius: ${({ borderRadius }) => borderRadius};
-`;
-export default Card;
+`
+export default Card
export const LightCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.bg2};
background-color: ${({ theme }) => theme.bg1};
-`;
+`
export const GreyCard = styled(Card)`
background-color: ${({ theme }) => theme.advancedBG};
-`;
+`
export const OutlineCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.advancedBG};
-`;
+`
export const YellowCard = styled(Card)`
background-color: rgba(243, 132, 30, 0.05);
color: ${({ theme }) => theme.yellow2};
font-weight: 500;
-`;
+`
export const PinkCard = styled(Card)`
background-color: rgba(255, 0, 122, 0.03);
color: ${({ theme }) => theme.primary1};
font-weight: 500;
-`;
+`
const BlueCardStyled = styled(Card)`
background-color: ${({ theme }) => theme.primary5};
color: ${({ theme }) => theme.primary1};
border-radius: 12px;
width: fit-content;
-`;
+`
export const BlueCard = ({ children, ...rest }: CardProps) => {
return (
-
+
{children}
- );
-};
+ )
+}
diff --git a/src/components/ClaimConfirmationModal/index.tsx b/src/components/ClaimConfirmationModal/index.tsx
index ed85ac6d8..6f5924173 100644
--- a/src/components/ClaimConfirmationModal/index.tsx
+++ b/src/components/ClaimConfirmationModal/index.tsx
@@ -1,49 +1,50 @@
-import React, { useContext } from "react";
-import styled, { ThemeContext } from "styled-components";
-import Modal from "../Modal";
-import Loader from "../Loader";
-import { ExternalLink } from "../../theme";
-import { Text } from "rebass";
-import { CloseIcon } from "../../theme/components";
-import { RowBetween } from "../Row";
-import { ArrowUpCircle } from "react-feather";
-import { ButtonPrimary } from "../Button";
-import { AutoColumn, ColumnCenter } from "../Column";
+import React, { useContext } from 'react'
+import { ArrowUpCircle } from 'react-feather'
+import styled, { ThemeContext } from 'styled-components'
-import { useActiveWeb3React } from "../../hooks";
-import { getEtherscanLink } from "../../utils";
+import { Text } from 'rebass'
+
+import { useActiveWeb3React } from '../../hooks'
+import { ExternalLink } from '../../theme'
+import { CloseIcon } from '../../theme/components'
+import { getEtherscanLink } from '../../utils'
+import { ButtonPrimary } from '../Button'
+import { AutoColumn, ColumnCenter } from '../Column'
+import Loader from '../Loader'
+import { RowBetween } from '../Row'
+import Modal from '../modals/Modal'
const Wrapper = styled.div`
width: 100%;
-`;
+`
const Section = styled(AutoColumn)`
padding: 24px;
-`;
+`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
-`;
+`
interface ConfirmationModalProps {
- isOpen: boolean;
- onDismiss: () => void;
- hash: string;
- pendingConfirmation: boolean;
- pendingText: string;
+ isOpen: boolean
+ onDismiss: () => void
+ hash: string
+ pendingConfirmation: boolean
+ pendingText: string
}
export default function ClaimConfirmationModal({
+ hash,
isOpen,
onDismiss,
- hash,
pendingConfirmation,
pendingText,
}: ConfirmationModalProps) {
- const { chainId } = useActiveWeb3React();
- const theme = useContext(ThemeContext);
+ const { chainId } = useActiveWeb3React()
+ const theme = useContext(ThemeContext)
return (
-
+
{
@@ -55,43 +56,27 @@ export default function ClaimConfirmationModal({
{pendingConfirmation ? (
) : (
-
+
)}
-
-
- {!pendingConfirmation
- ? "Transaction Submitted"
- : "Waiting For Confirmation"}
+
+
+ {!pendingConfirmation ? 'Transaction Submitted' : 'Waiting For Confirmation'}
-
-
+
+
{pendingText}
{!pendingConfirmation && (
<>
-
-
+
+
View on Etherscan
-
-
+
+
Close
@@ -99,7 +84,7 @@ export default function ClaimConfirmationModal({
)}
{pendingConfirmation && (
-
+
Confirm this transaction in your wallet
)}
@@ -108,5 +93,5 @@ export default function ClaimConfirmationModal({
}
- );
+ )
}
diff --git a/src/components/Claimer/index.tsx b/src/components/Claimer/index.tsx
deleted file mode 100644
index fe735ac31..000000000
--- a/src/components/Claimer/index.tsx
+++ /dev/null
@@ -1,156 +0,0 @@
-import React, { useState, useMemo } from "react";
-import { Text } from "rebass";
-import {
- ButtonPrimary,
- ButtonError,
- ButtonLight,
-} from "../../components/Button";
-import { BottomGrouping, Wrapper } from "../swap/styleds";
-import ClaimConfirmationModal from "../ClaimConfirmationModal";
-import styled from "styled-components";
-
-import { useActiveWeb3React } from "../../hooks";
-import {
- useClaimOrderCallback,
- useGetAuctionProceeds,
-} from "../../hooks/useClaimOrderCallback";
-import { useWalletModalToggle } from "../../state/application/hooks";
-import {
- useDerivedClaimInfo,
- useDerivedAuctionInfo,
- useSwapState,
-} from "../../state/orderPlacement/hooks";
-import TokenLogo from "../TokenLogo";
-import { getTokenDisplay } from "../../utils";
-
-export const AuctionTokenWrapper = styled.div`
- width: 100%;
- display: flex;
- flex-flow: column wrap;
- justify-content: center;
- box-sizing: border-box;
- padding: 0 0 16px;
-`;
-
-export const AuctionToken = styled.div`
- display: flex;
- padding: 0;
- margin: 0 0 10px;
- box-sizing: border-box;
- align-items: center;
-
- > img {
- margin: 0 10px 0 0;
- }
-`;
-
-export default function Claimer() {
- const { account } = useActiveWeb3React();
-
- // toggle wallet when disconnected
- const toggleWalletModal = useWalletModalToggle();
-
- // swap state
- const { auctionId } = useSwapState();
- const { biddingToken, auctioningToken } = useDerivedAuctionInfo();
- const { error } = useDerivedClaimInfo(auctionId);
-
- const isValid = !error;
- // modal and loading
- const [showConfirm, setShowConfirm] = useState(false);
- const [pendingConfirmation, setPendingConfirmation] = useState(true); // waiting for user confirmation
-
- const {
- claimableBiddingToken,
- claimableAuctioningToken,
- } = useGetAuctionProceeds();
-
- // txn values
- const [txHash, setTxHash] = useState("");
-
- // reset modal state when closed
- function resetModal() {
- setPendingConfirmation(true);
- }
-
- // the callback to execute the swap
- const claimOrderCallback = useClaimOrderCallback();
-
- function onClaimOrder() {
- claimOrderCallback().then((hash) => {
- setTxHash(hash);
- setPendingConfirmation(false);
- });
- }
-
- // text to show while loading
- const pendingText = `Claiming Funds`;
-
- const biddingTokenDisplay = useMemo(() => getTokenDisplay(biddingToken), [
- biddingToken,
- ]);
-
- const auctioningTokenDisplay = useMemo(
- () => getTokenDisplay(auctioningToken),
- [auctioningToken],
- );
- return (
- <>
-
-
-
-
-
- {claimableBiddingToken
- ? claimableBiddingToken.toSignificant(2)
- : `0 ${biddingTokenDisplay}`}
-
-
-
-
-
-
- {claimableAuctioningToken
- ? claimableAuctioningToken.toSignificant(2)
- : `0 ${auctioningTokenDisplay}`}
-
-
-
-
- {
- resetModal();
- setShowConfirm(false);
- }}
- pendingConfirmation={pendingConfirmation}
- hash={txHash}
- pendingText={pendingText}
- />
-
- {!account ? (
-
- Connect Wallet
-
- ) : error ? (
- {error}
- ) : (
- {
- setShowConfirm(true);
- onClaimOrder();
- }}
- id="swap-button"
- disabled={!isValid}
- error={isValid}
- >
-
- {error ?? `Claim Funds`}
-
-
- )}
-
-
- >
- );
-}
diff --git a/src/components/Column/index.tsx b/src/components/Column/index.tsx
index 9ea8eefa1..d5812d102 100644
--- a/src/components/Column/index.tsx
+++ b/src/components/Column/index.tsx
@@ -1,34 +1,24 @@
-import styled from "styled-components";
+import styled from 'styled-components'
const Column = styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
-`;
+`
export const ColumnCenter = styled(Column)`
width: 100%;
align-items: center;
-`;
+`
export const AutoColumn = styled.div<{
- gap?: "sm" | "md" | "lg" | string;
- justify?:
- | "stretch"
- | "center"
- | "start"
- | "end"
- | "flex-start"
- | "flex-end"
- | "space-between";
+ gap?: 'sm' | 'md' | 'lg' | string
+ justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'space-between'
}>`
display: grid;
grid-auto-rows: auto;
grid-row-gap: ${({ gap }) =>
- (gap === "sm" && "8px") ||
- (gap === "md" && "12px") ||
- (gap === "lg" && "24px") ||
- gap};
+ (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap};
justify-items: ${({ justify }) => justify && justify};
-`;
+`
-export default Column;
+export default Column
diff --git a/src/components/ConfirmationModal/index.tsx b/src/components/ConfirmationModal/index.tsx
index f7692ebf4..0a99169fe 100644
--- a/src/components/ConfirmationModal/index.tsx
+++ b/src/components/ConfirmationModal/index.tsx
@@ -1,68 +1,69 @@
-import React, { useContext } from "react";
-import styled, { ThemeContext } from "styled-components";
-import Modal from "../Modal";
-import Loader from "../Loader";
-import { ExternalLink } from "../../theme";
-import { Text } from "rebass";
-import { CloseIcon } from "../../theme/components";
-import { RowBetween } from "../Row";
-import { ArrowUpCircle } from "react-feather";
-import { ButtonPrimary } from "../Button";
-import { AutoColumn, ColumnCenter } from "../Column";
+import React, { useContext } from 'react'
+import { ArrowUpCircle } from 'react-feather'
+import styled, { ThemeContext } from 'styled-components'
-import { useActiveWeb3React } from "../../hooks";
-import { getEtherscanLink } from "../../utils";
+import { Text } from 'rebass'
+
+import { useActiveWeb3React } from '../../hooks'
+import { ExternalLink } from '../../theme'
+import { CloseIcon } from '../../theme/components'
+import { getEtherscanLink } from '../../utils'
+import { ButtonPrimary } from '../Button'
+import { AutoColumn, ColumnCenter } from '../Column'
+import Loader from '../Loader'
+import { RowBetween } from '../Row'
+import Modal from '../modals/Modal'
const Wrapper = styled.div`
width: 100%;
-`;
+`
const Section = styled(AutoColumn)`
padding: 24px;
-`;
+`
const BottomSection = styled(Section)`
background-color: ${({ theme }) => theme.bg2};
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
-`;
+`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
-`;
+`
interface ConfirmationModalProps {
- isOpen: boolean;
- onDismiss: () => void;
- hash: string;
- topContent: () => React.ReactChild;
- bottomContent: () => React.ReactChild;
- attemptingTxn: boolean;
- pendingConfirmation: boolean;
- pendingText: string;
- title?: string;
+ isOpen: boolean
+ onDismiss: () => void
+ hash: string
+ topContent: () => React.ReactChild
+ bottomContent: () => React.ReactChild
+ attemptingTxn: boolean
+ pendingConfirmation: boolean
+ pendingText: string
+ title?: string
}
export default function ConfirmationModal({
+ attemptingTxn,
+ bottomContent,
+ hash,
isOpen,
onDismiss,
- hash,
- topContent,
- bottomContent,
- attemptingTxn,
pendingConfirmation,
pendingText,
- title = "",
+ title = '',
+ topContent,
}: ConfirmationModalProps) {
- const { chainId } = useActiveWeb3React();
- const theme = useContext(ThemeContext);
+ const { chainId } = useActiveWeb3React()
+ const theme = useContext(ThemeContext)
return (
-
+
{!attemptingTxn ? (
-
+
{title}
@@ -82,43 +83,27 @@ export default function ConfirmationModal({
{pendingConfirmation ? (
) : (
-
+
)}
-
-
- {!pendingConfirmation
- ? "Transaction Submitted"
- : "Waiting For Confirmation"}
+
+
+ {!pendingConfirmation ? 'Transaction Submitted' : 'Waiting For Confirmation'}
-
-
+
+
{pendingText}
{!pendingConfirmation && (
<>
-
-
+
+
View on Etherscan
-
-
+
+
Close
@@ -126,7 +111,7 @@ export default function ConfirmationModal({
)}
{pendingConfirmation && (
-
+
Confirm this transaction in your wallet
)}
@@ -135,5 +120,5 @@ export default function ConfirmationModal({
)}
- );
+ )
}
diff --git a/src/components/CountDown/index.tsx b/src/components/CountDown/index.tsx
deleted file mode 100644
index 4eb303d2e..000000000
--- a/src/components/CountDown/index.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import styled from "styled-components";
-import React, { useEffect, useState } from "react";
-
-const CountDownStyled = styled.div`
- display: flex;
- flex: 0 1 auto;
- font-family: var(--font-mono);
- text-align: right;
- font-size: 13px;
- color: ${({ theme }) => `1px solid ${theme.text2}`};
- letter-spacing: 0;
- justify-content: center;
- flex-flow: row wrap;
- align-items: center;
- background: none;
- box-sizing: border-box;
- position: relative;
-
- > p {
- margin: 0 5px 0 0;
- }
-
- > strong {
- color: ${({ theme }) => `1px solid ${theme.text1}`};
- }
-`;
-
-export function formatSeconds(seconds: number): string {
- const days = Math.floor(seconds / 24 / 60 / 60) % 360;
- const hours = Math.floor(seconds / 60 / 60) % 24;
- const minutes = Math.floor(seconds / 60) % 60;
- const remainderSeconds = Math.floor(seconds % 60);
- let s = "";
-
- if (days > 0) {
- s += `${days}d `;
- }
- if (hours > 0) {
- s += `${hours}h `;
- }
- if (minutes > 0) {
- s += `${minutes}m `;
- }
- if (remainderSeconds > 0 && hours < 2) {
- s += `${remainderSeconds}s`;
- }
- if (minutes === 0 && remainderSeconds === 0) {
- s = "0s";
- }
-
- return s;
-}
-
-const calculateTimeLeft = (auctionEndDate) => {
- const diff = auctionEndDate - Date.now() / 1000;
- if (diff < 0) return 0;
- return diff;
-};
-
-export default function CountdownTimer({
- auctionEndDate,
- showText,
-}: {
- auctionEndDate: number;
- showText: boolean;
-}) {
- const [timeLeft, setTimeLeft] = useState(calculateTimeLeft(auctionEndDate));
-
- useEffect(() => {
- let mounted = true;
- setTimeout(() => {
- if (mounted) setTimeLeft(calculateTimeLeft(auctionEndDate));
- }, 1000);
-
- return () => (mounted = false);
- });
-
- return timeLeft && timeLeft > 0 ? (
-
- {showText ? Auction ends in
: <>>}
- {formatSeconds(timeLeft)}
-
- ) : null;
-}
diff --git a/src/components/CurrencyInputPanel/index.tsx b/src/components/CurrencyInputPanel/index.tsx
deleted file mode 100644
index 5bd66cebd..000000000
--- a/src/components/CurrencyInputPanel/index.tsx
+++ /dev/null
@@ -1,239 +0,0 @@
-import { Pair, Token } from "uniswap-xdai-sdk";
-import React, { useContext } from "react";
-import styled, { ThemeContext } from "styled-components";
-import { darken } from "polished";
-import { useTokenBalance } from "../../state/wallet/hooks";
-import TokenLogo from "../TokenLogo";
-import DoubleLogo from "../DoubleLogo";
-import { RowBetween } from "../Row";
-import { TYPE, CursorPointer } from "../../theme";
-import { Input as NumericalInput } from "../NumericalInput";
-
-import { useActiveWeb3React } from "../../hooks";
-import { useTranslation } from "react-i18next";
-
-const InputRow = styled.div<{ selected: boolean }>`
- ${({ theme }) => theme.flexRowNoWrap}
- align-items: center;
- padding: ${({ selected }) =>
- selected ? "0.75rem 0.5rem 0.75rem 1rem" : "0.75rem 0.75rem 0.75rem 1rem"};
-`;
-
-const CurrencySelect = styled.button<{ selected: boolean }>`
- align-items: center;
- height: 2.2rem;
- font-size: 20px;
- font-weight: 500;
- background-color: ${({ selected, theme }) =>
- selected ? theme.bg1 : theme.primary1};
- color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
- border-radius: 12px;
- box-shadow: ${({ selected }) =>
- selected ? "none" : "0px 6px 10px rgba(0, 0, 0, 0.075)"};
- outline: none;
- cursor: pointer;
- user-select: none;
- border: none;
- padding: 0 0.5rem;
-
- :focus,
- :hover {
- background-color: ${({ selected, theme }) =>
- selected ? theme.bg2 : darken(0.05, theme.primary1)};
- }
-`;
-
-const LabelRow = styled.div`
- ${({ theme }) => theme.flexRowNoWrap}
- align-items: center;
- color: ${({ theme }) => theme.text1};
- font-size: 0.75rem;
- line-height: 1rem;
- padding: 0.75rem 1rem 0 1rem;
- height: 20px;
- span:hover {
- cursor: pointer;
- color: ${({ theme }) => darken(0.2, theme.text2)};
- }
-`;
-
-const Aligner = styled.span`
- display: flex;
- align-items: center;
- justify-content: space-between;
-`;
-
-const InputPanel = styled.div<{ hideInput?: boolean }>`
- ${({ theme }) => theme.flexColumnNoWrap}
- position: relative;
- border-radius: ${({ hideInput }) => (hideInput ? "8px" : "20px")};
- background-color: ${({ theme }) => theme.bg2};
- z-index: 1;
-`;
-
-const Container = styled.div<{ hideInput: boolean }>`
- border-radius: ${({ hideInput }) => (hideInput ? "8px" : "20px")};
- border: 1px solid ${({ theme }) => theme.bg2};
- background-color: ${({ theme }) => theme.bg1};
-`;
-
-const StyledTokenName = styled.span<{ active?: boolean }>`
- ${({ active }) =>
- active
- ? " margin: 0 0.25rem 0 0.75rem;"
- : " margin: 0 0.25rem 0 0.25rem;"}
- font-size: ${({ active }) => (active ? "20px" : "16px")};
-`;
-
-const StyledBalanceMax = styled.button`
- height: 28px;
- background-color: ${({ theme }) => theme.primary5};
- border: 1px solid ${({ theme }) => theme.primary5};
- border-radius: 0.5rem;
- font-size: 0.875rem;
-
- font-weight: 500;
- cursor: pointer;
- margin-right: 0.5rem;
- color: ${({ theme }) => theme.primaryText1};
- :hover {
- border: 1px solid ${({ theme }) => theme.primary1};
- }
- :focus {
- border: 1px solid ${({ theme }) => theme.primary1};
- outline: none;
- }
-
- ${({ theme }) => theme.mediaWidth.upToExtraSmall`
- margin-right: 0.5rem;
- `};
-`;
-
-interface CurrencyInputPanelProps {
- value: string;
- onUserSellAmountInput: (val: string) => void;
- onMax?: () => void;
- showMaxButton: boolean;
- label?: string;
- onTokenSelection?: (tokenAddress: string) => void;
- token?: Token | null;
- disableTokenSelect?: boolean;
- hideBalance?: boolean;
- isExchange?: boolean;
- pair?: Pair | null;
- hideInput?: boolean;
- showSendWithSwap?: boolean;
- otherSelectedTokenAddress?: string | null;
- id: string;
-}
-
-export default function CurrencyInputPanel({
- value,
- onUserSellAmountInput,
- onMax,
- showMaxButton,
- label = "Input",
- token = null,
- disableTokenSelect = false,
- hideBalance = false,
- isExchange = false,
- pair = null, // used for double token logo
- hideInput = false,
- id,
-}: CurrencyInputPanelProps) {
- const { t } = useTranslation();
-
- const { account } = useActiveWeb3React();
- const userTokenBalance = useTokenBalance(account, token);
- const theme = useContext(ThemeContext);
-
- return (
-
-
- {!hideInput && (
-
-
-
- {label}
-
- {account && (
-
-
- {!hideBalance && !!token && userTokenBalance
- ? "Balance: " + userTokenBalance?.toSignificant(6)
- : " -"}
-
-
- )}
-
-
- )}
-
- {!hideInput && (
- <>
- {
- onUserSellAmountInput(val);
- }}
- />
- {account &&
- !!token?.address &&
- showMaxButton &&
- label !== "To" && (
- MAX
- )}
- >
- )}
-
-
- {isExchange ? (
-
- ) : token?.address ? (
-
- ) : null}
- {isExchange ? (
-
- {pair?.token0.symbol}:{pair?.token1.symbol}
-
- ) : (
-
- {(token && token.symbol && token.symbol.length > 20
- ? token.symbol.slice(0, 4) +
- "..." +
- token.symbol.slice(
- token.symbol.length - 5,
- token.symbol.length,
- )
- : token?.symbol) || t("selectToken")}
-
- )}
- {!disableTokenSelect}
-
-
-
-
-
- );
-}
diff --git a/src/components/DarkModeSwitch/index.tsx b/src/components/DarkModeSwitch/index.tsx
deleted file mode 100644
index ce5bcba26..000000000
--- a/src/components/DarkModeSwitch/index.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from "react";
-import { Sun, Moon } from "react-feather";
-import { useDarkModeManager } from "../../state/user/hooks";
-
-import { ButtonSecondary } from "../Button";
-
-export default function DarkModeSwitch() {
- const [darkMode, toggleDarkMode] = useDarkModeManager();
-
- return (
-
- {darkMode ? : }
-
- );
-}
diff --git a/src/components/DoubleLogo/index.tsx b/src/components/DoubleLogo/index.tsx
deleted file mode 100644
index 7039c56e4..000000000
--- a/src/components/DoubleLogo/index.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-import TokenLogo from "../TokenLogo";
-
-const TokenWrapper = styled.div`
- position: relative;
- display: flex;
- flex-direction: row;
- align-items: center;
-`;
-
-interface DoubleTokenLogoProps {
- margin?: boolean;
- size?: number;
- a0: string;
- a1: string;
-}
-
-const HigherLogo = styled(TokenLogo)`
- z-index: 2;
-`;
-
-const CoveredLogo = styled(TokenLogo)`
- margin-left: -5px;
-`;
-
-export default function DoubleTokenLogo({
- a0,
- a1,
- size = 28,
-}: DoubleTokenLogoProps) {
- return (
-
-
-
-
- );
-}
diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx
deleted file mode 100644
index 141b67982..000000000
--- a/src/components/Footer/index.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-
-const FooterFrame = styled.div`
- display: flex;
- align-items: center;
- justify-content: flex-end;
- position: fixed;
- right: 1rem;
- bottom: 1rem;
- ${({ theme }) => theme.mediaWidth.upToMedium`
- display: none;
- `};
-`;
-
-export default function Footer() {
- return ;
-}
diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx
deleted file mode 100644
index a57979161..000000000
--- a/src/components/Header/index.tsx
+++ /dev/null
@@ -1,187 +0,0 @@
-import React from "react";
-import { Link as HistoryLink } from "react-router-dom";
-
-import styled from "styled-components";
-import { useTokenBalanceTreatingWETHasETH } from "../../state/wallet/hooks";
-
-import Row from "../Row";
-import Menu from "../Menu";
-import Web3Status from "../Web3Status";
-
-import { WETH, ChainId } from "uniswap-xdai-sdk";
-import { isMobile } from "react-device-detect";
-import { YellowCard } from "../Card";
-import { useActiveWeb3React } from "../../hooks";
-
-import { RowBetween } from "../Row";
-
-const HeaderFrame = styled.div`
- display: flex;
- align-items: center;
- justify-content: space-between;
- flex-direction: column;
- width: 100%;
- top: 0;
- position: absolute;
- pointer-events: none;
-
- ${({ theme }) => theme.mediaWidth.upToExtraSmall`
- padding: 12px 0 0 0;
- position: relative;
- `};
- z-index: 2;
-`;
-
-const HeaderElement = styled.div`
- display: flex;
- align-items: center;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- flex-direction: row;
- justify-content: space-between;
- justify-self: center;
- padding: 0 10px;
- position: fixed;
- bottom: 0px;
- left: 0px;
- width: 100%;
- z-index: 99;
- height: 72px;
- border-radius: 12px 12px 0px 0px;
- background-color: ${({ theme }) => theme.bg3};
- box-sizing: border-box;
- `};
-`;
-
-const Title = styled.div`
- display: flex;
- align-items: center;
- pointer-events: auto;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- margin: 0 auto;
- `};
-
- :hover {
- cursor: pointer;
- }
-`;
-
-const TitleText = styled(Row)`
- width: fit-content;
- font-size: 18px;
- font-weight: 500;
- white-space: nowrap;
- color: ${({ theme }) => theme.text1};
-
- > a {
- color: inherit;
- text-decoration: none;
- }
-`;
-
-const EthBalance = styled.div`
- display: flex;
- align-items: center;
- pointer-events: auto;
- padding: 0 10px;
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- background-color: ${({ theme }) => theme.bg3};
- `};
-
- ${({ theme }) => theme.mediaWidth.upToSmall`
- display: none;
- `};
-`;
-
-const AccountElement = styled.div<{ active: boolean }>`
- display: flex;
- flex-direction: row;
- align-items: center;
- background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg3)};
- border-radius: 12px;
- white-space: nowrap;
-
- :focus {
- border: 1px solid blue;
- }
-
- ${({ theme }) => theme.mediaWidth.upToMedium`
- margin: 0 auto 0 0;
- `}
-`;
-
-const TestnetWrapper = styled.div`
- white-space: nowrap;
- width: fit-content;
- margin-left: 10px;
-
- ${({ theme }) => theme.mediaWidth.upToSmall`
- display: none;
- `};
-`;
-
-const NetworkCard = styled(YellowCard)`
- width: fit-content;
- margin-right: 10px;
- border-radius: 12px;
- padding: 8px 12px;
-`;
-
-const MenuWrapper = styled.div`
- pointer-events: auto;
- display: flex;
- position: relative;
-`;
-
-export default function Header() {
- const { account, chainId } = useActiveWeb3React();
-
- const userEthBalance = useTokenBalanceTreatingWETHasETH(
- account,
- WETH[chainId],
- );
-
- return (
-
-
-
-
-
- 🏁 GnosisAuction
-
-
-
-
-
-
-
-
- {!isMobile && chainId === ChainId.ROPSTEN && (
- Ropsten
- )}
- {!isMobile && chainId === ChainId.RINKEBY && (
- Rinkeby
- )}
- {!isMobile && chainId === ChainId.GÖRLI && (
- Görli
- )}
- {!isMobile && chainId === ChainId.KOVAN && (
- Kovan
- )}
-
-
- {account && userEthBalance ? (
- {userEthBalance?.toSignificant(4)} ETH
- ) : null}
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/components/Identicon/index.tsx b/src/components/Identicon/index.tsx
index 59821f47d..d30b2802b 100644
--- a/src/components/Identicon/index.tsx
+++ b/src/components/Identicon/index.tsx
@@ -1,28 +1,28 @@
-import React, { useEffect, useRef } from "react";
+import React, { useEffect, useRef } from 'react'
+import styled from 'styled-components'
-import styled from "styled-components";
+import Jazzicon from 'jazzicon'
-import { useActiveWeb3React } from "../../hooks";
-import Jazzicon from "jazzicon";
+import { useActiveWeb3React } from '../../hooks'
const StyledIdenticon = styled.div`
height: 1rem;
width: 1rem;
border-radius: 1.125rem;
background-color: ${({ theme }) => theme.bg4};
-`;
+`
export default function Identicon() {
- const ref = useRef();
+ const ref = useRef()
- const { account } = useActiveWeb3React();
+ const { account } = useActiveWeb3React()
useEffect(() => {
if (account && ref.current) {
- ref.current.innerHTML = "";
- ref.current.appendChild(Jazzicon(16, parseInt(account.slice(2, 10), 16)));
+ ref.current.innerHTML = ''
+ ref.current.appendChild(Jazzicon(16, parseInt(account.slice(2, 10), 16)))
}
- }, [account]);
+ }, [account])
- return ;
+ return
}
diff --git a/src/components/Loader/index.tsx b/src/components/Loader/index.tsx
index b2874fb87..dc9afc19d 100644
--- a/src/components/Loader/index.tsx
+++ b/src/components/Loader/index.tsx
@@ -1,15 +1,14 @@
-import React from "react";
+import React from 'react'
+import styled from 'styled-components'
-import styled from "styled-components";
-
-import { Spinner } from "../../theme";
-import Circle from "../../assets/images/blue-loader.svg";
+import Circle from '../../assets/images/blue-loader.svg'
+import { Spinner } from '../../theme'
const SpinnerWrapper = styled(Spinner)<{ size: string }>`
height: ${({ size }) => size};
width: ${({ size }) => size};
-`;
+`
export default function Loader({ size }: { size: string }) {
- return ;
+ return
}
diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx
index 5fdcb3b2a..559c17821 100644
--- a/src/components/Menu/index.tsx
+++ b/src/components/Menu/index.tsx
@@ -1,16 +1,17 @@
-import React, { useRef, useEffect } from "react";
-import { Info, Code, MessageCircle } from "react-feather";
-import styled from "styled-components";
-import { ReactComponent as MenuIcon } from "../../assets/images/menu.svg";
-import useToggle from "../../hooks/useToggle";
+import React, { useEffect, useRef } from 'react'
+import { Code, Info, MessageCircle } from 'react-feather'
+import styled from 'styled-components'
-import { ExternalLink } from "../../theme";
+import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
+import { GIT_COMMIT_HASH } from '../../constants/config'
+import useToggle from '../../hooks/useToggle'
+import { ExternalLink } from '../../theme'
const StyledMenuIcon = styled(MenuIcon)`
path {
stroke: ${({ theme }) => theme.text1};
}
-`;
+`
const StyledMenuButton = styled.button`
width: 100%;
@@ -38,7 +39,7 @@ const StyledMenuButton = styled.button`
svg {
margin-top: 2px;
}
-`;
+`
const StyledMenu = styled.div`
margin-left: 0.5rem;
@@ -48,7 +49,7 @@ const StyledMenu = styled.div`
position: relative;
border: none;
text-align: left;
-`;
+`
const MenuFlyout = styled.span`
min-width: 8.125rem;
@@ -69,7 +70,7 @@ const MenuFlyout = styled.span`
bottom: 70px;
top: initial;
`}
-`;
+`
const MenuItem = styled(ExternalLink)`
flex: 1;
@@ -83,34 +84,34 @@ const MenuItem = styled(ExternalLink)`
> svg {
margin-right: 8px;
}
-`;
+`
-const CODE_LINK = !!process.env.REACT_APP_GIT_COMMIT_HASH
- ? `https://github.com/gnosis/ido-contracts/tree/${process.env.REACT_APP_GIT_COMMIT_HASH}`
- : "https://github.com/gnosis/ido-contracts";
+const CODE_LINK = GIT_COMMIT_HASH
+ ? `https://github.com/gnosis/ido-contracts/tree/${GIT_COMMIT_HASH}`
+ : 'https://github.com/gnosis/ido-contracts'
export default function Menu() {
- const node = useRef();
- const [open, toggle] = useToggle(false);
+ const node = useRef()
+ const [open, toggle] = useToggle(false)
useEffect(() => {
const handleClickOutside = (e) => {
if (node.current?.contains(e.target) ?? false) {
- return;
+ return
}
- toggle();
- };
+ toggle()
+ }
if (open) {
- document.addEventListener("mousedown", handleClickOutside);
+ document.addEventListener('mousedown', handleClickOutside)
} else {
- document.removeEventListener("mousedown", handleClickOutside);
+ document.removeEventListener('mousedown', handleClickOutside)
}
return () => {
- document.removeEventListener("mousedown", handleClickOutside);
- };
- }, [open, toggle]);
+ document.removeEventListener('mousedown', handleClickOutside)
+ }
+ }, [open, toggle])
return (
@@ -120,22 +121,22 @@ export default function Menu() {
{open && (
-
)}
- );
+ )
}
diff --git a/src/components/NavigationTabs/index.tsx b/src/components/NavigationTabs/index.tsx
index a900cdfa6..6563a589e 100644
--- a/src/components/NavigationTabs/index.tsx
+++ b/src/components/NavigationTabs/index.tsx
@@ -1,113 +1,101 @@
-import React, { useCallback } from "react";
-import styled from "styled-components";
-import {
- withRouter,
- Link as HistoryLink,
- RouteComponentProps,
-} from "react-router-dom";
-import useBodyKeyDown from "../../hooks/useBodyKeyDown";
+import React, { useCallback } from 'react'
+import { ArrowLeft } from 'react-feather'
+import { Link as HistoryLink, RouteComponentProps, withRouter } from 'react-router-dom'
+import styled from 'styled-components'
-import { CursorPointer } from "../../theme";
-import { ArrowLeft } from "react-feather";
-import { RowBetween } from "../Row";
-import QuestionHelper from "../QuestionHelper";
+import useBodyKeyDown from '../../hooks/useBodyKeyDown'
+import { CursorPointer } from '../../theme'
+import QuestionHelper from '../QuestionHelper'
+import { RowBetween } from '../Row'
const tabOrder = [
{
- path: "/swap",
- textKey: "Place Order",
+ path: '/swap',
+ textKey: 'Place Order',
regex: /\/swap/,
},
-];
+]
const Tabs = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
border-radius: 3rem;
-`;
+`
const ActiveText = styled.div`
font-weight: 500;
font-size: 20px;
-`;
+`
const ArrowLink = styled(ArrowLeft)`
color: ${({ theme }) => theme.text1};
-`;
+`
-function NavigationTabs({
- location: { pathname },
- history,
-}: RouteComponentProps) {
+function NavigationTabs({ history, location: { pathname } }: RouteComponentProps) {
const navigate = useCallback(
(direction) => {
- const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex));
- history.push(
- tabOrder[(tabIndex + tabOrder.length + direction) % tabOrder.length]
- .path,
- );
+ const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex))
+ history.push(tabOrder[(tabIndex + tabOrder.length + direction) % tabOrder.length].path)
},
[pathname, history],
- );
+ )
const navigateRight = useCallback(() => {
- navigate(1);
- }, [navigate]);
+ navigate(1)
+ }, [navigate])
const navigateLeft = useCallback(() => {
- navigate(-1);
- }, [navigate]);
+ navigate(-1)
+ }, [navigate])
- useBodyKeyDown("ArrowRight", navigateRight);
- useBodyKeyDown("ArrowLeft", navigateLeft);
+ useBodyKeyDown('ArrowRight', navigateRight)
+ useBodyKeyDown('ArrowLeft', navigateLeft)
- const adding = pathname.match("/add");
- const removing = pathname.match("/remove");
- const finding = pathname.match("/find");
- const creating = pathname.match("/create");
+ const adding = pathname.match('/add')
+ const removing = pathname.match('/remove')
+ const finding = pathname.match('/find')
+ const creating = pathname.match('/create')
return (
<>
{adding || removing ? (
-
- history.push("/pool")}>
+
+ history.push('/pool')}>
- {adding ? "Add" : "Remove"} Liquidity
+ {adding ? 'Add' : 'Remove'} Liquidity
) : finding ? (
-
+
Import Pool
) : creating ? (
-
+
Create Pool
-
+
) : (
-
+
{/* {tabOrder.map(({ path, textKey, regex }) => (
)}
>
- );
+ )
}
-export default withRouter(NavigationTabs);
+export default withRouter(NavigationTabs)
diff --git a/src/components/NumericalInput/index.tsx b/src/components/NumericalInput/index.tsx
deleted file mode 100644
index 91a18e182..000000000
--- a/src/components/NumericalInput/index.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-import { escapeRegExp } from "../../utils";
-
-const StyledInput = styled.input<{
- error?: boolean;
- fontSize?: string;
- align?: string;
-}>`
- color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)};
- width: 0;
- position: relative;
- font-weight: 500;
- outline: none;
- border: none;
- flex: 1 1 auto;
- background-color: ${({ theme }) => theme.bg1};
- font-size: ${({ fontSize }) => fontSize ?? "24px"};
- text-align: ${({ align }) => align && align};
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- padding: 0px;
- -webkit-appearance: textfield;
-
- ::-webkit-search-decoration {
- -webkit-appearance: none;
- }
-
- [type="number"] {
- -moz-appearance: textfield;
- }
-
- ::-webkit-outer-spin-button,
- ::-webkit-inner-spin-button {
- -webkit-appearance: none;
- }
-
- ::placeholder {
- color: ${({ theme }) => theme.text4};
- }
-`;
-
-const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`); // match escaped "." characters via in a non-capturing group
-
-export const Input = React.memo(function InnerInput({
- value,
- onUserSellAmountInput,
- placeholder,
- ...rest
-}: {
- value: string | number;
- onUserSellAmountInput: (string) => void;
- error?: boolean;
- fontSize?: string;
- align?: "right" | "left";
-} & Omit, "ref" | "onChange" | "as">) {
- const enforcer = (nextUserInput: string) => {
- if (nextUserInput === "" || inputRegex.test(escapeRegExp(nextUserInput))) {
- onUserSellAmountInput(nextUserInput);
- }
- };
-
- return (
- {
- // replace commas with periods, because uniswap exclusively uses period as the decimal separator
- enforcer(event.target.value.replace(/,/g, "."));
- }}
- // universal input options
- inputMode="decimal"
- title="Token Amount"
- autoComplete="off"
- autoCorrect="off"
- // text-specific options
- type="text"
- pattern="^[0-9]*[.,]?[0-9]*$"
- placeholder={placeholder || "0.0"}
- minLength={1}
- maxLength={79}
- spellCheck="false"
- />
- );
-});
-
-export default Input;
-
-// const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped "." characters via in a non-capturing group
diff --git a/src/components/OrderDropdown/index.tsx b/src/components/OrderDropdown/index.tsx
deleted file mode 100644
index 2c4315f35..000000000
--- a/src/components/OrderDropdown/index.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React, { useContext } from "react";
-import { ChevronDown } from "react-feather";
-import { ThemeContext } from "styled-components";
-import { RowBetween } from "../Row";
-import { AdvancedDropdown, SectionBreak } from "../swap/styleds";
-
-import { ChevronUp } from "react-feather";
-import { Text } from "rebass";
-import { CursorPointer } from "../../theme";
-import { AutoColumn } from "../Column";
-import OrderTable from "../OrderTable";
-import { OrderDisplay } from "../../state/orders/reducer";
-
-export interface OrderTableDetailsProps {
- orders: OrderDisplay[];
-}
-
-export default function OrderDisplayDropdown({
- showAdvanced,
- orders,
- setShowAdvanced,
- ...rest
-}: Omit & {
- showAdvanced: boolean;
- setShowAdvanced: (showAdvanced: boolean) => void;
-}) {
- const theme = useContext(ThemeContext);
- return (
-
- {showAdvanced && !!orders ? (
- setShowAdvanced(false)}
- orders={orders}
- />
- ) : (
-
- setShowAdvanced(true)}
- padding="4px 4px"
- id="show-advanced"
- >
-
- {!orders || orders.length === 0
- ? "You have no orders yet"
- : `Show ${orders.length} orders`}
-
-
-
-
- )}
-
- );
-}
-
-export interface AdvancedOrderDetailsProps extends OrderTableDetailsProps {
- onDismiss: () => void;
-}
-
-export function AdvancedOrderDetails({
- orders,
- onDismiss,
-}: AdvancedOrderDetailsProps) {
- const theme = useContext(ThemeContext);
-
- return (
-
-
-
-
- Hide {orders.length} orders
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/components/OrderPlacement/index.tsx b/src/components/OrderPlacement/index.tsx
deleted file mode 100644
index 4f6f0fe13..000000000
--- a/src/components/OrderPlacement/index.tsx
+++ /dev/null
@@ -1,244 +0,0 @@
-import { TokenAmount, ChainId, Fraction } from "uniswap-xdai-sdk";
-import React, { useState, useEffect, useMemo } from "react";
-import { Text } from "rebass";
-import { ButtonLight, ButtonPrimary } from "../../components/Button";
-import { AutoColumn } from "../../components/Column";
-import ConfirmationModal from "../../components/ConfirmationModal";
-import WarningModal from "../../components/WarningModal";
-import CurrencyInputPanel from "../../components/CurrencyInputPanel";
-import PriceInputPanel from "../../components/PriceInputPanel";
-import { BottomGrouping, Dots, Wrapper } from "../../components/swap/styleds";
-import SwapModalFooter from "../swap/PlaceOrderModalFooter";
-import SwapModalHeader from "../../components/swap/SwapModalHeader";
-import { useActiveWeb3React } from "../../hooks";
-import { EASY_AUCTION_NETWORKS } from "../../constants";
-import {
- useApproveCallback,
- ApprovalState,
-} from "../../hooks/useApproveCallback";
-import { usePlaceOrderCallback } from "../../hooks/usePlaceOrderCallback";
-import { useWalletModalToggle } from "../../state/application/hooks";
-import {
- useDerivedAuctionInfo,
- useGetOrderPlacementError,
- useSwapActionHandlers,
- useSwapState,
-} from "../../state/orderPlacement/hooks";
-import { getTokenDisplay } from "../../utils";
-import { useOrderState } from "../../state/orders/hooks";
-import { OrderState } from "../../state/orders/reducer";
-
-export default function OrderPlacement() {
- const { chainId, account } = useActiveWeb3React();
- const orders: OrderState | undefined = useOrderState();
-
- // toggle wallet when disconnected
- const toggleWalletModal = useWalletModalToggle();
-
- // swap state
- const { price, sellAmount } = useSwapState();
- const {
- biddingTokenBalance,
- parsedBiddingAmount,
- auctioningToken,
- biddingToken,
- initialPrice,
- } = useDerivedAuctionInfo();
- const { error } = useGetOrderPlacementError();
- const { onUserSellAmountInput } = useSwapActionHandlers();
- const { onUserPriceInput } = useSwapActionHandlers();
-
- const isValid = !error;
-
- // modal and loading
- const [showConfirm, setShowConfirm] = useState(false);
- const [showWarning, setShowWarning] = useState(false);
- const [attemptingTxn, setAttemptingTxn] = useState(false); // clicked confirmed
- const [pendingConfirmation, setPendingConfirmation] = useState(true); // waiting for user confirmation
-
- // txn values
- const [txHash, setTxHash] = useState("");
-
- const approvalTokenAmount: TokenAmount | undefined = parsedBiddingAmount;
- // check whether the user has approved the EasyAuction Contract
- const [approval, approveCallback] = useApproveCallback(
- approvalTokenAmount,
- EASY_AUCTION_NETWORKS[chainId as ChainId],
- );
- const [approvalSubmitted, setApprovalSubmitted] = useState(false);
-
- useEffect(() => {
- if (approval === ApprovalState.PENDING) {
- setApprovalSubmitted(true);
- }
- }, [approval, approvalSubmitted]);
-
- const maxAmountInput: TokenAmount = !!biddingTokenBalance
- ? biddingTokenBalance
- : undefined;
- const atMaxAmountInput: boolean =
- maxAmountInput && parsedBiddingAmount
- ? maxAmountInput.equalTo(parsedBiddingAmount)
- : undefined;
-
- useEffect(() => {
- if (price == "-" && initialPrice) {
- onUserPriceInput(
- initialPrice.multiply(new Fraction("1001", "1000")).toSignificant(4),
- );
- }
- }, [onUserPriceInput, price, initialPrice]);
-
- // reset modal state when closed
- function resetModal() {
- // clear input if txn submitted
- if (!pendingConfirmation) {
- onUserSellAmountInput("");
- }
- setPendingConfirmation(true);
- setAttemptingTxn(false);
- }
-
- // the callback to execute the swap
- const placeOrderCallback = usePlaceOrderCallback(
- auctioningToken,
- biddingToken,
- );
-
- function onPlaceOrder() {
- setAttemptingTxn(true);
-
- placeOrderCallback().then((hash) => {
- setTxHash(hash);
- setPendingConfirmation(false);
- });
- }
-
- // errors
- const [showInverted, setShowInverted] = useState(false);
-
- function modalHeader() {
- return ;
- }
-
- function modalBottom() {
- return (
-
- );
- }
-
- // text to show while loading
- const pendingText = `Placing order`;
-
- const biddingTokenDisplay = useMemo(() => getTokenDisplay(biddingToken), [
- biddingToken,
- ]);
-
- const auctioningTokenDisplay = useMemo(
- () => getTokenDisplay(auctioningToken),
- [auctioningToken],
- );
-
- const handleShowConfirm = () => {
- const sameOrder = orders.orders.find((order) => order.price === price);
-
- if (!sameOrder) {
- setShowConfirm(true);
- } else {
- setShowWarning(true);
- }
- };
-
- return (
- <>
-
- {
- resetModal();
- setShowConfirm(false);
- }}
- attemptingTxn={attemptingTxn}
- pendingConfirmation={pendingConfirmation}
- hash={txHash}
- topContent={modalHeader}
- bottomContent={modalBottom}
- pendingText={pendingText}
- />
- {
- setShowWarning(false);
- }}
- />
-
- <>
- {
- maxAmountInput &&
- onUserSellAmountInput(maxAmountInput.toExact());
- }}
- id="auction-input"
- />
-
-
- >
-
-
- {!account ? (
-
- Connect Wallet
-
- ) : approval === ApprovalState.NOT_APPROVED ||
- approval === ApprovalState.PENDING ? (
-
- {approval === ApprovalState.PENDING ? (
- Approving {biddingTokenDisplay}
- ) : (
- `Approve ${biddingTokenDisplay}`
- )}
-
- ) : (
-
-
- {error ?? `Place Order`}
-
-
- )}
-
-
- >
- );
-}
diff --git a/src/components/OrderTable/index.tsx b/src/components/OrderTable/index.tsx
deleted file mode 100644
index 125793d76..000000000
--- a/src/components/OrderTable/index.tsx
+++ /dev/null
@@ -1,206 +0,0 @@
-import React, { useState } from "react";
-import styled from "styled-components";
-import { Text } from "rebass";
-
-import { useCancelOrderCallback } from "../../hooks/useCancelOrderCallback";
-import {
- AuctionState,
- useDerivedAuctionInfo,
- useDerivedAuctionState,
-} from "../../state/orderPlacement/hooks";
-import { ButtonCancel } from "../Button";
-import ConfirmationModal from "../ConfirmationModal";
-import CancelModalFooter from "../swap/CancelOrderModealFooter";
-import SwapModalHeader from "../swap/SwapModalHeader";
-import { useOrderActionHandlers } from "../../state/orders/hooks";
-import { OrderDisplay, OrderStatus } from "../../state/orders/reducer";
-import { useClearingPriceInfo } from "../../hooks/useCurrentClearingOrderAndVolumeCallback";
-import { ClearingPriceAndVolumeData } from "../../api/AdditionalServicesApi";
-import { decodeOrder, encodeOrder } from "../../hooks/Order";
-import { Fraction } from "uniswap-xdai-sdk";
-
-const StyledRow = styled.div`
- display: grid;
- grid-template-columns: 1.5fr 0.6fr 1fr 1fr 1fr;
- grid-template-areas: "amount price fill status action";
- font-weight: normal;
- font-size: 13px;
- padding: 8px 0;
- transition: background-color 0.1s ease-in-out;
-
- &:hover {
- background-color: ${({ theme }) => theme.advancedBG};
- }
-
- &:not(:last-child) {
- border-bottom: 1px solid rgba(43, 43, 43, 0.435);
- }
-
- > div {
- display: flex;
- align-items: center;
- }
-
- > div:last-of-type {
- margin: 0 0 0 auto;
- }
-`;
-
-const StyledHeader = styled(StyledRow)`
- &:hover {
- background: none;
- }
- > div {
- font-weight: 700;
- }
-`;
-
-const Wrapper = styled.div`
- width: 100%;
- padding: 0;
-`;
-
-function getMatchedVolume(
- orderId: string,
- clearingOrderInfo: ClearingPriceAndVolumeData,
-): Number {
- if (orderId == encodeOrder(clearingOrderInfo.clearingOrder)) {
- return Number(
- new Fraction(
- clearingOrderInfo.volume.mul(100).toString(),
- clearingOrderInfo.clearingOrder.sellAmount.toString(),
- ).toSignificant(2),
- );
- } else {
- const order = decodeOrder(orderId);
- if (
- order.sellAmount
- .mul(clearingOrderInfo.clearingOrder.buyAmount)
- .lt(order.buyAmount.mul(clearingOrderInfo.clearingOrder.sellAmount))
- ) {
- return Number(0);
- } else {
- return 100;
- }
- }
-}
-
-function Table(orders: OrderDisplay[]) {
- const { auctionState } = useDerivedAuctionState();
- const { biddingToken } = useDerivedAuctionInfo();
- const cancelOrderCallback = useCancelOrderCallback(biddingToken);
- const { onDeleteOrder } = useOrderActionHandlers();
- const clearingPriceInfo = useClearingPriceInfo();
-
- // modal and loading
- const [showConfirm, setShowConfirm] = useState(false);
- const [attemptingTxn, setAttemptingTxn] = useState(false); // clicked confirmed
- const [pendingConfirmation, setPendingConfirmation] = useState(true); // waiting for user confirmation
-
- // txn values
- const [txHash, setTxHash] = useState("");
- const [orderId, setOrderId] = useState("");
-
- // reset modal state when closed
- function resetModal() {
- setPendingConfirmation(true);
- setAttemptingTxn(false);
- }
-
- function onCancelOrder() {
- setAttemptingTxn(true);
-
- cancelOrderCallback(orderId).then((hash) => {
- onDeleteOrder(orderId);
- setTxHash(hash);
- setPendingConfirmation(false);
- });
- }
-
- function modalHeader() {
- return ;
- }
-
- function modalBottom() {
- return (
-
- );
- }
- const pendingText = `Canceling Order`;
- let error = undefined;
- if (auctionState != AuctionState.ORDER_PLACING_AND_CANCELING) {
- error = "Not allowed";
- }
- if (!orders || orders.length == 0) return null;
- return (
- <>
- {
- resetModal();
- setShowConfirm(false);
- }}
- attemptingTxn={attemptingTxn}
- pendingConfirmation={pendingConfirmation}
- hash={txHash}
- topContent={modalHeader}
- bottomContent={modalBottom}
- pendingText={pendingText}
- />
-
- Amount
- Price
- Est. Fill
- Status
- Actions
-
- {Object.entries(orders).map((order) => (
-
- {order[1].sellAmount}
- {order[1].price}
-
- {clearingPriceInfo
- ? getMatchedVolume(order[1].id, clearingPriceInfo)
- : "loading"}
-
-
- {order[1].status == OrderStatus.PLACED ? "Placed" : "Pending"}
-
-
- {order[1].status == OrderStatus.PENDING ? (
-
- ) : (
-
{
- if (!error) {
- setOrderId(order[1].id);
- setShowConfirm(true);
- }
- }}
- id="cancel-button"
- >
-
- {error ?? `Cancel Order`}
-
-
- )}
-
-
- ))}
- >
- );
-}
-
-export default function OrderTable(orders: OrderDisplay[]) {
- return (
-
-
-
- );
-}
diff --git a/src/components/OrderbookBtn.tsx b/src/components/OrderbookBtn.tsx
deleted file mode 100644
index 91834ffd5..000000000
--- a/src/components/OrderbookBtn.tsx
+++ /dev/null
@@ -1,177 +0,0 @@
-import React, { useMemo } from "react";
-import styled from "styled-components";
-import Modal, { useModal } from "./MesaModal";
-
-// const, types, utils
-import { Token } from "uniswap-xdai-sdk";
-
-// components
-import { DEFAULT_MODAL_OPTIONS } from "./Modal";
-import { ButtonLight } from "./Button";
-import OrderBookWidget, { processOrderbookData } from "./OrderbookWidget";
-
-// hooks
-import { useActiveWeb3React } from "../hooks";
-import { useSwapState } from "../state/orderPlacement/hooks";
-
-// utils
-import { getTokenDisplay } from "../utils";
-import OrderBookChartSmall, { OrderBookError } from "./OrderbookChartSmall";
-import { useOrderbookState } from "../state/orderbook/hooks";
-
-const ViewOrderBookBtn = styled(ButtonLight)`
- margin: 0 0 0 0;
- background: none;
- height: auto;
- width: 100%;
- padding: 0;
- color: ${({ theme }) => theme.text3};
-
- &:hover {
- background: none;
- }
-
- > svg {
- margin: 0 0 0 5px;
- }
-`;
-
-const Wrapper = styled.div`
- display: block;
-`;
-
-// todo correct circular reference:
-// const ModalWrapper = styled(ModalBodyWrapper)`
-const ModalWrapper = styled.div`
- display: flex;
- text-align: center;
- height: 100%;
- min-width: 100%;
- width: 100%;
- align-items: center;
- align-content: flex-start;
- flex-flow: row wrap;
- padding: 0;
- justify-content: center;
-
- > span {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- margin: 1.6rem 0 1rem;
- }
-
- > span:first-of-type::after {
- content: "/";
- margin: 0 1rem;
- }
-
- > span:first-of-type > p {
- margin: 0 1rem 0 0;
- }
-
- > span:last-of-type > p {
- margin: 0 0 0 1rem;
- }
-
- .amcharts-Sprite-group {
- font-size: 1rem;
- }
-
- .amcharts-Container .amcharts-Label {
- text-transform: uppercase;
- font-size: 11px;
- }
-
- .amcharts-ZoomOutButton-group > .amcharts-RoundedRectangle-group {
- fill: var(--color-text-active);
- opacity: 0.6;
- transition: 0.3s ease-in-out;
-
- &:hover {
- opacity: 1;
- }
- }
-`;
-
-interface OrderBookBtnProps {
- baseToken: Token;
- quoteToken: Token;
- label?: string;
- className?: string;
-}
-
-export const OrderBookBtn: React.FC = (
- props: OrderBookBtnProps,
-) => {
- const { baseToken, quoteToken, className } = props;
- // const theme = useContext(ThemeContext);
- const { chainId } = useActiveWeb3React();
- const { auctionId } = useSwapState();
-
- const biddingTokenDisplay = useMemo(() => getTokenDisplay(baseToken), [
- baseToken,
- ]);
-
- const auctioningTokenDisplay = useMemo(() => getTokenDisplay(quoteToken), [
- quoteToken,
- ]);
-
- const [modalHook, toggleModal] = useModal({
- ...DEFAULT_MODAL_OPTIONS,
- large: true,
- title: `${biddingTokenDisplay}-${auctioningTokenDisplay} Order book`,
- message: (
-
-
-
- ),
- buttons: [
- <> >,
- modalHook.hide()}
- />,
- ],
- });
- const {
- error,
- bids,
- asks,
- userOrderPrice,
- userOrderVolume,
- } = useOrderbookState();
-
- if (error || !asks || asks.length == 0)
- return ;
- const processedOrderbook = processOrderbookData({
- data: { bids, asks },
- userOrder: { price: userOrderPrice, volume: userOrderVolume },
- baseToken,
- quoteToken,
- });
- return (
-
-
-
-
-
-
- );
-};
diff --git a/src/components/OrderbookChart.tsx b/src/components/OrderbookChart.tsx
deleted file mode 100644
index bae1d0d1b..000000000
--- a/src/components/OrderbookChart.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import React, { useEffect, useRef } from "react";
-import styled from "styled-components";
-
-import * as am4core from "@amcharts/amcharts4/core";
-import * as am4charts from "@amcharts/amcharts4/charts";
-import am4themesSpiritedaway from "@amcharts/amcharts4/themes/spiritedaway";
-
-import { Token } from "uniswap-xdai-sdk";
-
-export interface OrderBookChartProps {
- /**
- * Base Token for Y-axis
- */
- baseToken: Token;
- /**
- * Quote Token for X-axis
- */
- quoteToken: Token;
- /**
- * current network id
- */
- networkId: number;
- /**
- * price/volume data with asks and bids
- */
- data: PricePointDetails[] | null;
-}
-
-const Wrapper = styled.div`
- display: flex;
- justify-content: center;
- min-height: calc(60vh - 30rem);
- text-align: center;
- width: 100%;
- height: 100%;
- min-width: 100%;
- padding: 16px;
- box-sizing: border-box;
-
- .amcharts-Sprite-group {
- font-size: 1rem;
- }
-
- .amcharts-Label {
- text-transform: uppercase;
- font-size: 10px;
- letter-spacing: 1px;
- color: ${({ theme }) => theme.text4};
- margin: 10px;
- }
-
- .amcharts-ZoomOutButton-group > .amcharts-RoundedRectangle-group {
- fill: var(--color-text-active);
- opacity: 0.6;
- transition: 0.3s ease-in-out;
-
- &:hover {
- opacity: 1;
- }
- }
-
- .amcharts-AxisLabel,
- .amcharts-CategoryAxis .amcharts-Label-group > .amcharts-Label,
- .amcharts-ValueAxis-group .amcharts-Label-group > .amcharts-Label {
- fill: ${({ theme }) => theme.text3};
- }
-`;
-
-interface OrderBookErrorProps {
- error: Error;
-}
-
-export const OrderBookError: React.FC = ({
- error,
-}: OrderBookErrorProps) => (
- {error ? error.message : "loading"}
-);
-
-export enum Offer {
- Bid,
- Ask,
-}
-
-/**
- * Price point data represented in the graph. Contains BigNumbers for operate with less errors and more precission
- * but for representation uses number as expected by the library
- */
-export interface PricePointDetails {
- // Basic data
- type: Offer;
- volume: number; // volume for the price point
- totalVolume: number; // cumulative volume
- price: number;
-
- // Data for representation
- priceNumber: number;
- priceFormatted: string;
- totalVolumeNumber: number;
- totalVolumeFormatted: string;
- askValueY: number | null;
- bidValueY: number | null;
- newOrderValueY: number | null;
- clearingPriceValueY: number | null;
-}
-
-export const createChart = (chartElement: HTMLElement): am4charts.XYChart => {
- am4core.useTheme(am4themesSpiritedaway);
- am4core.options.autoSetClassName = true;
- const chart = am4core.create(chartElement, am4charts.XYChart);
- chart.paddingTop = 20;
- chart.marginTop = 20;
- chart.paddingBottom = 0;
- chart.paddingLeft = 0;
- chart.paddingRight = 0;
- chart.marginBottom = 0;
-
- // Colors
- const colors = {
- green: "#28a745",
- red: "#dc3545",
- white: "#FFFFFF",
- grey: "#565A69",
- orange: "#FF6347",
- };
-
- // Create axes
- const priceAxis = chart.xAxes.push(new am4charts.ValueAxis());
- const volumeAxis = chart.yAxes.push(new am4charts.ValueAxis());
- priceAxis.renderer.labels.template.disabled = true;
- volumeAxis.renderer.labels.template.disabled = true;
- priceAxis.renderer.grid.template.disabled = true;
- volumeAxis.renderer.tooltip.getFillFromObject = false;
- priceAxis.renderer.tooltip.getFillFromObject = false;
-
- volumeAxis.renderer.grid.template.disabled = true;
- priceAxis.renderer.minGridDistance = 10;
- volumeAxis.renderer.minGridDistance = 10;
- // Create series
- const bidSeries = chart.series.push(new am4charts.StepLineSeries());
- bidSeries.dataFields.valueX = "priceNumber";
- bidSeries.dataFields.valueY = "bidValueY";
- bidSeries.strokeWidth = 2;
- bidSeries.stroke = am4core.color(colors.green);
- bidSeries.fill = bidSeries.stroke;
- bidSeries.fillOpacity = 0.2;
-
- const askSeries = chart.series.push(new am4charts.LineSeries());
- askSeries.dataFields.valueX = "priceNumber";
- askSeries.dataFields.valueY = "askValueY";
- askSeries.strokeWidth = 2;
- askSeries.stroke = am4core.color(colors.red);
- askSeries.fill = askSeries.stroke;
- askSeries.fillOpacity = 0.1;
-
- const inputSeries = chart.series.push(new am4charts.LineSeries());
- inputSeries.dataFields.valueX = "priceNumber";
- inputSeries.dataFields.valueY = "newOrderValueY";
- inputSeries.strokeWidth = 4;
- inputSeries.stroke = am4core.color(colors.orange);
- inputSeries.fill = inputSeries.stroke;
- inputSeries.fillOpacity = 0.1;
-
- const priceSeries = chart.series.push(new am4charts.LineSeries());
- priceSeries.dataFields.valueX = "priceNumber";
- priceSeries.dataFields.valueY = "clearingPriceValueY";
- priceSeries.strokeWidth = 2;
- priceSeries.strokeDasharray = "3,3";
- priceSeries.stroke = am4core.color(colors.white);
- priceSeries.fill = inputSeries.stroke;
- priceSeries.fillOpacity = 0.1;
-
- // Add cursor
- chart.cursor = new am4charts.XYCursor();
- chart.cursor.lineX.stroke = am4core.color(colors.white);
- chart.cursor.lineX.strokeWidth = 1;
- chart.cursor.lineX.strokeOpacity = 0.6;
- chart.cursor.lineX.strokeDasharray = "4";
-
- chart.cursor.lineY.stroke = am4core.color(colors.white);
- chart.cursor.lineY.strokeWidth = 1;
- chart.cursor.lineY.strokeOpacity = 0.6;
- chart.cursor.lineY.strokeDasharray = "4";
-
- // Button configuration
- chart.zoomOutButton.background.cornerRadius(5, 5, 5, 5);
- chart.zoomOutButton.background.fill = am4core.color("#25283D");
- chart.zoomOutButton.icon.stroke = am4core.color(colors.white);
- chart.zoomOutButton.icon.strokeWidth = 2;
-
- // Add default empty data array
- chart.data = [];
-
- return chart;
-};
-
-export interface DrawLabelsParams {
- chart: am4charts.XYChart;
- baseToken: Token;
- quoteToken: Token;
- networkId: number;
-}
-
-export const drawLabels = ({
- chart,
- baseToken,
- quoteToken,
-}: DrawLabelsParams): void => {
- const baseTokenLabel = baseToken.symbol;
- const quoteTokenLabel = quoteToken.symbol;
- const market = baseTokenLabel + "-" + quoteTokenLabel;
-
- const [xAxis] = chart.xAxes;
- const [yAxis] = chart.yAxes;
- xAxis.title.text = ` Price (${baseTokenLabel})`;
- yAxis.title.text = ` Volume (${quoteTokenLabel})`;
-
- xAxis.tooltip.background.cornerRadius = 0;
- xAxis.tooltip.background.fill = am4core.color("green");
- yAxis.tooltip.background.cornerRadius = 0;
- yAxis.tooltip.background.fill = am4core.color("red");
-
- xAxis.title.fill = am4core.color("white");
- yAxis.title.fill = am4core.color("white");
-
- const [bidSeries, askSeries] = chart.series;
-
- bidSeries.tooltipText = `[bold]${market}[/]\nBid Price: [bold]{priceFormatted}[/] ${quoteTokenLabel}\nVolume: [bold]{totalVolumeFormatted}[/] ${baseTokenLabel}`;
- askSeries.tooltipText = `[bold]${market}[/]\nAsk Price: [bold]{priceFormatted}[/] ${quoteTokenLabel}\nVolume: [bold]{totalVolumeFormatted}[/] ${baseTokenLabel}`;
-};
-
-const OrderBookChart: React.FC = (
- props: OrderBookChartProps,
-) => {
- const { baseToken, quoteToken, networkId, data } = props;
- const mountPoint = useRef(null);
- const chartRef = useRef(null);
-
- useEffect(() => {
- if (!mountPoint.current) return;
- const chart = createChart(mountPoint.current);
- chartRef.current = chart;
-
- // dispose on mount only
- return (): void => chart.dispose();
- }, []);
-
- useEffect(() => {
- if (!chartRef.current || data === null) return;
-
- if (data.length === 0) {
- chartRef.current.data = [];
- return;
- }
-
- // go on with the update when data is ready
- drawLabels({
- chart: chartRef.current,
- baseToken,
- quoteToken,
- networkId,
- });
-
- chartRef.current.data = data;
- }, [baseToken, networkId, quoteToken, data]);
-
- return Show order book for auction;
-};
-
-export default OrderBookChart;
diff --git a/src/components/OrderbookChartSmall.tsx b/src/components/OrderbookChartSmall.tsx
deleted file mode 100644
index a33030682..000000000
--- a/src/components/OrderbookChartSmall.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import React, { useEffect, useRef } from "react";
-import styled from "styled-components";
-
-import * as am4core from "@amcharts/amcharts4/core";
-import * as am4charts from "@amcharts/amcharts4/charts";
-import {
- OrderBookChartProps,
- DrawLabelsParams,
- createChart,
-} from "./OrderbookChart";
-
-const drawLabels = ({
- chart,
- baseToken,
- quoteToken,
-}: DrawLabelsParams): void => {
- const baseTokenLabel = baseToken.symbol;
- const quoteTokenLabel = quoteToken.symbol;
- const market = baseTokenLabel + "-" + quoteTokenLabel;
-
- const [xAxis] = chart.xAxes;
- const [yAxis] = chart.yAxes;
-
- xAxis.title.text = ` Price (${baseTokenLabel})`;
- yAxis.title.text = ` Volume (${quoteTokenLabel})`;
-
- xAxis.tooltip.background.cornerRadius = 0;
- xAxis.tooltip.background.fill = am4core.color("green");
- yAxis.tooltip.background.cornerRadius = 0;
- yAxis.tooltip.background.fill = am4core.color("red");
-
- xAxis.title.fill = am4core.color("white");
- yAxis.title.fill = am4core.color("white");
-
- const [bidSeries, askSeries] = chart.series;
-
- bidSeries.tooltipText = `[bold]${market}[/]\nBid Price: [bold]{priceFormatted}[/] ${quoteTokenLabel}\nVolume: [bold]{totalVolumeFormatted}[/] ${baseTokenLabel}`;
- askSeries.tooltipText = `[bold]${market}[/]\nAsk Price: [bold]{priceFormatted}[/] ${quoteTokenLabel}\nVolume: [bold]{totalVolumeFormatted}[/] ${baseTokenLabel}`;
-};
-
-const Wrapper = styled.div`
- display: flex;
- justify-content: center;
- align-items: center;
- align-content: center;
- min-height: calc(70vh - 30rem);
- min-width: 550px;
- max-height: 260.44px;
- padding: 26px;
-
- text-align: center;
- box-sizing: border-box;
- color: ${({ theme }) => theme.text2};
- position: relative;
-
- .amcharts-Sprite-group {
- pointer-events: none;
- }
-
- .amcharts-Label {
- text-transform: uppercase;
- font-size: 10px;
- letter-spacing: 1px;
- color: ${({ theme }) => theme.text4};
- margin: 10px;
- }
-
- .amcharts-ZoomOutButton-group > .amcharts-RoundedRectangle-group {
- fill: var(--color-text-active);
- opacity: 0.6;
- transition: 0.3s ease-in-out;
-
- &:hover {
- opacity: 1;
- }
- }
-
- .amcharts-CategoryAxis .amcharts-Label-group > .amcharts-Label,
- .amcharts-ValueAxis-group .amcharts-Label-group > .amcharts-Label {
- fill: ${({ theme }) => theme.text3};
- }
-`;
-
-const OrderBookChartSmall: React.FC = (
- props: OrderBookChartProps,
-) => {
- const { baseToken, quoteToken, networkId, data } = props;
- const mountPoint = useRef(null);
- const chartRef = useRef(null);
-
- useEffect(() => {
- if (!mountPoint.current) return;
- const chart = createChart(mountPoint.current);
- chartRef.current = chart;
-
- // dispose on mount only
- return (): void => chart.dispose();
- }, []);
-
- useEffect(() => {
- if (!chartRef.current) return;
-
- if (data && data.length !== 0) {
- chartRef.current.data = data;
- }
-
- // go on with the update when data is ready
- drawLabels({
- chart: chartRef.current,
- baseToken,
- quoteToken,
- networkId,
- });
- }, [baseToken, networkId, quoteToken, data]);
-
- return Show order book for this auction;
-};
-
-interface OrderBookErrorProps {
- error: Error;
-}
-
-export const OrderBookError: React.FC = ({
- error,
-}: OrderBookErrorProps) => (
- {error ? error.message : "loading"}
-);
-
-export default OrderBookChartSmall;
diff --git a/src/components/Popover/index.tsx b/src/components/Popover/index.tsx
index 977eb7656..987ec2850 100644
--- a/src/components/Popover/index.tsx
+++ b/src/components/Popover/index.tsx
@@ -1,10 +1,12 @@
-import { Placement } from "@popperjs/core";
-import { transparentize } from "polished";
-import React, { useState } from "react";
-import { usePopper } from "react-popper";
-import styled, { keyframes } from "styled-components";
-import useInterval from "../../hooks/useInterval";
-import Portal from "@reach/portal";
+import { transparentize } from 'polished'
+import React, { useState } from 'react'
+import styled, { keyframes } from 'styled-components'
+
+import { Placement } from '@popperjs/core'
+import Portal from '@reach/portal'
+import { usePopper } from 'react-popper'
+
+import useInterval from '../../hooks/useInterval'
const fadeIn = keyframes`
from {
@@ -14,7 +16,7 @@ const fadeIn = keyframes`
to {
opacity : 1;
}
-`;
+`
const fadeOut = keyframes`
from {
@@ -24,12 +26,12 @@ const fadeOut = keyframes`
to {
opacity : 0;
}
-`;
+`
const PopoverContainer = styled.div<{ show: boolean }>`
z-index: 9999;
- visibility: ${(props) => (!props.show ? "hidden" : "visible")};
+ visibility: ${(props) => (!props.show ? 'hidden' : 'visible')};
animation: ${(props) => (!props.show ? fadeOut : fadeIn)} 150ms linear;
transition: visibility 150ms linear;
@@ -38,11 +40,11 @@ const PopoverContainer = styled.div<{ show: boolean }>`
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.shadow1)};
color: ${({ theme }) => theme.text2};
border-radius: 8px;
-`;
+`
const ReferenceElement = styled.div`
display: inline-block;
-`;
+`
const Arrow = styled.div`
width: 8px;
@@ -55,7 +57,7 @@ const Arrow = styled.div`
height: 8px;
z-index: 9998;
- content: "";
+ content: '';
border: 1px solid ${({ theme }) => theme.bg3};
transform: rotate(45deg);
background: ${({ theme }) => theme.bg2};
@@ -93,55 +95,42 @@ const Arrow = styled.div`
border-top: none;
}
}
-`;
+`
export interface PopoverProps {
- content: React.ReactNode;
- show: boolean;
- children: React.ReactNode;
- placement?: Placement;
+ content: React.ReactNode
+ show: boolean
+ children: React.ReactNode
+ placement?: Placement
}
-export default function Popover({
- content,
- show,
- children,
- placement = "auto",
-}: PopoverProps) {
- const [referenceElement, setReferenceElement] = useState(
- null,
- );
- const [popperElement, setPopperElement] = useState(null);
- const [arrowElement, setArrowElement] = useState(null);
- const { styles, update, attributes } = usePopper(
- referenceElement,
- popperElement,
- {
- placement,
- strategy: "fixed",
- modifiers: [
- { name: "offset", options: { offset: [8, 8] } },
- { name: "arrow", options: { element: arrowElement } },
- ],
- },
- );
- useInterval(update, show ? 100 : null);
+export default function Popover({ children, content, placement = 'auto', show }: PopoverProps) {
+ const [referenceElement, setReferenceElement] = useState(null)
+ const [popperElement, setPopperElement] = useState(null)
+ const [arrowElement, setArrowElement] = useState(null)
+ const { attributes, styles, update } = usePopper(referenceElement, popperElement, {
+ placement,
+ strategy: 'fixed',
+ modifiers: [
+ { name: 'offset', options: { offset: [8, 8] } },
+ { name: 'arrow', options: { element: arrowElement } },
+ ],
+ })
+ useInterval(update, show ? 100 : null)
return (
<>
{children}
{content}
>
- );
+ )
}
diff --git a/src/components/Popups/index.tsx b/src/components/Popups/index.tsx
index 792a1ff60..092e19ecb 100644
--- a/src/components/Popups/index.tsx
+++ b/src/components/Popups/index.tsx
@@ -1,17 +1,18 @@
-import { ChainId, Pair, Token } from "uniswap-xdai-sdk";
-import React, { useContext, useMemo } from "react";
-import styled, { ThemeContext } from "styled-components";
-import { useMediaLayout } from "use-media";
-
-import { X } from "react-feather";
-import { PopupContent } from "../../state/application/actions";
-import { useActivePopups, useRemovePopup } from "../../state/application/hooks";
-import { ExternalLink } from "../../theme";
-import { AutoColumn } from "../Column";
-import DoubleTokenLogo from "../DoubleLogo";
-import Row from "../Row";
-import TxnPopup from "../TxnPopup";
-import { Text } from "rebass";
+import React, { useContext, useMemo } from 'react'
+import { X } from 'react-feather'
+import styled, { ThemeContext } from 'styled-components'
+import { ChainId, Pair, Token } from 'uniswap-xdai-sdk'
+
+import { Text } from 'rebass'
+import { useMediaLayout } from 'use-media'
+
+import { PopupContent } from '../../state/application/actions'
+import { useActivePopups, useRemovePopup } from '../../state/application/hooks'
+import { ExternalLink } from '../../theme'
+import { AutoColumn } from '../Column'
+import Row from '../Row'
+import TxnPopup from '../TxnPopup'
+import DoubleTokenLogo from '../common/DoubleLogo'
const StyledClose = styled(X)`
position: absolute;
@@ -21,15 +22,15 @@ const StyledClose = styled(X)`
:hover {
cursor: pointer;
}
-`;
+`
const MobilePopupWrapper = styled.div<{ height: string | number }>`
position: relative;
max-width: 100%;
height: ${({ height }) => height};
- margin: ${({ height }) => (height ? "0 auto;" : 0)};
- margin-bottom: ${({ height }) => (height ? "20px" : 0)}};
-`;
+ margin: ${({ height }) => (height ? '0 auto;' : 0)};
+ margin-bottom: ${({ height }) => (height ? '20px' : 0)}};
+`
const MobilePopupInner = styled.div`
height: 99%;
@@ -41,7 +42,7 @@ const MobilePopupInner = styled.div`
::-webkit-scrollbar {
display: none;
}
-`;
+`
const FixedPopupColumn = styled(AutoColumn)`
position: absolute;
@@ -53,7 +54,7 @@ const FixedPopupColumn = styled(AutoColumn)`
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
-`;
+`
const Popup = styled.div`
display: inline-block;
@@ -70,35 +71,44 @@ const Popup = styled.div`
${({ theme }) => theme.mediaWidth.upToSmall`
min-width: 290px;
`}
-`;
+`
function PoolPopup({
token0,
token1,
}: {
- token0: { address?: string; symbol?: string };
- token1: { address?: string; symbol?: string };
+ token0: { address?: string; symbol?: string }
+ token1: { address?: string; symbol?: string }
}) {
- const pairAddress: string | null = useMemo(() => {
- if (!token0 || !token1) return null;
+ const pairAddress: Maybe = useMemo(() => {
+ if (!token0 || !token1) return null
// just mock it out
return Pair.getAddress(
new Token(ChainId.MAINNET, token0.address, 18),
new Token(ChainId.MAINNET, token1.address, 18),
- );
- }, [token0, token1]);
+ )
+ }, [token0, token1])
return (
-
+
Pool Imported
-
+ {token0 && token1 ? (
+
+ ) : (
+ '-'
+ )}
UNI {token0?.symbol} / {token1?.symbol}
@@ -109,45 +119,32 @@ function PoolPopup({
) : null}
- );
+ )
}
-function PopupItem({
- content,
- popKey,
-}: {
- content: PopupContent;
- popKey: string;
-}) {
- if ("txn" in content) {
+function PopupItem({ content, popKey }: { content: PopupContent; popKey: string }) {
+ if ('txn' in content) {
const {
txn: { hash, success, summary },
- } = content;
- return (
-
- );
- } else if ("poolAdded" in content) {
+ } = content
+ return
+ } else if ('poolAdded' in content) {
const {
poolAdded: { token0, token1 },
- } = content;
+ } = content
- return ;
+ return
}
}
export default function Popups() {
- const theme = useContext(ThemeContext);
+ const theme = useContext(ThemeContext)
// get all popups
- const activePopups = useActivePopups();
- const removePopup = useRemovePopup();
+ const activePopups = useActivePopups()
+ const removePopup = useRemovePopup()
// switch view settings on mobile
- const isMobile = useMediaLayout({ maxWidth: "600px" });
+ const isMobile = useMediaLayout({ maxWidth: '600px' })
if (!isMobile) {
return (
@@ -155,21 +152,18 @@ export default function Popups() {
{activePopups.map((item) => {
return (
- removePopup(item.key)}
- />
+ removePopup(item.key)} />
- );
+ )
})}
- );
+ )
}
//mobile
else
return (
- 0 ? "fit-content" : 0}>
+ 0 ? 'fit-content' : 0}>
{activePopups // reverse so new items up front
.slice(0)
@@ -177,15 +171,12 @@ export default function Popups() {
.map((item) => {
return (
- removePopup(item.key)}
- />
+ removePopup(item.key)} />
- );
+ )
})}
- );
+ )
}
diff --git a/src/components/PositionCard/index.tsx b/src/components/PositionCard/index.tsx
index 4723bf9de..e8f55846c 100644
--- a/src/components/PositionCard/index.tsx
+++ b/src/components/PositionCard/index.tsx
@@ -1,62 +1,57 @@
-import React, { useState } from "react";
-import styled from "styled-components";
-import { darken } from "polished";
-import { RouteComponentProps, withRouter } from "react-router-dom";
-import { Percent, Pair, JSBI } from "uniswap-xdai-sdk";
+import { darken } from 'polished'
+import React, { useState } from 'react'
+import { ChevronDown, ChevronUp } from 'react-feather'
+import { RouteComponentProps, withRouter } from 'react-router-dom'
+import styled from 'styled-components'
+import { JSBI, Pair, Percent } from 'uniswap-xdai-sdk'
-import { useActiveWeb3React } from "../../hooks";
-import { useTotalSupply } from "../../data/TotalSupply";
-import { useTokenBalance } from "../../state/wallet/hooks";
+import { Text } from 'rebass'
-import Card, { GreyCard } from "../Card";
-import TokenLogo from "../TokenLogo";
-import DoubleLogo from "../DoubleLogo";
-import { Text } from "rebass";
-import { ExternalLink } from "../../theme/components";
-import { AutoColumn } from "../Column";
-import { ChevronDown, ChevronUp } from "react-feather";
-import { ButtonSecondary } from "../Button";
-import { RowBetween, RowFixed, AutoRow } from "../Row";
+import { useTotalSupply } from '../../data/TotalSupply'
+import { useActiveWeb3React } from '../../hooks'
+import { useTokenBalance } from '../../state/wallet/hooks'
+import { ExternalLink } from '../../theme/components'
+import { ButtonSecondary } from '../Button'
+import Card, { GreyCard } from '../Card'
+import { AutoColumn } from '../Column'
+import { AutoRow, RowBetween, RowFixed } from '../Row'
+import DoubleLogo from '../common/DoubleLogo'
+import TokenLogo from '../common/TokenLogo'
const FixedHeightRow = styled(RowBetween)`
height: 24px;
-`;
+`
const HoverCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.bg2};
:hover {
border: 1px solid ${({ theme }) => darken(0.06, theme.bg2)};
}
-`;
+`
interface PositionCardProps extends RouteComponentProps {
- pair: Pair;
- minimal?: boolean;
- border?: string;
+ pair: Pair
+ minimal?: boolean
+ border?: string
}
-function PositionCard({
- pair,
- history,
- border,
- minimal = false,
-}: PositionCardProps) {
- const { account } = useActiveWeb3React();
+function PositionCard({ border, history, minimal = false, pair }: PositionCardProps) {
+ const { account } = useActiveWeb3React()
- const token0 = pair?.token0;
- const token1 = pair?.token1;
+ const token0 = pair?.token0
+ const token1 = pair?.token1
- const [showMore, setShowMore] = useState(false);
+ const [showMore, setShowMore] = useState(false)
- const userPoolBalance = useTokenBalance(account, pair?.liquidityToken);
- const totalPoolTokens = useTotalSupply(pair?.liquidityToken);
+ const userPoolBalance = useTokenBalance(account, pair?.liquidityToken)
+ const totalPoolTokens = useTotalSupply(pair?.liquidityToken)
const poolTokenPercentage =
!!userPoolBalance &&
!!totalPoolTokens &&
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
- : undefined;
+ : undefined
const [token0Deposited, token1Deposited] =
!!pair &&
@@ -65,20 +60,10 @@ function PositionCard({
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? [
- pair.getLiquidityValue(
- token0,
- totalPoolTokens,
- userPoolBalance,
- false,
- ),
- pair.getLiquidityValue(
- token1,
- totalPoolTokens,
- userPoolBalance,
- false,
- ),
+ pair.getLiquidityValue(token0, totalPoolTokens, userPoolBalance, false),
+ pair.getLiquidityValue(token1, totalPoolTokens, userPoolBalance, false),
]
- : [undefined, undefined];
+ : [undefined, undefined]
if (minimal) {
return (
@@ -88,26 +73,35 @@ function PositionCard({
-
+
Your current position
setShowMore(!showMore)}>
-
-
+ {token0 && token1 ? (
+
+ ) : (
+ '-'
+ )}
+
{token0?.symbol}/{token1?.symbol}
-
- {userPoolBalance ? userPoolBalance.toSignificant(4) : "-"}
+
+ {userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
@@ -118,18 +112,15 @@ function PositionCard({
{token0Deposited ? (
- {!minimal && }
-
+ {!minimal && token0 && (
+
+ )}
+
{token0Deposited?.toSignificant(6)}
) : (
- "-"
+ '-'
)}
@@ -138,18 +129,15 @@ function PositionCard({
{token1Deposited ? (
- {!minimal && }
-
+ {!minimal && token1 && (
+
+ )}
+
{token1Deposited?.toSignificant(6)}
) : (
- "-"
+ '-'
)}
@@ -157,31 +145,37 @@ function PositionCard({
)}
>
- );
+ )
} else
return (
- setShowMore(!showMore)}
- style={{ cursor: "pointer" }}
- >
+ setShowMore(!showMore)} style={{ cursor: 'pointer' }}>
-
-
+ {token0 && token1 ? (
+
+ ) : (
+ '-'
+ )}
+
{token0?.symbol}/{token1?.symbol}
{showMore ? (
-
+
) : (
-
+
)}
@@ -195,19 +189,18 @@ function PositionCard({
{token0Deposited ? (
-
+
{token0Deposited?.toSignificant(6)}
- {!minimal && (
+ {!minimal && token0 && (
)}
) : (
- "-"
+ '-'
)}
@@ -219,19 +212,18 @@ function PositionCard({
{token1Deposited ? (
-
+
{token1Deposited?.toSignificant(6)}
- {!minimal && (
+ {!minimal && token1 && (
)}
) : (
- "-"
+ '-'
)}
{!minimal && (
@@ -240,7 +232,7 @@ function PositionCard({
Your pool tokens:
- {userPoolBalance ? userPoolBalance.toSignificant(4) : "-"}
+ {userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
)}
@@ -250,38 +242,30 @@ function PositionCard({
Your pool share
- {poolTokenPercentage
- ? poolTokenPercentage.toFixed(2) + "%"
- : "-"}
+ {poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
)}
-
-
+
+
View pool information ↗
{
- history.push(
- "/add/" + token0?.address + "-" + token1?.address,
- );
+ history.push('/add/' + token0?.address + '-' + token1?.address)
}}
+ width="48%"
>
Add
{
- history.push(
- "/remove/" + token0?.address + "-" + token1?.address,
- );
+ history.push('/remove/' + token0?.address + '-' + token1?.address)
}}
+ width="48%"
>
Remove
@@ -290,7 +274,7 @@ function PositionCard({
)}
- );
+ )
}
-export default withRouter(PositionCard);
+export default withRouter(PositionCard)
diff --git a/src/components/PriceInputPanel/index.tsx b/src/components/PriceInputPanel/index.tsx
deleted file mode 100644
index 1c8b0fc4c..000000000
--- a/src/components/PriceInputPanel/index.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import { Pair, Token } from "uniswap-xdai-sdk";
-import React, { useContext } from "react";
-import styled, { ThemeContext } from "styled-components";
-import { darken } from "polished";
-import DoubleLogo from "../DoubleLogo";
-import { RowBetween } from "../Row";
-import { TYPE } from "../../theme";
-import { Input as NumericalInput } from "../NumericalInput";
-
-const InputRow = styled.div<{ selected: boolean }>`
- ${({ theme }) => theme.flexRowNoWrap}
- align-items: center;
- padding: ${({ selected }) =>
- selected ? "0.75rem 0.5rem 0.75rem 1rem" : "0.75rem 0.75rem 0.75rem 1rem"};
-`;
-
-const CurrencySelect = styled.button<{ selected: boolean }>`
- align-items: center;
- height: 2.2rem;
- font-size: 20px;
- font-weight: 500;
- background-color: ${({ selected, theme }) =>
- selected ? theme.bg1 : theme.primary1};
- color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
- border-radius: 12px;
- box-shadow: ${({ selected }) =>
- selected ? "none" : "0px 6px 10px rgba(0, 0, 0, 0.075)"};
- outline: none;
- user-select: none;
- border: none;
- padding: 0 0.5rem;
-
- /* :focus,
- :hover {
- background-color: ${({ selected, theme }) =>
- selected ? theme.bg2 : darken(0.05, theme.primary1)};
- } */
-`;
-
-const LabelRow = styled.div`
- ${({ theme }) => theme.flexRowNoWrap}
- align-items: center;
- color: ${({ theme }) => theme.text1};
- font-size: 0.75rem;
- line-height: 1rem;
- padding: 0.75rem 1rem 0 1rem;
- height: 20px;
- span:hover {
- cursor: pointer;
- color: ${({ theme }) => darken(0.2, theme.text2)};
- }
-`;
-
-const Aligner = styled.span`
- display: flex;
- align-items: center;
- justify-content: space-between;
-`;
-
-const InputPanel = styled.div<{ hideInput?: boolean }>`
- ${({ theme }) => theme.flexColumnNoWrap}
- position: relative;
- border-radius: ${({ hideInput }) => (hideInput ? "8px" : "20px")};
- background-color: ${({ theme }) => theme.bg2};
- z-index: 1;
-`;
-
-const Container = styled.div<{ hideInput: boolean }>`
- border-radius: ${({ hideInput }) => (hideInput ? "8px" : "20px")};
- border: 1px solid ${({ theme }) => theme.bg2};
- background-color: ${({ theme }) => theme.bg1};
-`;
-
-interface CurrencyInputPanelProps {
- value: string;
- onUserPriceInput: (val: string) => void;
- onMax?: () => void;
- showMaxButton: boolean;
- label?: string;
- onTokenSelection?: (tokenAddress: string) => void;
- biddingToken: Token | null;
- auctioningToken: Token | null;
- disableTokenSelect?: boolean;
- hideBalance?: boolean;
- isExchange?: boolean;
- pair?: Pair | null;
- hideInput?: boolean;
- showSendWithSwap?: boolean;
- otherSelectedTokenAddress?: string | null;
- id: string;
-}
-
-export default function PriceInputPanel({
- value,
- onUserPriceInput,
- label = "Input",
- biddingToken = null,
- auctioningToken = null,
- disableTokenSelect = false,
- hideInput = false,
- id,
-}: CurrencyInputPanelProps) {
- const theme = useContext(ThemeContext);
-
- return (
-
-
- {!hideInput && (
-
-
-
- {label}
-
-
-
- )}
-
- {!hideInput && (
- <>
- {
- onUserPriceInput(val);
- }}
- />
- >
- )}
-
-
- {
-
- }
- {!disableTokenSelect}
-
-
-
-
-
- );
-}
diff --git a/src/components/QuestionHelper/index.tsx b/src/components/QuestionHelper/index.tsx
index 9b7369aa9..9c6476eaa 100644
--- a/src/components/QuestionHelper/index.tsx
+++ b/src/components/QuestionHelper/index.tsx
@@ -1,7 +1,8 @@
-import React, { useCallback, useState } from "react";
-import { HelpCircle as Question } from "react-feather";
-import styled from "styled-components";
-import Tooltip from "../Tooltip";
+import React, { useCallback, useState } from 'react'
+import { HelpCircle as Question } from 'react-feather'
+import styled from 'styled-components'
+
+import Tooltip from '../Tooltip'
const QuestionWrapper = styled.div`
display: flex;
@@ -20,31 +21,21 @@ const QuestionWrapper = styled.div`
:focus {
opacity: 0.7;
}
-`;
+`
-export default function QuestionHelper({
- text,
- disabled,
-}: {
- text: string;
- disabled?: boolean;
-}) {
- const [show, setShow] = useState(false);
+export default function QuestionHelper({ disabled, text }: { text: string; disabled?: boolean }) {
+ const [show, setShow] = useState(false)
- const open = useCallback(() => setShow(true), [setShow]);
- const close = useCallback(() => setShow(false), [setShow]);
+ const open = useCallback(() => setShow(true), [setShow])
+ const close = useCallback(() => setShow(false), [setShow])
return (
-
-
+
+
- );
+ )
}
diff --git a/src/components/Row/index.tsx b/src/components/Row/index.tsx
index c23a75548..181142dab 100644
--- a/src/components/Row/index.tsx
+++ b/src/components/Row/index.tsx
@@ -1,11 +1,11 @@
-import styled from "styled-components";
-import { Box } from "rebass/styled-components";
+import { Box } from 'rebass/styled-components'
+import styled from 'styled-components'
const Row = styled(Box)<{
- align?: string;
- padding?: string;
- border?: string;
- borderRadius?: string;
+ align?: string
+ padding?: string
+ border?: string
+ borderRadius?: string
}>`
width: 100%;
display: flex;
@@ -15,21 +15,21 @@ const Row = styled(Box)<{
padding: ${({ padding }) => padding};
border: ${({ border }) => border};
border-radius: ${({ borderRadius }) => borderRadius};
-`;
+`
export const RowBetween = styled(Row)<{
- align?: string;
- padding?: string;
- border?: string;
- borderRadius?: string;
+ align?: string
+ padding?: string
+ border?: string
+ borderRadius?: string
}>`
justify-content: space-between;
-`;
+`
export const RowFlat = styled.div`
display: flex;
align-items: flex-end;
-`;
+`
export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>`
flex-wrap: wrap;
@@ -39,11 +39,11 @@ export const AutoRow = styled(Row)<{ gap?: string; justify?: string }>`
& > * {
margin: ${({ gap }) => gap} !important;
}
-`;
+`
export const RowFixed = styled(Row)<{ gap?: string; justify?: string }>`
width: fit-content;
margin: ${({ gap }) => gap && `-${gap}`};
-`;
+`
-export default Row;
+export default Row
diff --git a/src/components/Slider/index.tsx b/src/components/Slider/index.tsx
index 68f1cb921..0ff0a538f 100644
--- a/src/components/Slider/index.tsx
+++ b/src/components/Slider/index.tsx
@@ -1,5 +1,5 @@
-import React, { useCallback } from "react";
-import styled from "styled-components";
+import React, { useCallback } from 'react'
+import styled from 'styled-components'
const StyledRangeInput = styled.input<{ value: number }>`
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
@@ -27,9 +27,8 @@ const StyledRangeInput = styled.input<{ value: number }>`
&:hover,
&:focus {
- box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1),
- 0px 4px 8px rgba(0, 0, 0, 0.08), 0px 16px 24px rgba(0, 0, 0, 0.06),
- 0px 24px 32px rgba(0, 0, 0, 0.04);
+ box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1), 0px 4px 8px rgba(0, 0, 0, 0.08),
+ 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 24px 32px rgba(0, 0, 0, 0.04);
}
}
@@ -43,9 +42,8 @@ const StyledRangeInput = styled.input<{ value: number }>`
&:hover,
&:focus {
- box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1),
- 0px 4px 8px rgba(0, 0, 0, 0.08), 0px 16px 24px rgba(0, 0, 0, 0.06),
- 0px 24px 32px rgba(0, 0, 0, 0.04);
+ box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1), 0px 4px 8px rgba(0, 0, 0, 0.08),
+ 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 24px 32px rgba(0, 0, 0, 0.04);
}
}
@@ -58,9 +56,8 @@ const StyledRangeInput = styled.input<{ value: number }>`
&:hover,
&:focus {
- box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1),
- 0px 4px 8px rgba(0, 0, 0, 0.08), 0px 16px 24px rgba(0, 0, 0, 0.06),
- 0px 24px 32px rgba(0, 0, 0, 0.04);
+ box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1), 0px 4px 8px rgba(0, 0, 0, 0.08),
+ 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 24px 32px rgba(0, 0, 0, 0.04);
}
}
@@ -100,36 +97,36 @@ const StyledRangeInput = styled.input<{ value: number }>`
&::-ms-fill-upper {
background: ${({ theme }) => theme.bg3};
}
-`;
+`
interface InputSliderProps {
- value: number;
- onChange: (value: number) => void;
+ value: number
+ onChange: (value: number) => void
}
-export default function InputSlider({ value, onChange }: InputSliderProps) {
+export default function InputSlider({ onChange, value }: InputSliderProps) {
const changeCallback = useCallback(
(e) => {
- onChange(e.target.value);
+ onChange(e.target.value)
},
[onChange],
- );
+ )
return (
- );
+ )
}
diff --git a/src/components/TokenLogo/index.tsx b/src/components/TokenLogo/index.tsx
deleted file mode 100644
index 99a4b7475..000000000
--- a/src/components/TokenLogo/index.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import React, { useState } from "react";
-import styled from "styled-components";
-import { isAddress } from "../../utils";
-import { useActiveWeb3React } from "../../hooks";
-import { WETH } from "uniswap-xdai-sdk";
-
-import EthereumLogo from "../../assets/images/ethereum-logo.png";
-
-const TOKEN_ICON_API = (address) =>
- `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`;
-const BAD_IMAGES = {};
-
-const Image = styled.img<{ size: string }>`
- width: ${({ size }) => size};
- height: ${({ size }) => size};
- background-color: white;
- border-radius: ${({ size }) => size};
- box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
-`;
-
-const Emoji = styled.span<{ size?: string }>`
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: ${({ size }) => size};
- width: ${({ size }) => size};
- height: ${({ size }) => size};
- margin-bottom: -4px;
-`;
-
-const StyledEthereumLogo = styled.img<{ size: string }>`
- width: ${({ size }) => size};
- height: ${({ size }) => size};
- box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
- border-radius: 24px;
-`;
-
-export default function TokenLogo({
- address,
- size = "24px",
- ...rest
-}: {
- address?: string;
- size?: string;
- style?: React.CSSProperties;
-}) {
- const [error, setError] = useState(false);
- const { chainId } = useActiveWeb3React();
-
- // mock rinkeby DAI
- if (
- chainId === 4 &&
- address &&
- address.toLowerCase() ===
- "0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa".toLowerCase()
- ) {
- address = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
- }
- // mock rinkeby WBTC
- if (
- chainId === 4 &&
- address &&
- address.toLowerCase() ===
- "0x577d296678535e4903d59a4c929b718e1d575e0a".toLowerCase()
- ) {
- address = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599";
- }
-
- // mock rinkeby GNO
- if (
- chainId === 4 &&
- address &&
- address.toLowerCase() ===
- "0xd0dab4e640d95e9e8a47545598c33e31bdb53c7c".toLowerCase()
- ) {
- address = "0x6810e776880C02933D47DB1b9fc05908e5386b96";
- }
-
- // mock rinkeby STAKE on xDAI
- if (
- chainId === 100 &&
- address &&
- address.toLowerCase() ===
- "0xb7D311E2Eb55F2f68a9440da38e7989210b9A05e".toLowerCase()
- ) {
- address = "0x0Ae055097C6d159879521C384F1D2123D1f195e6";
- }
-
- let path = "";
- // hard code to show ETH instead of WETH in UI
- if (
- address &&
- address.toLowerCase() === WETH[chainId].address.toLowerCase()
- ) {
- return ;
- } else if (!error && !BAD_IMAGES[address] && isAddress(address)) {
- path = TOKEN_ICON_API(address);
- } else {
- return (
-
-
- 🤔
-
-
- );
- }
-
- return (
- {
- BAD_IMAGES[address] = true;
- setError(true);
- }}
- />
- );
-}
diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx
index 1df511c45..3172703ee 100644
--- a/src/components/Tooltip/index.tsx
+++ b/src/components/Tooltip/index.tsx
@@ -1,20 +1,19 @@
-import React from "react";
-import styled from "styled-components";
-import Popover, { PopoverProps } from "../Popover";
+import React from 'react'
+import styled from 'styled-components'
+
+import Popover, { PopoverProps } from '../Popover'
const TooltipContainer = styled.div`
width: 228px;
padding: 0.6rem 1rem;
line-height: 150%;
font-weight: 400;
-`;
+`
-interface TooltipProps extends Omit {
- text: string;
+interface TooltipProps extends Omit {
+ text: string
}
export default function Tooltip({ text, ...rest }: TooltipProps) {
- return (
- {text}} {...rest} />
- );
+ return {text}} {...rest} />
}
diff --git a/src/components/TxnPopup/index.tsx b/src/components/TxnPopup/index.tsx
index 01cf772ef..f47466193 100644
--- a/src/components/TxnPopup/index.tsx
+++ b/src/components/TxnPopup/index.tsx
@@ -1,17 +1,15 @@
-import React, { useCallback, useState } from "react";
-import { AlertCircle, CheckCircle } from "react-feather";
+import React, { useCallback, useState } from 'react'
+import { AlertCircle, CheckCircle } from 'react-feather'
+import styled from 'styled-components'
-import styled from "styled-components";
-
-import { useActiveWeb3React } from "../../hooks";
-import useInterval from "../../hooks/useInterval";
-import { useRemovePopup } from "../../state/application/hooks";
-import { TYPE } from "../../theme";
-
-import { ExternalLink } from "../../theme/components";
-import { getEtherscanLink } from "../../utils";
-import { AutoColumn } from "../Column";
-import { AutoRow } from "../Row";
+import { useActiveWeb3React } from '../../hooks'
+import useInterval from '../../hooks/useInterval'
+import { useRemovePopup } from '../../state/application/hooks'
+import { TYPE } from '../../theme'
+import { ExternalLink } from '../../theme/components'
+import { getEtherscanLink } from '../../utils'
+import { AutoColumn } from '../Column'
+import { AutoRow } from '../Row'
const Fader = styled.div<{ count: number }>`
position: absolute;
@@ -21,68 +19,52 @@ const Fader = styled.div<{ count: number }>`
height: 2px;
background-color: ${({ theme }) => theme.bg3};
transition: width 100ms linear;
-`;
+`
-const delay = 100;
+const delay = 100
export default function TxnPopup({
hash,
+ popKey,
success,
summary,
- popKey,
}: {
- hash: string;
- success?: boolean;
- summary?: string;
- popKey?: string;
+ hash: string
+ success?: boolean
+ summary?: string
+ popKey?: string
}) {
- const { chainId } = useActiveWeb3React();
- const [count, setCount] = useState(1);
+ const { chainId } = useActiveWeb3React()
+ const [count, setCount] = useState(1)
- const [isRunning, setIsRunning] = useState(true);
- const removePopup = useRemovePopup();
+ const [isRunning, setIsRunning] = useState(true)
+ const removePopup = useRemovePopup()
- const removeThisPopup = useCallback(() => removePopup(popKey), [
- popKey,
- removePopup,
- ]);
+ const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup])
useInterval(
() => {
- count > 150 ? removeThisPopup() : setCount(count + 1);
+ count > 150 ? removeThisPopup() : setCount(count + 1)
},
isRunning ? delay : null,
- );
+ )
return (
- setIsRunning(false)}
- onMouseLeave={() => setIsRunning(true)}
- >
+ setIsRunning(false)} onMouseLeave={() => setIsRunning(true)}>
{success ? (
-
+
) : (
-
+
)}
- {summary
- ? summary
- : "Hash: " + hash.slice(0, 8) + "..." + hash.slice(58, 65)}
+ {summary ? summary : 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}
-
+
View on Etherscan
- );
+ )
}
diff --git a/src/components/WalletModal/WalletConnectData.tsx b/src/components/WalletModal/WalletConnectData.tsx
deleted file mode 100644
index 3e34eb5b7..000000000
--- a/src/components/WalletModal/WalletConnectData.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-import QRCode from "qrcode.react";
-
-const QRCodeWrapper = styled.div`
- ${({ theme }) => theme.flexColumnNoWrap};
- align-items: center;
- justify-content: center;
- border-radius: 12px;
- margin-bottom: 20px;
-`;
-
-interface WalletConnectDataProps {
- uri?: string;
- size: number;
-}
-
-export default function WalletConnectData({
- uri = "",
- size,
-}: WalletConnectDataProps) {
- return (
- {uri && }
- );
-}
diff --git a/src/components/Web3ReactManager/index.tsx b/src/components/Web3ReactManager/index.tsx
index 87df6c680..459963426 100644
--- a/src/components/Web3ReactManager/index.tsx
+++ b/src/components/Web3ReactManager/index.tsx
@@ -1,29 +1,27 @@
-import React, { useState, useEffect } from "react";
-import { useWeb3React } from "@web3-react/core";
-import styled from "styled-components";
-import { useTranslation } from "react-i18next";
-
-import { network } from "../../connectors";
-import {
- useEagerConnect,
- useInactiveListener,
- useActiveListener,
-} from "../../hooks";
-import { Spinner } from "../../theme";
-import Circle from "../../assets/images/circle.svg";
-import { NetworkContextName } from "../../constants";
-import { useSwapState } from "../../state/orderPlacement/hooks";
+import React, { useEffect, useState } from 'react'
+import styled from 'styled-components'
+
+import { useWeb3React } from '@web3-react/core'
+import { useTranslation } from 'react-i18next'
+
+import { tokenLogosServiceApi } from '../../api'
+import Circle from '../../assets/images/circle.svg'
+import { network } from '../../connectors'
+import { NetworkContextName } from '../../constants'
+import { useActiveListener, useEagerConnect, useInactiveListener } from '../../hooks'
+import { useTokenListActionHandlers } from '../../state/tokenList/hooks'
+import { Spinner } from '../../theme'
const MessageWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
height: 20rem;
-`;
+`
const Message = styled.h2`
color: ${({ theme }) => theme.secondary1};
-`;
+`
const SpinnerWrapper = styled(Spinner)`
font-size: 4rem;
@@ -33,65 +31,77 @@ const SpinnerWrapper = styled(Spinner)`
color: ${({ theme }) => theme.secondary1};
}
}
-`;
+`
export default function Web3ReactManager({ children }) {
- const { t } = useTranslation();
- const { active } = useWeb3React();
- const {
- active: networkActive,
- error: networkError,
- activate: activateNetwork,
- } = useWeb3React(NetworkContextName);
- const { chainId } = useSwapState();
+ const { t } = useTranslation()
+ const { active } = useWeb3React()
+ const { activate: activateNetwork, active: networkActive, error: networkError } = useWeb3React(
+ NetworkContextName,
+ )
+ const { onLoadTokenList } = useTokenListActionHandlers()
// try to eagerly connect to an injected provider, if it exists and has granted access already
- const triedEager = useEagerConnect();
+ const triedEager = useEagerConnect()
// after eagerly trying injected, if the network connect ever isn't active or in an error state, activate itd
useEffect(() => {
if (triedEager && !networkActive && !networkError && !active) {
- activateNetwork(network[chainId]);
+ activateNetwork(network)
}
- }, [
- triedEager,
- networkActive,
- networkError,
- activateNetwork,
- active,
- chainId,
- ]);
+ }, [triedEager, networkActive, networkError, activateNetwork, active])
// when there's no account connected, react to logins (broadly speaking) on the injected provider, if it exists
- useInactiveListener(!triedEager);
+ useInactiveListener(!triedEager)
// So we can trigger some events on accountsChanged
- useActiveListener();
+ useActiveListener()
// handle delayed loader state
- const [showLoader, setShowLoader] = useState(false);
+ const [showLoader, setShowLoader] = useState(false)
useEffect(() => {
const timeout = setTimeout(() => {
- setShowLoader(true);
- }, 600);
+ setShowLoader(true)
+ }, 600)
return () => {
- clearTimeout(timeout);
- };
- }, []);
+ clearTimeout(timeout)
+ }
+ }, [])
+
+ // Fetch token logos by chain ID
+ useEffect(() => {
+ const fetchTokenList = async (): Promise => {
+ try {
+ setShowLoader(true)
+
+ const data = await tokenLogosServiceApi.getAllTokens()
+
+ onLoadTokenList(data)
+ setShowLoader(false)
+ } catch (error) {
+ console.error('Error getting token list', error)
+
+ onLoadTokenList(null)
+ setShowLoader(false)
+ }
+ }
+
+ fetchTokenList()
+ }, [onLoadTokenList])
// on page load, do nothing until we've tried to connect to the injected connector
if (!triedEager) {
- return null;
+ return null
}
// if the account context isn't active, and there's an error on the network context, it's an irrecoverable error
if (!active && networkError) {
return (
- {t("unknownError")}
+ {t('unknownError')}
- );
+ )
}
// if neither context is active, spin
@@ -100,8 +110,8 @@ export default function Web3ReactManager({ children }) {
- ) : null;
+ ) : null
}
- return children;
+ return children
}
diff --git a/src/components/Web3Status/index.tsx b/src/components/Web3Status/index.tsx
index d8991f45e..9ecbe1c19 100644
--- a/src/components/Web3Status/index.tsx
+++ b/src/components/Web3Status/index.tsx
@@ -1,51 +1,44 @@
-import React, { useMemo } from "react";
-import styled, { css } from "styled-components";
-import { useTranslation } from "react-i18next";
-import { useWeb3React, UnsupportedChainIdError } from "@web3-react/core";
-import { darken, lighten } from "polished";
-import { Activity } from "react-feather";
-import useENSName from "../../hooks/useENSName";
-import { useWalletModalToggle } from "../../state/application/hooks";
-import { TransactionDetails } from "../../state/transactions/reducer";
+import { darken, lighten } from 'polished'
+import React, { useMemo } from 'react'
+import { Activity } from 'react-feather'
+import styled, { css } from 'styled-components'
-import Identicon from "../Identicon";
-import PortisIcon from "../../assets/images/portisIcon.png";
-import WalletModal from "../WalletModal";
-import { ButtonSecondary } from "../Button";
-import FortmaticIcon from "../../assets/images/fortmaticIcon.png";
-import WalletConnectIcon from "../../assets/images/walletConnectIcon.svg";
-import CoinbaseWalletIcon from "../../assets/images/coinbaseWalletIcon.svg";
+import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { useTranslation } from 'react-i18next'
-import { Spinner } from "../../theme";
-import LightCircle from "../../assets/svg/lightcircle.svg";
-
-import { RowBetween } from "../Row";
-import { shortenAddress } from "../../utils";
-import { useAllTransactions } from "../../state/transactions/hooks";
-import { chainNames, NetworkContextName } from "../../constants";
-import {
- injected,
- walletconnect,
- walletlink,
- fortmatic,
- portis,
-} from "../../connectors";
-import { useSwapState } from "../../state/orderPlacement/hooks";
-import { useActiveWeb3React } from "../../hooks";
+import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
+import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
+import PortisIcon from '../../assets/images/portisIcon.png'
+import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
+import LightCircle from '../../assets/svg/lightcircle.svg'
+import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
+import { NetworkContextName, chainNames } from '../../constants'
+import { useActiveWeb3React } from '../../hooks'
+import useENSName from '../../hooks/useENSName'
+import { useWalletModalToggle } from '../../state/application/hooks'
+import { useSwapState } from '../../state/orderPlacement/hooks'
+import { useAllTransactions } from '../../state/transactions/hooks'
+import { TransactionDetails } from '../../state/transactions/reducer'
+import { Spinner } from '../../theme'
+import { shortenAddress } from '../../utils'
+import { ButtonSecondary } from '../Button'
+import Identicon from '../Identicon'
+import { RowBetween } from '../Row'
+import WalletModal from '../modals/WalletModal'
const SpinnerWrapper = styled(Spinner)`
margin: 0 0.25rem 0 0.25rem;
-`;
+`
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
& > * {
- height: ${({ size }) => (size ? size + "px" : "32px")};
- width: ${({ size }) => (size ? size + "px" : "32px")};
+ height: ${({ size }) => (size ? size + 'px' : '32px')};
+ width: ${({ size }) => (size ? size + 'px' : '32px')};
}
-`;
+`
const Web3StatusGeneric = styled(ButtonSecondary)`
${({ theme }) => theme.flexRowNoWrap}
@@ -58,7 +51,7 @@ const Web3StatusGeneric = styled(ButtonSecondary)`
:focus {
outline: none;
}
-`;
+`
const Web3StatusError = styled(Web3StatusGeneric)`
background-color: ${({ theme }) => theme.red1};
border: 1px solid ${({ theme }) => theme.red1};
@@ -68,7 +61,7 @@ const Web3StatusError = styled(Web3StatusGeneric)`
:focus {
background-color: ${({ theme }) => darken(0.1, theme.red1)};
}
-`;
+`
const Web3StatusConnect = styled(Web3StatusGeneric)<{ faded?: boolean }>`
background-color: ${({ theme }) => theme.primary4};
@@ -95,13 +88,11 @@ const Web3StatusConnect = styled(Web3StatusGeneric)<{ faded?: boolean }>`
color: ${({ theme }) => darken(0.05, theme.primaryText1)};
}
`}
-`;
+`
const Web3StatusConnected = styled(Web3StatusGeneric)<{ pending?: boolean }>`
- background-color: ${({ pending, theme }) =>
- pending ? theme.primary1 : theme.bg2};
- border: 1px solid
- ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg3)};
+ background-color: ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg2)};
+ border: 1px solid ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg3)};
color: ${({ pending, theme }) => (pending ? theme.white : theme.text1)};
font-weight: 500;
:hover,
@@ -111,11 +102,10 @@ const Web3StatusConnected = styled(Web3StatusGeneric)<{ pending?: boolean }>`
:focus {
border: 1px solid
- ${({ pending, theme }) =>
- pending ? darken(0.1, theme.primary1) : darken(0.1, theme.bg3)};
+ ${({ pending, theme }) => (pending ? darken(0.1, theme.primary1) : darken(0.1, theme.bg3))};
}
}
-`;
+`
const Text = styled.p`
flex: 1 1 auto;
@@ -126,90 +116,86 @@ const Text = styled.p`
font-size: 1rem;
width: fit-content;
font-weight: 500;
-`;
+`
const NetworkIcon = styled(Activity)`
margin-left: 0.25rem;
margin-right: 0.5rem;
width: 16px;
height: 16px;
-`;
+`
// we want the latest one to come first, so return negative if a is after b
function newTranscationsFirst(a: TransactionDetails, b: TransactionDetails) {
- return b.addedTime - a.addedTime;
+ return b.addedTime - a.addedTime
}
function recentTransactionsOnly(a: TransactionDetails) {
- return new Date().getTime() - a.addedTime < 86_400_000;
+ return new Date().getTime() - a.addedTime < 86_400_000
}
export function useNetworkCheck(): { errorWrongNetwork: string | undefined } {
- const { chainId: injectedChainId } = useActiveWeb3React();
- const { chainId } = useSwapState();
+ const { chainId: injectedChainId } = useActiveWeb3React()
+ const { chainId } = useSwapState()
const errorWrongNetwork =
injectedChainId == undefined || chainId == injectedChainId || chainId == 0
? undefined
- : `Please make sure you connect to the network: ${chainNames[chainId]} in your wallet`;
+ : `Please make sure you connect to the network: ${chainNames[chainId]} in your wallet`
return {
errorWrongNetwork,
- };
+ }
}
export default function Web3Status() {
- const { t } = useTranslation();
- const { active, account, connector, error } = useWeb3React();
- const contextNetwork = useWeb3React(NetworkContextName);
+ const { t } = useTranslation()
+ const { account, active, connector, error } = useWeb3React()
+ const contextNetwork = useWeb3React(NetworkContextName)
- const ENSName = useENSName(account);
+ const ENSName = useENSName(account)
- const allTransactions = useAllTransactions();
+ const allTransactions = useAllTransactions()
const sortedRecentTransactions = useMemo(() => {
- const txs = Object.values(allTransactions);
- return txs.filter(recentTransactionsOnly).sort(newTranscationsFirst);
- }, [allTransactions]);
+ const txs = Object.values(allTransactions)
+ return txs.filter(recentTransactionsOnly).sort(newTranscationsFirst)
+ }, [allTransactions])
- const pending = sortedRecentTransactions
- .filter((tx) => !tx.receipt)
- .map((tx) => tx.hash);
- const confirmed = sortedRecentTransactions
- .filter((tx) => tx.receipt)
- .map((tx) => tx.hash);
+ const pending = sortedRecentTransactions.filter((tx) => !tx.receipt).map((tx) => tx.hash)
+ const confirmed = sortedRecentTransactions.filter((tx) => tx.receipt).map((tx) => tx.hash)
- const hasPendingTransactions = !!pending.length;
+ const hasPendingTransactions = !!pending.length
- const toggleWalletModal = useWalletModalToggle();
+ const toggleWalletModal = useWalletModalToggle()
- const { errorWrongNetwork } = useNetworkCheck();
+ const { errorWrongNetwork } = useNetworkCheck()
// handle the logo we want to show with the account
function getStatusIcon() {
if (connector === injected) {
- return ;
+ return
} else if (connector === walletconnect) {
return (
-
+
- );
+ )
} else if (connector === walletlink) {
return (
-
+
- );
+ )
} else if (connector === fortmatic) {
return (
-
+
- );
+ )
} else if (connector === portis) {
return (
-
+
- );
+ )
}
}
@@ -223,51 +209,48 @@ export default function Web3Status() {
>
{hasPendingTransactions ? (
- {pending?.length} Pending{" "}
-
+ {pending?.length} Pending{' '}
+
) : (
{ENSName || shortenAddress(account)}
)}
{!hasPendingTransactions && getStatusIcon()}
- );
+ )
} else if (error || errorWrongNetwork) {
return (
{error instanceof UnsupportedChainIdError || errorWrongNetwork
- ? "Wrong Network"
- : "Error"}
+ ? 'Wrong Network'
+ : 'Error'}
- );
+ )
} else {
return (
-
- {t("Connect to a wallet")}
+
+ {t('Connect to a wallet')}
- );
+ )
}
}
if (!contextNetwork.active && !active) {
- return null;
+ return null
}
return (
<>
{getWeb3Status()}
>
- );
+ )
}
diff --git a/src/components/auction/AuctionDetails/index.tsx b/src/components/auction/AuctionDetails/index.tsx
new file mode 100644
index 000000000..d78a94517
--- /dev/null
+++ b/src/components/auction/AuctionDetails/index.tsx
@@ -0,0 +1,208 @@
+import React, { useMemo } from 'react'
+import styled from 'styled-components'
+
+import { useActiveWeb3React } from '../../../hooks'
+import { useClearingPriceInfo } from '../../../hooks/useCurrentClearingOrderAndVolumeCallback'
+import {
+ AuctionState,
+ orderToPrice,
+ orderToSellOrder,
+ useDerivedAuctionInfo,
+ useDerivedAuctionState,
+} from '../../../state/orderPlacement/hooks'
+import { getEtherscanLink, getTokenDisplay } from '../../../utils'
+import { normalizePrice } from '../../../utils/tools'
+import { KeyValue } from '../../common/KeyValue'
+import TokenLogo from '../../common/TokenLogo'
+import { Tooltip } from '../../common/Tooltip'
+import { ExternalLink } from '../../navigation/ExternalLink'
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+import { AuctionTimer } from '../AuctionTimer'
+
+const Wrapper = styled(BaseCard)`
+ align-items: center;
+ display: grid;
+ grid-template-columns: 1fr 3px 1fr 154px 1fr 3px 1fr;
+ margin: 0 0 50px;
+ max-width: 100%;
+ min-height: 130px;
+`
+
+const Cell = styled(KeyValue)`
+ padding: 0 10px;
+
+ &:first-child {
+ padding-left: 0;
+ }
+
+ &:last-child {
+ padding-right: 0;
+ }
+`
+
+const Break = styled.div`
+ background-color: ${({ theme }) => theme.primary1};
+ border-radius: 3px;
+ min-height: 50px;
+ width: 3px;
+`
+
+const TimerWrapper = styled.div`
+ max-height: 130px;
+ position: relative;
+`
+
+const AuctionDetails = () => {
+ const { chainId } = useActiveWeb3React()
+ const {
+ auctioningToken,
+ biddingToken,
+ clearingPrice,
+ initialAuctionOrder,
+ initialPrice,
+ } = useDerivedAuctionInfo()
+ const { auctionState } = useDerivedAuctionState()
+
+ const auctionTokenAddress = useMemo(
+ () => getEtherscanLink(chainId, auctioningToken?.address, 'address'),
+ [chainId, auctioningToken],
+ )
+
+ const biddingTokenAddress = useMemo(
+ () => getEtherscanLink(chainId, biddingToken?.address, 'address'),
+ [chainId, biddingToken],
+ )
+
+ const clearingPriceInfo = useClearingPriceInfo()
+ const biddingTokenDisplay = useMemo(() => getTokenDisplay(biddingToken), [biddingToken])
+ const auctioningTokenDisplay = useMemo(() => getTokenDisplay(auctioningToken), [auctioningToken])
+
+ const clearingPriceDisplay = useMemo(() => {
+ const clearingPriceInfoAsSellOrder =
+ clearingPriceInfo &&
+ orderToSellOrder(clearingPriceInfo.clearingOrder, biddingToken, auctioningToken)
+ let clearingPriceNumber = orderToPrice(clearingPriceInfoAsSellOrder)?.toSignificant(4)
+
+ if (clearingPrice && auctioningToken && biddingToken) {
+ clearingPriceNumber = normalizePrice(
+ auctioningToken,
+ biddingToken,
+ clearingPrice,
+ ).toSignificant(4)
+ }
+
+ return clearingPriceNumber
+ ? `${clearingPriceNumber} ${getTokenDisplay(biddingToken)}/${getTokenDisplay(
+ auctioningToken,
+ )}`
+ : '-'
+ }, [auctioningToken, biddingToken, clearingPrice, clearingPriceInfo])
+
+ const titlePrice = useMemo(
+ () =>
+ auctionState === AuctionState.ORDER_PLACING ||
+ auctionState === AuctionState.ORDER_PLACING_AND_CANCELING
+ ? 'Current price'
+ : auctionState === AuctionState.PRICE_SUBMISSION
+ ? 'Clearing price'
+ : 'Closing price',
+ [auctionState],
+ )
+
+ const initialPriceToDisplay = useMemo(() => {
+ if (initialPrice && auctioningToken && biddingToken) {
+ return normalizePrice(auctioningToken, biddingToken, initialPrice)
+ } else {
+ return initialPrice
+ }
+ }, [initialPrice, auctioningToken, biddingToken])
+
+ return (
+
+
+ {titlePrice}
+
+ >
+ }
+ itemValue={clearingPriceDisplay ? clearingPriceDisplay : '-'}
+ />
+
+
+ Bidding with
+
+ >
+ }
+ itemValue={
+ biddingToken ? (
+ <>
+
+ {biddingTokenDisplay}
+
+ >
+ ) : (
+ '-'
+ )
+ }
+ />
+
+
+
+
+ Total auctioned
+
+ >
+ }
+ itemValue={
+ auctioningToken && initialAuctionOrder ? (
+ <>
+
+ {`${initialAuctionOrder?.sellAmount.toSignificant(
+ 2,
+ )} ${auctioningTokenDisplay}`}
+
+ >
+ ) : (
+ '-'
+ )
+ }
+ />
+
+
+
+ Min Sell Price
+
+ >
+ }
+ itemValue={
+ <>
+ {initialPriceToDisplay ? initialPriceToDisplay?.toSignificant(2) : ' - '}
+ {initialPriceToDisplay && auctioningTokenDisplay
+ ? `${biddingTokenDisplay}/${auctioningTokenDisplay}`
+ : '-'}
+ >
+ }
+ />
+ | | | |
+ )
+}
+
+export default AuctionDetails
diff --git a/src/components/auction/AuctionNotStarted/index.tsx b/src/components/auction/AuctionNotStarted/index.tsx
new file mode 100644
index 000000000..0ef5161ba
--- /dev/null
+++ b/src/components/auction/AuctionNotStarted/index.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+
+import { LockBig } from '../../icons/LockBig'
+import { EmptyContentText, EmptyContentWrapper } from '../../pureStyledComponents/EmptyContent'
+
+export const AuctionNotStarted: React.FC = () => {
+ return (
+
+
+ Auction not started yet.
+
+ )
+}
diff --git a/src/components/auction/AuctionPending/index.tsx b/src/components/auction/AuctionPending/index.tsx
new file mode 100644
index 000000000..2607fdca9
--- /dev/null
+++ b/src/components/auction/AuctionPending/index.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+
+import { LockBig } from '../../icons/LockBig'
+import { EmptyContentText, EmptyContentWrapper } from '../../pureStyledComponents/EmptyContent'
+
+export const AuctionPending: React.FC = () => {
+ return (
+
+
+ Pending on-chain price-calculation.
+
+ )
+}
diff --git a/src/components/auction/AuctionTimer/index.tsx b/src/components/auction/AuctionTimer/index.tsx
new file mode 100644
index 000000000..2bb0fae43
--- /dev/null
+++ b/src/components/auction/AuctionTimer/index.tsx
@@ -0,0 +1,212 @@
+import React from 'react'
+import styled, { keyframes } from 'styled-components'
+
+import {
+ AuctionState,
+ useDerivedAuctionInfo,
+ useDerivedAuctionState,
+} from '../../../state/orderPlacement/hooks'
+import {
+ calculateTimeLeft,
+ calculateTimeProgress,
+ getDays,
+ getHours,
+ getMinutes,
+ getSeconds,
+} from '../../../utils/tools'
+
+const TIMER_SIZE = '154px'
+
+const Wrapper = styled.div<{ progress?: string }>`
+ align-items: center;
+ background: ${({ theme }) => theme.primary1};
+ background: conic-gradient(
+ ${({ theme }) => theme.primary1} calc(${(props) => props.progress}),
+ ${({ theme }) => theme.primary3} 0%
+ );
+ border-radius: 50%;
+ display: flex;
+ height: ${TIMER_SIZE};
+ justify-content: center;
+ margin-top: -13px;
+ width: ${TIMER_SIZE};
+`
+
+Wrapper.defaultProps = {
+ progress: '0%',
+}
+
+const Center = styled.div`
+ align-items: center;
+ background-color: ${({ theme }) => theme.mainBackground};
+ border-radius: 50%;
+ box-shadow: 0 0 6px 0 ${({ theme }) => theme.mainBackground};
+ display: flex;
+ flex-flow: column;
+ height: 126px;
+ justify-content: center;
+ width: 126px;
+`
+
+const Days = styled.div`
+ font-size: 20px;
+ line-height: 1;
+ margin: 0;
+ text-transform: uppercase;
+`
+
+const Time = styled.div`
+ color: ${({ theme }) => theme.primary1};
+ flex-shrink: 1;
+ font-size: 18px;
+ font-weight: 700;
+ letter-spacing: -1px;
+ line-height: 1.2;
+ margin-bottom: 3px;
+ min-width: 0;
+ text-align: center;
+ white-space: nowrap;
+`
+
+const Text = styled.div`
+ color: ${({ theme }) => theme.primary1};
+ font-size: 15px;
+ font-weight: 700;
+ line-height: 1;
+ opacity: 0.8;
+ text-align: center;
+ text-transform: uppercase;
+`
+
+const TextBig = styled.div`
+ color: ${({ theme }) => theme.primary1};
+ font-size: 17px;
+ font-weight: 600;
+ line-height: 1.2;
+ text-align: center;
+ text-transform: uppercase;
+`
+
+const Blinker = keyframes`
+ 0% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 50.01% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 0;
+ }
+`
+
+const Blink = styled.span`
+ animation-direction: alternate;
+ animation-duration: 0.5s;
+ animation-iteration-count: infinite;
+ animation-name: ${Blinker};
+ animation-timing-function: linear;
+
+ &::before {
+ content: ':';
+ }
+`
+
+const formatSeconds = (seconds: number): React.ReactNode => {
+ const days = getDays(seconds)
+ const hours = getHours(seconds)
+ const minutes = getMinutes(seconds)
+ const remainderSeconds = getSeconds(seconds)
+
+ return (
+ <>
+ {days > 0 && (
+
+ {`${days} `}
+ {days === 1 ? 'Day' : 'Days'}
+
+ )}
+
+ <>
+ {hours >= 0 && hours < 10 && `0`}
+ {hours}
+ >
+ <>
+
+ {minutes >= 0 && minutes < 10 && `0`}
+ {minutes}
+ >
+ <>
+
+ {remainderSeconds >= 0 && remainderSeconds < 10 && `0`}
+ {remainderSeconds}
+ >
+
+ >
+ )
+}
+
+export const AuctionTimer = () => {
+ const { auctionState, loading } = useDerivedAuctionState()
+ const { auctionEndDate, auctionStartDate } = useDerivedAuctionInfo()
+ const [timeLeft, setTimeLeft] = React.useState(calculateTimeLeft(auctionEndDate))
+
+ React.useEffect(() => {
+ const id = setInterval(() => {
+ setTimeLeft(calculateTimeLeft(auctionEndDate))
+ }, 1000)
+ return () => {
+ clearInterval(id)
+ }
+ }, [auctionEndDate])
+
+ return (
+
+
+ {(auctionState === null || loading) && Loading}
+ {auctionState === AuctionState.NOT_YET_STARTED && (
+
+ Auction
+
not
+
+ started
+
+ )}
+ {auctionState === AuctionState.CLAIMING && (
+
+ Auction
+
claiming
+
+ )}
+ {(auctionState === AuctionState.ORDER_PLACING_AND_CANCELING ||
+ auctionState === AuctionState.ORDER_PLACING) && (
+ <>
+
+ Ends in
+ >
+ )}
+ {auctionState === AuctionState.PRICE_SUBMISSION && Auction Closed}
+ {auctionState !== AuctionState.NOT_YET_STARTED &&
+ auctionState !== AuctionState.ORDER_PLACING_AND_CANCELING &&
+ auctionState !== AuctionState.ORDER_PLACING &&
+ auctionState !== AuctionState.PRICE_SUBMISSION &&
+ auctionState !== AuctionState.CLAIMING &&
+ auctionState !== null && Auction Settled}
+
+
+ )
+}
diff --git a/src/components/auction/Claimer/index.tsx b/src/components/auction/Claimer/index.tsx
new file mode 100644
index 000000000..560ce5ec2
--- /dev/null
+++ b/src/components/auction/Claimer/index.tsx
@@ -0,0 +1,170 @@
+import React, { useMemo, useState } from 'react'
+import styled from 'styled-components'
+
+import { useActiveWeb3React } from '../../../hooks'
+import { useClaimOrderCallback, useGetAuctionProceeds } from '../../../hooks/useClaimOrderCallback'
+import { useWalletModalToggle } from '../../../state/application/hooks'
+import {
+ useDerivedAuctionInfo,
+ useDerivedClaimInfo,
+ useSwapState,
+} from '../../../state/orderPlacement/hooks'
+import { getTokenDisplay } from '../../../utils'
+import ClaimConfirmationModal from '../../ClaimConfirmationModal'
+import { Button } from '../../buttons/Button'
+import TokenLogo from '../../common/TokenLogo'
+import { ErrorInfo } from '../../icons/ErrorInfo'
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+import { ErrorRow, ErrorText, ErrorWrapper } from '../../pureStyledComponents/Error'
+
+const Wrapper = styled(BaseCard)`
+ max-width: 100%;
+ min-height: 380px;
+ min-width: 100%;
+`
+
+const ActionButton = styled(Button)`
+ height: 52px;
+ margin-top: auto;
+`
+
+const TokensWrapper = styled.div`
+ background-color: ${({ theme }) => theme.primary4};
+ border-radius: 12px;
+ border: 1px solid ${({ theme }) => theme.textField.borderColor};
+ margin-bottom: 20px;
+`
+
+const TokenItem = styled.div`
+ align-items: center;
+ border-bottom: 1px solid ${({ theme }) => theme.textField.borderColor};
+ display: flex;
+ justify-content: space-between;
+ padding: 7px 14px;
+
+ &:last-child {
+ border-bottom: none;
+ }
+`
+
+const Token = styled.div`
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+`
+
+const Text = styled.div`
+ color: ${({ theme }) => theme.text1};
+ font-size: 24px;
+ font-weight: 600;
+ line-height: 1.2;
+ margin-left: 10px;
+`
+
+const Claimer: React.FC = () => {
+ const { account } = useActiveWeb3React()
+ const toggleWalletModal = useWalletModalToggle()
+ const { auctionId } = useSwapState()
+ const { auctioningToken, biddingToken } = useDerivedAuctionInfo()
+ const { error } = useDerivedClaimInfo(auctionId)
+
+ const isValid = !error
+ const [showConfirm, setShowConfirm] = useState(false)
+ const [pendingConfirmation, setPendingConfirmation] = useState(true) // waiting for user confirmation
+
+ const { claimableAuctioningToken, claimableBiddingToken } = useGetAuctionProceeds()
+ const [txHash, setTxHash] = useState('')
+
+ function resetModal() {
+ setPendingConfirmation(true)
+ }
+
+ const claimOrderCallback = useClaimOrderCallback()
+
+ function onClaimOrder() {
+ claimOrderCallback().then((hash) => {
+ setTxHash(hash)
+ setPendingConfirmation(false)
+ })
+ }
+
+ const pendingText = `Claiming Funds`
+ const biddingTokenDisplay = useMemo(() => getTokenDisplay(biddingToken), [biddingToken])
+ const auctioningTokenDisplay = useMemo(() => getTokenDisplay(auctioningToken), [auctioningToken])
+
+ return (
+
+
+
+
+ {biddingToken && biddingTokenDisplay ? (
+ <>
+
+ {biddingTokenDisplay}
+ >
+ ) : (
+ '-'
+ )}
+
+
+ {claimableBiddingToken ? `${claimableBiddingToken.toSignificant(2)} ` : `0.00`}
+
+
+
+
+ {auctioningToken && auctioningTokenDisplay ? (
+ <>
+
+ {auctioningTokenDisplay}
+ >
+ ) : (
+ '-'
+ )}
+
+
+ {claimableAuctioningToken ? `${claimableAuctioningToken.toSignificant(2)}` : `0.00`}
+
+
+
+ {!isValid && (
+
+
+
+ {error}
+
+
+ )}
+ {!account ? (
+ Connect Wallet
+ ) : (
+ {
+ setShowConfirm(true)
+ onClaimOrder()
+ }}
+ >
+ Claim
+
+ )}
+ {
+ resetModal()
+ setShowConfirm(false)
+ }}
+ pendingConfirmation={pendingConfirmation}
+ pendingText={pendingText}
+ />
+
+ )
+}
+
+export default Claimer
diff --git a/src/components/auction/OrderPlacement/index.tsx b/src/components/auction/OrderPlacement/index.tsx
new file mode 100644
index 000000000..8b6de3498
--- /dev/null
+++ b/src/components/auction/OrderPlacement/index.tsx
@@ -0,0 +1,294 @@
+import React, { useEffect, useMemo, useState } from 'react'
+import styled from 'styled-components'
+import { ChainId, Fraction, TokenAmount } from 'uniswap-xdai-sdk'
+
+import { EASY_AUCTION_NETWORKS } from '../../../constants'
+import { useActiveWeb3React } from '../../../hooks'
+import { ApprovalState, useApproveCallback } from '../../../hooks/useApproveCallback'
+import { usePlaceOrderCallback } from '../../../hooks/usePlaceOrderCallback'
+import { useWalletModalToggle } from '../../../state/application/hooks'
+import {
+ AuctionState,
+ useDerivedAuctionInfo,
+ useDerivedAuctionState,
+ useGetOrderPlacementError,
+ useSwapActionHandlers,
+ useSwapState,
+} from '../../../state/orderPlacement/hooks'
+import { useOrderState } from '../../../state/orders/hooks'
+import { OrderState } from '../../../state/orders/reducer'
+import { useTokenBalance } from '../../../state/wallet/hooks'
+import { getTokenDisplay } from '../../../utils'
+import ConfirmationModal from '../../ConfirmationModal'
+import { Button } from '../../buttons/Button'
+import { ButtonType } from '../../buttons/buttonStylingTypes'
+import TokenLogo from '../../common/TokenLogo'
+import CurrencyInputPanel from '../../form/CurrencyInputPanel'
+import PriceInputPanel from '../../form/PriceInputPanel'
+import { ErrorInfo } from '../../icons/ErrorInfo'
+import { ErrorLock } from '../../icons/ErrorLock'
+import WarningModal from '../../modals/WarningModal'
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+import { ErrorRow, ErrorText, ErrorWrapper } from '../../pureStyledComponents/Error'
+import SwapModalFooter from '../../swap/PlaceOrderModalFooter'
+import SwapModalHeader from '../../swap/SwapModalHeader'
+
+const Wrapper = styled(BaseCard)`
+ max-width: 100%;
+ min-width: 100%;
+`
+
+const ActionButton = styled(Button)`
+ height: 52px;
+ margin-top: auto;
+`
+
+const BalanceWrapper = styled.div`
+ display: flex;
+ align-items: center;
+ margin-bottom: 20px;
+`
+
+const Balance = styled.p`
+ color: ${({ theme }) => theme.text1};
+ font-size: 18px;
+ font-weight: 600;
+ line-height: 1.2;
+ margin: 0 10px 0 0;
+ text-align: left;
+`
+
+const Total = styled.span`
+ font-weight: 400;
+`
+
+const ApprovalWrapper = styled.div`
+ align-items: center;
+ border-radius: 6px;
+ border: 1px solid ${({ theme }) => theme.primary1};
+ display: flex;
+ margin-bottom: 10px;
+ padding: 7px 12px;
+`
+
+const ApprovalText = styled.p`
+ color: ${({ theme }) => theme.primary1};
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1.23;
+ margin: 0 25px 0 0;
+ text-align: left;
+`
+
+const ApprovalButton = styled(Button)`
+ border-radius: 4px;
+ font-size: 14px;
+ font-weight: 600;
+ height: 26px;
+ padding: 0 14px;
+`
+
+const OrderPlacement: React.FC = () => {
+ const { account, chainId } = useActiveWeb3React()
+ const orders: OrderState | undefined = useOrderState()
+ const toggleWalletModal = useWalletModalToggle()
+ const { price, sellAmount } = useSwapState()
+ const {
+ auctioningToken,
+ biddingToken,
+ biddingTokenBalance,
+ initialPrice,
+ parsedBiddingAmount,
+ } = useDerivedAuctionInfo()
+ const { error } = useGetOrderPlacementError()
+ const { onUserSellAmountInput } = useSwapActionHandlers()
+ const { onUserPriceInput } = useSwapActionHandlers()
+ const { auctionState } = useDerivedAuctionState()
+
+ const isValid = !error
+
+ const [showConfirm, setShowConfirm] = useState(false)
+ const [showWarning, setShowWarning] = useState(false)
+ const [attemptingTxn, setAttemptingTxn] = useState(false) // clicked confirmed
+ const [pendingConfirmation, setPendingConfirmation] = useState(true) // waiting for user confirmation
+ const [txHash, setTxHash] = useState('')
+ const approvalTokenAmount: TokenAmount | undefined = parsedBiddingAmount
+
+ const [approval, approveCallback] = useApproveCallback(
+ approvalTokenAmount,
+ EASY_AUCTION_NETWORKS[chainId as ChainId],
+ )
+ const [approvalSubmitted, setApprovalSubmitted] = useState(false)
+
+ useEffect(() => {
+ if (approval === ApprovalState.PENDING) {
+ setApprovalSubmitted(true)
+ }
+ }, [approval, approvalSubmitted])
+
+ const maxAmountInput: TokenAmount = biddingTokenBalance ? biddingTokenBalance : undefined
+
+ useEffect(() => {
+ if (price == '-' && initialPrice) {
+ onUserPriceInput(initialPrice.multiply(new Fraction('1001', '1000')).toSignificant(4))
+ }
+ }, [onUserPriceInput, price, initialPrice])
+
+ const resetModal = () => {
+ if (!pendingConfirmation) {
+ onUserSellAmountInput('')
+ }
+ setPendingConfirmation(true)
+ setAttemptingTxn(false)
+ }
+
+ const placeOrderCallback = usePlaceOrderCallback(auctioningToken, biddingToken)
+
+ const onPlaceOrder = () => {
+ setAttemptingTxn(true)
+
+ placeOrderCallback().then((hash) => {
+ setTxHash(hash)
+ setPendingConfirmation(false)
+ })
+ }
+
+ const [showInverted, setShowInverted] = useState(false)
+
+ const modalHeader = () => {
+ return
+ }
+
+ const modalBottom = () => {
+ return (
+
+ )
+ }
+
+ const pendingText = `Placing order`
+ const biddingTokenDisplay = useMemo(() => getTokenDisplay(biddingToken), [biddingToken])
+ const auctioningTokenDisplay = useMemo(() => getTokenDisplay(auctioningToken), [auctioningToken])
+ const userTokenBalance = useTokenBalance(account, biddingToken)
+ const notApproved = approval === ApprovalState.NOT_APPROVED || approval === ApprovalState.PENDING
+ const orderPlacingOnly = auctionState === AuctionState.ORDER_PLACING
+
+ const handleShowConfirm = () => {
+ const sameOrder = orders.orders.find((order) => order.price === price)
+
+ if (!sameOrder) {
+ setShowConfirm(true)
+ } else {
+ setShowWarning(true)
+ }
+ }
+
+ return (
+
+
+
+ Your Balance:{' '}
+ {`${
+ account
+ ? `${userTokenBalance?.toSignificant(6)} ${biddingToken?.symbol}`
+ : 'Connect your wallet'
+ } `}
+
+ {account && biddingToken && biddingToken.address && (
+
+ )}
+
+ {
+ maxAmountInput && onUserSellAmountInput(maxAmountInput.toExact())
+ }}
+ onUserSellAmountInput={onUserSellAmountInput}
+ token={biddingToken}
+ value={sellAmount}
+ />
+
+ {(error || orderPlacingOnly) && (
+
+ {error && sellAmount !== '' && price !== '' && (
+
+
+ {error}
+
+ )}
+ {orderPlacingOnly && (
+
+
+
+ New orders can't be cancelled once you confirm the transaction in the next
+ step.
+
+
+ )}
+
+ )}
+ {notApproved && (
+
+
+ You need to unlock {biddingToken.symbol} to allow the smart contract to interact with
+ it. This has to be done for each new token.
+
+
+ {approval === ApprovalState.PENDING ? `Approving` : `Approve`}
+
+
+ )}
+ {!account ? (
+ Connect Wallet
+ ) : (
+
+ Place Order
+
+ )}
+ {
+ setShowWarning(false)
+ }}
+ title="Warning!"
+ />
+ {
+ resetModal()
+ setShowConfirm(false)
+ }}
+ pendingConfirmation={pendingConfirmation}
+ pendingText={pendingText}
+ title="Confirm Order"
+ topContent={modalHeader}
+ />
+
+ )
+}
+
+export default OrderPlacement
diff --git a/src/components/auction/Orderbook/index.tsx b/src/components/auction/Orderbook/index.tsx
new file mode 100644
index 000000000..d860bf9ca
--- /dev/null
+++ b/src/components/auction/Orderbook/index.tsx
@@ -0,0 +1,51 @@
+import React from 'react'
+import styled from 'styled-components'
+import { Token } from 'uniswap-xdai-sdk'
+
+import { useActiveWeb3React } from '../../../hooks'
+import { useOrderbookState } from '../../../state/orderbook/hooks'
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+import OrderBookChart, { OrderBookError } from '../OrderbookChart'
+import { processOrderbookData } from '../OrderbookWidget'
+
+const Wrapper = styled(BaseCard)`
+ max-width: 100%;
+ min-width: 100%;
+`
+
+interface OrderBookProps {
+ baseToken: Token
+ label?: string
+ quoteToken: Token
+}
+
+export const OrderBook: React.FC = (props: OrderBookProps) => {
+ const { baseToken, quoteToken } = props
+ const { chainId } = useActiveWeb3React()
+
+ const { asks, bids, error, userOrderPrice, userOrderVolume } = useOrderbookState()
+
+ const processedOrderbook = processOrderbookData({
+ data: { bids, asks },
+ userOrder: { price: userOrderPrice, volume: userOrderVolume },
+ baseToken,
+ quoteToken,
+ })
+
+ return (
+ <>
+
+ {error || !asks || asks.length === 0 ? (
+
+ ) : (
+
+ )}
+
+ >
+ )
+}
diff --git a/src/components/auction/OrderbookChart/index.tsx b/src/components/auction/OrderbookChart/index.tsx
new file mode 100644
index 000000000..94245a441
--- /dev/null
+++ b/src/components/auction/OrderbookChart/index.tsx
@@ -0,0 +1,286 @@
+import React, { useEffect, useRef } from 'react'
+import styled from 'styled-components'
+import { Token } from 'uniswap-xdai-sdk'
+
+import * as am4charts from '@amcharts/amcharts4/charts'
+import * as am4core from '@amcharts/amcharts4/core'
+import am4themesSpiritedaway from '@amcharts/amcharts4/themes/spiritedaway'
+
+import { InlineLoading } from '../../common/InlineLoading'
+import { SpinnerSize } from '../../common/Spinner'
+
+export enum Offer {
+ Bid,
+ Ask,
+}
+
+/**
+ * Price point data represented in the graph. Contains BigNumbers for operate with less errors and more precission
+ * but for representation uses number as expected by the library
+ */
+export interface PricePointDetails {
+ // Basic data
+ type: Offer
+ volume: number // volume for the price point
+ totalVolume: number // cumulative volume
+ price: number
+
+ // Data for representation
+ priceNumber: number
+ priceFormatted: string
+ totalVolumeNumber: number
+ totalVolumeFormatted: string
+ askValueY: Maybe
+ bidValueY: Maybe
+ newOrderValueY: Maybe
+ clearingPriceValueY: Maybe
+}
+
+export interface OrderBookChartProps {
+ baseToken: Token
+ quoteToken: Token
+ networkId: number
+ data: Maybe
+}
+
+const Wrapper = styled.div`
+ align-content: center;
+ align-items: center;
+ box-sizing: border-box;
+ color: ${({ theme }) => theme.text2};
+ display: flex;
+ height: 100%;
+ justify-content: center;
+ position: relative;
+ width: 100%;
+
+ .amcharts-Sprite-group {
+ pointer-events: none;
+ }
+
+ .amcharts-Label {
+ text-transform: uppercase;
+ font-size: 10px;
+ letter-spacing: 1px;
+ color: ${({ theme }) => theme.text4};
+ margin: 10px;
+ }
+
+ .amcharts-ZoomOutButton-group > .amcharts-RoundedRectangle-group {
+ fill: var(--color-text-active);
+ opacity: 0.6;
+ transition: 0.3s ease-in-out;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ .amcharts-CategoryAxis .amcharts-Label-group > .amcharts-Label,
+ .amcharts-ValueAxis-group .amcharts-Label-group > .amcharts-Label {
+ fill: ${({ theme }) => theme.text3};
+ }
+`
+
+am4core.useTheme(am4themesSpiritedaway)
+
+const OrderBookChart: React.FC = (props: OrderBookChartProps) => {
+ const { baseToken, data, quoteToken } = props
+ const chartRef = useRef(null)
+
+ useEffect(() => {
+ if (!baseToken || !quoteToken || !data) return
+
+ if (chartRef.current) return
+
+ const baseTokenLabel = baseToken.symbol
+ const quoteTokenLabel = quoteToken.symbol
+ const market = quoteTokenLabel + '-' + baseTokenLabel
+
+ const priceTitle = ` Price (${baseTokenLabel})`
+ const volumeTitle = ` Volume (${quoteTokenLabel})`
+
+ chartRef.current = am4core.create('chartdiv', am4charts.XYChart)
+ // Add data
+ chartRef.current.data = data
+
+ chartRef.current.paddingTop = 20
+ chartRef.current.marginTop = 20
+ chartRef.current.paddingBottom = 0
+ chartRef.current.paddingLeft = 0
+ chartRef.current.paddingRight = 0
+ chartRef.current.marginBottom = 0
+
+ // Colors
+ const colors = {
+ green: '#28a745',
+ red: '#dc3545',
+ white: '#FFFFFF',
+ grey: '#565A69',
+ orange: '#FF6347',
+ }
+
+ // Create axes
+ const priceAxis = chartRef.current.xAxes.push(new am4charts.ValueAxis())
+ const volumeAxis = chartRef.current.yAxes.push(new am4charts.ValueAxis())
+ volumeAxis.renderer.grid.template.stroke = am4core.color(colors.white)
+ volumeAxis.renderer.grid.template.strokeWidth = 0.5
+ volumeAxis.renderer.grid.template.strokeOpacity = 0.5
+ volumeAxis.title.text = volumeTitle
+ volumeAxis.title.fill = am4core.color(colors.white)
+ volumeAxis.renderer.labels.template.fill = am4core.color(colors.white)
+
+ priceAxis.renderer.grid.template.stroke = am4core.color(colors.white)
+ priceAxis.renderer.grid.template.strokeWidth = 0.5
+ priceAxis.renderer.grid.template.strokeOpacity = 0.5
+ priceAxis.title.text = priceTitle
+ priceAxis.title.fill = am4core.color(colors.white)
+ priceAxis.renderer.labels.template.fill = am4core.color(colors.white)
+
+ const min = Math.min.apply(
+ 0,
+ data.map((order) => order.priceNumber),
+ )
+ // Reduce the min in a 5%
+ priceAxis.min = min - min * 0.05
+
+ const max = Math.max.apply(
+ 0,
+ data.map((order) => order.priceNumber),
+ )
+ // Reduce the max in a 5%
+ priceAxis.max = max + max * 0.05
+
+ priceAxis.strictMinMax = true
+ priceAxis.renderer.grid.template.disabled = true
+ priceAxis.renderer.labels.template.disabled = true
+
+ const createGrid = (value) => {
+ const range = priceAxis.axisRanges.create()
+ range.value = value
+ range.label.text = '{value}'
+ }
+
+ const factor = (priceAxis.max - priceAxis.min) / 5
+
+ const firstGrid = priceAxis.min + factor
+ const secondGrid = priceAxis.min + factor * 2
+ const thirdGrid = priceAxis.min + factor * 3
+ const fourGrid = priceAxis.min + factor * 4
+ const fiveGrid = priceAxis.min + factor * 5
+
+ createGrid(firstGrid.toFixed(2))
+ createGrid(secondGrid.toFixed(2))
+ createGrid(thirdGrid.toFixed(2))
+ createGrid(fourGrid.toFixed(2))
+ createGrid(fiveGrid.toFixed(2))
+
+ // Create serie, green line shows the price (x axis) and size (y axis) of the bids that have been placed, both expressed in the bid token
+ const bidSeries = chartRef.current.series.push(new am4charts.StepLineSeries())
+ bidSeries.dataFields.valueX = 'priceNumber'
+ bidSeries.dataFields.valueY = 'bidValueY'
+ bidSeries.strokeWidth = 2
+ bidSeries.stroke = am4core.color(colors.green)
+ bidSeries.fill = bidSeries.stroke
+ bidSeries.fillOpacity = 0.2
+ bidSeries.dummyData = {
+ description:
+ 'Shows the price (x axis) and size (y axis) of the bids that have been placed, both expressed in the bid token',
+ }
+ bidSeries.tooltipText = `[bold]${market}[/]\nBid Price: [bold]{priceFormatted}[/] ${quoteTokenLabel}\nVolume: [bold]{totalVolumeFormatted}[/] ${baseTokenLabel}`
+
+ // Create serie, red line, shows the minimum sell price (x axis) the auctioneer is willing to accept
+ const askSeries = chartRef.current.series.push(new am4charts.LineSeries())
+ askSeries.dataFields.valueX = 'priceNumber'
+ askSeries.dataFields.valueY = 'askValueY'
+ askSeries.strokeWidth = 2
+ askSeries.stroke = am4core.color(colors.red)
+ askSeries.fill = askSeries.stroke
+ askSeries.fillOpacity = 0.1
+ askSeries.dummyData = {
+ description: 'Shows the minimum sell price (x axis) the auctioneer is willing to accept',
+ }
+ askSeries.tooltipText = `[bold]${market}[/]\nAsk Price: [bold]{priceFormatted}[/] ${quoteTokenLabel}\nVolume: [bold]{totalVolumeFormatted}[/] ${baseTokenLabel}`
+
+ // New order to be placed
+ const inputSeries = chartRef.current.series.push(new am4charts.LineSeries())
+ inputSeries.dataFields.valueX = 'priceNumber'
+ inputSeries.dataFields.valueY = 'newOrderValueY'
+ inputSeries.strokeWidth = 4
+ inputSeries.stroke = am4core.color(colors.orange)
+ inputSeries.fill = inputSeries.stroke
+ inputSeries.fillOpacity = 0.1
+ inputSeries.dummyData = {
+ description: 'New orders to be placed',
+ }
+
+ // Dotted white line -> shows the Current price, which is the closing price of the auction if
+ // no more bids are submitted or canceled and the auction ends
+ const priceSeries = chartRef.current.series.push(new am4charts.LineSeries())
+ priceSeries.dataFields.valueX = 'priceNumber'
+ priceSeries.dataFields.valueY = 'clearingPriceValueY'
+ priceSeries.strokeWidth = 2
+ priceSeries.strokeDasharray = '3,3'
+ priceSeries.stroke = am4core.color(colors.white)
+ priceSeries.fill = inputSeries.stroke
+ priceSeries.fillOpacity = 0.1
+ priceSeries.dummyData = {
+ description:
+ 'Shows the Current price, which is the closing price of the auction if no more bids are submitted or canceled and the auction ends',
+ }
+
+ // Add cursor
+ chartRef.current.cursor = new am4charts.XYCursor()
+ chartRef.current.cursor.snapToSeries = [bidSeries, askSeries]
+ chartRef.current.cursor.lineX.stroke = am4core.color(colors.white)
+ chartRef.current.cursor.lineX.strokeWidth = 1
+ chartRef.current.cursor.lineX.strokeOpacity = 0.6
+ chartRef.current.cursor.lineX.strokeDasharray = '4'
+
+ chartRef.current.cursor.lineY.stroke = am4core.color(colors.white)
+ chartRef.current.cursor.lineY.strokeWidth = 1
+ chartRef.current.cursor.lineY.strokeOpacity = 0.6
+ chartRef.current.cursor.lineY.strokeDasharray = '4'
+
+ // Button configuration
+ chartRef.current.zoomOutButton.background.cornerRadius(5, 5, 5, 5)
+ chartRef.current.zoomOutButton.background.fill = am4core.color(colors.grey)
+ chartRef.current.zoomOutButton.icon.stroke = am4core.color(colors.white)
+ chartRef.current.zoomOutButton.icon.strokeWidth = 2
+ chartRef.current.zoomOutButton.tooltip.text = 'Zoom out'
+
+ // Legend
+ chartRef.current.legend = new am4charts.Legend()
+ chartRef.current.legend.labels.template.fill = am4core.color(colors.white)
+ chartRef.current.legend.itemContainers.template.tooltipText =
+ '{dataContext.dummyData.description}'
+ }, [data, baseToken, quoteToken])
+
+ // Handle component unmounting, dispose chart
+ useEffect(() => {
+ return () => {
+ chartRef.current && chartRef.current.dispose()
+ }
+ }, [])
+
+ useEffect(() => {
+ if (!chartRef.current || data === null) return
+ chartRef.current.data = data.length === 0 ? [] : data
+ }, [data])
+
+ return (
+
+
+
+ )
+}
+
+interface OrderBookErrorProps {
+ error: Error
+}
+
+export const OrderBookError: React.FC = ({ error }: OrderBookErrorProps) => (
+ {error ? error.message : }
+)
+
+export default OrderBookChart
diff --git a/src/components/OrderbookWidget.tsx b/src/components/auction/OrderbookWidget/index.tsx
similarity index 65%
rename from src/components/OrderbookWidget.tsx
rename to src/components/auction/OrderbookWidget/index.tsx
index 33582dbdc..4d140c3cb 100644
--- a/src/components/OrderbookWidget.tsx
+++ b/src/components/auction/OrderbookWidget/index.tsx
@@ -1,25 +1,22 @@
-import React from "react";
-
-import { OrderBookData, PricePoint } from "../api/AdditionalServicesApi";
+import React from 'react'
+import { Token } from 'uniswap-xdai-sdk'
+import { OrderBookData, PricePoint } from '../../../api/AdditionalServicesApi'
+import { useOrderbookState } from '../../../state/orderbook/hooks'
import OrderBookChart, {
+ Offer,
OrderBookChartProps,
OrderBookError,
PricePointDetails,
- Offer,
-} from "./OrderbookChart";
-import { Token } from "uniswap-xdai-sdk";
-import { useOrderbookState } from "../state/orderbook/hooks";
+} from '../OrderbookChart'
-const SMALL_VOLUME_THRESHOLD = 0.001;
+const SMALL_VOLUME_THRESHOLD = 0.001
-// Todo: to be removed
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const logDebug = (...args: any[]): void => {
- if (true) {
- console.log(...args);
- }
-};
+ // eslint-disable-next-line no-console
+ console.log(...args)
+}
+
const addClearingPriceInfo = (
price: number,
pricePointsDetails: PricePointDetails[],
@@ -34,16 +31,14 @@ const addClearingPriceInfo = (
priceNumber: price,
totalVolumeNumber: 0,
priceFormatted: price.toString(),
- totalVolumeFormatted: "0",
+ totalVolumeFormatted: '0',
askValueY: null,
bidValueY: null,
newOrderValueY: null,
clearingPriceValueY: 0,
- };
- const valueYofBids = pricePointsDetails.map((y) =>
- Math.max(y.bidValueY, y.askValueY),
- );
- const maxValueYofBid = Math.max(...valueYofBids);
+ }
+ const valueYofBids = pricePointsDetails.map((y) => Math.max(y.bidValueY, y.askValueY))
+ const maxValueYofBid = Math.max(...valueYofBids)
const pricePointTop: PricePointDetails = {
type: null,
volume: null,
@@ -54,14 +49,14 @@ const addClearingPriceInfo = (
priceNumber: price,
totalVolumeNumber: 0,
priceFormatted: price.toString(),
- totalVolumeFormatted: "0",
+ totalVolumeFormatted: '0',
askValueY: null,
bidValueY: null,
newOrderValueY: null,
clearingPriceValueY: maxValueYofBid,
- };
- return [pricePointBottom, pricePointTop];
-};
+ }
+ return [pricePointBottom, pricePointTop]
+}
/**
* This method turns the raw data that the backend returns into data that can be displayed by the chart.
* This involves aggregating the total volume and accounting for decimals
@@ -73,56 +68,48 @@ const processData = (
lowestValue: number,
type: Offer,
): PricePointDetails[] => {
- const isBid = type == Offer.Bid;
+ const isBid = type == Offer.Bid
// Filter tiny orders
if (isBid) {
- pricePoints = pricePoints.filter(
- (pricePoint) => pricePoint.volume > SMALL_VOLUME_THRESHOLD,
- );
+ pricePoints = pricePoints.filter((pricePoint) => pricePoint.volume > SMALL_VOLUME_THRESHOLD)
} else {
pricePoints = pricePoints.filter(
- (pricePoint) =>
- pricePoint.volume * pricePoint.price > SMALL_VOLUME_THRESHOLD,
- );
+ (pricePoint) => pricePoint.volume * pricePoint.price > SMALL_VOLUME_THRESHOLD,
+ )
}
// Adding first and last element to round up the picture
if (type == Offer.Bid) {
- if (
- userOrder &&
- highestValue * 1.5 > userOrder.price &&
- userOrder.price > lowestValue
- ) {
- highestValue =
- highestValue > userOrder.price ? highestValue : userOrder.price;
- pricePoints = pricePoints.concat(userOrder);
+ if (userOrder && highestValue * 1.5 > userOrder.price && userOrder.price > lowestValue) {
+ highestValue = highestValue > userOrder.price ? highestValue : userOrder.price
+ pricePoints = pricePoints.concat(userOrder)
}
- pricePoints.sort((lhs, rhs) => -1 * (lhs.price - rhs.price));
+ pricePoints.sort((lhs, rhs) => -1 * (lhs.price - rhs.price))
pricePoints.push({
price: (highestValue * 101) / 100,
volume: 0,
- });
+ })
- pricePoints.sort((lhs, rhs) => -1 * (lhs.price - rhs.price));
+ pricePoints.sort((lhs, rhs) => -1 * (lhs.price - rhs.price))
} else {
pricePoints.push({
price: (highestValue * 101) / 100,
volume: 0,
- });
+ })
pricePoints.push({
price: (pricePoints[0].price * 99) / 100,
volume: 0,
- });
- pricePoints.sort((lhs, rhs) => lhs.price - rhs.price);
+ })
+ pricePoints.sort((lhs, rhs) => lhs.price - rhs.price)
}
// Convert the price points that can be represented in the graph (PricePointDetails)
const { points } = pricePoints.reduce(
(acc, pricePoint, index) => {
- const { price, volume } = pricePoint;
- const totalVolume = acc.totalVolume;
+ const { price, volume } = pricePoint
+ const totalVolume = acc.totalVolume
// Amcharts draws step lines so that the x value is centered (Default). To correctly display the order book, we want
// the x value to be at the left side of the step for asks and at the right side of the step for bids.
@@ -136,13 +123,13 @@ const processData = (
// For asks, we can offset the "startLocation" by 0.5. However, Amcharts does not support a "startLocation" of -0.5.
// For bids, we therefore offset the curve by -1 (expose the previous total volume) and use an offset of 0.5.
// Otherwise our steps would be off by one.
- let askValueY, bidValueY;
+ let askValueY, bidValueY
if (isBid) {
- askValueY = null;
- bidValueY = totalVolume;
+ askValueY = null
+ bidValueY = totalVolume
} else {
- askValueY = totalVolume * price;
- bidValueY = null;
+ askValueY = totalVolume * price
+ bidValueY = null
}
// Add the new point
const pricePointDetails: PricePointDetails = {
@@ -160,8 +147,8 @@ const processData = (
bidValueY,
newOrderValueY: null,
clearingPriceValueY: null,
- };
- acc.points.push(pricePointDetails);
+ }
+ acc.points.push(pricePointDetails)
if (!isBid) {
// Add the new point at the beginning of order
// ------------
@@ -182,8 +169,8 @@ const processData = (
bidValueY,
newOrderValueY: null,
clearingPriceValueY: null,
- };
- acc.points.push(pricePointDetails);
+ }
+ acc.points.push(pricePointDetails)
}
// Next two points are only added for displaying new Order
@@ -204,8 +191,8 @@ const processData = (
bidValueY: null,
newOrderValueY: bidValueY + volume,
clearingPriceValueY: null,
- };
- acc.points.push(pricePointDetails);
+ }
+ acc.points.push(pricePointDetails)
const pricePointDetails_2: PricePointDetails = {
type,
volume,
@@ -221,8 +208,8 @@ const processData = (
bidValueY: null,
newOrderValueY: bidValueY,
clearingPriceValueY: null,
- };
- acc.points.push(pricePointDetails_2);
+ }
+ acc.points.push(pricePointDetails_2)
}
if (
index > 0 &&
@@ -249,41 +236,41 @@ const processData = (
bidValueY: null,
newOrderValueY: bidValueY,
clearingPriceValueY: null,
- };
- acc.points.push(pricePointDetails);
+ }
+ acc.points.push(pricePointDetails)
}
- return { totalVolume: totalVolume + volume, points: acc.points };
+ return { totalVolume: totalVolume + volume, points: acc.points }
},
{
totalVolume: 0,
points: [] as PricePointDetails[],
},
- );
+ )
- return points;
-};
+ return points
+}
function _printOrderBook(
pricePoints: PricePointDetails[],
- baseTokenSymbol = "",
- quoteTokenSymbol = "",
+ baseTokenSymbol = '',
+ quoteTokenSymbol = '',
): void {
- logDebug("Order Book: " + baseTokenSymbol + "-" + quoteTokenSymbol);
+ logDebug('Order Book: ' + baseTokenSymbol + '-' + quoteTokenSymbol)
pricePoints.forEach((pricePoint) => {
- const isBid = pricePoint.type === Offer.Bid;
+ const isBid = pricePoint.type === Offer.Bid
logDebug(
- `\t${isBid ? "Bid" : "Ask"} ${
- pricePoint.totalVolumeFormatted
- } ${baseTokenSymbol} at ${pricePoint.priceFormatted} ${quoteTokenSymbol}`,
- );
- });
+ `\t${isBid ? 'Bid' : 'Ask'} ${pricePoint.totalVolumeFormatted} ${baseTokenSymbol} at ${
+ pricePoint.priceFormatted
+ } ${quoteTokenSymbol}`,
+ )
+ })
}
interface ProcessRawDataParams {
- data: OrderBookData;
- userOrder: PricePoint;
- baseToken: Pick;
- quoteToken: Pick;
+ data: OrderBookData
+ userOrder: PricePoint
+ baseToken: Pick
+ quoteToken: Pick
}
export function findClearingPrice(
sellOrders: PricePoint[],
@@ -291,109 +278,87 @@ export function findClearingPrice(
initialAuctionOrder: PricePoint,
): number | undefined {
if (userOrder) {
- if (userOrder.price > initialAuctionOrder.price && userOrder.volume > 0) {
- sellOrders = sellOrders.concat(userOrder);
+ if (userOrder?.price > initialAuctionOrder?.price && userOrder.volume > 0) {
+ sellOrders = sellOrders.concat(userOrder)
}
}
- sellOrders = Object.values(sellOrders);
+ sellOrders = Object.values(sellOrders)
- sellOrders.sort((lhs, rhs) => -1 * (lhs.price - rhs.price));
- let totalSellVolume = 0;
+ sellOrders.sort((lhs, rhs) => -1 * (lhs.price - rhs.price))
+ let totalSellVolume = 0
for (const order of sellOrders) {
- totalSellVolume = totalSellVolume + order.volume;
+ totalSellVolume = totalSellVolume + order.volume
if (totalSellVolume >= initialAuctionOrder.volume * order.price) {
const coveredBuyAmount =
- initialAuctionOrder.volume * order.price -
- (totalSellVolume - order.volume);
+ initialAuctionOrder.volume * order.price - (totalSellVolume - order.volume)
if (coveredBuyAmount < order.volume) {
- return order.price;
+ return order.price
} else {
- return (totalSellVolume - order.volume) / initialAuctionOrder.volume;
+ return (totalSellVolume - order.volume) / initialAuctionOrder.volume
}
}
}
- if (
- totalSellVolume >=
- initialAuctionOrder.volume * initialAuctionOrder.price
- ) {
- return totalSellVolume / initialAuctionOrder.volume;
+ if (totalSellVolume >= initialAuctionOrder?.volume * initialAuctionOrder?.price) {
+ return totalSellVolume / initialAuctionOrder.volume
} else {
- return initialAuctionOrder.price;
+ return initialAuctionOrder?.price
}
}
export const processOrderbookData = ({
- data,
- userOrder,
baseToken,
+ data,
quoteToken,
+ userOrder,
}: ProcessRawDataParams): PricePointDetails[] => {
try {
- const clearingPrice = findClearingPrice(data.bids, userOrder, data.asks[0]);
- const bids = processData(
- data.bids,
- userOrder,
- data.asks[0].price,
- data.asks[0].price,
- Offer.Bid,
- );
+ const clearingPrice = findClearingPrice(data.bids, userOrder, data.asks[0])
+ const value = data.asks[0]?.price ?? 0
+ const bids = processData(data.bids, userOrder, value, value, Offer.Bid)
+
+ const asks = processData(data.asks, null, bids[0].price, value, Offer.Ask)
+ let pricePoints = bids.concat(asks)
- const asks = processData(
- data.asks,
- null,
- bids[0].price,
- data.asks[0].price,
- Offer.Ask,
- );
- let pricePoints = bids.concat(asks);
if (clearingPrice) {
- const priceInfo = addClearingPriceInfo(clearingPrice, pricePoints);
- pricePoints = pricePoints.concat(priceInfo);
+ const priceInfo = addClearingPriceInfo(clearingPrice, pricePoints)
+ pricePoints = pricePoints.concat(priceInfo)
}
- // Sort points by price
- pricePoints.sort((lhs, rhs) => lhs.price - rhs.price);
- const debug = false;
- if (debug)
- _printOrderBook(pricePoints, baseToken.symbol, quoteToken.symbol);
- return pricePoints;
+ // Sort points by price
+ pricePoints.sort((lhs, rhs) => lhs.price - rhs.price)
+ const debug = false
+ if (debug) _printOrderBook(pricePoints, baseToken.symbol, quoteToken.symbol)
+ return pricePoints
} catch (error) {
- console.error("Error processing data", error);
- return [];
+ console.error('Error processing data', error)
+ return []
}
-};
+}
-export interface OrderBookProps extends Omit {
- auctionId?: number;
+export interface OrderBookProps extends Omit {
+ auctionId?: number
}
const OrderBookWidget: React.FC = (props: OrderBookProps) => {
- const { baseToken, quoteToken, networkId } = props;
- const {
- error,
- bids,
- asks,
- userOrderPrice,
- userOrderVolume,
- } = useOrderbookState();
+ const { baseToken, networkId, quoteToken } = props
+ const { asks, bids, error, userOrderPrice, userOrderVolume } = useOrderbookState()
- if (error || !asks || asks.length == 0)
- return ;
+ if (error || !asks || asks.length == 0) return
const processedOrderbook = processOrderbookData({
data: { bids, asks },
userOrder: { price: userOrderPrice, volume: userOrderVolume },
baseToken,
quoteToken,
- });
+ })
return (
- );
-};
+ )
+}
-export default OrderBookWidget;
+export default OrderBookWidget
diff --git a/src/components/auction/OrdersTable/index.tsx b/src/components/auction/OrdersTable/index.tsx
new file mode 100644
index 000000000..5089bfb8b
--- /dev/null
+++ b/src/components/auction/OrdersTable/index.tsx
@@ -0,0 +1,207 @@
+import React, { useCallback, useState } from 'react'
+import styled, { css } from 'styled-components'
+
+import { useCancelOrderCallback } from '../../../hooks/useCancelOrderCallback'
+import { useDerivedAuctionInfo } from '../../../state/orderPlacement/hooks'
+import { useOrderActionHandlers } from '../../../state/orders/hooks'
+import { OrderDisplay, OrderStatus } from '../../../state/orders/reducer'
+import ConfirmationModal from '../../ConfirmationModal'
+import { Button } from '../../buttons/Button'
+import { KeyValue } from '../../common/KeyValue'
+import { Tooltip } from '../../common/Tooltip'
+import { InfoIcon } from '../../icons/InfoIcon'
+import { OrderPending } from '../../icons/OrderPending'
+import { OrderPlaced } from '../../icons/OrderPlaced'
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+import { EmptyContentText, EmptyContentWrapper } from '../../pureStyledComponents/EmptyContent'
+import { PageTitle } from '../../pureStyledComponents/PageTitle'
+import CancelModalFooter from '../../swap/CancelOrderModealFooter'
+import SwapModalHeader from '../../swap/SwapModalHeader'
+
+const Wrapper = styled(BaseCard)`
+ padding: 4px 0;
+`
+
+const SectionTitle = styled(PageTitle)`
+ margin-bottom: 16px;
+ margin-top: 0;
+`
+
+const Grid = styled.div`
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 115px;
+`
+
+const ActionButton = styled(Button)`
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 600;
+ height: 28px;
+`
+
+const CommonCellCSS = css`
+ border-bottom: 1px solid ${({ theme }) => theme.border};
+ padding: 13px 15px;
+
+ &:nth-last-child(-n + 4) {
+ border-bottom: none;
+ }
+`
+
+const Cell = styled(KeyValue)`
+ ${CommonCellCSS}
+`
+
+const ButtonWrapper = styled.div`
+ ${CommonCellCSS}
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+`
+
+const OrderTable: React.FC<{ orders: OrderDisplay[] }> = (props) => {
+ const { orders } = props
+ const { biddingToken, orderCancellationEndDate } = useDerivedAuctionInfo()
+ const cancelOrderCallback = useCancelOrderCallback(biddingToken)
+ const { onDeleteOrder } = useOrderActionHandlers()
+
+ // modal and loading
+ const [showConfirm, setShowConfirm] = useState(false)
+ const [attemptingTxn, setAttemptingTxn] = useState(false) // clicked confirmed
+ const [pendingConfirmation, setPendingConfirmation] = useState(true) // waiting for user confirmation
+
+ // txn values
+ const [txHash, setTxHash] = useState('')
+ const [orderId, setOrderId] = useState('')
+
+ // reset modal state when closed
+ const resetModal = useCallback(() => {
+ setPendingConfirmation(true)
+ setAttemptingTxn(false)
+ }, [setPendingConfirmation, setAttemptingTxn])
+
+ const onCancelOrder = useCallback(() => {
+ setAttemptingTxn(true)
+
+ cancelOrderCallback(orderId).then((hash) => {
+ onDeleteOrder(orderId)
+ setTxHash(hash)
+ setPendingConfirmation(false)
+ })
+ }, [
+ setAttemptingTxn,
+ setTxHash,
+ setPendingConfirmation,
+ onDeleteOrder,
+ orderId,
+ cancelOrderCallback,
+ ])
+
+ const modalHeader = () => {
+ return
+ }
+
+ const modalBottom = () => {
+ return (
+
+ )
+ }
+
+ const pendingText = `Canceling Order`
+ const now = Math.trunc(Date.now() / 1000)
+ const isOrderCancelationAllowed = now < orderCancellationEndDate
+ const ordersEmpty = !orders || orders.length == 0
+
+ return (
+ <>
+ Your Orders
+ {ordersEmpty && (
+
+
+ You have no orders for this auction.
+
+ )}
+ {!ordersEmpty && (
+
+
+ {Object.entries(orders).map((order, index) => (
+
+
+ Amount
+
+ >
+ }
+ itemValue={order[1].sellAmount}
+ />
+
+ Limit Price
+
+ >
+ }
+ itemValue={order[1].price}
+ />
+ Status}
+ itemValue={
+ order[1].status == OrderStatus.PLACED ? (
+ <>
+ Placed
+
+ >
+ ) : (
+ <>
+ Pending
+
+ >
+ )
+ }
+ />
+
+ {
+ if (isOrderCancelationAllowed) {
+ setOrderId(order[1].id)
+ setShowConfirm(true)
+ }
+ }}
+ >
+ Cancel
+
+
+ | | |
+ ))}
+
+ {
+ resetModal()
+ setShowConfirm(false)
+ }}
+ pendingConfirmation={pendingConfirmation}
+ pendingText={pendingText}
+ title="Confirm Order Cancellation"
+ topContent={modalHeader}
+ />
+
+ )}
+ >
+ )
+}
+
+export default OrderTable
diff --git a/src/components/auctions/AuctionInfoCard/index.tsx b/src/components/auctions/AuctionInfoCard/index.tsx
new file mode 100644
index 000000000..34bb8772e
--- /dev/null
+++ b/src/components/auctions/AuctionInfoCard/index.tsx
@@ -0,0 +1,274 @@
+import React from 'react'
+import styled, { keyframes } from 'styled-components'
+
+import { HashLink } from 'react-router-hash-link'
+
+import { AuctionInfo } from '../../../hooks/useAllAuctionInfos'
+import {
+ calculateTimeLeft,
+ calculateTimeProgress,
+ getChainName,
+ getDays,
+ getHours,
+ getMinutes,
+ getSeconds,
+} from '../../../utils/tools'
+import DoubleLogo from '../../common/DoubleLogo'
+import { NetworkIcon } from '../../icons/NetworkIcon'
+
+const Wrapper = styled(HashLink)`
+ align-items: center;
+ border-radius: 12px;
+ border: 1px solid ${({ theme }) => theme.border};
+ display: flex;
+ flex-flow: column;
+ justify-content: space-between;
+ min-height: 290px;
+ padding: 12px 18px;
+ text-decoration: none;
+ transition: all 0.15s linear;
+
+ &:hover {
+ box-shadow: 10px -10px 24px 0 rgba(0, 34, 73, 0.7);
+ }
+`
+
+const Top = styled.span`
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+`
+
+const Tokens = styled.span`
+ color: ${({ theme }) => theme.text1};
+ font-size: 24px;
+ font-weight: 700;
+ line-height: 1.2;
+ margin: 0;
+ text-transform: uppercase;
+`
+
+const Badge = styled.span`
+ align-items: center;
+ background-color: rgba(232, 102, 61, 0.3);
+ border-radius: 17px;
+ color: ${({ theme }) => theme.primary1};
+ display: flex;
+ height: 34px;
+ padding: 0 18px;
+`
+
+const Blinker = keyframes`
+ 0% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 50.01% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 0;
+ }
+`
+
+const Blink = styled.span`
+ animation-direction: alternate;
+ animation-duration: 0.5s;
+ animation-iteration-count: infinite;
+ animation-name: ${Blinker};
+ animation-timing-function: linear;
+
+ &::before {
+ content: ':';
+ }
+`
+
+const Details = styled.span`
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ flex-shrink: 0;
+ justify-content: center;
+ margin: auto 0;
+ padding: 10px 0;
+`
+
+const TokenIcons = styled(DoubleLogo)``
+
+const SellingText = styled.span`
+ color: ${({ theme }) => theme.text1};
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.2;
+ margin: 15px 0 0 0;
+ text-align: center;
+`
+
+const PriceAndDuration = styled.span`
+ display: flex;
+ justify-content: space-between;
+ margin: auto 0 0 0;
+ width: 100%;
+`
+
+const Cell = styled.span`
+ display: flex;
+ flex-direction: column;
+`
+
+const Subtitle = styled.span<{ textAlign?: string }>`
+ color: ${({ theme }) => theme.text1};
+ display: block;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1.2;
+ margin: 0;
+ opacity: 0.7;
+ padding: 0 0 5px;
+ text-align: ${(props) => props.textAlign};
+`
+
+Subtitle.defaultProps = {
+ textAlign: 'left',
+}
+
+const Text = styled.span`
+ color: ${({ theme }) => theme.primary1};
+ font-size: 13px;
+ font-weight: 700;
+ line-height: 1.2;
+ margin-top: auto;
+`
+
+const ProgressBar = styled.span`
+ background-color: rgba(232, 102, 61, 0.15);
+ border-radius: 5px;
+ display: block;
+ height: 5px;
+ margin-bottom: 3px;
+ margin-top: auto;
+ width: 120px;
+`
+
+const Progress = styled.span<{ width: string }>`
+ background-color: ${({ theme }) => theme.primary1};
+ border-radius: 5px;
+ display: block;
+ height: 100%;
+ max-width: 100%;
+ width: ${(props) => props.width};
+`
+
+const Network = styled.div`
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ opacity: 0.7;
+ padding-top: 4px;
+`
+
+const NetworkName = styled.div`
+ color: ${({ theme }) => theme.text1};
+ font-size: 11px;
+ font-weight: 600;
+ margin-left: 5px;
+`
+
+const formatSeconds = (seconds: number): React.ReactNode => {
+ const days = getDays(seconds)
+ const hours = getHours(seconds)
+ const minutes = getMinutes(seconds)
+ const remainderSeconds = getSeconds(seconds)
+
+ return (
+ <>
+ {days > 0 && <>{`${days}d `}>}
+ <>
+ {hours >= 0 && hours < 10 && `0`}
+ {hours}
+ >
+ <>
+
+ {minutes >= 0 && minutes < 10 && `0`}
+ {minutes}
+ >
+ <>
+
+ {remainderSeconds >= 0 && remainderSeconds < 10 && `0`}
+ {remainderSeconds}
+ >
+ >
+ )
+}
+
+interface Props {
+ auctionInfo: AuctionInfo
+}
+
+const AuctionInfoCard: React.FC = (props) => {
+ const { auctionInfo, ...restProps } = props
+ const { chainId, endTimeTimestamp, startingTimestamp } = auctionInfo
+ const [timeLeft, setTimeLeft] = React.useState(calculateTimeLeft(endTimeTimestamp))
+
+ setInterval(() => {
+ setTimeLeft(calculateTimeLeft(endTimeTimestamp))
+ }, 1000)
+
+ return (
+
+
+
+ {auctionInfo.symbolAuctioningToken}/{auctionInfo.symbolBiddingToken}
+
+ {timeLeft && timeLeft > -1 ? formatSeconds(timeLeft) : '-'}
+
+
+
+
+ Selling {auctionInfo.order.volume + ` `}
+ {auctionInfo.symbolAuctioningToken}
+
+
+
+ Selling on {getChainName(parseInt(chainId.toString()))}
+
+
+
+
+ Current price
+
+ {auctionInfo.order.price.toFixed(2)} {` ` + auctionInfo.symbolBiddingToken} per{' '}
+ {auctionInfo.symbolAuctioningToken}
+
+ |
+
+ Duration
+
+
+
+ |
+
+
+ )
+}
+
+export default AuctionInfoCard
diff --git a/src/components/auctions/FeaturedAuctions/index.tsx b/src/components/auctions/FeaturedAuctions/index.tsx
new file mode 100644
index 000000000..b1fabed21
--- /dev/null
+++ b/src/components/auctions/FeaturedAuctions/index.tsx
@@ -0,0 +1,59 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { useInterestingAuctionInfo } from '../../../hooks/useInterestingAuctionDetails'
+import { InlineLoading } from '../../common/InlineLoading'
+import { SpinnerSize } from '../../common/Spinner'
+import { InfoIcon } from '../../icons/InfoIcon'
+import { EmptyContentText, EmptyContentWrapper } from '../../pureStyledComponents/EmptyContent'
+import { PageTitle } from '../../pureStyledComponents/PageTitle'
+import AuctionInfoCard from '../AuctionInfoCard'
+
+const Wrapper = styled.div`
+ margin-bottom: 40px;
+`
+
+const Row = styled.div`
+ column-gap: 40px;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+`
+
+const SectionTitle = styled(PageTitle)`
+ margin: 0 0 40px;
+`
+
+export const FeaturedAuctions = () => {
+ const highlightedAuctions = useInterestingAuctionInfo()
+
+ const auctions = React.useMemo(() => {
+ const items: React.ReactNodeArray = []
+
+ if (highlightedAuctions && highlightedAuctions.length > 0) {
+ const highlightedAuctionsFirstThree = highlightedAuctions.slice(0, 3)
+ for (const highlightedAuction of highlightedAuctionsFirstThree) {
+ items.push(
+ ,
+ )
+ }
+ }
+
+ return items
+ }, [highlightedAuctions])
+
+ return (
+
+ Featured Auctions
+ {(highlightedAuctions === undefined || highlightedAuctions === null) && (
+
+ )}
+ {highlightedAuctions && highlightedAuctions.length === 0 && (
+
+
+ No featured auctions.
+
+ )}
+ {highlightedAuctions && highlightedAuctions.length > 0 && {auctions}
}
+
+ )
+}
diff --git a/src/components/buttons/Button/index.tsx b/src/components/buttons/Button/index.tsx
new file mode 100644
index 000000000..24d2e7fcd
--- /dev/null
+++ b/src/components/buttons/Button/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { ButtonCSS, ButtonProps } from '../buttonStylingTypes'
+
+const Wrapper = styled.button`
+ ${ButtonCSS}
+`
+
+export const Button: React.FC = (props: ButtonProps) => {
+ const { children, ...restProps } = props
+
+ return {children}
+}
diff --git a/src/components/buttons/ButtonAnchor/index.tsx b/src/components/buttons/ButtonAnchor/index.tsx
new file mode 100644
index 000000000..5c6e2d69c
--- /dev/null
+++ b/src/components/buttons/ButtonAnchor/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { ButtonCSS, ButtonLinkProps } from '../buttonStylingTypes'
+
+const Wrapper = styled.a`
+ ${ButtonCSS}
+`
+
+export const ButtonAnchor: React.FC = (props: ButtonLinkProps) => {
+ const { children, ...restProps } = props
+
+ return {children}
+}
diff --git a/src/components/buttons/ButtonConnect/index.tsx b/src/components/buttons/ButtonConnect/index.tsx
new file mode 100644
index 000000000..4a9e74180
--- /dev/null
+++ b/src/components/buttons/ButtonConnect/index.tsx
@@ -0,0 +1,59 @@
+import { darken } from 'polished'
+import React, { ButtonHTMLAttributes } from 'react'
+import styled from 'styled-components'
+
+import { ChevronRight } from '../../icons/ChevronRight'
+
+const Wrapper = styled.button`
+ align-items: center;
+ background: transparent;
+ border: none;
+ color: ${({ theme }) => theme.primary1};
+ cursor: pointer;
+ display: flex;
+ font-size: 16px;
+ font-weight: 400;
+ height: 100%;
+ line-height: 1.2;
+ outline: none;
+ padding: 0;
+
+ &[disabled] {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+
+ .fill {
+ fill: ${({ theme }) => theme.primary1};
+ }
+
+ &:hover {
+ color: ${({ theme }) => darken(0.15, theme.primary1)};
+
+ .fill {
+ fill: ${({ theme }) => darken(0.15, theme.primary1)};
+ }
+ }
+`
+
+const Text = styled.span`
+ margin-right: 10px;
+`
+
+export const ButtonConnect: React.FC> = (props) => {
+ const { className, ...restProps } = props
+
+ return (
+ {
+ // eslint-disable-next-line no-console
+ console.log('connect')
+ }}
+ {...restProps}
+ >
+ Connect a Wallet
+
+
+ )
+}
diff --git a/src/components/buttons/ButtonCopy/index.tsx b/src/components/buttons/ButtonCopy/index.tsx
new file mode 100644
index 000000000..9fab7eebc
--- /dev/null
+++ b/src/components/buttons/ButtonCopy/index.tsx
@@ -0,0 +1,56 @@
+import React, { ButtonHTMLAttributes } from 'react'
+import styled from 'styled-components'
+
+import { CopyToClipboard } from 'react-copy-to-clipboard'
+
+import { CopyIcon } from '../../icons/CopyIcon'
+
+const Wrapper = styled.button`
+ background: none;
+ border: none;
+ cursor: pointer;
+ height: 14px;
+ margin: 0;
+ outline: none;
+ padding: 0;
+ width: 14px;
+
+ svg {
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ transition: fill 0.1s linear;
+ }
+ }
+
+ &:active {
+ opacity: 0.7;
+ }
+
+ &:hover {
+ .fill {
+ fill: ${({ theme }) => theme.primary1};
+ }
+ }
+
+ &[disabled],
+ &[disabled]:hover {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+`
+
+interface ButtonCopyProps extends ButtonHTMLAttributes {
+ copyValue: string
+}
+
+export const ButtonCopy: React.FC = (props) => {
+ const { copyValue, ...restProps } = props
+
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/components/buttons/ButtonMenu/index.tsx b/src/components/buttons/ButtonMenu/index.tsx
new file mode 100644
index 000000000..c93b7130e
--- /dev/null
+++ b/src/components/buttons/ButtonMenu/index.tsx
@@ -0,0 +1,36 @@
+import React, { ButtonHTMLAttributes } from 'react'
+import styled from 'styled-components'
+
+import { MenuIcon } from '../../icons/MenuIcon'
+
+const Wrapper = styled.button`
+ background: none;
+ border: none;
+ cursor: pointer;
+ height: ${(props) => props.theme.header.height};
+ margin: 0 auto 0 0;
+ outline: none;
+ padding: 0;
+ transition: ease-out 0.15s all;
+ width: 40px;
+
+ &:active {
+ opacity: 0.5;
+ }
+
+ &[disabled],
+ &[disabled]:hover {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+`
+
+export const ButtonMenu: React.FC> = (props) => {
+ const { ...restProps } = props
+
+ return (
+
+
+
+ )
+}
diff --git a/src/components/buttons/ButtonSelect/index.tsx b/src/components/buttons/ButtonSelect/index.tsx
new file mode 100644
index 000000000..85a3ca647
--- /dev/null
+++ b/src/components/buttons/ButtonSelect/index.tsx
@@ -0,0 +1,47 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { ChevronDown } from '../../icons/ChevronDown'
+
+const Wrapper = styled.button`
+ align-items: center;
+ background-color: ${(props) => props.theme.textField.backgroundColor};
+ border-color: ${(props) => props.theme.textField.borderColor};
+ border-radius: ${(props) => props.theme.textField.borderRadius};
+ border-style: ${(props) => props.theme.textField.borderStyle};
+ border-width: ${(props) => props.theme.textField.borderWidth};
+ color: ${(props) => props.theme.textField.color};
+ display: flex;
+ font-size: ${(props) => props.theme.textField.fontSize};
+ font-weight: ${(props) => props.theme.textField.fontWeight};
+ height: ${(props) => props.theme.textField.height};
+ justify-content: space-between;
+ outline: none;
+ padding: 0 ${(props) => props.theme.textField.paddingHorizontal};
+ transition: border-color 0.15s linear;
+ width: 100%;
+
+ .isOpen & {
+ background-color: ${(props) => props.theme.textField.backgroundColorActive};
+ border-color: ${(props) => props.theme.textField.borderColorActive};
+ }
+`
+
+const Chevron = styled(ChevronDown)`
+ margin-left: 10px;
+`
+
+interface Props {
+ content: React.ReactNode | string
+}
+
+export const ButtonSelect: React.FC = (props) => {
+ const { content, ...restProps } = props
+
+ return (
+
+ {content}
+
+
+ )
+}
diff --git a/src/components/buttons/buttonStylingTypes.ts b/src/components/buttons/buttonStylingTypes.ts
new file mode 100644
index 000000000..6d3b88efc
--- /dev/null
+++ b/src/components/buttons/buttonStylingTypes.ts
@@ -0,0 +1,97 @@
+import { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react'
+import { css } from 'styled-components'
+
+export enum ButtonType {
+ primary,
+ primaryInverted,
+ danger,
+}
+
+export interface ButtonCommonProps {
+ buttonType?: ButtonType
+ theme?: any
+}
+
+export interface ButtonProps extends ButtonHTMLAttributes, ButtonCommonProps {}
+
+export interface ButtonLinkProps
+ extends AnchorHTMLAttributes,
+ ButtonCommonProps {}
+
+const PrimaryCSS = css`
+ background-color: ${(props) => props.theme.buttonPrimary.backgroundColor};
+ border-color: ${(props) => props.theme.buttonPrimary.borderColor};
+ color: ${(props) => props.theme.buttonPrimary.color};
+
+ &:hover {
+ background-color: ${(props) => props.theme.buttonPrimary.backgroundColorHover};
+ border-color: ${(props) => props.theme.buttonPrimary.borderColorHover};
+ color: ${(props) => props.theme.buttonPrimary.colorHover};
+ }
+
+ &[disabled],
+ &[disabled]:hover {
+ background-color: ${(props) => props.theme.buttonPrimary.borderColor};
+ border-color: ${(props) => props.theme.buttonPrimary.borderColor};
+ color: ${(props) => props.theme.buttonPrimary.color};
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+`
+
+const PrimaryInvertedCSS = css`
+ background-color: ${(props) => props.theme.buttonPrimaryInverted.backgroundColor};
+ border-color: ${(props) => props.theme.buttonPrimaryInverted.borderColor};
+ color: ${(props) => props.theme.buttonPrimaryInverted.color};
+
+ &:hover {
+ background-color: ${(props) => props.theme.buttonPrimaryInverted.backgroundColorHover};
+ border-color: ${(props) => props.theme.buttonPrimaryInverted.borderColorHover};
+ color: ${(props) => props.theme.buttonPrimaryInverted.colorHover};
+ }
+
+ &[disabled],
+ &[disabled]:hover {
+ background-color: ${(props) => props.theme.buttonPrimaryInverted.backgroundColor};
+ border-color: ${(props) => props.theme.buttonPrimaryInverted.borderColor};
+ color: ${(props) => props.theme.buttonPrimaryInverted.color};
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+`
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const getButtonTypeStyles = (buttonType: ButtonType = ButtonType.primary): any => {
+ if (buttonType === ButtonType.primary) {
+ return PrimaryCSS
+ }
+
+ if (buttonType === ButtonType.primaryInverted) {
+ return PrimaryInvertedCSS
+ }
+
+ return PrimaryCSS
+}
+
+export const ButtonCSS = css`
+ align-items: center;
+ border-radius: 6px;
+ border-style: solid;
+ border-width: 1px;
+ cursor: pointer;
+ display: flex;
+ font-size: 20px;
+ font-weight: 600;
+ height: 35px;
+ justify-content: center;
+ line-height: 1;
+ outline: none;
+ padding: 0 25px;
+ text-align: center;
+ text-decoration: none;
+ transition: all 0.15s ease-out;
+ user-select: none;
+ white-space: nowrap;
+
+ ${(props) => getButtonTypeStyles(props.buttonType)}
+`
diff --git a/src/components/common/CenteredCard/index.tsx b/src/components/common/CenteredCard/index.tsx
new file mode 100644
index 000000000..95b41b864
--- /dev/null
+++ b/src/components/common/CenteredCard/index.tsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+
+const Wrapper = styled(BaseCard)`
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 100%;
+ position: relative;
+ width: ${(props) => props.theme.layout.commonContainerMaxWidth};
+`
+
+const DropdownContainer = styled.span`
+ position: absolute;
+ right: ${(props) => props.theme.cards.paddingHorizontal};
+ top: ${(props) => props.theme.cards.paddingHorizontal};
+ z-index: 5;
+`
+
+interface FormCardProps {
+ children: React.ReactNode
+ dropdown?: React.ReactNode
+}
+
+export const CenteredCard: React.FC = (props) => {
+ const { children, dropdown, ...restProps } = props
+
+ return (
+
+ <>
+ {dropdown && {dropdown}}
+ {children}
+ >
+
+ )
+}
diff --git a/src/components/common/CookiesBanner/index.tsx b/src/components/common/CookiesBanner/index.tsx
new file mode 100644
index 000000000..77bdb1e7b
--- /dev/null
+++ b/src/components/common/CookiesBanner/index.tsx
@@ -0,0 +1,257 @@
+import React, { useCallback, useEffect, useState } from 'react'
+import { NavLink, useLocation } from 'react-router-dom'
+import styled from 'styled-components'
+
+import { isMobile } from 'react-device-detect'
+import ReactGA from 'react-ga'
+
+import { GOOGLE_ANALYTICS_ID } from '../../../constants/config'
+import { Button } from '../../buttons/Button'
+import { CloseIcon } from '../../icons/CloseIcon'
+import { Checkbox } from '../../pureStyledComponents/Checkbox'
+
+const INNER_WIDTH = '840px'
+
+const Wrapper = styled.div`
+ background-color: ${({ theme }) => theme.mainBackground};
+ bottom: 0;
+ box-shadow: 0 -20px 24px 0 #002249;
+ display: flex;
+ justify-content: center;
+ left: 0;
+ min-height: 160px;
+ padding: 20px;
+ position: fixed;
+ width: 100%;
+ z-index: 123;
+`
+
+const Content = styled.div`
+ max-width: 100%;
+ position: relative;
+ width: ${(props) => props.theme.layout.maxWidth};
+`
+
+const Text = styled.p`
+ color: ${({ theme }) => theme.text1};
+ font-size: 17px;
+ font-weight: normal;
+ line-height: 1.4;
+ margin: 0 auto 20px;
+ max-width: 100%;
+ padding: 0 20px;
+ position: relative;
+ text-align: center;
+ width: ${INNER_WIDTH};
+ z-index: 1;
+`
+
+const Link = styled(NavLink)`
+ color: ${({ theme }) => theme.text1};
+ text-decoration: underline;
+
+ &:hover {
+ text-decoration: none;
+ }
+`
+
+const ButtonContainer = styled.div`
+ &.buttonContainer {
+ align-items: center;
+ display: flex;
+ inset: auto;
+ justify-content: center;
+ position: relative;
+ }
+`
+
+const Labels = styled.div`
+ align-items: center;
+ display: flex;
+`
+
+const Label = styled.div<{ clickable?: boolean }>`
+ align-items: center;
+ color: ${({ theme }) => theme.text1};
+ display: flex;
+ font-size: 17px;
+ font-weight: normal;
+ line-height: 1.4;
+ margin: 0 25px 0 0;
+
+ &:last-child {
+ margin-right: 80px;
+ }
+
+ ${(props) => props.clickable && 'cursor: pointer'}
+`
+
+Label.defaultProps = {
+ clickable: false,
+}
+
+const CheckboxStyled = styled(Checkbox)`
+ margin: 0 10px 0 0;
+`
+
+const ButtonAccept = styled(Button)`
+ &.buttonAccept {
+ bottom: auto;
+ font-size: 18px;
+ height: 36px;
+ left: auto;
+ max-width: 170px;
+ position: relative;
+ right: auto;
+ top: auto;
+ }
+`
+
+const ButtonClose = styled.button`
+ align-items: center;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ outline: none;
+ padding: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ transition: all 0.15s linear;
+ z-index: 5;
+
+ &:hover {
+ opacity: 0.5;
+ }
+`
+
+const VISIBLE_COOKIES_BANNER = 'VISIBLE_COOKIES_BANNER'
+const COOKIES_FALSE = 'false'
+const ACCEPT_GOOGLE_ANALYTICS = 'ACCEPT_GOOGLE_ANALYTICS'
+
+interface Props {
+ isBannerVisible: boolean
+ onHide: () => void
+}
+
+export const CookiesBanner: React.FC = (props) => {
+ const { isBannerVisible, onHide } = props
+ const storage = window.localStorage
+
+ const isCookiesBannerVisible = useCallback(
+ () => !(storage.getItem(VISIBLE_COOKIES_BANNER) === COOKIES_FALSE),
+ [storage],
+ )
+
+ const location = useLocation()
+ const [cookiesWarningVisible, setCookiesWarningVisible] = useState(isCookiesBannerVisible())
+
+ const showCookiesWarning = useCallback(() => {
+ setCookiesWarningVisible(true)
+ storage.setItem(VISIBLE_COOKIES_BANNER, '')
+ }, [storage])
+
+ const isGoogleAnalyticsAccepted = useCallback(
+ () => storage.getItem(ACCEPT_GOOGLE_ANALYTICS) === ACCEPT_GOOGLE_ANALYTICS,
+ [storage],
+ )
+
+ const hideCookiesWarning = useCallback(() => {
+ setCookiesWarningVisible(false)
+ storage.setItem(VISIBLE_COOKIES_BANNER, COOKIES_FALSE)
+ onHide()
+ if (!isGoogleAnalyticsAccepted()) {
+ setGoogleAnalyticsAccepted(false)
+ }
+ }, [isGoogleAnalyticsAccepted, onHide, storage])
+
+ const [googleAnalyticsAccepted, setGoogleAnalyticsAccepted] = useState(
+ isGoogleAnalyticsAccepted(),
+ )
+
+ const acceptGoogleAnalytics = useCallback(() => {
+ setGoogleAnalyticsAccepted(true)
+ storage.setItem(ACCEPT_GOOGLE_ANALYTICS, ACCEPT_GOOGLE_ANALYTICS)
+ }, [storage])
+
+ const rejectGoogleAnalytics = useCallback(() => {
+ setGoogleAnalyticsAccepted(false)
+ storage.setItem(ACCEPT_GOOGLE_ANALYTICS, '')
+ }, [storage])
+
+ const toggleAcceptGoogleAnalytics = useCallback(() => {
+ if (googleAnalyticsAccepted) {
+ rejectGoogleAnalytics()
+ } else {
+ setGoogleAnalyticsAccepted(true)
+ }
+ }, [googleAnalyticsAccepted, rejectGoogleAnalytics])
+
+ const acceptAll = useCallback(() => {
+ acceptGoogleAnalytics()
+ hideCookiesWarning()
+ }, [acceptGoogleAnalytics, hideCookiesWarning])
+
+ const loadGoogleAnalytics = useCallback(() => {
+ if (!GOOGLE_ANALYTICS_ID) {
+ console.warn(
+ 'In order to use Google Analytics you need to add a trackingID using the REACT_APP_GOOGLE_ANALYTICS_ID environment variable.',
+ )
+ return
+ }
+
+ if (typeof GOOGLE_ANALYTICS_ID === 'string') {
+ ReactGA.initialize(GOOGLE_ANALYTICS_ID, { gaOptions: { cookieDomain: 'auto' } })
+ ReactGA.set({
+ customBrowserType: !isMobile
+ ? 'desktop'
+ : 'web3' in window || 'ethereum' in window
+ ? 'mobileWeb3'
+ : 'mobileRegular',
+ })
+ ReactGA.set({ anonymizeIp: true })
+ ReactGA.set({ page: location.pathname })
+ ReactGA.pageview(location.pathname)
+ }
+ }, [location])
+
+ useEffect(() => {
+ if (googleAnalyticsAccepted) {
+ loadGoogleAnalytics()
+ }
+ if (isBannerVisible) {
+ showCookiesWarning()
+ }
+ }, [googleAnalyticsAccepted, isBannerVisible, loadGoogleAnalytics, showCookiesWarning])
+
+ return cookiesWarningVisible ? (
+
+
+
+ We use cookies to give you the best experience and to help improve our website. Please
+ read our Cookie Policy for more information. By
+ clicking "Accept All", you agree to the storing of cookies on
+ your device to enhance site navigation, analyze site usage and provide customer support.
+
+
+
+
+
+
+
+ Accept All
+
+
+
+
+
+
+
+ ) : null
+}
diff --git a/src/components/common/DoubleLogo/index.tsx b/src/components/common/DoubleLogo/index.tsx
new file mode 100644
index 000000000..75e02990d
--- /dev/null
+++ b/src/components/common/DoubleLogo/index.tsx
@@ -0,0 +1,43 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import TokenLogo from '../TokenLogo'
+
+const TokenWrapper = styled.div`
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ position: relative;
+`
+
+const Logo = styled(TokenLogo)`
+ border: 3px solid #001429;
+`
+
+const HigherLogo = styled(Logo)`
+ margin-right: calc(-${(props) => props.size} / 3);
+ z-index: 5;
+`
+
+const CoveredLogo = styled(Logo)`
+ z-index: 1;
+`
+
+interface DoubleTokenLogoProps {
+ auctioningToken: { address: string; symbol: string }
+ biddingToken: { address: string; symbol: string }
+ size?: string
+}
+
+const DoubleLogo: React.FC = (props) => {
+ const { auctioningToken, biddingToken, size = '24px', ...restProps } = props
+
+ return (
+
+
+
+
+ )
+}
+
+export default DoubleLogo
diff --git a/src/components/common/Dropdown/index.tsx b/src/components/common/Dropdown/index.tsx
new file mode 100644
index 000000000..425b8876d
--- /dev/null
+++ b/src/components/common/Dropdown/index.tsx
@@ -0,0 +1,263 @@
+import React, { DOMAttributes, createRef, useCallback, useEffect, useState } from 'react'
+import styled, { css } from 'styled-components'
+
+import { BaseCard } from '../../pureStyledComponents/BaseCard'
+
+export enum DropdownPosition {
+ center,
+ left,
+ right,
+}
+
+export enum DropdownDirection {
+ downwards,
+ upwards,
+}
+
+const Wrapper = styled.div<{ isOpen: boolean; disabled: boolean }>`
+ outline: none;
+ pointer-events: ${(props) => (props.disabled ? 'none' : 'initial')};
+ position: relative;
+ z-index: ${(props) => (props.isOpen ? '100' : '50')};
+
+ &[disabled] {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+`
+
+const ButtonContainer = styled.div`
+ background-color: transparent;
+ border: none;
+ display: block;
+ outline: none;
+ padding: 0;
+ user-select: none;
+ width: 100%;
+`
+
+const PositionLeftCSS = css`
+ left: 0;
+`
+
+const PositionRightCSS = css`
+ right: 0;
+`
+
+const PositionCenterCSS = css`
+ left: 50%;
+ transform: translateX(-50%);
+`
+
+const DirectionDownwardsCSS = css`
+ top: calc(100% + 10px);
+`
+
+const DirectionUpwardsCSS = css`
+ bottom: calc(100% + 10px);
+`
+
+const Items = styled(BaseCard)<{
+ dropdownDirection?: DropdownDirection
+ dropdownPosition?: DropdownPosition
+ fullWidth?: boolean
+ isOpen: boolean
+}>`
+ background: ${({ theme }) => theme.dropdown.background};
+ border-radius: 1px solid ${({ theme }) => theme.dropdown.borderRadius};
+ border: 1px solid ${({ theme }) => theme.dropdown.border};
+ box-shadow: ${({ theme }) => theme.dropdown.boxShadow};
+ display: ${(props) => (props.isOpen ? 'block' : 'none')};
+ min-width: 160px;
+ position: absolute;
+ white-space: nowrap;
+
+ ${(props) => props.fullWidth && 'width: 100%;'}
+ ${(props) => (props.dropdownPosition === DropdownPosition.left ? PositionLeftCSS : '')}
+ ${(props) => (props.dropdownPosition === DropdownPosition.right ? PositionRightCSS : '')}
+ ${(props) => (props.dropdownPosition === DropdownPosition.center ? PositionCenterCSS : '')}
+ ${(props) =>
+ props.dropdownDirection === DropdownDirection.downwards ? DirectionDownwardsCSS : ''}
+ ${(props) => (props.dropdownDirection === DropdownDirection.upwards ? DirectionUpwardsCSS : '')}
+`
+
+Items.defaultProps = {
+ dropdownDirection: DropdownDirection.downwards,
+ dropdownPosition: DropdownPosition.left,
+ fullWidth: false,
+ isOpen: false,
+}
+
+export interface DropdownItemProps {
+ disabled?: boolean
+}
+
+export const DropdownItemCSS = css`
+ align-items: center;
+ background-color: ${(props) => props.theme.dropdown.item.backgroundColor};
+ border-bottom: 1px solid ${(props) => props.theme.dropdown.item.borderColor};
+ color: ${(props) => props.theme.dropdown.item.color};
+ cursor: pointer;
+ display: flex;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.4;
+ min-height: ${(props) => props.theme.dropdown.item.height};
+ overflow: hidden;
+ padding: 10px ${(props) => props.theme.dropdown.item.paddingHorizontal};
+ text-decoration: none;
+ user-select: none;
+
+ &.isActive {
+ background-color: ${(props) => props.theme.dropdown.item.backgroundColorActive};
+ color: ${(props) => props.theme.dropdown.item.colorActive};
+ font-weight: 600;
+ }
+
+ &:first-child {
+ border-top-left-radius: ${(props) => props.theme.cards.borderRadius};
+ border-top-right-radius: ${(props) => props.theme.cards.borderRadius};
+ }
+
+ &:last-child {
+ border-bottom-left-radius: ${(props) => props.theme.cards.borderRadius};
+ border-bottom-right-radius: ${(props) => props.theme.cards.borderRadius};
+ border-bottom: none;
+ }
+
+ &:hover {
+ background-color: ${(props) => props.theme.dropdown.item.backgroundColorHover};
+ }
+
+ &:disabled,
+ &[disabled] {
+ &,
+ &:hover {
+ background-color: ${(props) => props.theme.dropdown.item.backgroundColor};
+ cursor: not-allowed;
+ font-weight: 400;
+ opacity: 0.5;
+ pointer-events: none;
+ }
+ }
+`
+
+export const DropdownItem = styled.div`
+ ${DropdownItemCSS}
+`
+
+DropdownItem.defaultProps = {
+ disabled: false,
+}
+
+interface Props extends DOMAttributes {
+ activeItemHighlight?: boolean | undefined
+ className?: string
+ closeOnClick?: boolean
+ currentItem?: number | undefined
+ disabled?: boolean
+ dropdownButtonContent?: React.ReactNode | string
+ dropdownDirection?: DropdownDirection | undefined
+ dropdownPosition?: DropdownPosition | undefined
+ fullWidth?: boolean
+ items: Array
+ triggerClose?: boolean
+}
+
+export const Dropdown: React.FC = (props) => {
+ const {
+ activeItemHighlight = true,
+ className = '',
+ closeOnClick = true,
+ currentItem = 0,
+ disabled = false,
+ dropdownButtonContent,
+ dropdownDirection,
+ dropdownPosition,
+ fullWidth,
+ items,
+ triggerClose,
+ ...restProps
+ } = props
+ const [isOpen, setIsOpen] = useState(false)
+ const node = createRef()
+
+ const onButtonClick = useCallback(
+ (e) => {
+ e.stopPropagation()
+ if (disabled) return
+ setIsOpen(!isOpen)
+ },
+ [disabled, isOpen],
+ )
+
+ useEffect(() => {
+ // Note: you can use triggerClose to close the dropdown when clicking on a specific element
+ if (triggerClose) {
+ setIsOpen(false)
+ }
+
+ // Note: This code handles closing when clickin outside of the dropdown
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const handleClick = (e: any) => {
+ if (node && node.current && node.current.contains(e.target)) {
+ return
+ }
+ setIsOpen(false)
+ }
+
+ document.addEventListener('mousedown', handleClick)
+
+ return () => {
+ document.removeEventListener('mousedown', handleClick)
+ }
+ }, [node, triggerClose])
+
+ return (
+
+
+ {dropdownButtonContent}
+
+
+ {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ items.map((item: any, index: number) => {
+ const isActive = activeItemHighlight && index === currentItem
+ const dropdownItem = React.cloneElement(item, {
+ className: `dropdownItem ${isActive && 'isActive'}`,
+ key: item.key ? item.key : index,
+ onClick: (e) => {
+ e.stopPropagation()
+
+ if (closeOnClick) {
+ setIsOpen(false)
+ }
+
+ if (!item.props.onClick) {
+ return
+ }
+
+ item.props.onClick()
+ },
+ })
+
+ return dropdownItem
+ })
+ }
+
+
+ )
+}
diff --git a/src/components/common/InlineLoading/index.tsx b/src/components/common/InlineLoading/index.tsx
new file mode 100644
index 000000000..48e9c5d04
--- /dev/null
+++ b/src/components/common/InlineLoading/index.tsx
@@ -0,0 +1,59 @@
+import React, { HTMLAttributes } from 'react'
+import styled, { css } from 'styled-components'
+
+import { Spinner, SpinnerSize } from '../Spinner'
+
+const FlexCSS = css`
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ justify-content: center;
+`
+
+const AbsoluteCSS = css`
+ left: 0;
+ position: absolute;
+ top: 0;
+ z-index: 100;
+`
+
+const Wrapper = styled.div<{ absolute?: boolean }>`
+ height: 100%;
+ width: 100%;
+
+ ${(props) => (props.absolute ? AbsoluteCSS : '')}
+ ${(props) => (!props.absolute ? FlexCSS : '')}
+`
+
+Wrapper.defaultProps = {
+ absolute: false,
+}
+
+const Text = styled.p`
+ color: ${({ theme }) => theme.text1};
+ font-size: 22px;
+ font-weight: 400;
+ line-height: 1.4;
+ margin: 0;
+ padding: 15px 20px;
+ text-align: center;
+ width: 100%;
+`
+
+interface Props extends HTMLAttributes {
+ absolute?: boolean
+ message?: string
+ size?: SpinnerSize
+}
+
+export const InlineLoading: React.FC = (props: Props) => {
+ const { message, size, ...restProps } = props
+
+ return (
+
+
+ {message ? {message} : null}
+
+ )
+}
diff --git a/src/components/common/KeyValue/index.tsx b/src/components/common/KeyValue/index.tsx
new file mode 100644
index 000000000..73a368dcd
--- /dev/null
+++ b/src/components/common/KeyValue/index.tsx
@@ -0,0 +1,71 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+`
+
+const Value = styled.div<{ align: string }>`
+ align-items: center;
+ color: ${({ theme }) => theme.text1};
+ display: flex;
+ font-size: 20px;
+ font-weight: 700;
+ justify-content: ${(props) => props.align};
+ line-height: 1;
+ margin: 0 0 2px;
+
+ > * {
+ margin-right: 8px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+`
+
+const Key = styled.div<{ align: string }>`
+ align-items: center;
+ color: ${({ theme }) => theme.text1};
+ display: flex;
+ font-size: 18px;
+ font-weight: 400;
+ justify-content: ${(props) => props.align};
+ line-height: 1.3;
+ margin: 0;
+ text-transform: capitalize;
+
+ > * {
+ margin-right: 8px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+`
+
+Key.defaultProps = {
+ align: 'center',
+}
+
+Value.defaultProps = {
+ align: 'center',
+}
+
+interface Props {
+ align?: string
+ itemKey: React.ReactNode
+ itemValue: React.ReactNode
+}
+
+export const KeyValue: React.FC = (props) => {
+ const { align, itemKey, itemValue, ...restProps } = props
+
+ return (
+
+ {itemValue}
+ {itemKey}
+
+ )
+}
diff --git a/src/components/common/Logo/index.tsx b/src/components/common/Logo/index.tsx
new file mode 100644
index 000000000..43acb557b
--- /dev/null
+++ b/src/components/common/Logo/index.tsx
@@ -0,0 +1,34 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.span`
+ align-items: center;
+ display: flex;
+`
+
+const LogoSVG = styled.svg`
+ .text {
+ fill: #fff;
+ font-family: ${(props) => props.theme.fonts.fontFamily};
+ font-size: 24px;
+ font-weight: 700;
+ }
+`
+
+export const Logo: React.FC = (props) => {
+ return (
+
+
+
+
+ Gnosis Auction
+
+
+
+
+
+ )
+}
diff --git a/src/components/common/Spinner/index.tsx b/src/components/common/Spinner/index.tsx
new file mode 100644
index 000000000..b65aa89f7
--- /dev/null
+++ b/src/components/common/Spinner/index.tsx
@@ -0,0 +1,50 @@
+import React, { HTMLAttributes } from 'react'
+import styled, { keyframes } from 'styled-components'
+
+import { Spinner as SpinnerSVG } from '../../icons/Spinner'
+
+const rotate = keyframes`
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+`
+
+const Wrapper = styled.div<{ size?: SpinnerSize | string | undefined }>`
+ animation: ${rotate} 2s linear infinite;
+ flex-grow: 0;
+ flex-shrink: 0;
+ height: ${(props) => props.size};
+ width: ${(props) => props.size};
+
+ svg {
+ height: 100%;
+ width: 100%;
+ }
+`
+
+export enum SpinnerSize {
+ small = '30px',
+ regular = '50px',
+ large = '60px',
+}
+
+Wrapper.defaultProps = {
+ size: SpinnerSize.regular,
+}
+
+interface Props extends HTMLAttributes {
+ size?: SpinnerSize | string | undefined
+}
+
+export const Spinner: React.FC = (props: Props) => {
+ const { size, ...restProps } = props
+
+ return (
+
+
+
+ )
+}
diff --git a/src/components/common/TokenLogo/index.tsx b/src/components/common/TokenLogo/index.tsx
new file mode 100644
index 000000000..5249a07ff
--- /dev/null
+++ b/src/components/common/TokenLogo/index.tsx
@@ -0,0 +1,60 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { useTokenListState } from '../../../state/tokenList/hooks'
+import { isAddress } from '../../../utils'
+
+const Wrapper = styled.div<{ size?: string }>`
+ background-color: #606467;
+ border-radius: 50%;
+ height: ${({ size }) => size};
+ overflow: hidden;
+ position: relative;
+ top: -1px;
+ width: ${({ size }) => size};
+`
+
+const Image = styled.img`
+ display: block;
+ height: 100%;
+ width: 100%;
+`
+
+const Placeholder = styled.div<{ size?: string }>`
+ align-items: center;
+ color: #fff;
+ display: flex;
+ font-size: calc(${(props) => props.size} * 0.26);
+ font-weight: bold;
+ height: 100%;
+ justify-content: center;
+ letter-spacing: 0px;
+ line-height: 1.3;
+ text-align: center;
+ width: 100%;
+ white-space: nowrap;
+`
+
+interface TokenLogoProps {
+ token: { address: string; symbol?: string }
+ size?: string
+}
+
+const TokenLogo: React.FC = (props) => {
+ const { size = '24px', token, ...restProps } = props
+ const { address, symbol } = token
+ const { tokens } = useTokenListState()
+ const validToken = isAddress(address) && tokens && tokens.length > 0
+ const tokenInfo =
+ validToken && tokens.find((token) => token.address.toLowerCase() === address.toLowerCase())
+ const imageURL = validToken && tokenInfo && tokenInfo.logoURI ? tokenInfo.logoURI : undefined
+ const tokenSymbol = tokenInfo && tokenInfo.symbol ? tokenInfo.symbol : symbol
+
+ return (
+
+ {imageURL ? : {tokenSymbol}}
+
+ )
+}
+
+export default TokenLogo
diff --git a/src/components/common/Tooltip/index.tsx b/src/components/common/Tooltip/index.tsx
new file mode 100644
index 000000000..f63aa5fd5
--- /dev/null
+++ b/src/components/common/Tooltip/index.tsx
@@ -0,0 +1,62 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import ReactTooltip, { TooltipProps } from 'react-tooltip'
+
+import { TooltipIcon } from '../../icons/TooltipIcon'
+
+const Wrapper = styled.span`
+ cursor: pointer;
+ position: relative;
+ top: -1px;
+
+ .tooltipIcon {
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ transition: fill 0.1s linear;
+ }
+ }
+
+ &:hover {
+ .fill {
+ fill: ${({ theme }) => theme.primary1};
+ }
+ }
+`
+
+interface Props extends TooltipProps {
+ className?: string
+ id: string
+ text: string
+}
+
+export const Tooltip: React.FC = (props) => {
+ const { className, delayHide = 50, delayShow = 250, id, text, ...restProps } = props
+ const tooltipId = `tooltip_${id}`
+
+ return (
+
+
+
+
+ )
+}
diff --git a/src/components/common/UserDropdown/index.tsx b/src/components/common/UserDropdown/index.tsx
new file mode 100644
index 000000000..02ebdcbba
--- /dev/null
+++ b/src/components/common/UserDropdown/index.tsx
@@ -0,0 +1,262 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { useWeb3React } from '@web3-react/core'
+
+import { useActiveWeb3React } from '../../../hooks'
+import { useDarkModeManager } from '../../../state/user/hooks'
+import { getChainName, truncateStringInTheMiddle } from '../../../utils/tools'
+import { Button } from '../../buttons/Button'
+import { ButtonType } from '../../buttons/buttonStylingTypes'
+import { Dropdown, DropdownItem, DropdownPosition } from '../../common/Dropdown'
+import { Switch } from '../../form/Switch'
+import { ChevronDown } from '../../icons/ChevronDown'
+import { ChevronRight } from '../../icons/ChevronRight'
+import { TransactionsModal } from '../../modals/TransactionsModal'
+
+const Wrapper = styled(Dropdown)`
+ align-items: center;
+ display: flex;
+ height: 100%;
+
+ .dropdownButton {
+ height: 100%;
+ }
+
+ &.isOpen {
+ .chevronDown {
+ transform: rotateX(180deg);
+ }
+ }
+`
+
+const DropdownButton = styled.div`
+ align-items: flex-start;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ justify-content: center;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+
+ &:hover {
+ .addressText {
+ color: ${({ theme }) => theme.text1};
+ }
+
+ .chevronDown {
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+ }
+ }
+`
+
+const Address = styled.div`
+ align-items: center;
+ display: flex;
+ margin-top: 10px;
+`
+
+const AddressText = styled.div`
+ color: ${({ theme }) => theme.text1};
+ font-size: 15px;
+ font-weight: 400;
+ line-height: 1.2;
+ margin-right: 8px;
+`
+
+const Connection = styled.div`
+ align-items: center;
+ display: flex;
+`
+
+const ConnectionStatus = styled.div`
+ background-color: ${({ theme }) => theme.green1};
+ border-radius: 8px;
+ flex-grow: 0;
+ flex-shrink: 0;
+ height: 8px;
+ margin-right: 4px;
+ width: 8px;
+`
+
+const ConnectionText = styled.div`
+ color: ${({ theme }) => theme.green1};
+ font-size: 9px;
+ font-weight: 600;
+ line-height: 1.2;
+ margin-bottom: -2px;
+ text-transform: capitalize;
+`
+
+const Content = styled.div`
+ width: 245px;
+`
+const DropdownItemStyled = styled(DropdownItem)`
+ cursor: default;
+ padding: 0;
+
+ &:hover {
+ background-color: transparent;
+ }
+`
+
+const Item = styled.div<{ hasOnClick?: boolean; disabled?: boolean; hide?: boolean }>`
+ align-items: center;
+ border-bottom: 1px solid ${({ theme }) => theme.border};
+ color: ${({ theme }) => theme.dropdown.item.color};
+ cursor: ${(props) => (props.hasOnClick ? 'pointer' : 'default')};
+ display: ${(props) => (props.hide ? 'none' : 'flex')};
+ font-size: 13px;
+ justify-content: space-between;
+ line-height: 1.2;
+ padding: 12px;
+ width: 100%;
+
+ &:hover {
+ background-color: ${(props) =>
+ props.hasOnClick ? props.theme.dropdown.item.backgroundColorHover : 'transparent'};
+ }
+
+ ${(props) => props.disabled && 'pointer-events: none;'}
+`
+
+const Title = styled.div`
+ padding-right: 10px;
+`
+
+const Value = styled.div`
+ font-weight: 600;
+ text-transform: capitalize;
+ position: relative;
+`
+
+const DisconnectButton = styled(Button)`
+ border-radius: 4px;
+ font-size: 14px;
+ height: 28px;
+ line-height: 1;
+ width: 100%;
+`
+
+const UserDropdownButton = () => {
+ const { account } = useWeb3React()
+ const { chainId } = useActiveWeb3React()
+
+ return (
+
+
+
+ {account ? truncateStringInTheMiddle(account, 8, 6) : 'Invalid address.'}
+
+
+
+
+
+ {getChainName(chainId)}
+
+
+ )
+}
+
+export const UserDropdown: React.FC = (props) => {
+ const [transactionsModalVisible, setTransactionsModalVisible] = React.useState(false)
+ const [darkMode, toggleDarkMode] = useDarkModeManager()
+
+ const { deactivate, library } = useActiveWeb3React()
+
+ const getWalletName = React.useCallback((): string => {
+ const provider = library.provider
+
+ const isMetaMask =
+ Object.prototype.hasOwnProperty.call(provider, 'isMetaMask') && provider.isMetaMask
+ const isWalletConnect = Object.prototype.hasOwnProperty.call(provider, 'wc')
+
+ return isMetaMask ? 'MetaMask' : isWalletConnect ? 'WalletConnect' : 'Unknown'
+ }, [library])
+
+ const disconnect = React.useCallback(async () => {
+ deactivate()
+ }, [deactivate])
+
+ const UserDropdownContent = () => {
+ const items = [
+ {
+ title: 'Wallet',
+ value: getWalletName(),
+ },
+ {
+ title: 'Your transactions',
+ onClick: () => {
+ setTransactionsModalVisible(true)
+ },
+ value: ,
+ },
+ {
+ disabled: true,
+ hide: true,
+ onClick: toggleDarkMode,
+ title: 'Night mode',
+ value: ,
+ },
+ ]
+
+ return (
+
+ {items.map((item, index) => {
+ return (
+ -
+ {item.title}
+ {item.value}
+
+ )
+ })}
+ -
+ {
+ disconnect()
+ }}
+ >
+ Disconnect
+
+
+
+ )
+ }
+
+ const headerDropdownItems = [
+
+
+ ,
+ ]
+
+ return (
+ <>
+ }
+ dropdownPosition={DropdownPosition.right}
+ items={headerDropdownItems}
+ {...props}
+ />
+ setTransactionsModalVisible(false)}
+ />
+ >
+ )
+}
diff --git a/src/components/form/CurrencyInputPanel/index.tsx b/src/components/form/CurrencyInputPanel/index.tsx
new file mode 100644
index 000000000..54a487ab9
--- /dev/null
+++ b/src/components/form/CurrencyInputPanel/index.tsx
@@ -0,0 +1,73 @@
+import React from 'react'
+import styled from 'styled-components'
+import { Token } from 'uniswap-xdai-sdk'
+
+import { useActiveWeb3React } from '../../../hooks'
+import TokenLogo from '../../common/TokenLogo'
+import { ControlButton, FormLabel } from '../../form/FormLabel'
+import { Input as NumericalInput } from '../../form/NumericalInput'
+import { FormRow } from '../../pureStyledComponents/FormRow'
+import { TextfieldCSS } from '../../pureStyledComponents/Textfield'
+
+const TextfieldWrapper = styled.div`
+ ${TextfieldCSS}
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+`
+
+const TokenInfo = styled.div`
+ align-items: center;
+ display: flex;
+ flex-shrink: 0;
+ justify-content: space-between;
+ margin-left: 15px;
+`
+
+const TokenSymbol = styled.div`
+ color: ${({ theme }) => theme.text1};
+ font-size: 18px;
+ font-stretch: 400;
+ font-weight: 600;
+ margin-right: 8px;
+ text-align: right;
+`
+
+interface CurrencyInputPanelProps {
+ onMax?: () => void
+ onUserSellAmountInput: (val: string) => void
+ token: Maybe
+ value: string
+}
+
+export default function CurrencyInputPanel({
+ onMax,
+ onUserSellAmountInput,
+ token = null,
+ value,
+}: CurrencyInputPanelProps) {
+ const { account } = useActiveWeb3React()
+
+ return (
+
+ Max}
+ text={'Amount'}
+ />
+
+ {
+ onUserSellAmountInput(val)
+ }}
+ value={value}
+ />
+
+ {token && token.symbol && {token.symbol}}{' '}
+ {token && token.address && (
+
+ )}
+
+
+
+ )
+}
diff --git a/src/components/form/FormLabel/index.tsx b/src/components/form/FormLabel/index.tsx
new file mode 100644
index 000000000..2c34d1df2
--- /dev/null
+++ b/src/components/form/FormLabel/index.tsx
@@ -0,0 +1,65 @@
+import React from 'react'
+import styled, { css } from 'styled-components'
+
+const Wrapper = styled.div`
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+ margin: 0 0 10px;
+`
+
+export const Label = styled.label`
+ color: ${({ theme }) => theme.text1};
+ font-size: 18px;
+ font-weight: 400;
+ line-height: 1.2;
+ margin: 0;
+ text-align: left;
+`
+
+const ControlCSS = css`
+ background: none;
+ border: none;
+ color: ${({ theme }) => theme.primary1};
+ cursor: pointer;
+ font-size: 17px;
+ font-weight: 400;
+ line-height: 1.2;
+ margin: 0;
+ outline: none;
+ padding: 0;
+ text-align: right;
+ text-decoration: underline;
+
+ &:hover {
+ text-decoration: none;
+ }
+`
+
+export const ControlA = styled.a`
+ ${ControlCSS}
+`
+
+export const ControlSpan = styled.span`
+ ${ControlCSS}
+`
+
+export const ControlButton = styled.button`
+ ${ControlCSS}
+`
+
+interface Props {
+ extraControls?: React.ReactNode
+ text: string
+}
+
+export const FormLabel: React.FC = (props) => {
+ const { extraControls, text, ...restProps } = props
+
+ return (
+
+
+ {extraControls && extraControls}
+
+ )
+}
diff --git a/src/components/form/NumericalInput/index.tsx b/src/components/form/NumericalInput/index.tsx
new file mode 100644
index 000000000..27bc8fb02
--- /dev/null
+++ b/src/components/form/NumericalInput/index.tsx
@@ -0,0 +1,67 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { escapeRegExp } from '../../../utils'
+import { TexfieldPartsCSS } from '../../pureStyledComponents/Textfield'
+
+const StyledInput = styled.input<{ error?: boolean }>`
+ background-color: ${({ theme }) => theme.textField.backgroundColor};
+ border: none;
+ border-radius: 0;
+ color: ${({ theme }) => (props) =>
+ props.error ? theme.textField.errorColor : theme.textField.color};
+ font-size: ${({ theme }) => theme.textField.fontSize};
+ font-weight: ${({ theme }) => theme.textField.fontWeight};
+ height: 100%;
+ outline: none;
+ padding: 0;
+ width: 100%;
+
+ ${TexfieldPartsCSS}
+`
+
+const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped "." characters via in a non-capturing group
+
+export const Input = React.memo(function InnerInput({
+ onUserSellAmountInput,
+ placeholder,
+ value,
+ ...rest
+}: {
+ value: string | number
+ onUserSellAmountInput: (string) => void
+ error?: boolean
+ fontSize?: string
+ align?: 'right' | 'left'
+} & Omit, 'ref' | 'onChange' | 'as'>) {
+ const enforcer = (nextUserInput: string) => {
+ if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
+ onUserSellAmountInput(nextUserInput)
+ }
+ }
+
+ return (
+ {
+ // replace commas with periods, because uniswap exclusively uses period as the decimal separator
+ enforcer(event.target.value.replace(/,/g, '.'))
+ }}
+ // text-specific options
+ pattern="^[0-9]*[.,]?[0-9]*$"
+ placeholder={placeholder || '0.0'}
+ spellCheck="false"
+ title="Token Amount"
+ type="text"
+ value={value}
+ {...rest}
+ />
+ )
+})
+
+export default Input
diff --git a/src/components/form/PriceInputPanel/index.tsx b/src/components/form/PriceInputPanel/index.tsx
new file mode 100644
index 000000000..ef9544eed
--- /dev/null
+++ b/src/components/form/PriceInputPanel/index.tsx
@@ -0,0 +1,70 @@
+import React from 'react'
+import styled from 'styled-components'
+import { Token } from 'uniswap-xdai-sdk'
+
+import DoubleLogo from '../../common/DoubleLogo'
+import { FormLabel } from '../../form/FormLabel'
+import { Input as NumericalInput } from '../../form/NumericalInput'
+import { FormRow } from '../../pureStyledComponents/FormRow'
+import { TextfieldCSS } from '../../pureStyledComponents/Textfield'
+
+const TextfieldWrapper = styled.div`
+ ${TextfieldCSS}
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+`
+
+const TokenInfo = styled.div`
+ align-items: center;
+ display: flex;
+ flex-shrink: 0;
+ margin-left: 15px;
+`
+
+interface CurrencyInputPanelProps {
+ auctioningToken: Maybe
+ biddingToken: Maybe
+ label: string
+ onUserPriceInput: (val: string) => void
+ value: string
+}
+
+export default function PriceInputPanel({
+ auctioningToken = null,
+ biddingToken = null,
+ label,
+ onUserPriceInput,
+ value,
+}: CurrencyInputPanelProps) {
+ return (
+
+
+
+ {
+ onUserPriceInput(val)
+ }}
+ value={value}
+ />
+
+ {auctioningToken && biddingToken ? (
+
+ ) : (
+ '-'
+ )}
+
+
+
+ )
+}
diff --git a/src/components/form/Switch/index.tsx b/src/components/form/Switch/index.tsx
new file mode 100644
index 000000000..c9a156f13
--- /dev/null
+++ b/src/components/form/Switch/index.tsx
@@ -0,0 +1,74 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.div<{ disabled?: boolean }>`
+ align-items: center;
+ cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
+ display: flex;
+ justify-content: center;
+ opacity: ${(props) => (props.disabled ? '0.5' : '1')};
+ pointer-events: ${(props) => (props.disabled ? 'none' : 'all')};
+`
+
+const SwitchWrapper = styled.div<{ active: boolean; small?: boolean }>`
+ background-color: ${(props) => (props.active ? props.theme.primary1 : '#ccc')};
+ border-radius: ${(props) => (props.small ? '15px' : '20px')};
+ cursor: pointer;
+ height: ${(props) => (props.small ? '16px' : '20px')};
+ position: relative;
+ transition: all 0.1s linear;
+ width: ${(props) => (props.small ? '26px' : '36px')};
+`
+
+SwitchWrapper.defaultProps = {
+ small: false,
+}
+
+const Circle = styled.div<{ active: boolean; small?: boolean }>`
+ background-color: #fff;
+ border-radius: 50%;
+ box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.16);
+ cursor: pointer;
+ height: ${(props) => (props.small ? '11px' : '16px')};
+ position: absolute;
+ top: 2px;
+ transition: all 0.1s linear;
+ width: ${(props) => (props.small ? '11px' : '16px')};
+
+ ${(props) => props.small && `left: ${props.active ? '13px' : '2px'};`}
+ ${(props) => !props.small && `left: ${props.active ? '18px' : '2px'};`}
+`
+
+Circle.defaultProps = {
+ small: false,
+}
+
+const Label = styled.span`
+ color: ${({ theme }) => theme.text1};
+ font-size: 15px;
+ font-weight: 600;
+ line-height: 1.2;
+ margin-left: 6px;
+ text-align: left;
+`
+
+interface Props {
+ active: boolean
+ disabled?: boolean
+ label?: React.ReactNode
+ onClick?: () => void
+ small?: boolean
+}
+
+export const Switch: React.FC = (props) => {
+ const { active = false, disabled, label, onClick, small, ...restProps } = props
+
+ return (
+
+
+
+
+ {label && }
+
+ )
+}
diff --git a/src/components/icons/ChevronDown.tsx b/src/components/icons/ChevronDown.tsx
new file mode 100644
index 000000000..180d9e006
--- /dev/null
+++ b/src/components/icons/ChevronDown.tsx
@@ -0,0 +1,30 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const ChevronDown: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/ChevronRight.tsx b/src/components/icons/ChevronRight.tsx
new file mode 100644
index 000000000..799870669
--- /dev/null
+++ b/src/components/icons/ChevronRight.tsx
@@ -0,0 +1,28 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const ChevronRight: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/ClearSearch.tsx b/src/components/icons/ClearSearch.tsx
new file mode 100644
index 000000000..7f00903b5
--- /dev/null
+++ b/src/components/icons/ClearSearch.tsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const ClearSearch: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/CloseIcon.tsx b/src/components/icons/CloseIcon.tsx
new file mode 100755
index 000000000..bc0807eb6
--- /dev/null
+++ b/src/components/icons/CloseIcon.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const CloseIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/CopyIcon.tsx b/src/components/icons/CopyIcon.tsx
new file mode 100644
index 000000000..318ef4710
--- /dev/null
+++ b/src/components/icons/CopyIcon.tsx
@@ -0,0 +1,29 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const CopyIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/ErrorIcon.tsx b/src/components/icons/ErrorIcon.tsx
new file mode 100644
index 000000000..748deef9a
--- /dev/null
+++ b/src/components/icons/ErrorIcon.tsx
@@ -0,0 +1,39 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ flex-shrink: 0;
+
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const ErrorIcon: React.FC = (props) => {
+ const { ...restProps } = props
+
+ return (
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/icons/ErrorInfo.tsx b/src/components/icons/ErrorInfo.tsx
new file mode 100644
index 000000000..a6ac7a820
--- /dev/null
+++ b/src/components/icons/ErrorInfo.tsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ flex-shrink: 0;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.error};
+ }
+`
+
+export const ErrorInfo: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/ErrorLock.tsx b/src/components/icons/ErrorLock.tsx
new file mode 100644
index 000000000..5965b1210
--- /dev/null
+++ b/src/components/icons/ErrorLock.tsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ flex-shrink: 0;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.error};
+ }
+`
+
+export const ErrorLock: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/ExternalLinkIcon.tsx b/src/components/icons/ExternalLinkIcon.tsx
new file mode 100644
index 000000000..d39694a07
--- /dev/null
+++ b/src/components/icons/ExternalLinkIcon.tsx
@@ -0,0 +1,34 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const ExternalLinkIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/IconDelete.tsx b/src/components/icons/IconDelete.tsx
new file mode 100644
index 000000000..152366774
--- /dev/null
+++ b/src/components/icons/IconDelete.tsx
@@ -0,0 +1,32 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const IconDelete: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/IconOk.tsx b/src/components/icons/IconOk.tsx
new file mode 100644
index 000000000..588a12ca8
--- /dev/null
+++ b/src/components/icons/IconOk.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const IconOk: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+
+)
diff --git a/src/components/icons/InfoIcon.tsx b/src/components/icons/InfoIcon.tsx
new file mode 100644
index 000000000..a96d01bc6
--- /dev/null
+++ b/src/components/icons/InfoIcon.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const InfoIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+
+)
diff --git a/src/components/icons/LockBig.tsx b/src/components/icons/LockBig.tsx
new file mode 100644
index 000000000..0c5378db3
--- /dev/null
+++ b/src/components/icons/LockBig.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const LockBig: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/Magnifier.tsx b/src/components/icons/Magnifier.tsx
new file mode 100644
index 000000000..b7deab9cb
--- /dev/null
+++ b/src/components/icons/Magnifier.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const Magnifier: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/MenuIcon.tsx b/src/components/icons/MenuIcon.tsx
new file mode 100644
index 000000000..11c30de41
--- /dev/null
+++ b/src/components/icons/MenuIcon.tsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const MenuIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/NetworkIcon.tsx b/src/components/icons/NetworkIcon.tsx
new file mode 100644
index 000000000..4ac2c8748
--- /dev/null
+++ b/src/components/icons/NetworkIcon.tsx
@@ -0,0 +1,30 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const NetworkIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/OrderPending.tsx b/src/components/icons/OrderPending.tsx
new file mode 100644
index 000000000..04b7df43e
--- /dev/null
+++ b/src/components/icons/OrderPending.tsx
@@ -0,0 +1,35 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const OrderPending: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/OrderPlaced.tsx b/src/components/icons/OrderPlaced.tsx
new file mode 100644
index 000000000..fcbcd79f0
--- /dev/null
+++ b/src/components/icons/OrderPlaced.tsx
@@ -0,0 +1,30 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: #008c73;
+ }
+`
+
+export const OrderPlaced: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/Send.tsx b/src/components/icons/Send.tsx
new file mode 100755
index 000000000..689a88089
--- /dev/null
+++ b/src/components/icons/Send.tsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: ${({ theme }) => theme.buttonPrimary.color};
+ }
+`
+
+export const Send: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/SendIcon.tsx b/src/components/icons/SendIcon.tsx
new file mode 100644
index 000000000..96e160730
--- /dev/null
+++ b/src/components/icons/SendIcon.tsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #fff;
+ }
+`
+
+export const SendIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/SettingsIcon.tsx b/src/components/icons/SettingsIcon.tsx
new file mode 100644
index 000000000..03e7f8289
--- /dev/null
+++ b/src/components/icons/SettingsIcon.tsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const SettingsIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+)
diff --git a/src/components/icons/Spinner.tsx b/src/components/icons/Spinner.tsx
new file mode 100644
index 000000000..d156760d1
--- /dev/null
+++ b/src/components/icons/Spinner.tsx
@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+`
+
+export const Spinner: React.FC = (props) => {
+ return (
+
+
+
+
+ )
+}
diff --git a/src/components/icons/TooltipIcon.tsx b/src/components/icons/TooltipIcon.tsx
new file mode 100644
index 000000000..f850b6bed
--- /dev/null
+++ b/src/components/icons/TooltipIcon.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ display: block;
+ max-height: 100%;
+ max-width: 100%;
+
+ .fill {
+ fill: ${({ theme }) => theme.text1};
+ }
+`
+
+export const TooltipIcon: React.FC<{ className?: string }> = (props) => (
+
+
+
+
+)
diff --git a/src/components/icons/WarningIcon.tsx b/src/components/icons/WarningIcon.tsx
new file mode 100644
index 000000000..17e57b2fc
--- /dev/null
+++ b/src/components/icons/WarningIcon.tsx
@@ -0,0 +1,34 @@
+import React from 'react'
+import styled from 'styled-components'
+
+const Wrapper = styled.svg`
+ .fill {
+ fill: #ccc;
+ }
+`
+
+export const WarningIcon: React.FC = (props) => {
+ const { ...restProps } = props
+
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/layout/Footer/index.tsx b/src/components/layout/Footer/index.tsx
new file mode 100644
index 000000000..aff71a36d
--- /dev/null
+++ b/src/components/layout/Footer/index.tsx
@@ -0,0 +1,128 @@
+import React from 'react'
+import { NavLink } from 'react-router-dom'
+import styled, { css } from 'styled-components'
+
+import { SettingsIcon } from '../../icons/SettingsIcon'
+import { InnerContainer } from '../../pureStyledComponents/InnerContainer'
+
+const Wrapper = styled.footer`
+ border-top: solid 1px #002249;
+ display: flex;
+ height: auto;
+ justify-content: center;
+ margin-top: auto;
+ overflow: visible;
+ padding: 25px 0;
+ width: 100%;
+`
+
+const Inner = styled(InnerContainer)`
+ align-items: center;
+ flex-flow: row;
+ flex-grow: 1;
+ flex-shrink: 0;
+ justify-content: center;
+ list-style: none;
+ margin: 0;
+ padding-bottom: 0;
+ padding-left: ${(props) => props.theme.layout.horizontalPadding};
+ padding-right: ${(props) => props.theme.layout.horizontalPadding};
+ padding-top: 0;
+
+ @media (min-width: ${(props) => props.theme.themeBreakPoints.mdPre}) {
+ flex-direction: row;
+ justify-content: center;
+ }
+`
+
+const Item = styled.li`
+ color: ${({ theme }) => theme.text1};
+ margin-right: 30px;
+ opacity: 0.8;
+
+ &:hover {
+ opacity: 1;
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+`
+
+const LinkCSS = css`
+ color: ${({ theme }) => theme.text1};
+ text-decoration: none;
+
+ &:hover {
+ color: ${({ theme }) => theme.primary2};
+ }
+`
+
+const ExternalLink = styled.a`
+ ${LinkCSS}
+`
+
+const Link = styled(NavLink)`
+ ${LinkCSS}
+`
+
+const IconWrapper = styled.span`
+ cursor: pointer;
+ display: inline-block;
+ height: 14px;
+ margin-left: 6px;
+ position: relative;
+ width: 14px;
+`
+
+const SettingsIconStyled = styled(SettingsIcon)`
+ fill: ${({ theme }) => theme.text1};
+ height: 11px;
+ width: 11px;
+
+ &:hover {
+ .fill {
+ fill: ${({ theme }) => theme.primary2};
+ }
+ }
+`
+
+interface Props {
+ onCookiesBannerShow: () => void
+}
+
+export const Footer: React.FC = (props) => {
+ const { onCookiesBannerShow, ...restProps } = props
+ const date = new Date()
+ const year = date.getFullYear()
+
+ return (
+
+
+ -
+
+ {`©${year} Gnosis`}
+
+
+ -
+ Terms
+
+ -
+ Privacy
+
+ -
+ Cookies
+
+
+
+
+ -
+ Licenses
+
+ -
+ Imprint
+
+
+
+ )
+}
diff --git a/src/components/layout/Header/index.tsx b/src/components/layout/Header/index.tsx
new file mode 100644
index 000000000..2eeda736a
--- /dev/null
+++ b/src/components/layout/Header/index.tsx
@@ -0,0 +1,166 @@
+import React, { useState } from 'react'
+import styled from 'styled-components'
+
+import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { HashLink } from 'react-router-hash-link'
+
+import useENSName from '../../../hooks/useENSName'
+import { useWalletModalToggle } from '../../../state/application/hooks'
+import { useAllTransactions } from '../../../state/transactions/hooks'
+import { TransactionDetails } from '../../../state/transactions/reducer'
+import { useNetworkCheck } from '../../Web3Status'
+import { ButtonConnect } from '../../buttons/ButtonConnect'
+import { ButtonMenu } from '../../buttons/ButtonMenu'
+import { Logo } from '../../common/Logo'
+import { UserDropdown } from '../../common/UserDropdown'
+import WalletModal from '../../modals/WalletModal'
+import { Mainmenu } from '../../navigation/Mainmenu'
+import { Mobilemenu } from '../../navigation/Mobilemenu'
+import { InnerContainer } from '../../pureStyledComponents/InnerContainer'
+
+const Wrapper = styled.header`
+ background-color: ${({ theme }) => theme.mainBackground};
+ display: flex;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 100;
+`
+
+const Inner = styled(InnerContainer)`
+ align-items: center;
+ flex-flow: row;
+ flex-grow: 1;
+ flex-shrink: 0;
+ height: ${(props) => props.theme.header.height};
+ justify-content: space-between;
+ padding-left: ${(props) => props.theme.layout.horizontalPadding};
+ padding-right: ${(props) => props.theme.layout.horizontalPadding};
+`
+
+const LogoLink = styled(HashLink)``
+
+const ButtonMenuStyled = styled(ButtonMenu)`
+ display: block;
+ position: relative;
+ z-index: 5;
+
+ @media (min-width: ${(props) => props.theme.themeBreakPoints.md}) {
+ display: none;
+ }
+`
+
+const ButtonConnectStyled = styled(ButtonConnect)`
+ margin-left: auto;
+ position: relative;
+ z-index: 5;
+
+ @media (min-width: ${(props) => props.theme.themeBreakPoints.md}) {
+ margin-left: 0;
+ }
+`
+
+const UserDropdownStyled = styled(UserDropdown)`
+ margin-left: auto;
+ position: relative;
+ z-index: 5;
+
+ @media (min-width: ${(props) => props.theme.themeBreakPoints.md}) {
+ margin-left: 0;
+ }
+`
+
+const Menu = styled(Mainmenu)`
+ display: none;
+
+ @media (min-width: ${(props) => props.theme.themeBreakPoints.md}) {
+ display: flex;
+ margin-left: auto;
+ }
+`
+
+const MobilemenuStyled = styled(Mobilemenu)`
+ display: block;
+ height: calc(100vh - ${(props) => props.theme.header.height});
+ left: 0;
+ position: fixed;
+ top: ${(props) => props.theme.header.height};
+ width: 100%;
+ z-index: 12345;
+
+ @media (min-width: ${(props) => props.theme.themeBreakPoints.md}) {
+ display: none;
+ }
+`
+
+const Error = styled.span`
+ align-items: center;
+ color: ${({ theme }) => theme.primary1};
+ display: flex;
+ font-size: 16px;
+ font-weight: 600;
+ height: 100%;
+ line-height: 1.2;
+ margin-left: auto;
+`
+
+export const Header: React.FC = (props) => {
+ const { account, error } = useWeb3React()
+ const { errorWrongNetwork } = useNetworkCheck()
+ const isConnected = !!account
+ const [mobileMenuVisible, setMobileMenuVisible] = useState(false)
+ const wrongNetwork = error instanceof UnsupportedChainIdError || errorWrongNetwork !== undefined
+
+ const toggleWalletModal = useWalletModalToggle()
+ const allTransactions = useAllTransactions()
+ const ENSName = useENSName(account)
+
+ const recentTransactionsOnly = (a: TransactionDetails) => {
+ return new Date().getTime() - a.addedTime < 86_400_000
+ }
+
+ const newTransactionFirst = (a: TransactionDetails, b: TransactionDetails) => {
+ return b.addedTime - a.addedTime
+ }
+
+ const sortedRecentTransactions = React.useMemo(() => {
+ const txs = Object.values(allTransactions)
+ return txs.filter(recentTransactionsOnly).sort(newTransactionFirst)
+ }, [allTransactions])
+
+ const confirmed = sortedRecentTransactions.filter((tx) => tx.receipt).map((tx) => tx.hash)
+ const pending = sortedRecentTransactions.filter((tx) => !tx.receipt).map((tx) => tx.hash)
+
+ const mobileMenuToggle = () => {
+ setMobileMenuVisible(!mobileMenuVisible)
+ }
+
+ const web3Status = isConnected ? (
+
+ ) : wrongNetwork ? (
+ Invalid network
+ ) : (
+
+ )
+
+ return (
+ <>
+
+
+
+ {mobileMenuVisible && setMobileMenuVisible(false)} />}
+
+
+
+
+ {web3Status}
+
+
+
+ >
+ )
+}
diff --git a/src/components/MesaModal/index.tsx b/src/components/modals/MesaModal/index.tsx
similarity index 95%
rename from src/components/MesaModal/index.tsx
rename to src/components/modals/MesaModal/index.tsx
index a1f69fa54..b9e416a17 100644
--- a/src/components/MesaModal/index.tsx
+++ b/src/components/modals/MesaModal/index.tsx
@@ -1,15 +1,16 @@
// file copied from gnosis/dex-react
-import React from "react";
-import { createGlobalStyle } from "styled-components";
+import React from 'react'
+import { createGlobalStyle } from 'styled-components'
+
import Modali, {
ModalHook,
ModalOptions,
ModalProps,
toggleModaliComponent,
useModali,
-} from "modali";
+} from 'modali'
-const MODALI_OVERLAY_COLOUR = "#2f3e4e80";
+const MODALI_OVERLAY_COLOUR = '#2f3e4e80'
const ModaliGlobalStyle = createGlobalStyle`
/* Hack to fix Modali screen flash */
@@ -155,17 +156,17 @@ const ModaliGlobalStyle = createGlobalStyle`
body.modali-open .modali-body-style {
padding: 0;
}
-`;
+`
const Modal: React.FC = (props) => (
<>
>
-);
+)
// To import default Modali and not change much code elsewhere
-const StyledModali = { ...Modali, Modal };
+const StyledModali = { ...Modali, Modal }
export {
StyledModali as default,
@@ -174,4 +175,4 @@ export {
ModalProps,
toggleModaliComponent as toggleModal,
useModali as useModal,
-};
+}
diff --git a/src/components/Modal/index.tsx b/src/components/modals/Modal/index.tsx
similarity index 76%
rename from src/components/Modal/index.tsx
rename to src/components/modals/Modal/index.tsx
index 8686c31c1..e87b0d2e3 100644
--- a/src/components/Modal/index.tsx
+++ b/src/components/modals/Modal/index.tsx
@@ -1,19 +1,19 @@
-import React from "react";
-import styled, { css } from "styled-components";
-import { animated, useTransition, useSpring } from "react-spring";
-import { Spring } from "react-spring/renderprops";
+import { transparentize } from 'polished'
+import React from 'react'
+import styled, { css } from 'styled-components'
-import { DialogOverlay, DialogContent } from "@reach/dialog";
-import { isMobile } from "react-device-detect";
-import "@reach/dialog/styles.css";
-import { transparentize } from "polished";
-import { useGesture } from "react-use-gesture";
+import { DialogContent, DialogOverlay } from '@reach/dialog'
+import { isMobile } from 'react-device-detect'
+import { animated, useSpring, useTransition } from 'react-spring'
+import { Spring } from 'react-spring/renderprops'
+import '@reach/dialog/styles.css'
+import { useGesture } from 'react-use-gesture'
-const AnimatedDialogOverlay = animated(DialogOverlay);
+const AnimatedDialogOverlay = animated(DialogOverlay)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const StyledDialogOverlay = styled(({ mobile, ...rest }) => (
-
-))<{ mobile: boolean }>`
+const StyledDialogOverlay = styled(({ mobile, ...rest }) => )<{
+ mobile: boolean
+}>`
&[data-reach-dialog-overlay] {
z-index: 2;
display: flex;
@@ -28,7 +28,7 @@ const StyledDialogOverlay = styled(({ mobile, ...rest }) => (
`}
&::after {
- content: "";
+ content: '';
background-color: ${({ theme }) => theme.modalBG};
opacity: 0.5;
top: 0;
@@ -39,12 +39,12 @@ const StyledDialogOverlay = styled(({ mobile, ...rest }) => (
z-index: -1;
}
}
-`;
+`
// destructure to not pass custom props to Dialog DOM element
const StyledDialogContent = styled(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- ({ minHeight, maxHeight, mobile, isOpen, ...rest }) => (
+ ({ isOpen, maxHeight, minHeight, mobile, ...rest }) => (
),
)`
@@ -52,8 +52,7 @@ const StyledDialogContent = styled(
margin: 0 0 2rem 0;
border: 1px solid ${({ 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;
width: 50vw;
@@ -75,7 +74,7 @@ const StyledDialogContent = styled(
max-height: 65vh;
margin: 0;
`}
- ${({ theme, mobile }) => theme.mediaWidth.upToSmall`
+ ${({ mobile, theme }) => theme.mediaWidth.upToSmall`
width: 85vw;
max-height: 66vh;
${
@@ -89,7 +88,7 @@ const StyledDialogContent = styled(
}
`}
}
-`;
+`
const HiddenCloseButton = styled.button`
margin: 0;
@@ -97,13 +96,13 @@ const HiddenCloseButton = styled.button`
width: 0;
height: 0;
border: none;
-`;
+`
export const DEFAULT_MODAL_OPTIONS = {
centered: true,
animated: true,
closeButton: true,
-};
+}
export const ModalBodyWrapper = styled.div`
padding: 0.5rem 1.5rem;
@@ -113,50 +112,50 @@ export const ModalBodyWrapper = styled.div`
color: inherit;
padding: 0;
}
-`;
+`
interface ModalProps {
- isOpen: boolean;
- onDismiss: () => void;
- minHeight?: number | false;
- maxHeight?: number;
- initialFocusRef?: React.RefObject;
- children?: React.ReactNode;
+ isOpen: boolean
+ onDismiss: () => void
+ minHeight?: number | false
+ maxHeight?: number
+ initialFocusRef?: React.RefObject
+ children?: React.ReactNode
}
export default function Modal({
+ children,
+ initialFocusRef = null,
isOpen,
- onDismiss,
- minHeight = false,
maxHeight = 50,
- initialFocusRef = null,
- children,
+ minHeight = false,
+ onDismiss,
}: ModalProps) {
const transitions = useTransition(isOpen, null, {
config: { duration: 200 },
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 },
- });
+ })
- const [{ xy }, set] = useSpring(() => ({ xy: [0, 0] }));
+ const [{ xy }, set] = useSpring(() => ({ xy: [0, 0] }))
const bind = useGesture({
onDrag: (state) => {
- let velocity = state.velocity;
+ let velocity = state.velocity
if (velocity < 1) {
- velocity = 1;
+ velocity = 1
}
if (velocity > 8) {
- velocity = 8;
+ velocity = 8
}
set({
xy: state.down ? state.movement : [0, 0],
config: { mass: 1, tension: 210, friction: 20 },
- });
+ })
if (velocity > 3 && state.direction[1] > 0) {
- onDismiss();
+ onDismiss()
}
},
- });
+ })
if (isMobile) {
return (
@@ -165,20 +164,18 @@ export default function Modal({
({ item, key, props }) =>
item && (
{(props) => (
@@ -191,12 +188,11 @@ export default function Modal({
}}
>
{children}
@@ -208,7 +204,7 @@ export default function Modal({
),
)}
>
- );
+ )
} else {
return (
<>
@@ -216,17 +212,17 @@ export default function Modal({
({ item, key, props }) =>
item && (
{children}
@@ -235,6 +231,6 @@ export default function Modal({
),
)}
>
- );
+ )
}
}
diff --git a/src/components/modals/TransactionsModal/index.tsx b/src/components/modals/TransactionsModal/index.tsx
new file mode 100644
index 000000000..c6515418f
--- /dev/null
+++ b/src/components/modals/TransactionsModal/index.tsx
@@ -0,0 +1,137 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { ReactComponent as Close } from '../../../assets/images/x.svg'
+import { useAllTransactions } from '../../../state/transactions/hooks'
+import { TransactionDetails } from '../../../state/transactions/reducer'
+import Transaction from '../../AccountDetails/Transaction'
+import Modal from '../Modal'
+
+const CloseIcon = styled.div`
+ position: absolute;
+ right: 1rem;
+ top: 14px;
+ &:hover {
+ cursor: pointer;
+ opacity: 0.6;
+ }
+`
+
+const CloseColor = styled(Close)`
+ path {
+ stroke: ${({ theme }) => theme.text4};
+ }
+`
+
+const Wrapper = styled.div`
+ ${({ theme }) => theme.flexColumnNoWrap}
+ margin: 0;
+ padding: 0;
+ width: 100%;
+`
+
+const HeaderRow = styled.div`
+ ${({ theme }) => theme.flexRowNoWrap};
+ padding: 1rem 1rem;
+ font-weight: 500;
+ color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
+ ${({ theme }) => theme.mediaWidth.upToMedium`
+ padding: 1rem;
+ `};
+`
+
+const ContentWrapper = styled.div`
+ background-color: ${({ theme }) => theme.bg2};
+ padding: 2rem;
+ border-bottom-left-radius: 20px;
+ border-bottom-right-radius: 20px;
+
+ ${({ theme }) => theme.mediaWidth.upToMedium`padding: 1rem`};
+`
+
+const UpperSection = styled.div`
+ position: relative;
+
+ h5 {
+ margin: 0;
+ margin-bottom: 0.5rem;
+ font-size: 1rem;
+ font-weight: 400;
+ }
+
+ h5:last-child {
+ margin-bottom: 0px;
+ }
+
+ h4 {
+ margin-top: 0;
+ font-weight: 500;
+ }
+`
+
+const HoverText = styled.div`
+ :hover {
+ cursor: pointer;
+ }
+`
+
+const TransactionListWrapper = styled.div`
+ ${({ theme }) => theme.flexColumnNoWrap};
+`
+
+interface Props {
+ isOpen: boolean
+ maxHeight?: number
+ minHeight?: Maybe
+ onDismiss: () => void
+}
+
+export const TransactionsModal: React.FC = (props) => {
+ const { isOpen, maxHeight, minHeight, onDismiss } = props
+ const allTransactions = useAllTransactions()
+
+ const newTranscationsFirst = (a: TransactionDetails, b: TransactionDetails) => {
+ return b.addedTime - a.addedTime
+ }
+
+ const recentTransactionsOnly = (a: TransactionDetails) => {
+ return new Date().getTime() - a.addedTime < 86_400_000
+ }
+
+ const sortedRecentTransactions = React.useMemo(() => {
+ const txs = Object.values(allTransactions)
+ return txs.filter(recentTransactionsOnly).sort(newTranscationsFirst)
+ }, [allTransactions])
+
+ const pending = sortedRecentTransactions.filter((tx) => !tx.receipt).map((tx) => tx.hash)
+ const confirmed = sortedRecentTransactions.filter((tx) => tx.receipt).map((tx) => tx.hash)
+
+ const renderTransactions = (transactions) => {
+ return (
+
+ {transactions.map((hash, i) => {
+ return
+ })}
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+ Transactions
+
+
+ {renderTransactions(pending)}
+ {renderTransactions(confirmed)}
+
+
+
+
+ )
+}
diff --git a/src/components/WalletModal/Option.tsx b/src/components/modals/WalletModal/Option.tsx
similarity index 64%
rename from src/components/WalletModal/Option.tsx
rename to src/components/modals/WalletModal/Option.tsx
index 28effc08a..746198929 100644
--- a/src/components/WalletModal/Option.tsx
+++ b/src/components/modals/WalletModal/Option.tsx
@@ -1,9 +1,10 @@
-import React from "react";
-import styled from "styled-components";
-import { ExternalLink } from "../../theme";
+import React from 'react'
+import styled from 'styled-components'
+
+import { ExternalLink } from '../../../theme'
const InfoCard = styled.button<{ active?: boolean }>`
- background-color: ${({ theme, active }) => (active ? theme.bg3 : theme.bg2)};
+ background-color: ${({ active, theme }) => (active ? theme.bg3 : theme.bg2)};
padding: 1rem;
outline: none;
border: 1px solid;
@@ -12,8 +13,8 @@ const InfoCard = styled.button<{ active?: boolean }>`
&:focus {
box-shadow: 0 0 0 1px ${({ theme }) => theme.primary1};
}
- border-color: ${({ theme, active }) => (active ? "transparent" : theme.bg3)};
-`;
+ border-color: ${({ active, theme }) => (active ? 'transparent' : theme.bg3)};
+`
const OptionCard = styled(InfoCard)`
display: flex;
@@ -22,23 +23,22 @@ const OptionCard = styled(InfoCard)`
justify-content: space-between;
margin-top: 2rem;
padding: 1rem;
-`;
+`
const OptionCardLeft = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
justify-content: center;
height: 100%;
-`;
+`
const OptionCardClickable = styled(OptionCard)<{ clickable?: boolean }>`
margin-top: 0;
&:hover {
- cursor: ${({ clickable }) => (clickable ? "pointer" : "")};
- border: ${({ clickable, theme }) =>
- clickable ? `1px solid ${theme.primary1}` : ``};
+ cursor: ${({ clickable }) => (clickable ? 'pointer' : '')};
+ border: ${({ clickable, theme }) => (clickable ? `1px solid ${theme.primary1}` : ``)};
}
- opacity: ${({ disabled }) => (disabled ? "0.5" : "1")};
-`;
+ opacity: ${({ disabled }) => (disabled ? '0.5' : '1')};
+`
const GreenCircle = styled.div`
${({ theme }) => theme.flexRowNoWrap}
@@ -52,30 +52,28 @@ const GreenCircle = styled.div`
background-color: ${({ theme }) => theme.green1};
border-radius: 50%;
}
-`;
+`
const CircleWrapper = styled.div`
color: ${({ theme }) => theme.green1};
display: flex;
justify-content: center;
align-items: center;
-`;
+`
const HeaderText = styled.div`
${({ theme }) => theme.flexRowNoWrap};
color: ${(props) =>
- props.color === "blue"
- ? ({ theme }) => theme.primary1
- : ({ theme }) => theme.text1};
+ props.color === 'blue' ? ({ theme }) => theme.primary1 : ({ theme }) => theme.text1};
font-size: 1rem;
font-weight: 500;
-`;
+`
const SubHeader = styled.div`
color: ${({ theme }) => theme.text1};
margin-top: 10px;
font-size: 12px;
-`;
+`
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
@@ -83,47 +81,42 @@ const IconWrapper = styled.div<{ size?: number }>`
justify-content: center;
& > img,
span {
- height: ${({ size }) => (size ? size + "px" : "24px")};
- width: ${({ size }) => (size ? size + "px" : "24px")};
+ height: ${({ size }) => (size ? size + 'px' : '24px')};
+ width: ${({ size }) => (size ? size + 'px' : '24px')};
}
${({ theme }) => theme.mediaWidth.upToMedium`
align-items: flex-end;
`};
-`;
+`
export default function Option({
- link = null,
+ active = false,
clickable = true,
- size = null,
- onClick = null,
color,
header,
- subheader = null,
icon,
- active = false,
id,
+ link = null,
+ onClick = null,
+ size = null,
+ subheader = null,
}: {
- link?: string | null;
- clickable?: boolean;
- size?: number | null;
- onClick?: null | (() => void);
- color: string;
- header: React.ReactNode;
- subheader: React.ReactNode | null;
- icon: string;
- active?: boolean;
- id: string;
+ link?: Maybe
+ clickable?: boolean
+ size?: Maybe
+ onClick?: null | (() => void)
+ color: string
+ header: React.ReactNode
+ subheader: Maybe
+ icon: string
+ active?: boolean
+ id: string
}) {
const content = (
-
+
- {" "}
+ {' '}
{active ? (
@@ -131,20 +124,20 @@ export default function Option({
) : (
- ""
+ ''
)}
{header}
{subheader && {subheader}}
-
+
- );
+ )
if (link) {
- return {content};
+ return {content}
}
- return content;
+ return content
}
diff --git a/src/components/WalletModal/PendingView.tsx b/src/components/modals/WalletModal/PendingView.tsx
similarity index 63%
rename from src/components/WalletModal/PendingView.tsx
rename to src/components/modals/WalletModal/PendingView.tsx
index 48d3e2161..2ff881f9f 100644
--- a/src/components/WalletModal/PendingView.tsx
+++ b/src/components/modals/WalletModal/PendingView.tsx
@@ -1,13 +1,16 @@
-import { AbstractConnector } from "@web3-react/abstract-connector";
-import React from "react";
-import styled from "styled-components";
-import Option from "./Option";
-import { SUPPORTED_WALLETS } from "../../constants";
-import WalletConnectData from "./WalletConnectData";
-import { walletconnect, injected } from "../../connectors";
-import { Spinner } from "../../theme";
-import Circle from "../../assets/images/circle.svg";
-import { darken } from "polished";
+import { darken } from 'polished'
+import React from 'react'
+import styled from 'styled-components'
+
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { AbstractConnector } from '@web3-react/abstract-connector'
+
+import Circle from '../../../assets/images/circle.svg'
+import { injected, walletconnect } from '../../../connectors'
+import { SUPPORTED_WALLETS } from '../../../constants'
+import { Spinner } from '../../../theme'
+import Option from './Option'
+import WalletConnectData from './WalletConnectData'
const PendingSection = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
@@ -17,7 +20,7 @@ const PendingSection = styled.div`
& > * {
width: 100%;
}
-`;
+`
const SpinnerWrapper = styled(Spinner)`
font-size: 4rem;
@@ -27,7 +30,7 @@ const SpinnerWrapper = styled(Spinner)`
color: ${({ theme }) => theme.bg4};
}
}
-`;
+`
const LoadingMessage = styled.div<{ error?: boolean }>`
${({ theme }) => theme.flexRowNoWrap};
@@ -35,19 +38,19 @@ const LoadingMessage = styled.div<{ error?: boolean }>`
justify-content: flex-start;
border-radius: 12px;
margin-bottom: 20px;
- color: ${({ theme, error }) => (error ? theme.red1 : "inherit")};
- border: 1px solid ${({ theme, error }) => (error ? theme.red1 : theme.text4)};
+ color: ${({ error, theme }) => (error ? theme.red1 : 'inherit')};
+ border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.text4)};
& > * {
padding: 1rem;
}
-`;
+`
const ErrorGroup = styled.div`
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
justify-content: flex-start;
-`;
+`
const ErrorButton = styled.div`
border-radius: 8px;
@@ -63,36 +66,34 @@ const ErrorButton = styled.div`
cursor: pointer;
background-color: ${({ theme }) => darken(0.1, theme.text4)};
}
-`;
+`
const LoadingWrapper = styled.div`
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
justify-content: center;
-`;
+`
export default function PendingView({
- uri = "",
- size,
connector,
error = false,
setPendingError,
+ size,
tryActivation,
+ uri = '',
}: {
- uri?: string;
- size?: number;
- connector?: AbstractConnector;
- error?: boolean;
- setPendingError: (error: boolean) => void;
- tryActivation: (connector: AbstractConnector) => void;
+ uri?: string
+ size?: number
+ connector?: AbstractConnector
+ error?: boolean
+ setPendingError: (error: boolean) => void
+ tryActivation: (connector: AbstractConnector) => void
}) {
- const isMetamask = window.ethereum && window.ethereum.isMetaMask;
+ const isMetamask = window.ethereum && window.ethereum.isMetaMask
return (
- {!error && connector === walletconnect && (
-
- )}
+ {!error && connector === walletconnect && }
{!error && }
@@ -101,45 +102,45 @@ export default function PendingView({
Error connecting.
{
- setPendingError(false);
- tryActivation(connector);
+ setPendingError(false)
+ tryActivation(connector)
}}
>
Try Again
) : connector === walletconnect ? (
- "Scan QR code with a compatible wallet..."
+ 'Scan QR code with a compatible wallet...'
) : (
- "Initializing..."
+ 'Initializing...'
)}
{Object.keys(SUPPORTED_WALLETS).map((key) => {
- const option = SUPPORTED_WALLETS[key];
+ const option = SUPPORTED_WALLETS[key]
if (option.connector === connector) {
if (option.connector === injected) {
- if (isMetamask && option.name !== "MetaMask") {
- return null;
+ if (isMetamask && option.name !== 'MetaMask') {
+ return null
}
- if (!isMetamask && option.name === "MetaMask") {
- return null;
+ if (!isMetamask && option.name === 'MetaMask') {
+ return null
}
}
return (
- );
+ )
}
- return null;
+ return null
})}
- );
+ )
}
diff --git a/src/components/modals/WalletModal/WalletConnectData.tsx b/src/components/modals/WalletModal/WalletConnectData.tsx
new file mode 100644
index 000000000..d6efdc171
--- /dev/null
+++ b/src/components/modals/WalletModal/WalletConnectData.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import QRCode from 'qrcode.react'
+
+const QRCodeWrapper = styled.div`
+ ${({ theme }) => theme.flexColumnNoWrap};
+ align-items: center;
+ justify-content: center;
+ border-radius: 12px;
+ margin-bottom: 20px;
+`
+
+interface WalletConnectDataProps {
+ uri?: string
+ size: number
+}
+
+export default function WalletConnectData({ size, uri = '' }: WalletConnectDataProps) {
+ return {uri && }
+}
diff --git a/src/components/WalletModal/index.tsx b/src/components/modals/WalletModal/index.tsx
similarity index 64%
rename from src/components/WalletModal/index.tsx
rename to src/components/modals/WalletModal/index.tsx
index b9e6d5317..355347630 100644
--- a/src/components/WalletModal/index.tsx
+++ b/src/components/modals/WalletModal/index.tsx
@@ -1,26 +1,24 @@
-import React, { useState, useEffect } from "react";
-import ReactGA from "react-ga";
-import styled from "styled-components";
-import { isMobile } from "react-device-detect";
-import { UnsupportedChainIdError, useWeb3React } from "@web3-react/core";
-import { URI_AVAILABLE } from "@web3-react/walletconnect-connector";
-import usePrevious from "../../hooks/usePrevious";
-import {
- useWalletModalOpen,
- useWalletModalToggle,
-} from "../../state/application/hooks";
-
-import Modal from "../Modal";
-import AccountDetails from "../AccountDetails";
-import PendingView from "./PendingView";
-import Option from "./Option";
-import { SUPPORTED_WALLETS } from "../../constants";
-import { ExternalLink } from "../../theme";
-import MetamaskIcon from "../../assets/images/metamask.png";
-import { ReactComponent as Close } from "../../assets/images/x.svg";
-import { injected, walletconnect, fortmatic, portis } from "../../connectors";
-import { OVERLAY_READY } from "../../connectors/Fortmatic";
-import { useNetworkCheck } from "../Web3Status";
+import React, { useEffect, useState } from 'react'
+import styled from 'styled-components'
+
+import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { URI_AVAILABLE } from '@web3-react/walletconnect-connector'
+import { isMobile } from 'react-device-detect'
+import ReactGA from 'react-ga'
+
+import MetamaskIcon from '../../../assets/images/metamask.png'
+import { ReactComponent as Close } from '../../../assets/images/x.svg'
+import { fortmatic, injected, portis, walletconnect } from '../../../connectors'
+import { OVERLAY_READY } from '../../../connectors/Fortmatic'
+import { SUPPORTED_WALLETS } from '../../../constants'
+import usePrevious from '../../../hooks/usePrevious'
+import { useWalletModalOpen, useWalletModalToggle } from '../../../state/application/hooks'
+import { ExternalLink } from '../../../theme'
+import AccountDetails from '../../AccountDetails'
+import { useNetworkCheck } from '../../Web3Status'
+import Modal from '../Modal'
+import Option from './Option'
+import PendingView from './PendingView'
const CloseIcon = styled.div`
position: absolute;
@@ -30,31 +28,30 @@ const CloseIcon = styled.div`
cursor: pointer;
opacity: 0.6;
}
-`;
+`
const CloseColor = styled(Close)`
path {
stroke: ${({ theme }) => theme.text4};
}
-`;
+`
const Wrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
margin: 0;
padding: 0;
width: 100%;
-`;
+`
const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
padding: 1rem 1rem;
font-weight: 500;
- color: ${(props) =>
- props.color === "blue" ? ({ theme }) => theme.primary1 : "inherit"};
+ color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem;
`};
-`;
+`
const ContentWrapper = styled.div`
background-color: ${({ theme }) => theme.bg2};
@@ -63,7 +60,7 @@ const ContentWrapper = styled.div`
border-bottom-right-radius: 20px;
${({ theme }) => theme.mediaWidth.upToMedium`padding: 1rem`};
-`;
+`
const UpperSection = styled.div`
position: relative;
@@ -83,7 +80,7 @@ const UpperSection = styled.div`
margin-top: 0;
font-weight: 500;
}
-`;
+`
const Blurb = styled.div`
${({ theme }) => theme.flexRowNoWrap}
@@ -95,7 +92,7 @@ const Blurb = styled.div`
margin: 1rem;
font-size: 12px;
`};
-`;
+`
const OptionGrid = styled.div`
display: grid;
@@ -104,87 +101,84 @@ const OptionGrid = styled.div`
grid-template-columns: 1fr;
grid-gap: 10px;
`};
-`;
+`
const HoverText = styled.div`
:hover {
cursor: pointer;
}
-`;
+`
const WALLET_VIEWS = {
- OPTIONS: "options",
- OPTIONS_SECONDARY: "options_secondary",
- ACCOUNT: "account",
- PENDING: "pending",
-};
+ OPTIONS: 'options',
+ OPTIONS_SECONDARY: 'options_secondary',
+ ACCOUNT: 'account',
+ PENDING: 'pending',
+}
export default function WalletModal({
- pendingTransactions,
- confirmedTransactions,
ENSName,
+ confirmedTransactions,
+ pendingTransactions,
}: {
- pendingTransactions: string[]; // hashes of pending
- confirmedTransactions: string[]; // hashes of confirmed
- ENSName?: string;
+ pendingTransactions: string[] // hashes of pending
+ confirmedTransactions: string[] // hashes of confirmed
+ ENSName?: string
}) {
// important that these are destructed from the account-specific web3-react context
- const { active, account, connector, activate, error } = useWeb3React();
+ const { account, activate, active, connector, error } = useWeb3React()
- const [walletView, setWalletView] = useState(WALLET_VIEWS.ACCOUNT);
+ const [walletView, setWalletView] = useState(WALLET_VIEWS.ACCOUNT)
- const [pendingWallet, setPendingWallet] = useState();
+ const [pendingWallet, setPendingWallet] = useState()
- const [pendingError, setPendingError] = useState();
+ const [pendingError, setPendingError] = useState()
- const walletModalOpen = useWalletModalOpen();
- const toggleWalletModal = useWalletModalToggle();
+ const walletModalOpen = useWalletModalOpen()
+ const toggleWalletModal = useWalletModalToggle()
- const previousAccount = usePrevious(account);
+ const previousAccount = usePrevious(account)
- const { errorWrongNetwork } = useNetworkCheck();
+ const { errorWrongNetwork } = useNetworkCheck()
// close on connection, when logged out before
useEffect(() => {
if (account && !previousAccount && walletModalOpen) {
- toggleWalletModal();
+ toggleWalletModal()
}
- }, [account, previousAccount, toggleWalletModal, walletModalOpen]);
+ }, [account, previousAccount, toggleWalletModal, walletModalOpen])
// always reset to account view
useEffect(() => {
if (walletModalOpen) {
- setPendingError(false);
- setWalletView(WALLET_VIEWS.ACCOUNT);
+ setPendingError(false)
+ setWalletView(WALLET_VIEWS.ACCOUNT)
}
- }, [walletModalOpen]);
+ }, [walletModalOpen])
// set up uri listener for walletconnect
- const [uri, setUri] = useState();
+ const [uri, setUri] = useState()
useEffect(() => {
const activateWC = (uri) => {
- setUri(uri);
+ setUri(uri)
// setWalletView(WALLET_VIEWS.PENDING)
- };
- walletconnect.on(URI_AVAILABLE, activateWC);
+ }
+ walletconnect.on(URI_AVAILABLE, activateWC)
return () => {
- walletconnect.off(URI_AVAILABLE, activateWC);
- };
- }, []);
+ walletconnect.off(URI_AVAILABLE, activateWC)
+ }
+ }, [])
// close modal when a connection is successful
- const activePrevious = usePrevious(active);
- const connectorPrevious = usePrevious(connector);
+ const activePrevious = usePrevious(active)
+ const connectorPrevious = usePrevious(connector)
useEffect(() => {
if (
walletModalOpen &&
((active && !activePrevious) ||
- (connector &&
- connector !== connectorPrevious &&
- !error &&
- !errorWrongNetwork))
+ (connector && connector !== connectorPrevious && !error && !errorWrongNetwork))
) {
- setWalletView(WALLET_VIEWS.ACCOUNT);
+ setWalletView(WALLET_VIEWS.ACCOUNT)
}
}, [
setWalletView,
@@ -195,101 +189,99 @@ export default function WalletModal({
walletModalOpen,
activePrevious,
connectorPrevious,
- ]);
+ ])
const tryActivation = async (connector) => {
- let name = "";
+ let name = ''
Object.keys(SUPPORTED_WALLETS).map((key) => {
if (connector === SUPPORTED_WALLETS[key].connector) {
- return (name = SUPPORTED_WALLETS[key].name);
+ return (name = SUPPORTED_WALLETS[key].name)
}
- return true;
- });
+ return true
+ })
// log selected wallet
ReactGA.event({
- category: "Wallet",
- action: "Change Wallet",
+ category: 'Wallet',
+ action: 'Change Wallet',
label: name,
- });
- setPendingWallet(connector); // set wallet for pending view
- setWalletView(WALLET_VIEWS.PENDING);
+ })
+ setPendingWallet(connector) // set wallet for pending view
+ setWalletView(WALLET_VIEWS.PENDING)
activate(connector, undefined, true).catch((error) => {
if (error instanceof UnsupportedChainIdError) {
- activate(connector); // a little janky...can't use setError because the connector isn't set
+ activate(connector) // a little janky...can't use setError because the connector isn't set
} else {
- setPendingError(true);
+ setPendingError(true)
}
- });
- };
+ })
+ }
// close wallet modal if fortmatic modal is active
useEffect(() => {
fortmatic.on(OVERLAY_READY, () => {
- toggleWalletModal();
- });
- }, [toggleWalletModal]);
+ toggleWalletModal()
+ })
+ }, [toggleWalletModal])
// get wallets user can switch too, depending on device/browser
function getOptions() {
- const isMetamask = window.ethereum && window.ethereum.isMetaMask;
+ const isMetamask = window.ethereum && window.ethereum.isMetaMask
return Object.keys(SUPPORTED_WALLETS).map((key) => {
- const option = SUPPORTED_WALLETS[key];
+ const option = SUPPORTED_WALLETS[key]
// check for mobile options
if (isMobile) {
//disable portis on mobile for now
if (option.connector === portis) {
- return null;
+ return null
}
if (!window.web3 && !window.ethereum && option.mobile) {
return (
- );
+ )
} else {
- return null; //dont want to return install twice
+ return null //dont want to return install twice
}
}
// don't return metamask if injected provider isn't metamask
- else if (option.name === "MetaMask" && !isMetamask) {
- return null;
+ else if (option.name === 'MetaMask' && !isMetamask) {
+ return null
}
// likewise for generic
- else if (option.name === "Injected" && isMetamask) {
- return null;
+ else if (option.name === 'Injected' && isMetamask) {
+ return null
}
}
@@ -298,23 +290,23 @@ export default function WalletModal({
!isMobile &&
!option.mobileOnly && (