diff --git a/package-lock.json b/package-lock.json index a09e320c17..2dee25ad58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,30 +1,18 @@ { "name": "@balancer-labs/frontend-v2", - "version": "1.11.5", + "version": "1.12.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@balancer-labs/frontend-v2", - "version": "1.11.5", + "version": "1.12.0", "license": "MIT", "dependencies": { "@balancer-labs/assets": "github:balancer-labs/assets#master", "@balancer-labs/sor": "github:balancer-labs/balancer-sor#john/v1-consideringFees-package", - "@balancer-labs/sor2": "github:balancer-labs/balancer-sor#develop-V2", + "@balancer-labs/sor2": "github:balancer-labs/balancer-sor#john/ui-629-sor-integrate-sdk-maths", "@ensdomains/content-hash": "^2.5.3", - "@ethersproject/abi": "^5.0.1", - "@ethersproject/abstract-signer": "^5.3.0", - "@ethersproject/address": "^5.0.1", - "@ethersproject/bignumber": "^5.2.0", - "@ethersproject/bytes": "^5.0.5", - "@ethersproject/constants": "^5.0.1", - "@ethersproject/contracts": "^5.0.1", - "@ethersproject/hash": "^5.0.6", - "@ethersproject/providers": "^5.0.12", - "@ethersproject/strings": "^5.0.5", - "@ethersproject/units": "^5.0.1", - "@ethersproject/wallet": "^5.0.1", "@gnosis.pm/gp-v2-contracts": "^0.0.1-alpha.19", "@metamask/detect-provider": "^1.2.0", "@popperjs/core": "^2.9.2", @@ -32,7 +20,6 @@ "@sentry/browser": "^6.3.2", "@sentry/tracing": "^6.3.2", "@tailwindcss/postcss7-compat": "^2.0.4", - "@toruslabs/torus-embed": "^1.9.4", "@types/animejs": "^3.1.3", "@vue/cli-plugin-babel": "^4.4.0", "@vue/cli-plugin-eslint": "^4.4.0", @@ -41,39 +28,30 @@ "@vueuse/core": "^4.3.4", "@walletconnect/web3-provider": "^1.5.2", "animejs": "^3.2.1", - "apexcharts": "^3.23.1", "autoprefixer": "^9.8.6", "axios": "0.21.1", "bnc-sdk": "^3.4.1", - "bs58": "^4.0.1", - "core-js": "^3.6.5", - "d3": "^6.5.0", "date-fns": "^2.21.1", "echarts": "^5.1.0", - "eslint": "^6.7.2", "ethereumjs-util": "^7.0.7", "ethers": "^5.4.1", "feather-icons": "^4.28.0", - "fortmatic": "^2.0.6", "json-to-graphql-query": "^2.0.0", "lodash": "^4.17.15", "numeral": "^2.0.4", "parse-filepath": "1.0.2", "postcss": "^7.0.35", "postcss-nesting": "^7.0.1", - "pretty-ms": "^7.0.0", "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.4", - "typescript": "~3.9.3", "vue": "^3.1.5", "vue-echarts": "^6.0.0-rc.4", "vue-i18n": "^9.0.0-rc.1", "vue-query": "^1.6.0", - "vue-router": "^4.0.0-alpha.6", + "vue-router": "^4.0.10", "vue-slider-component": "^4.0.0-beta.3", - "vue3-apexcharts": "^1.2.1", "vue3-jazzicon": "^0.1.2", "vue3-virtual-scroller": "^0.2.1", - "vuex": "^4.0.0-alpha.1", + "vuex": "^4.0.2", "walletlink": "^2.1.5", "web3-utils": "^1.3.1" }, @@ -100,11 +78,13 @@ "@vue/eslint-config-typescript": "^5.0.2", "@vue/test-utils": "^2.0.0-alpha.1", "babel-loader": "^8.2.2", + "eslint": "^6.7.2", "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-vue": "^7.0.0-alpha.0", "husky": "^6.0.0", "prettier": "^1.19.1", "tailwind-config-viewer": "^1.5.0", + "typescript": "~3.9.3", "vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0", "vue-loader": "^16.1.2" }, @@ -1483,7 +1463,7 @@ }, "node_modules/@balancer-labs/sor2": { "version": "2.0.1", - "resolved": "git+ssh://git@github.com/balancer-labs/balancer-sor.git#740030e9a79de2c412c7336ae0253de1ced67f71", + "resolved": "git+ssh://git@github.com/balancer-labs/balancer-sor.git#f41d4548556007dc2fe9471bcd052715c92b68bb", "license": "GPL-3.0-only", "dependencies": { "@ethersproject/address": "^5.0.5", @@ -1491,9 +1471,8 @@ "@ethersproject/contracts": "^5.0.5", "@ethersproject/providers": "5.0.12", "@ethersproject/solidity": "^5.0.5", - "@ethersproject/units": "^5.0.6", - "bignumber.js": "^9.0.0", - "decimal.js": "^10.2.1", + "@georgeroman/balancer-v2-pools": "^0.0.5", + "bignumber.js": "^9.0.1", "isomorphic-fetch": "^2.2.1" } }, @@ -1523,16 +1502,17 @@ "ws": "7.2.3" } }, + "node_modules/@balancer-labs/v2-deployments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@balancer-labs/v2-deployments/-/v2-deployments-1.1.1.tgz", + "integrity": "sha512-emB/xHCM/njxpReZQx2BqzHXwrWF+iIwn/hSwnSOxp+P6GH0rOaboio6/+Aapj7f9tCCjqRspetX//9TumX82Q==" + }, "node_modules/@base2/pretty-print-object": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz", "integrity": "sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==", "dev": true }, - "node_modules/@chaitanyapotti/random-id": { - "version": "1.0.3", - "license": "MIT" - }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", "dev": true, @@ -2387,6 +2367,18 @@ "purgecss": "^3.1.3" } }, + "node_modules/@georgeroman/balancer-v2-pools": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@georgeroman/balancer-v2-pools/-/balancer-v2-pools-0.0.5.tgz", + "integrity": "sha512-fx+QmDJ80+E4RoaBfmnKFaOlRLM/FSbzksaTrqXM+0EeWX5Zu99SO03pjTIBDsBifq2WfHBcCU5CW5ulGF/kkg==", + "dependencies": { + "@balancer-labs/v2-deployments": "^1.0.0", + "bignumber.js": "^9.0.1", + "ethers": "^5.2.0", + "graphql": "^15.5.0", + "graphql-request": "^3.4.0" + } + }, "node_modules/@gnosis.pm/gp-v2-contracts": { "version": "0.0.1-alpha.19", "resolved": "https://registry.npmjs.org/@gnosis.pm/gp-v2-contracts/-/gp-v2-contracts-0.0.1-alpha.19.tgz", @@ -7511,88 +7503,6 @@ "node": ">=8" } }, - "node_modules/@toruslabs/eccrypto": { - "version": "1.1.6", - "hasInstallScript": true, - "license": "CC0-1.0", - "dependencies": { - "acorn": "^7.4.0", - "elliptic": "^6.5.3", - "es6-promise": "^4.2.8", - "nan": "^2.14.1" - }, - "optionalDependencies": { - "secp256k1": "^3.8.0" - } - }, - "node_modules/@toruslabs/fetch-node-details": { - "version": "2.4.0", - "license": "MIT", - "dependencies": { - "web3-eth-contract": "^1.3.4", - "web3-utils": "^1.3.4" - }, - "peerDependencies": { - "@babel/runtime": "7.x" - } - }, - "node_modules/@toruslabs/http-helpers": { - "version": "1.3.7", - "license": "MIT", - "dependencies": { - "deepmerge": "^4.2.2" - }, - "peerDependencies": { - "@babel/runtime": "7.x" - } - }, - "node_modules/@toruslabs/torus-embed": { - "version": "1.9.14", - "license": "MIT", - "dependencies": { - "@chaitanyapotti/random-id": "^1.0.3", - "@toruslabs/fetch-node-details": "^2.4.0", - "@toruslabs/http-helpers": "^1.3.7", - "@toruslabs/torus.js": "^2.3.0", - "create-hash": "^1.2.0", - "deepmerge": "^4.2.2", - "eth-rpc-errors": "^4.0.3", - "fast-deep-equal": "^3.1.3", - "is-stream": "^2.0.0", - "json-rpc-engine": "^6.1.0", - "json-rpc-middleware-stream": "^3.0.0", - "loglevel": "^1.7.1", - "obj-multiplex": "^1.0.0", - "obs-store": "^4.0.3", - "post-message-stream": "^3.0.0", - "pump": "^3.0.0", - "safe-event-emitter": "^1.0.1" - }, - "peerDependencies": { - "@babel/runtime": "7.x" - } - }, - "node_modules/@toruslabs/torus.js": { - "version": "2.3.0", - "license": "MIT", - "dependencies": { - "@toruslabs/eccrypto": "^1.1.5", - "@toruslabs/http-helpers": "^1.3.5", - "bn.js": "^5.1.3", - "elliptic": "^6.5.4", - "json-stable-stringify": "^1.0.1", - "loglevel": "^1.7.1", - "memory-cache": "^0.2.0", - "web3-utils": "^1.3.3" - }, - "peerDependencies": { - "@babel/runtime": "7.x" - } - }, - "node_modules/@toruslabs/torus.js/node_modules/bn.js": { - "version": "5.2.0", - "license": "MIT" - }, "node_modules/@types/animejs": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/animejs/-/animejs-3.1.3.tgz", @@ -8969,8 +8879,9 @@ "license": "ISC" }, "node_modules/@vue/devtools-api": { - "version": "6.0.0-beta.7", - "license": "MIT" + "version": "6.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.15.tgz", + "integrity": "sha512-quBx4Jjpexo6KDiNUGFr/zF/2A4srKM9S9v2uHgMXSU//hjgq1eGzqkIFql8T9gfX5ZaVOUzYBP3jIdIR3PKIA==" }, "node_modules/@vue/eslint-config-prettier": { "version": "6.0.0", @@ -9758,21 +9669,6 @@ "node": ">= 8" } }, - "node_modules/apexcharts": { - "version": "3.26.0", - "license": "MIT", - "dependencies": { - "svg.draggable.js": "^2.2.2", - "svg.easing.js": "^2.0.0", - "svg.filter.js": "^2.0.2", - "svg.pathmorphing.js": "^0.1.3", - "svg.resize.js": "^1.4.3", - "svg.select.js": "^3.0.1" - }, - "funding": { - "url": "https://github.com/apexcharts/apexcharts.js?sponsor=1" - } - }, "node_modules/app-root-dir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", @@ -10106,17 +10002,6 @@ "url": "https://tidelift.com/funding/github/npm/autoprefixer" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", - "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/aws-sign2": { "version": "0.7.0", "license": "Apache-2.0", @@ -11428,6 +11313,8 @@ "version": "4.0.3", "hasInstallScript": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "node-gyp-build": "^4.2.0" } @@ -13250,258 +13137,6 @@ "version": "1.0.1", "license": "MIT" }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/d3": { - "version": "6.6.2", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "2", - "d3-axis": "2", - "d3-brush": "2", - "d3-chord": "2", - "d3-color": "2", - "d3-contour": "2", - "d3-delaunay": "5", - "d3-dispatch": "2", - "d3-drag": "2", - "d3-dsv": "2", - "d3-ease": "2", - "d3-fetch": "2", - "d3-force": "2", - "d3-format": "2", - "d3-geo": "2", - "d3-hierarchy": "2", - "d3-interpolate": "2", - "d3-path": "2", - "d3-polygon": "2", - "d3-quadtree": "2", - "d3-random": "2", - "d3-scale": "3", - "d3-scale-chromatic": "2", - "d3-selection": "2", - "d3-shape": "2", - "d3-time": "2", - "d3-time-format": "3", - "d3-timer": "2", - "d3-transition": "2", - "d3-zoom": "2" - } - }, - "node_modules/d3-array": { - "version": "2.12.1", - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-axis": { - "version": "2.1.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-brush": { - "version": "2.1.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1 - 2", - "d3-drag": "2", - "d3-interpolate": "1 - 2", - "d3-selection": "2", - "d3-transition": "2" - } - }, - "node_modules/d3-chord": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1 - 2" - } - }, - "node_modules/d3-color": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-contour": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "2" - } - }, - "node_modules/d3-delaunay": { - "version": "5.3.0", - "license": "ISC", - "dependencies": { - "delaunator": "4" - } - }, - "node_modules/d3-dispatch": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-drag": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1 - 2", - "d3-selection": "2" - } - }, - "node_modules/d3-dsv": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json", - "csv2tsv": "bin/dsv2dsv", - "dsv2dsv": "bin/dsv2dsv", - "dsv2json": "bin/dsv2json", - "json2csv": "bin/json2dsv", - "json2dsv": "bin/json2dsv", - "json2tsv": "bin/json2dsv", - "tsv2csv": "bin/dsv2dsv", - "tsv2json": "bin/dsv2json" - } - }, - "node_modules/d3-ease": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-fetch": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dsv": "1 - 2" - } - }, - "node_modules/d3-force": { - "version": "2.1.1", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1 - 2", - "d3-quadtree": "1 - 2", - "d3-timer": "1 - 2" - } - }, - "node_modules/d3-format": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-geo": { - "version": "2.0.1", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": ">=2.5" - } - }, - "node_modules/d3-hierarchy": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-interpolate": { - "version": "2.0.1", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1 - 2" - } - }, - "node_modules/d3-path": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-polygon": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-quadtree": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-random": { - "version": "2.2.2", - "license": "BSD-3-Clause" - }, - "node_modules/d3-scale": { - "version": "3.2.4", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "^2.3.0", - "d3-format": "1 - 2", - "d3-interpolate": "1.2.0 - 2", - "d3-time": "1 - 2", - "d3-time-format": "2 - 3" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1 - 2", - "d3-interpolate": "1 - 2" - } - }, - "node_modules/d3-selection": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-shape": { - "version": "2.1.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1 - 2" - } - }, - "node_modules/d3-time": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-time-format": { - "version": "3.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-time": "1 - 2" - } - }, - "node_modules/d3-timer": { - "version": "2.0.0", - "license": "BSD-3-Clause" - }, - "node_modules/d3-transition": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1 - 2", - "d3-dispatch": "1 - 2", - "d3-ease": "1 - 2", - "d3-interpolate": "1 - 2", - "d3-timer": "1 - 2" - }, - "peerDependencies": { - "d3-selection": "2" - } - }, - "node_modules/d3-zoom": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1 - 2", - "d3-drag": "2", - "d3-interpolate": "1 - 2", - "d3-selection": "2", - "d3-transition": "2" - } - }, "node_modules/dashdash": { "version": "1.14.1", "license": "MIT", @@ -13584,11 +13219,6 @@ "node": ">=0.10.0" } }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, "node_modules/decode-uri-component": { "version": "0.2.0", "license": "MIT", @@ -13631,6 +13261,7 @@ }, "node_modules/deepmerge": { "version": "4.2.2", + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13826,10 +13457,6 @@ "node": ">=0.10.0" } }, - "node_modules/delaunator": { - "version": "4.0.1", - "license": "ISC" - }, "node_modules/delayed-stream": { "version": "1.0.0", "license": "MIT", @@ -14539,16 +14166,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, "node_modules/es5-shim": { "version": "4.5.15", "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.15.tgz", @@ -14558,35 +14175,12 @@ "node": ">=0.4.0" } }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "license": "MIT" - }, "node_modules/es6-shim": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.6.tgz", "integrity": "sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==", "dev": true }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "node_modules/escalade": { "version": "3.1.1", "license": "MIT", @@ -15753,19 +15347,6 @@ "version": "1.1.1", "license": "ISC" }, - "node_modules/ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dependencies": { - "type": "^2.0.0" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" - }, "node_modules/extend": { "version": "3.0.2", "license": "MIT" @@ -15837,6 +15418,17 @@ "node": ">=0.10.0" } }, + "node_modules/extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", + "engines": { + "node": "^10.17.0 || ^12.0.0 || >= 13.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/jaydenseric" + } + }, "node_modules/extract-from-css": { "version": "0.4.4", "dev": true, @@ -16227,11 +15819,6 @@ "node": ">=0.10.0" } }, - "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, "node_modules/forever-agent": { "version": "0.6.1", "license": "Apache-2.0", @@ -16482,10 +16069,6 @@ "node": ">=0.4.x" } }, - "node_modules/fortmatic": { - "version": "2.2.1", - "license": "MIT" - }, "node_modules/forwarded": { "version": "0.1.2", "license": "MIT", @@ -17012,6 +16595,56 @@ "version": "4.2.6", "license": "ISC" }, + "node_modules/graphql": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz", + "integrity": "sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/graphql-request": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz", + "integrity": "sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg==", + "dependencies": { + "cross-fetch": "^3.0.6", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x" + } + }, + "node_modules/graphql-request/node_modules/cross-fetch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", + "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", + "dependencies": { + "node-fetch": "2.6.1" + } + }, + "node_modules/graphql-request/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graphql-request/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/growly": { "version": "1.3.0", "dev": true, @@ -17646,11 +17279,6 @@ "node": ">= 0.6" } }, - "node_modules/http-https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" - }, "node_modules/http-parser-js": { "version": "0.5.3", "license": "MIT" @@ -18071,10 +17699,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "1.0.1", - "license": "ISC" - }, "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -18165,6 +17789,7 @@ }, "node_modules/is-arguments": { "version": "1.1.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.0" @@ -18401,6 +18026,7 @@ }, "node_modules/is-generator-function": { "version": "1.0.8", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -18683,24 +18309,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typed-array": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", - "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", - "dependencies": { - "available-typed-arrays": "^1.0.2", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.0-next.2", - "foreach": "^2.0.5", - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typedarray": { "version": "1.0.0", "license": "MIT" @@ -20050,14 +19658,6 @@ "node": ">=10.0.0" } }, - "node_modules/json-rpc-middleware-stream": { - "version": "3.0.0", - "license": "ISC", - "dependencies": { - "@metamask/safe-event-emitter": "^2.0.0", - "readable-stream": "^2.3.3" - } - }, "node_modules/json-rpc-random-id": { "version": "1.0.1", "license": "ISC" @@ -20954,10 +20554,6 @@ "map-or-similar": "^1.5.0" } }, - "node_modules/memory-cache": { - "version": "0.2.0", - "license": "BSD-2-Clause" - }, "node_modules/memory-fs": { "version": "0.5.0", "license": "MIT", @@ -21540,11 +21136,6 @@ "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", "dev": true }, - "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, "node_modules/nice-try": { "version": "1.0.5", "license": "MIT" @@ -21899,15 +21490,6 @@ "node": "*" } }, - "node_modules/obj-multiplex": { - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "end-of-stream": "^1.4.0", - "once": "^1.4.0", - "readable-stream": "^2.3.3" - } - }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -22116,24 +21698,6 @@ "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" }, - "node_modules/oboe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", - "integrity": "sha1-VVQoTFQ6ImbXo48X4HOCH73jk80=", - "dependencies": { - "http-https": "^1.0.0" - } - }, - "node_modules/obs-store": { - "version": "4.0.3", - "license": "ISC", - "dependencies": { - "readable-stream": "^2.2.2", - "safe-event-emitter": "^1.0.1", - "through2": "^2.0.3", - "xtend": "^4.0.1" - } - }, "node_modules/obuf": { "version": "1.1.2", "license": "MIT" @@ -22554,13 +22118,6 @@ "node": ">=4" } }, - "node_modules/parse-ms": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/parse5": { "version": "5.1.1", "license": "MIT" @@ -22939,13 +22496,6 @@ "node": ">=0.10.0" } }, - "node_modules/post-message-stream": { - "version": "3.0.0", - "license": "ISC", - "dependencies": { - "readable-stream": "^2.1.4" - } - }, "node_modules/postcss": { "version": "7.0.35", "license": "MIT", @@ -23662,19 +23212,6 @@ "node": ">= 0.8" } }, - "node_modules/pretty-ms": { - "version": "7.0.1", - "license": "MIT", - "dependencies": { - "parse-ms": "^2.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pretty/node_modules/extend-shallow": { "version": "2.0.1", "dev": true, @@ -25939,10 +25476,6 @@ "version": "0.2.0", "license": "(MIT OR Apache-2.0)" }, - "node_modules/rw": { - "version": "1.3.3", - "license": "BSD-3-Clause" - }, "node_modules/rxjs": { "version": "6.6.7", "license": "Apache-2.0", @@ -27354,81 +26887,6 @@ "node_modules/svg-tags": { "version": "1.0.0" }, - "node_modules/svg.draggable.js": { - "version": "2.2.2", - "license": "MIT", - "dependencies": { - "svg.js": "^2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.easing.js": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "svg.js": ">=2.3.x" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.filter.js": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.js": { - "version": "2.7.1", - "license": "MIT" - }, - "node_modules/svg.pathmorphing.js": { - "version": "0.1.3", - "license": "MIT", - "dependencies": { - "svg.js": "^2.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.resize.js": { - "version": "1.4.3", - "license": "MIT", - "dependencies": { - "svg.js": "^2.6.5", - "svg.select.js": "^2.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.resize.js/node_modules/svg.select.js": { - "version": "2.1.2", - "license": "MIT", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.select.js": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "svg.js": "^2.6.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/svgo": { "version": "1.3.2", "license": "MIT", @@ -28350,11 +27808,6 @@ "version": "0.14.5", "license": "Unlicense" }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "node_modules/type-check": { "version": "0.3.2", "license": "MIT", @@ -29053,6 +28506,8 @@ "version": "5.0.4", "hasInstallScript": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "node-gyp-build": "^4.2.0" } @@ -29061,19 +28516,6 @@ "version": "3.0.0", "license": "MIT" }, - "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "license": "MIT" @@ -29668,8 +29110,12 @@ } }, "node_modules/vue-router": { - "version": "4.0.5", - "license": "MIT", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.10.tgz", + "integrity": "sha512-YbPf6QnZpyyWfnk7CUt2Bme+vo7TLfg1nGZNkvYqKYh4vLaFw6Gn8bPGdmt5m4qrGnKoXLqc4htAsd3dIukICA==", + "dependencies": { + "@vue/devtools-api": "^6.0.0-beta.14" + }, "peerDependencies": { "vue": "^3.0.0" } @@ -29740,14 +29186,6 @@ "node": ">=0.10.0" } }, - "node_modules/vue3-apexcharts": { - "version": "1.2.1", - "license": "MIT", - "peerDependencies": { - "apexcharts": "> 3.0.0", - "vue": "> 3.0.0" - } - }, "node_modules/vue3-jazzicon": { "version": "0.1.2", "license": "MIT", @@ -29789,8 +29227,12 @@ } }, "node_modules/vuex": { - "version": "4.0.0", - "license": "MIT", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", + "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", + "dependencies": { + "@vue/devtools-api": "^6.0.0-beta.11" + }, "peerDependencies": { "vue": "^3.0.2" } @@ -30059,182 +29501,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/web3-core": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.3.6.tgz", - "integrity": "sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q==", - "dependencies": { - "@types/bn.js": "^4.11.5", - "@types/node": "^12.12.6", - "bignumber.js": "^9.0.0", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-requestmanager": "1.3.6", - "web3-utils": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-helpers": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz", - "integrity": "sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA==", - "dependencies": { - "underscore": "1.12.1", - "web3-eth-iban": "1.3.6", - "web3-utils": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-method": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.3.6.tgz", - "integrity": "sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg==", - "dependencies": { - "@ethersproject/transactions": "^5.0.0-beta.135", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-utils": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-promievent": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz", - "integrity": "sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw==", - "dependencies": { - "eventemitter3": "4.0.4" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-promievent/node_modules/eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - }, - "node_modules/web3-core-requestmanager": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz", - "integrity": "sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA==", - "dependencies": { - "underscore": "1.12.1", - "util": "^0.12.0", - "web3-core-helpers": "1.3.6", - "web3-providers-http": "1.3.6", - "web3-providers-ipc": "1.3.6", - "web3-providers-ws": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-subscriptions": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz", - "integrity": "sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g==", - "dependencies": { - "eventemitter3": "4.0.4", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-subscriptions/node_modules/eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - }, - "node_modules/web3-core/node_modules/@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/web3-core/node_modules/@types/node": { - "version": "12.20.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.15.tgz", - "integrity": "sha512-F6S4Chv4JicJmyrwlDkxUdGNSplsQdGwp1A0AJloEVDirWdZOAiRHhovDlsFkKUrquUXhz1imJhXHsf59auyAg==" - }, - "node_modules/web3-eth-abi": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz", - "integrity": "sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ==", - "dependencies": { - "@ethersproject/abi": "5.0.7", - "underscore": "1.12.1", - "web3-utils": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-eth-abi/node_modules/@ethersproject/abi": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz", - "integrity": "sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==", - "dependencies": { - "@ethersproject/address": "^5.0.4", - "@ethersproject/bignumber": "^5.0.7", - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/constants": "^5.0.4", - "@ethersproject/hash": "^5.0.4", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "@ethersproject/strings": "^5.0.4" - } - }, - "node_modules/web3-eth-contract": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz", - "integrity": "sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA==", - "dependencies": { - "@types/bn.js": "^4.11.5", - "underscore": "1.12.1", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-eth-abi": "1.3.6", - "web3-utils": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-eth-contract/node_modules/@types/bn.js": { - "version": "4.11.6", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/web3-eth-iban": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz", - "integrity": "sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ==", - "dependencies": { - "bn.js": "^4.11.9", - "web3-utils": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/web3-provider-engine": { "version": "16.0.1", "license": "MIT", @@ -30297,50 +29563,6 @@ "async-limiter": "~1.0.0" } }, - "node_modules/web3-providers-http": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.3.6.tgz", - "integrity": "sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q==", - "dependencies": { - "web3-core-helpers": "1.3.6", - "xhr2-cookies": "1.1.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-providers-ipc": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz", - "integrity": "sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA==", - "dependencies": { - "oboe": "2.1.5", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-providers-ws": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz", - "integrity": "sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ==", - "dependencies": { - "eventemitter3": "4.0.4", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6", - "websocket": "^1.0.32" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-providers-ws/node_modules/eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - }, "node_modules/web3-utils": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.3.6.tgz", @@ -31063,22 +30285,6 @@ "webpack": "^4.0.0" } }, - "node_modules/websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/websocket-driver": { "version": "0.7.4", "license": "Apache-2.0", @@ -31098,19 +30304,6 @@ "node": ">=0.8.0" } }, - "node_modules/websocket/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/websocket/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "node_modules/whatwg-encoding": { "version": "1.0.5", "dev": true, @@ -31167,26 +30360,6 @@ "version": "2.0.0", "license": "ISC" }, - "node_modules/which-typed-array": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", - "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", - "dependencies": { - "available-typed-arrays": "^1.0.2", - "call-bind": "^1.0.0", - "es-abstract": "^1.18.0-next.1", - "foreach": "^2.0.5", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.1", - "is-typed-array": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -31492,14 +30665,6 @@ "version": "4.0.1", "license": "ISC" }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", - "engines": { - "node": ">=0.10.32" - } - }, "node_modules/yallist": { "version": "3.1.1", "license": "ISC" @@ -32785,17 +31950,16 @@ } }, "@balancer-labs/sor2": { - "version": "git+ssh://git@github.com/balancer-labs/balancer-sor.git#740030e9a79de2c412c7336ae0253de1ced67f71", - "from": "@balancer-labs/sor2@github:balancer-labs/balancer-sor#develop-V2", + "version": "git+ssh://git@github.com/balancer-labs/balancer-sor.git#f41d4548556007dc2fe9471bcd052715c92b68bb", + "from": "@balancer-labs/sor2@github:balancer-labs/balancer-sor#john/ui-629-sor-integrate-sdk-maths", "requires": { "@ethersproject/address": "^5.0.5", "@ethersproject/constants": "^5.0.5", "@ethersproject/contracts": "^5.0.5", "@ethersproject/providers": "5.0.12", "@ethersproject/solidity": "^5.0.5", - "@ethersproject/units": "^5.0.6", - "bignumber.js": "^9.0.0", - "decimal.js": "^10.2.1", + "@georgeroman/balancer-v2-pools": "^0.0.5", + "bignumber.js": "^9.0.1", "isomorphic-fetch": "^2.2.1" }, "dependencies": { @@ -32827,15 +31991,17 @@ } } }, + "@balancer-labs/v2-deployments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@balancer-labs/v2-deployments/-/v2-deployments-1.1.1.tgz", + "integrity": "sha512-emB/xHCM/njxpReZQx2BqzHXwrWF+iIwn/hSwnSOxp+P6GH0rOaboio6/+Aapj7f9tCCjqRspetX//9TumX82Q==" + }, "@base2/pretty-print-object": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz", "integrity": "sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==", "dev": true }, - "@chaitanyapotti/random-id": { - "version": "1.0.3" - }, "@cnakazawa/watch": { "version": "1.0.4", "dev": true, @@ -33358,6 +32524,18 @@ "purgecss": "^3.1.3" } }, + "@georgeroman/balancer-v2-pools": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@georgeroman/balancer-v2-pools/-/balancer-v2-pools-0.0.5.tgz", + "integrity": "sha512-fx+QmDJ80+E4RoaBfmnKFaOlRLM/FSbzksaTrqXM+0EeWX5Zu99SO03pjTIBDsBifq2WfHBcCU5CW5ulGF/kkg==", + "requires": { + "@balancer-labs/v2-deployments": "^1.0.0", + "bignumber.js": "^9.0.1", + "ethers": "^5.2.0", + "graphql": "^15.5.0", + "graphql-request": "^3.4.0" + } + }, "@gnosis.pm/gp-v2-contracts": { "version": "0.0.1-alpha.19", "resolved": "https://registry.npmjs.org/@gnosis.pm/gp-v2-contracts/-/gp-v2-contracts-0.0.1-alpha.19.tgz", @@ -37088,69 +36266,6 @@ } } }, - "@toruslabs/eccrypto": { - "version": "1.1.6", - "requires": { - "acorn": "^7.4.0", - "elliptic": "^6.5.3", - "es6-promise": "^4.2.8", - "nan": "^2.14.1", - "secp256k1": "^3.8.0" - } - }, - "@toruslabs/fetch-node-details": { - "version": "2.4.0", - "requires": { - "web3-eth-contract": "^1.3.4", - "web3-utils": "^1.3.4" - } - }, - "@toruslabs/http-helpers": { - "version": "1.3.7", - "requires": { - "deepmerge": "^4.2.2" - } - }, - "@toruslabs/torus-embed": { - "version": "1.9.14", - "requires": { - "@chaitanyapotti/random-id": "^1.0.3", - "@toruslabs/fetch-node-details": "^2.4.0", - "@toruslabs/http-helpers": "^1.3.7", - "@toruslabs/torus.js": "^2.3.0", - "create-hash": "^1.2.0", - "deepmerge": "^4.2.2", - "eth-rpc-errors": "^4.0.3", - "fast-deep-equal": "^3.1.3", - "is-stream": "^2.0.0", - "json-rpc-engine": "^6.1.0", - "json-rpc-middleware-stream": "^3.0.0", - "loglevel": "^1.7.1", - "obj-multiplex": "^1.0.0", - "obs-store": "^4.0.3", - "post-message-stream": "^3.0.0", - "pump": "^3.0.0", - "safe-event-emitter": "^1.0.1" - } - }, - "@toruslabs/torus.js": { - "version": "2.3.0", - "requires": { - "@toruslabs/eccrypto": "^1.1.5", - "@toruslabs/http-helpers": "^1.3.5", - "bn.js": "^5.1.3", - "elliptic": "^6.5.4", - "json-stable-stringify": "^1.0.1", - "loglevel": "^1.7.1", - "memory-cache": "^0.2.0", - "web3-utils": "^1.3.3" - }, - "dependencies": { - "bn.js": { - "version": "5.2.0" - } - } - }, "@types/animejs": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/animejs/-/animejs-3.1.3.tgz", @@ -38183,7 +37298,9 @@ } }, "@vue/devtools-api": { - "version": "6.0.0-beta.7" + "version": "6.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.15.tgz", + "integrity": "sha512-quBx4Jjpexo6KDiNUGFr/zF/2A4srKM9S9v2uHgMXSU//hjgq1eGzqkIFql8T9gfX5ZaVOUzYBP3jIdIR3PKIA==" }, "@vue/eslint-config-prettier": { "version": "6.0.0", @@ -38821,17 +37938,6 @@ "picomatch": "^2.0.4" } }, - "apexcharts": { - "version": "3.26.0", - "requires": { - "svg.draggable.js": "^2.2.2", - "svg.easing.js": "^2.0.0", - "svg.filter.js": "^2.0.2", - "svg.pathmorphing.js": "^0.1.3", - "svg.resize.js": "^1.4.3", - "svg.select.js": "^3.0.1" - } - }, "app-root-dir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", @@ -39053,11 +38159,6 @@ "postcss-value-parser": "^4.1.0" } }, - "available-typed-arrays": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", - "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==" - }, "aws-sign2": { "version": "0.7.0" }, @@ -40029,6 +39130,8 @@ }, "bufferutil": { "version": "4.0.3", + "optional": true, + "peer": true, "requires": { "node-gyp-build": "^4.2.0" } @@ -41303,213 +40406,6 @@ "cyclist": { "version": "1.0.1" }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "d3": { - "version": "6.6.2", - "requires": { - "d3-array": "2", - "d3-axis": "2", - "d3-brush": "2", - "d3-chord": "2", - "d3-color": "2", - "d3-contour": "2", - "d3-delaunay": "5", - "d3-dispatch": "2", - "d3-drag": "2", - "d3-dsv": "2", - "d3-ease": "2", - "d3-fetch": "2", - "d3-force": "2", - "d3-format": "2", - "d3-geo": "2", - "d3-hierarchy": "2", - "d3-interpolate": "2", - "d3-path": "2", - "d3-polygon": "2", - "d3-quadtree": "2", - "d3-random": "2", - "d3-scale": "3", - "d3-scale-chromatic": "2", - "d3-selection": "2", - "d3-shape": "2", - "d3-time": "2", - "d3-time-format": "3", - "d3-timer": "2", - "d3-transition": "2", - "d3-zoom": "2" - } - }, - "d3-array": { - "version": "2.12.1", - "requires": { - "internmap": "^1.0.0" - } - }, - "d3-axis": { - "version": "2.1.0" - }, - "d3-brush": { - "version": "2.1.0", - "requires": { - "d3-dispatch": "1 - 2", - "d3-drag": "2", - "d3-interpolate": "1 - 2", - "d3-selection": "2", - "d3-transition": "2" - } - }, - "d3-chord": { - "version": "2.0.0", - "requires": { - "d3-path": "1 - 2" - } - }, - "d3-color": { - "version": "2.0.0" - }, - "d3-contour": { - "version": "2.0.0", - "requires": { - "d3-array": "2" - } - }, - "d3-delaunay": { - "version": "5.3.0", - "requires": { - "delaunator": "4" - } - }, - "d3-dispatch": { - "version": "2.0.0" - }, - "d3-drag": { - "version": "2.0.0", - "requires": { - "d3-dispatch": "1 - 2", - "d3-selection": "2" - } - }, - "d3-dsv": { - "version": "2.0.0", - "requires": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" - } - }, - "d3-ease": { - "version": "2.0.0" - }, - "d3-fetch": { - "version": "2.0.0", - "requires": { - "d3-dsv": "1 - 2" - } - }, - "d3-force": { - "version": "2.1.1", - "requires": { - "d3-dispatch": "1 - 2", - "d3-quadtree": "1 - 2", - "d3-timer": "1 - 2" - } - }, - "d3-format": { - "version": "2.0.0" - }, - "d3-geo": { - "version": "2.0.1", - "requires": { - "d3-array": ">=2.5" - } - }, - "d3-hierarchy": { - "version": "2.0.0" - }, - "d3-interpolate": { - "version": "2.0.1", - "requires": { - "d3-color": "1 - 2" - } - }, - "d3-path": { - "version": "2.0.0" - }, - "d3-polygon": { - "version": "2.0.0" - }, - "d3-quadtree": { - "version": "2.0.0" - }, - "d3-random": { - "version": "2.2.2" - }, - "d3-scale": { - "version": "3.2.4", - "requires": { - "d3-array": "^2.3.0", - "d3-format": "1 - 2", - "d3-interpolate": "1.2.0 - 2", - "d3-time": "1 - 2", - "d3-time-format": "2 - 3" - } - }, - "d3-scale-chromatic": { - "version": "2.0.0", - "requires": { - "d3-color": "1 - 2", - "d3-interpolate": "1 - 2" - } - }, - "d3-selection": { - "version": "2.0.0" - }, - "d3-shape": { - "version": "2.1.0", - "requires": { - "d3-path": "1 - 2" - } - }, - "d3-time": { - "version": "2.0.0" - }, - "d3-time-format": { - "version": "3.0.0", - "requires": { - "d3-time": "1 - 2" - } - }, - "d3-timer": { - "version": "2.0.0" - }, - "d3-transition": { - "version": "2.0.0", - "requires": { - "d3-color": "1 - 2", - "d3-dispatch": "1 - 2", - "d3-ease": "1 - 2", - "d3-interpolate": "1 - 2", - "d3-timer": "1 - 2" - } - }, - "d3-zoom": { - "version": "2.0.0", - "requires": { - "d3-dispatch": "1 - 2", - "d3-drag": "2", - "d3-interpolate": "1 - 2", - "d3-selection": "2", - "d3-transition": "2" - } - }, "dashdash": { "version": "1.14.1", "requires": { @@ -41564,11 +40460,6 @@ "decamelize": { "version": "1.2.0" }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, "decode-uri-component": { "version": "0.2.0" }, @@ -41597,7 +40488,8 @@ "dev": true }, "deepmerge": { - "version": "4.2.2" + "version": "4.2.2", + "devOptional": true }, "default-gateway": { "version": "5.0.5", @@ -41726,9 +40618,6 @@ } } }, - "delaunator": { - "version": "4.0.1" - }, "delayed-stream": { "version": "1.0.0" }, @@ -42280,50 +41169,18 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, "es5-shim": { "version": "4.5.15", "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.15.tgz", "integrity": "sha512-FYpuxEjMeDvU4rulKqFdukQyZSTpzhg4ScQHrAosrlVpR6GFyaw14f74yn2+4BugniIS0Frpg7TvwZocU4ZMTw==", "dev": true }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-promise": { - "version": "4.2.8" - }, "es6-shim": { "version": "0.35.6", "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.6.tgz", "integrity": "sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==", "dev": true }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "escalade": { "version": "3.1.1" }, @@ -43189,21 +42046,6 @@ } } }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" - } - } - }, "extend": { "version": "3.0.2" }, @@ -43252,6 +42094,11 @@ } } }, + "extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==" + }, "extract-from-css": { "version": "0.4.4", "dev": true, @@ -43535,11 +42382,6 @@ "for-in": { "version": "1.0.2" }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, "forever-agent": { "version": "0.6.1" }, @@ -43695,9 +42537,6 @@ "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", "dev": true }, - "fortmatic": { - "version": "2.2.1" - }, "forwarded": { "version": "0.1.2" }, @@ -44081,6 +42920,46 @@ "graceful-fs": { "version": "4.2.6" }, + "graphql": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz", + "integrity": "sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==" + }, + "graphql-request": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz", + "integrity": "sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg==", + "requires": { + "cross-fetch": "^3.0.6", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + }, + "dependencies": { + "cross-fetch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", + "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", + "requires": { + "node-fetch": "2.6.1" + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + } + } + }, "growly": { "version": "1.3.0", "dev": true @@ -44552,11 +43431,6 @@ "toidentifier": "1.0.0" } }, - "http-https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" - }, "http-parser-js": { "version": "0.5.3" }, @@ -44822,9 +43696,6 @@ "side-channel": "^1.0.4" } }, - "internmap": { - "version": "1.0.1" - }, "interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -44883,6 +43754,7 @@ }, "is-arguments": { "version": "1.1.0", + "dev": true, "requires": { "call-bind": "^1.0.0" } @@ -45007,7 +43879,8 @@ "dev": true }, "is-generator-function": { - "version": "1.0.8" + "version": "1.0.8", + "dev": true }, "is-glob": { "version": "4.0.1", @@ -45164,18 +44037,6 @@ "has-symbols": "^1.0.1" } }, - "is-typed-array": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", - "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", - "requires": { - "available-typed-arrays": "^1.0.2", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.0-next.2", - "foreach": "^2.0.5", - "has-symbols": "^1.0.1" - } - }, "is-typedarray": { "version": "1.0.0" }, @@ -46180,13 +45041,6 @@ "eth-rpc-errors": "^4.0.2" } }, - "json-rpc-middleware-stream": { - "version": "3.0.0", - "requires": { - "@metamask/safe-event-emitter": "^2.0.0", - "readable-stream": "^2.3.3" - } - }, "json-rpc-random-id": { "version": "1.0.1" }, @@ -46859,9 +45713,6 @@ "map-or-similar": "^1.5.0" } }, - "memory-cache": { - "version": "0.2.0" - }, "memory-fs": { "version": "0.5.0", "requires": { @@ -47278,11 +46129,6 @@ "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", "dev": true }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, "nice-try": { "version": "1.0.5" }, @@ -47542,14 +46388,6 @@ "oauth-sign": { "version": "0.9.0" }, - "obj-multiplex": { - "version": "1.0.0", - "requires": { - "end-of-stream": "^1.4.0", - "once": "^1.4.0", - "readable-stream": "^2.3.3" - } - }, "object-assign": { "version": "4.1.1" }, @@ -47679,23 +46517,6 @@ "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" }, - "oboe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", - "integrity": "sha1-VVQoTFQ6ImbXo48X4HOCH73jk80=", - "requires": { - "http-https": "^1.0.0" - } - }, - "obs-store": { - "version": "4.0.3", - "requires": { - "readable-stream": "^2.2.2", - "safe-event-emitter": "^1.0.1", - "through2": "^2.0.3", - "xtend": "^4.0.1" - } - }, "obuf": { "version": "1.1.2" }, @@ -47977,9 +46798,6 @@ "json-parse-better-errors": "^1.0.1" } }, - "parse-ms": { - "version": "2.1.0" - }, "parse5": { "version": "5.1.1" }, @@ -48252,12 +47070,6 @@ "posix-character-classes": { "version": "0.1.1" }, - "post-message-stream": { - "version": "3.0.0", - "requires": { - "readable-stream": "^2.1.4" - } - }, "postcss": { "version": "7.0.35", "requires": { @@ -48793,12 +47605,6 @@ "pretty-hrtime": { "version": "1.0.3" }, - "pretty-ms": { - "version": "7.0.1", - "requires": { - "parse-ms": "^2.1.0" - } - }, "prismjs": { "version": "1.23.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", @@ -50456,9 +49262,6 @@ "rustbn.js": { "version": "0.2.0" }, - "rw": { - "version": "1.3.3" - }, "rxjs": { "version": "6.6.7", "requires": { @@ -51459,54 +50262,6 @@ "svg-tags": { "version": "1.0.0" }, - "svg.draggable.js": { - "version": "2.2.2", - "requires": { - "svg.js": "^2.0.1" - } - }, - "svg.easing.js": { - "version": "2.0.0", - "requires": { - "svg.js": ">=2.3.x" - } - }, - "svg.filter.js": { - "version": "2.0.2", - "requires": { - "svg.js": "^2.2.5" - } - }, - "svg.js": { - "version": "2.7.1" - }, - "svg.pathmorphing.js": { - "version": "0.1.3", - "requires": { - "svg.js": "^2.4.0" - } - }, - "svg.resize.js": { - "version": "1.4.3", - "requires": { - "svg.js": "^2.6.5", - "svg.select.js": "^2.1.2" - }, - "dependencies": { - "svg.select.js": { - "version": "2.1.2", - "requires": { - "svg.js": "^2.2.5" - } - } - } - }, - "svg.select.js": { - "version": "3.0.1", - "requires": { - "svg.js": "^2.6.5" - } - }, "svgo": { "version": "1.3.2", "requires": { @@ -52134,11 +50889,6 @@ "tweetnacl": { "version": "0.14.5" }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "type-check": { "version": "0.3.2", "requires": { @@ -52613,6 +51363,8 @@ }, "utf-8-validate": { "version": "5.0.4", + "optional": true, + "peer": true, "requires": { "node-gyp-build": "^4.2.0" } @@ -52620,19 +51372,6 @@ "utf8": { "version": "3.0.0" }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" - } - }, "util-deprecate": { "version": "1.0.2" }, @@ -53067,8 +51806,12 @@ } }, "vue-router": { - "version": "4.0.5", - "requires": {} + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.10.tgz", + "integrity": "sha512-YbPf6QnZpyyWfnk7CUt2Bme+vo7TLfg1nGZNkvYqKYh4vLaFw6Gn8bPGdmt5m4qrGnKoXLqc4htAsd3dIukICA==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.14" + } }, "vue-slider-component": { "version": "4.0.0-beta.3", @@ -53098,10 +51841,6 @@ "vue-template-es2015-compiler": { "version": "1.9.1" }, - "vue3-apexcharts": { - "version": "1.2.1", - "requires": {} - }, "vue3-jazzicon": { "version": "0.1.2", "requires": { @@ -53135,8 +51874,12 @@ } }, "vuex": { - "version": "4.0.0", - "requires": {} + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", + "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.11" + } }, "w3c-hr-time": { "version": "1.0.2", @@ -53345,164 +52088,6 @@ "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", "dev": true }, - "web3-core": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.3.6.tgz", - "integrity": "sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q==", - "requires": { - "@types/bn.js": "^4.11.5", - "@types/node": "^12.12.6", - "bignumber.js": "^9.0.0", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-requestmanager": "1.3.6", - "web3-utils": "1.3.6" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "12.20.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.15.tgz", - "integrity": "sha512-F6S4Chv4JicJmyrwlDkxUdGNSplsQdGwp1A0AJloEVDirWdZOAiRHhovDlsFkKUrquUXhz1imJhXHsf59auyAg==" - } - } - }, - "web3-core-helpers": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz", - "integrity": "sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA==", - "requires": { - "underscore": "1.12.1", - "web3-eth-iban": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-core-method": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.3.6.tgz", - "integrity": "sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg==", - "requires": { - "@ethersproject/transactions": "^5.0.0-beta.135", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-utils": "1.3.6" - } - }, - "web3-core-promievent": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz", - "integrity": "sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw==", - "requires": { - "eventemitter3": "4.0.4" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - } - } - }, - "web3-core-requestmanager": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz", - "integrity": "sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA==", - "requires": { - "underscore": "1.12.1", - "util": "^0.12.0", - "web3-core-helpers": "1.3.6", - "web3-providers-http": "1.3.6", - "web3-providers-ipc": "1.3.6", - "web3-providers-ws": "1.3.6" - } - }, - "web3-core-subscriptions": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz", - "integrity": "sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g==", - "requires": { - "eventemitter3": "4.0.4", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - } - } - }, - "web3-eth-abi": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz", - "integrity": "sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ==", - "requires": { - "@ethersproject/abi": "5.0.7", - "underscore": "1.12.1", - "web3-utils": "1.3.6" - }, - "dependencies": { - "@ethersproject/abi": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz", - "integrity": "sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==", - "requires": { - "@ethersproject/address": "^5.0.4", - "@ethersproject/bignumber": "^5.0.7", - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/constants": "^5.0.4", - "@ethersproject/hash": "^5.0.4", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "@ethersproject/strings": "^5.0.4" - } - } - } - }, - "web3-eth-contract": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz", - "integrity": "sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA==", - "requires": { - "@types/bn.js": "^4.11.5", - "underscore": "1.12.1", - "web3-core": "1.3.6", - "web3-core-helpers": "1.3.6", - "web3-core-method": "1.3.6", - "web3-core-promievent": "1.3.6", - "web3-core-subscriptions": "1.3.6", - "web3-eth-abi": "1.3.6", - "web3-utils": "1.3.6" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "requires": { - "@types/node": "*" - } - } - } - }, - "web3-eth-iban": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz", - "integrity": "sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ==", - "requires": { - "bn.js": "^4.11.9", - "web3-utils": "1.3.6" - } - }, "web3-provider-engine": { "version": "16.0.1", "requires": { @@ -53559,43 +52144,6 @@ } } }, - "web3-providers-http": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.3.6.tgz", - "integrity": "sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q==", - "requires": { - "web3-core-helpers": "1.3.6", - "xhr2-cookies": "1.1.0" - } - }, - "web3-providers-ipc": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz", - "integrity": "sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA==", - "requires": { - "oboe": "2.1.5", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6" - } - }, - "web3-providers-ws": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz", - "integrity": "sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ==", - "requires": { - "eventemitter3": "4.0.4", - "underscore": "1.12.1", - "web3-core-helpers": "1.3.6", - "websocket": "^1.0.32" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - } - } - }, "web3-utils": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.3.6.tgz", @@ -54107,34 +52655,6 @@ } } }, - "websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "websocket-driver": { "version": "0.7.4", "requires": { @@ -54190,20 +52710,6 @@ "which-module": { "version": "2.0.0" }, - "which-typed-array": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", - "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", - "requires": { - "available-typed-arrays": "^1.0.2", - "call-bind": "^1.0.0", - "es-abstract": "^1.18.0-next.1", - "foreach": "^2.0.5", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.1", - "is-typed-array": "^1.1.3" - } - }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -54416,11 +52922,6 @@ "y18n": { "version": "4.0.1" }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, "yallist": { "version": "3.1.1" }, diff --git a/package.json b/package.json index 31677eb727..1d297afc0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/frontend-v2", - "version": "1.11.5", + "version": "1.12.0", "engines": { "node": "14.x", "npm": ">=7" @@ -22,20 +22,8 @@ "dependencies": { "@balancer-labs/assets": "github:balancer-labs/assets#master", "@balancer-labs/sor": "github:balancer-labs/balancer-sor#john/v1-consideringFees-package", - "@balancer-labs/sor2": "github:balancer-labs/balancer-sor#develop-V2", + "@balancer-labs/sor2": "github:balancer-labs/balancer-sor#john/ui-629-sor-integrate-sdk-maths", "@ensdomains/content-hash": "^2.5.3", - "@ethersproject/abi": "^5.0.1", - "@ethersproject/abstract-signer": "^5.3.0", - "@ethersproject/address": "^5.0.1", - "@ethersproject/bignumber": "^5.2.0", - "@ethersproject/bytes": "^5.0.5", - "@ethersproject/constants": "^5.0.1", - "@ethersproject/contracts": "^5.0.1", - "@ethersproject/hash": "^5.0.6", - "@ethersproject/providers": "^5.0.12", - "@ethersproject/strings": "^5.0.5", - "@ethersproject/units": "^5.0.1", - "@ethersproject/wallet": "^5.0.1", "@gnosis.pm/gp-v2-contracts": "^0.0.1-alpha.19", "@metamask/detect-provider": "^1.2.0", "@popperjs/core": "^2.9.2", @@ -43,7 +31,6 @@ "@sentry/browser": "^6.3.2", "@sentry/tracing": "^6.3.2", "@tailwindcss/postcss7-compat": "^2.0.4", - "@toruslabs/torus-embed": "^1.9.4", "@types/animejs": "^3.1.3", "@vue/cli-plugin-babel": "^4.4.0", "@vue/cli-plugin-eslint": "^4.4.0", @@ -52,39 +39,30 @@ "@vueuse/core": "^4.3.4", "@walletconnect/web3-provider": "^1.5.2", "animejs": "^3.2.1", - "apexcharts": "^3.23.1", "autoprefixer": "^9.8.6", "axios": "0.21.1", "bnc-sdk": "^3.4.1", - "bs58": "^4.0.1", - "core-js": "^3.6.5", - "d3": "^6.5.0", "date-fns": "^2.21.1", "echarts": "^5.1.0", - "eslint": "^6.7.2", "ethereumjs-util": "^7.0.7", "ethers": "^5.4.1", "feather-icons": "^4.28.0", - "fortmatic": "^2.0.6", "json-to-graphql-query": "^2.0.0", "lodash": "^4.17.15", "numeral": "^2.0.4", "parse-filepath": "1.0.2", "postcss": "^7.0.35", "postcss-nesting": "^7.0.1", - "pretty-ms": "^7.0.0", "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.4", - "typescript": "~3.9.3", "vue": "^3.1.5", "vue-echarts": "^6.0.0-rc.4", "vue-i18n": "^9.0.0-rc.1", "vue-query": "^1.6.0", - "vue-router": "^4.0.0-alpha.6", + "vue-router": "^4.0.10", "vue-slider-component": "^4.0.0-beta.3", - "vue3-apexcharts": "^1.2.1", "vue3-jazzicon": "^0.1.2", "vue3-virtual-scroller": "^0.2.1", - "vuex": "^4.0.0-alpha.1", + "vuex": "^4.0.2", "walletlink": "^2.1.5", "web3-utils": "^1.3.1" }, @@ -111,11 +89,13 @@ "@vue/eslint-config-typescript": "^5.0.2", "@vue/test-utils": "^2.0.0-alpha.1", "babel-loader": "^8.2.2", + "eslint": "^6.7.2", "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-vue": "^7.0.0-alpha.0", "husky": "^6.0.0", "prettier": "^1.19.1", "tailwind-config-viewer": "^1.5.0", + "typescript": "~3.9.3", "vue-cli-plugin-webpack-bundle-analyzer": "^2.0.0", "vue-loader": "^16.1.2" }, diff --git a/src/Root.ts b/src/Root.ts new file mode 100644 index 0000000000..0edfecd7cd --- /dev/null +++ b/src/Root.ts @@ -0,0 +1,31 @@ +import { defineComponent, h } from 'vue'; +import App from './App.vue'; +import * as providerMap from './providers'; + +const providers = Object.values(providerMap); + +export default defineComponent({ + components: { + App, + ...providerMap + }, + + render() { + function renderProviders(providers) { + if (!providers.length) return h(App); + + const [provider, ...remainingProviders] = providers; + return h( + provider, + {}, + { + default() { + return [renderProviders(remainingProviders)]; + } + } + ); + } + + return renderProviders(providers); + } +}); diff --git a/src/components/_global/BalAsset/BalAsset.vue b/src/components/_global/BalAsset/BalAsset.vue index 99bb457333..f985aa0b58 100644 --- a/src/components/_global/BalAsset/BalAsset.vue +++ b/src/components/_global/BalAsset/BalAsset.vue @@ -1,7 +1,7 @@ + + diff --git a/src/components/_global/BalTable/BalTable.vue b/src/components/_global/BalTable/BalTable.vue index 66fcf303ae..27aedb8956 100644 --- a/src/components/_global/BalTable/BalTable.vue +++ b/src/components/_global/BalTable/BalTable.vue @@ -7,6 +7,7 @@ >
+ + + + + +
-
+
{{ column.name }}
@@ -169,6 +174,37 @@
+ + Total + + + +
@@ -194,7 +230,7 @@ import { watch, computed } from 'vue'; -import { sortBy, sumBy } from 'lodash'; +import { sortBy, sumBy, tail } from 'lodash'; type Sticky = 'horizontal' | 'vertical' | 'both'; type Data = any; @@ -229,6 +265,8 @@ export type ColumnDefinition = { sortKey?: string | ((row: T) => unknown); width?: number; + + totalsCell?: string; }; export default defineComponent({ @@ -380,6 +418,10 @@ export default defineComponent({ props.columns.filter(column => !column.hidden) ); + const shouldRenderTotals = computed(() => + props.columns.some(column => column.totalsCell !== undefined) + ); + watch( () => props.data, newData => { @@ -402,6 +444,7 @@ export default defineComponent({ getHorizontalStickyClass, handleSort, handleRowClick, + tail, //data isColumnStuck, @@ -411,7 +454,8 @@ export default defineComponent({ placeholderBlockWidth, // computed - filteredColumns + filteredColumns, + shouldRenderTotals }; } }); diff --git a/src/components/cards/TradeCard/GasReimbursement.vue b/src/components/cards/TradeCard/GasReimbursement.vue index 36c4ef6eb7..af1153fe1c 100644 --- a/src/components/cards/TradeCard/GasReimbursement.vue +++ b/src/components/cards/TradeCard/GasReimbursement.vue @@ -18,16 +18,16 @@ diff --git a/src/components/tables/PoolsTable/PoolsTable.vue b/src/components/tables/PoolsTable/PoolsTable.vue index 875dacda5e..3f3bf66f1b 100644 --- a/src/components/tables/PoolsTable/PoolsTable.vue +++ b/src/components/tables/PoolsTable/PoolsTable.vue @@ -47,10 +47,10 @@ @@ -82,15 +82,14 @@ import { getAddress } from '@ethersproject/address'; import useNumbers from '@/composables/useNumbers'; import useFathom from '@/composables/useFathom'; -import useAccountBalances from '@/composables/useAccountBalances'; import LiquidityMiningTooltip from '@/components/tooltips/LiquidityMiningTooltip.vue'; import TokenPills from './TokenPills/TokenPills.vue'; -import useTokens from '@/composables/useTokens'; import { ColumnDefinition } from '@/components/_global/BalTable/BalTable.vue'; import useDarkMode from '@/composables/useDarkMode'; import useBreakpoints from '@/composables/useBreakpoints'; +import { isStable } from '@/composables/usePool'; export default defineComponent({ components: { @@ -128,16 +127,9 @@ export default defineComponent({ setup(props) { // COMPOSABLES const { fNum } = useNumbers(); - const { tokens } = useTokens(); const router = useRouter(); const { t } = useI18n(); const { trackGoal, Goals } = useFathom(); - const { - balances, - hasBalance, - isLoading: isLoadingBalances, - isIdle: isBalancesQueryIdle - } = useAccountBalances(); const { darkMode } = useDarkMode(); const { upToLargeBreakpoint } = useBreakpoints(); @@ -202,7 +194,7 @@ export default defineComponent({ } function orderedPoolTokens(pool: DecoratedPoolWithShares): PoolToken[] { - if (pool.poolType === 'Stable') return pool.tokens; + if (isStable(pool)) return pool.tokens; const sortedTokens = pool.tokens.slice(); sortedTokens.sort((a, b) => parseFloat(b.weight) - parseFloat(a.weight)); @@ -217,10 +209,6 @@ export default defineComponent({ return { // data columns, - tokens, - balances, - isLoadingBalances, - isBalancesQueryIdle, // computed darkMode, @@ -232,7 +220,7 @@ export default defineComponent({ fNum, orderedTokenAddressesFor, orderedPoolTokens, - hasBalance + isStable }; } }); diff --git a/src/components/tables/PoolsTable/TokenPills/TokenPills.vue b/src/components/tables/PoolsTable/TokenPills/TokenPills.vue index 4d44a21a6e..a49bc1b1d0 100644 --- a/src/components/tables/PoolsTable/TokenPills/TokenPills.vue +++ b/src/components/tables/PoolsTable/TokenPills/TokenPills.vue @@ -27,8 +27,6 @@ import useNumbers from '@/composables/useNumbers'; import { PoolToken } from '@/services/balancer/subgraph/types'; import { defineComponent, PropType } from 'vue'; -import { getAddress } from '@ethersproject/address'; -import useAccountBalances from '@/composables/useAccountBalances'; import useTokens from '@/composables/useTokens'; import WeightedTokenPill from './WeightedTokenPill.vue'; import StableTokenPill from './StableTokenPill.vue'; @@ -50,14 +48,17 @@ export default defineComponent({ }, setup() { - // COMPOSABLES + /** + * COMPOSABLES + */ const { fNum } = useNumbers(); - const { hasBalance } = useAccountBalances(); - const { tokens } = useTokens(); + const { tokens, hasBalance } = useTokens(); - // METHODS + /** + * METHODS + */ function symbolFor(token: PoolToken): string { - return tokens.value[getAddress(token.address)]?.symbol; + return tokens.value[token.address]?.symbol || '---'; } function weightFor(token: PoolToken): string { diff --git a/src/composables/pools/useTokenApprovals.ts b/src/composables/pools/useTokenApprovals.ts index 6f764f062c..929fddb876 100644 --- a/src/composables/pools/useTokenApprovals.ts +++ b/src/composables/pools/useTokenApprovals.ts @@ -1,33 +1,34 @@ import { ref, computed, Ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { parseUnits } from '@ethersproject/units'; - import { approveTokens } from '@/lib/utils/balancer/tokens'; - import useWeb3 from '@/services/web3/useWeb3'; - import useTokens from '@/composables/useTokens'; import useEthers from '@/composables/useEthers'; -import useAllowances from '../useAllowances'; import useTransactions from '../useTransactions'; export default function useTokenApprovals( tokenAddresses: string[], shortAmounts: Ref ) { - // DATA + /** + * STATE + */ const approving = ref(false); const approvedAll = ref(false); - // COMPOSABLES + /** + * COMPOSABLES + */ const { getProvider, appNetworkConfig } = useWeb3(); - const { tokens } = useTokens(); - const { getRequiredAllowances, refetchAllowances } = useAllowances(); + const { tokens, refetchAllowances, approvalsRequired } = useTokens(); const { txListener } = useEthers(); const { addTransaction } = useTransactions(); const { t } = useI18n(); - // COMPUTED + /** + * COMPUTED + */ const amounts = computed(() => tokenAddresses.map((token, index) => { const shortAmount = shortAmounts.value[index] || '0'; @@ -37,15 +38,13 @@ export default function useTokenApprovals( }) ); - const requiredAllowances = computed(() => { - const allowances = getRequiredAllowances({ - tokens: tokenAddresses, - amounts: amounts.value - }); - return allowances; - }); + const requiredAllowances = computed(() => + approvalsRequired(tokenAddresses, amounts.value) + ); - // METHODS + /** + * METHODS + */ async function approveAllowances(): Promise { try { approving.value = true; @@ -89,12 +88,9 @@ export default function useTokenApprovals( // data approving, approvedAll, - // computed requiredAllowances, - // methods - approveAllowances }; } diff --git a/src/composables/queries/useAccountBalancesQuery.ts b/src/composables/queries/useAccountBalancesQuery.ts deleted file mode 100644 index a1a0776b70..0000000000 --- a/src/composables/queries/useAccountBalancesQuery.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { reactive, ref, Ref, computed } from 'vue'; -import { useQuery } from 'vue-query'; -import { UseQueryOptions } from 'react-query/types'; -import QUERY_KEYS from '@/constants/queryKeys'; -import TokenService from '@/services/token/token.service'; -import { BalanceDictionary } from '@/services/token/concerns/balance.concern'; -import useWeb3 from '@/services/web3/useWeb3'; - -// TYPES -type Response = BalanceDictionary; - -// SERVICES -const tokenService = new TokenService(); - -export default function useAccountBalancesQuery( - tokens: Ref = ref([]), - options: UseQueryOptions = {} -) { - const { account, isWalletReady, userNetworkConfig } = useWeb3(); - - const userNetworkKey = computed(() => userNetworkConfig.value.key); - - const queryKey = reactive( - QUERY_KEYS.Account.Balances(account, userNetworkKey, tokens) - ); - - const queryFn = async () => { - return await tokenService.balance.getMany( - account.value, - userNetworkKey.value, - tokens.value - ); - }; - - const queryOptions = reactive({ - enabled: isWalletReady, - ...options - }); - - return useQuery(queryKey, queryFn, queryOptions); -} diff --git a/src/composables/queries/useAllowancesQuery.ts b/src/composables/queries/useAllowancesQuery.ts new file mode 100644 index 0000000000..8624a67fc7 --- /dev/null +++ b/src/composables/queries/useAllowancesQuery.ts @@ -0,0 +1,58 @@ +import { reactive, ref, Ref, computed } from 'vue'; +import { useQuery } from 'vue-query'; +import { UseQueryOptions } from 'react-query/types'; +import QUERY_KEYS from '@/constants/queryKeys'; +import { tokenService } from '@/services/token/token.service'; +import { ContractAllowancesMap } from '@/services/token/concerns/allowances.concern'; +import useWeb3 from '@/services/web3/useWeb3'; +import useTokenLists from '../useTokenLists'; +import { TokenInfoMap } from '@/types/TokenList'; + +/** + * TYPES + */ +type QueryResponse = ContractAllowancesMap; + +/** + * Fetches all allowances for given tokens for each provided contract address. + */ +export default function useAllowancesQuery( + tokens: Ref = ref({}), + contractAddesses: Ref = ref([]), + options: UseQueryOptions = {} +) { + /** + * COMPOSABLES + */ + const { account, isWalletReady } = useWeb3(); + const { tokenListsLoaded } = useTokenLists(); + + /** + * COMPUTED + */ + const enabled = computed(() => isWalletReady.value && tokenListsLoaded.value); + const tokenAddresses = computed(() => Object.keys(tokens.value)); + + /** + * QUERY INPUTS + */ + const queryKey = reactive( + QUERY_KEYS.Account.Allowances(account, contractAddesses, tokenAddresses) + ); + + const queryFn = async () => { + console.log('Fetching', tokenAddresses.value.length, 'allowances'); + return await tokenService.allowances.get( + account.value, + contractAddesses.value, + tokens.value + ); + }; + + const queryOptions = reactive({ + enabled, + ...options + }); + + return useQuery(queryKey, queryFn, queryOptions); +} diff --git a/src/composables/queries/useBalancesQuery.ts b/src/composables/queries/useBalancesQuery.ts new file mode 100644 index 0000000000..8ea4b2763b --- /dev/null +++ b/src/composables/queries/useBalancesQuery.ts @@ -0,0 +1,53 @@ +import { reactive, ref, computed, Ref } from 'vue'; +import { useQuery } from 'vue-query'; +import { UseQueryOptions } from 'react-query/types'; +import QUERY_KEYS from '@/constants/queryKeys'; +import { tokenService } from '@/services/token/token.service'; +import { BalanceMap } from '@/services/token/concerns/balances.concern'; +import useWeb3 from '@/services/web3/useWeb3'; +import useTokenLists from '../useTokenLists'; +import { TokenInfoMap } from '@/types/TokenList'; + +/** + * TYPES + */ +type QueryResponse = BalanceMap; + +/** + * Fetches all balances for provided tokens. + */ +export default function useBalancesQuery( + tokens: Ref = ref({}), + options: UseQueryOptions = {} +) { + /** + * COMPOSABLES + */ + const { account, isWalletReady } = useWeb3(); + const { tokenListsLoaded } = useTokenLists(); + + /** + * COMPUTED + */ + const enabled = computed(() => isWalletReady.value && tokenListsLoaded.value); + const tokenAddresses = computed(() => Object.keys(tokens.value)); + + /** + * QUERY INPUTS + */ + const queryKey = reactive( + QUERY_KEYS.Account.Balances(account, tokenAddresses) + ); + + const queryFn = async () => { + console.log('Fetching', tokenAddresses.value.length, 'balances'); + return await tokenService.balances.get(account.value, tokens.value); + }; + + const queryOptions = reactive({ + enabled, + ...options + }); + + return useQuery(queryKey, queryFn, queryOptions); +} diff --git a/src/composables/queries/usePaginatedUserPoolsQuery.ts b/src/composables/queries/usePaginatedUserPoolsQuery.ts deleted file mode 100644 index 86f5dca5b2..0000000000 --- a/src/composables/queries/usePaginatedUserPoolsQuery.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { computed, reactive } from 'vue'; -import { useInfiniteQuery } from 'vue-query'; -import { InfiniteData } from 'react-query/core'; -import { UseInfiniteQueryOptions } from 'react-query/types'; -import { useStore } from 'vuex'; -import { flatten, isEmpty, keyBy } from 'lodash'; -import { getAddress } from '@ethersproject/address'; - -import { bnum } from '@/lib/utils'; - -import QUERY_KEYS from '@/constants/queryKeys'; -import { POOLS } from '@/constants/pools'; - -import BalancerSubgraph from '@/services/balancer/subgraph/service'; -import { DecoratedPoolWithShares } from '@/services/balancer/subgraph/types'; -import useWeb3 from '@/services/web3/useWeb3'; - -type UserPaginatedPoolsQueryResponse = { - pools: DecoratedPoolWithShares[]; - tokens: string[]; - skip?: number; -}; - -export default function usePaginatedUserPoolsQuery( - options: UseInfiniteQueryOptions = {} -) { - // SERVICES - const balancerSubgraph = new BalancerSubgraph(); - - // COMPOSABLES - const store = useStore(); - const { account, isWalletReady } = useWeb3(); - - // DATA - const queryKey = reactive(QUERY_KEYS.Pools.User(account)); - - // COMPUTED - const prices = computed(() => store.state.market.prices); - const isQueryEnabled = computed( - () => isWalletReady.value && account.value != null && !isEmpty(prices.value) - ); - - // METHODS - const queryFn = async ({ pageParam = 0 }) => { - const poolShares = await balancerSubgraph.poolShares.get({ - where: { - userAddress: account.value.toLowerCase() - } - }); - - const poolSharesIds = poolShares.map(poolShare => poolShare.poolId.id); - const poolSharesMap = keyBy(poolShares, poolShare => poolShare.poolId.id); - - const pools = await balancerSubgraph.pools.getDecorated( - '24h', - prices.value, - { - first: POOLS.Pagination.PerPage, - skip: pageParam, - where: { - id_in: poolSharesIds - } - } - ); - - const tokens = flatten(pools.map(pool => pool.tokensList.map(getAddress))); - - const poolsWithShares = pools.map(pool => ({ - ...pool, - shares: bnum(pool.totalLiquidity) - .div(pool.totalShares) - .times(poolSharesMap[pool.id].balance) - .toString() - })); - - return { - pools: poolsWithShares, - tokens, - skip: - pools.length >= POOLS.Pagination.PerPage - ? pageParam + POOLS.Pagination.PerPage - : undefined - }; - }; - - const queryOptions = reactive({ - enabled: isQueryEnabled, - onSuccess: async ( - poolsData: InfiniteData - ) => { - await store.dispatch( - 'registry/injectTokens', - poolsData.pages.map(page => page.tokens) - ); - }, - getNextPageParam: (lastPage: UserPaginatedPoolsQueryResponse) => - lastPage.skip, - ...options - }); - - return useInfiniteQuery( - queryKey, - queryFn, - queryOptions - ); -} diff --git a/src/composables/queries/usePoolActivitiesQuery.ts b/src/composables/queries/usePoolActivitiesQuery.ts index 946bb7b46b..6e57bcd562 100644 --- a/src/composables/queries/usePoolActivitiesQuery.ts +++ b/src/composables/queries/usePoolActivitiesQuery.ts @@ -5,7 +5,7 @@ import { UseInfiniteQueryOptions } from 'react-query/types'; import QUERY_KEYS from '@/constants/queryKeys'; import { POOLS } from '@/constants/pools'; -import BalancerSubgraph from '@/services/balancer/subgraph/service'; +import BalancerSubgraph from '@/services/balancer/subgraph/balancer-subgraph.service'; import { PoolActivity } from '@/services/balancer/subgraph/types'; type PoolActivitiesQueryResponse = { diff --git a/src/composables/queries/usePoolQuery.ts b/src/composables/queries/usePoolQuery.ts index 1a351c6692..58927ca231 100644 --- a/src/composables/queries/usePoolQuery.ts +++ b/src/composables/queries/usePoolQuery.ts @@ -2,82 +2,75 @@ import { computed, reactive } from 'vue'; import { useQuery } from 'vue-query'; import { QueryObserverOptions } from 'react-query/core'; import useTokens from '@/composables/useTokens'; - -import { useStore } from 'vuex'; -import { pick } from 'lodash'; - import QUERY_KEYS from '@/constants/queryKeys'; - -import BalancerContracts from '@/services/balancer/contracts/service'; -import BalancerSubgraph from '@/services/balancer/subgraph/service'; -import { DecoratedPool, FullPool } from '@/services/balancer/subgraph/types'; +import { balancerContractsService } from '@/services/balancer/contracts/balancer-contracts.service'; +import { balancerSubgraphService } from '@/services/balancer/subgraph/balancer-subgraph.service'; +import { FullPool } from '@/services/balancer/subgraph/types'; import { POOLS } from '@/constants/pools'; +import useApp from '../useApp'; +import useUserSettings from '../useUserSettings'; +import { forChange } from '@/lib/utils'; +import { isStable } from '../usePool'; export default function usePoolQuery( id: string, options: QueryObserverOptions = {} ) { - // COMPOSABLES - const store = useStore(); - const { tokens: allTokens } = useTokens(); - - // SERVICES - const balancerSubgraph = new BalancerSubgraph(); - const balancerContracts = new BalancerContracts(); - - // DATA + /** + * COMPOSABLES + */ + const { getTokens, injectTokens, prices, dynamicDataLoading } = useTokens(); + const { appLoading } = useApp(); + const { currency } = useUserSettings(); + + /** + * COMPUTED + */ + const enabled = computed( + () => !appLoading.value && !dynamicDataLoading.value + ); + + /** + * QUERY INPUTS + */ const queryKey = QUERY_KEYS.Pools.Current(id); - // COMPUTED - const appLoading = computed(() => store.state.app.loading); - const prices = computed(() => store.state.market.prices); - const isQueryEnabled = computed(() => !appLoading.value); - - function tokensInjected(pool: DecoratedPool): boolean { - if (!allTokens.value) return false; - - const allAddresses = Object.keys(allTokens.value); - return [...pool.tokenAddresses, pool.address].every(address => - allAddresses.includes(address) - ); - } - - // METHODS const queryFn = async () => { - const [pool] = await balancerSubgraph.pools.getDecorated( - '24h', - prices.value, - { - where: { - id: id.toLowerCase(), - totalShares_gt: -1 // Avoid the filtering for low liquidity pools - } + const [pool] = await balancerSubgraphService.pools.get({ + where: { + id: id.toLowerCase(), + totalShares_gt: -1 // Avoid the filtering for low liquidity pools } - ); + }); - if (pool.poolType === 'Stable' && !POOLS.Stable.AllowList.includes(id)) { + if (isStable(pool) && !POOLS.Stable.AllowList.includes(id)) { throw new Error('Pool not allowed'); } - if (!tokensInjected(pool)) { - await store.dispatch('registry/injectTokens', [ - ...pool.tokenAddresses, - pool.address - ]); - } + await injectTokens([ + ...pool.tokensList, + balancerSubgraphService.pools.addressFor(pool.id) + ]); + await forChange(dynamicDataLoading, false); + + const [decoratedPool] = await balancerSubgraphService.pools.decorate( + [pool], + '24h', + prices.value, + currency.value + ); - const tokens = pick(allTokens.value, pool.tokenAddresses); - const onchainData = await balancerContracts.vault.getPoolData( + const onchainData = await balancerContractsService.vault.getPoolData( id, pool.poolType, - tokens + getTokens(decoratedPool.tokenAddresses) ); - return { ...pool, onchain: onchainData }; + return { ...decoratedPool, onchain: onchainData }; }; const queryOptions = reactive({ - enabled: isQueryEnabled, + enabled, ...options }); diff --git a/src/composables/queries/usePoolSnapshotsQuery.ts b/src/composables/queries/usePoolSnapshotsQuery.ts index 6f61b18448..91d7455c75 100644 --- a/src/composables/queries/usePoolSnapshotsQuery.ts +++ b/src/composables/queries/usePoolSnapshotsQuery.ts @@ -1,18 +1,16 @@ import { computed, reactive } from 'vue'; import { useQuery } from 'vue-query'; import { QueryObserverOptions } from 'react-query/core'; - import QUERY_KEYS from '@/constants/queryKeys'; - -import BalancerSubgraph from '@/services/balancer/subgraph/service'; +import { balancerSubgraphService } from '@/services/balancer/subgraph/balancer-subgraph.service'; import { PoolSnapshots } from '@/services/balancer/subgraph/types'; -import { - getTokensHistoricalPrice, - HistoricalPrices -} from '@/services/coingecko'; import usePoolQuery from './usePoolQuery'; -import useWeb3 from '@/services/web3/useWeb3'; +import { coingeckoService } from '@/services/coingecko/coingecko.service'; +import { HistoricalPrices } from '@/services/coingecko/api/price.service'; +/** + * TYPES + */ interface QueryResponse { prices: HistoricalPrices; snapshots: PoolSnapshots; @@ -23,32 +21,30 @@ export default function usePoolSnapshotsQuery( days: number, options: QueryObserverOptions = {} ) { - // COMPOSABLES - const { appNetworkConfig } = useWeb3(); - - // SERVICES - const balancerSubgraph = new BalancerSubgraph(); - - // DEPENDENT QUERIES + /** + * QUERY DEPENDENCIES + */ const poolQuery = usePoolQuery(id); - // COMPUTED + /** + * COMPUTED + */ const pool = computed(() => poolQuery.data.value); const enabled = computed(() => !!pool.value?.id); - // DATA + /** + * QUERY INPUTS + */ const queryKey = QUERY_KEYS.Pools.Snapshot(id); - // METHODS const queryFn = async () => { if (!pool.value) throw new Error('No pool'); - const prices = await getTokensHistoricalPrice( - appNetworkConfig.chainId, + const prices = await coingeckoService.prices.getTokensHistorical( pool.value.tokensList, days ); - const snapshots = await balancerSubgraph.poolSnapshots.get(id, days); + const snapshots = await balancerSubgraphService.poolSnapshots.get(id, days); return { prices, snapshots }; }; diff --git a/src/composables/queries/usePoolUserActivitiesQuery.ts b/src/composables/queries/usePoolUserActivitiesQuery.ts index b4f16934c1..406c96b76e 100644 --- a/src/composables/queries/usePoolUserActivitiesQuery.ts +++ b/src/composables/queries/usePoolUserActivitiesQuery.ts @@ -5,7 +5,7 @@ import { UseInfiniteQueryOptions } from 'react-query/types'; import QUERY_KEYS from '@/constants/queryKeys'; import { POOLS } from '@/constants/pools'; -import BalancerSubgraph from '@/services/balancer/subgraph/service'; +import BalancerSubgraph from '@/services/balancer/subgraph/balancer-subgraph.service'; import { PoolActivity } from '@/services/balancer/subgraph/types'; import useWeb3 from '@/services/web3/useWeb3'; diff --git a/src/composables/queries/usePoolsQuery.ts b/src/composables/queries/usePoolsQuery.ts index 05dc714418..1afeec3865 100644 --- a/src/composables/queries/usePoolsQuery.ts +++ b/src/composables/queries/usePoolsQuery.ts @@ -1,15 +1,15 @@ import { computed, reactive, ref, Ref } from 'vue'; import { useInfiniteQuery } from 'vue-query'; import { UseInfiniteQueryOptions } from 'react-query/types'; -import { useStore } from 'vuex'; import { flatten } from 'lodash'; - import QUERY_KEYS from '@/constants/queryKeys'; import { POOLS } from '@/constants/pools'; - -import BalancerSubgraph from '@/services/balancer/subgraph/service'; +import { balancerSubgraphService } from '@/services/balancer/subgraph/balancer-subgraph.service'; import { DecoratedPool } from '@/services/balancer/subgraph/types'; import useTokens from '../useTokens'; +import useUserSettings from '../useUserSettings'; +import useApp from '../useApp'; +import { forChange } from '@/lib/utils'; type PoolsQueryResponse = { pools: DecoratedPool[]; @@ -17,52 +17,55 @@ type PoolsQueryResponse = { skip?: number; }; +type FilterOptions = { + poolIds?: Ref; + pageSize?: number; +}; + export default function usePoolsQuery( tokenList: Ref = ref([]), - options: UseInfiniteQueryOptions = {} + options: UseInfiniteQueryOptions = {}, + filterOptions?: FilterOptions ) { - // SERVICES - const balancerSubgraph = new BalancerSubgraph(); - // COMPOSABLES - const store = useStore(); - const { tokens: allTokens } = useTokens(); + const { injectTokens, dynamicDataLoading, prices } = useTokens(); + const { currency } = useUserSettings(); + const { appLoading } = useApp(); // DATA - const queryKey = QUERY_KEYS.Pools.All(tokenList); + const queryKey = QUERY_KEYS.Pools.All(tokenList, filterOptions?.poolIds); // COMPUTED - const appLoading = computed(() => store.state.app.loading); - const prices = computed(() => store.state.market.prices); - const isQueryEnabled = computed(() => !appLoading.value); - - function uninjected(tokens: string[]): string[] { - const allAddresses = Object.keys(allTokens.value); - return tokens.filter(address => !allAddresses.includes(address)); - } + const enabled = computed(() => !appLoading.value); // METHODS const queryFn = async ({ pageParam = 0 }) => { - const pools = await balancerSubgraph.pools.getDecorated( + const queryArgs: any = { + first: filterOptions?.pageSize || POOLS.Pagination.PerPage, + skip: pageParam, + where: { + tokensList_contains: tokenList.value + } + }; + if (filterOptions?.poolIds?.value.length) { + queryArgs.where.id_in = filterOptions.poolIds.value; + } + + const pools = await balancerSubgraphService.pools.get(queryArgs); + + const tokens = flatten(pools.map(pool => pool.tokensList)); + await injectTokens(tokens); + await forChange(dynamicDataLoading, false); + + const decoratedPools = await balancerSubgraphService.pools.decorate( + pools, '24h', prices.value, - { - first: POOLS.Pagination.PerPage, - skip: pageParam, - where: { - tokensList_contains: tokenList.value - } - } + currency.value ); - const tokens = flatten(pools.map(pool => pool.tokenAddresses)); - const uninjectedTokens = uninjected(tokens); - if (uninjectedTokens.length > 0) { - await store.dispatch('registry/injectTokens', uninjectedTokens); - } - return { - pools, + pools: decoratedPools, tokens, skip: pools.length >= POOLS.Pagination.PerPage @@ -72,7 +75,7 @@ export default function usePoolsQuery( }; const queryOptions = reactive({ - enabled: isQueryEnabled, + enabled, getNextPageParam: (lastPage: PoolsQueryResponse) => lastPage.skip, ...options }); diff --git a/src/composables/queries/useTokenListsQuery.ts b/src/composables/queries/useTokenListsQuery.ts new file mode 100644 index 0000000000..1c7d534c67 --- /dev/null +++ b/src/composables/queries/useTokenListsQuery.ts @@ -0,0 +1,34 @@ +import { reactive } from 'vue'; +import { useQuery } from 'vue-query'; +import { UseQueryOptions } from 'react-query/types'; +import QUERY_KEYS from '@/constants/queryKeys'; +import { TokenListMap } from '@/types/TokenList'; +import { tokenListService } from '@/services/token-list/token-list.service'; +import { FETCH_ONCE_OPTIONS } from '@/constants/vue-query'; + +/** + * TYPES + */ +type QueryResponse = TokenListMap; + +/** + * Fetch all token lists, should only happen once. + */ +export default function useTokenListsQuery( + options: UseQueryOptions = {} +) { + const queryKey = reactive(QUERY_KEYS.TokenLists.All); + + const queryFn = async () => { + console.log('Fetching tokenLists'); + return await tokenListService.getAll(); + }; + + const queryOptions = reactive({ + enabled: true, + ...FETCH_ONCE_OPTIONS, + ...options + }); + + return useQuery(queryKey, queryFn, queryOptions); +} diff --git a/src/composables/queries/useTokenPricesQuery.ts b/src/composables/queries/useTokenPricesQuery.ts index 2b83677813..578f7410c4 100644 --- a/src/composables/queries/useTokenPricesQuery.ts +++ b/src/composables/queries/useTokenPricesQuery.ts @@ -2,23 +2,48 @@ import { reactive, ref, Ref } from 'vue'; import { useQuery } from 'vue-query'; import { UseQueryOptions } from 'react-query/types'; import QUERY_KEYS from '@/constants/queryKeys'; -import { CoingeckoService } from '@/services/coingecko/coingecko.service'; +import { coingeckoService } from '@/services/coingecko/coingecko.service'; import { TokenPrices } from '@/services/coingecko/api/price.service'; +import { sleep } from '@/lib/utils'; -// TYPES -type Response = TokenPrices; +/** + * TYPES + */ +type QueryResponse = TokenPrices; -// SERVICES -const coingeckoService = new CoingeckoService(); +/** + * CONSTANTS + */ +const PER_PAGE = 1000; +/** + * Fetches token prices for all provided addresses. + */ export default function useTokenPricesQuery( - tokens: Ref = ref([]), - options: UseQueryOptions = {} + addresses: Ref = ref([]), + options: UseQueryOptions = {} ) { - const queryKey = reactive(QUERY_KEYS.Tokens.Prices(tokens)); + const queryKey = reactive(QUERY_KEYS.Tokens.Prices(addresses)); const queryFn = async () => { - return await coingeckoService.prices.getTokens(tokens.value); + // Sequential pagination required to avoid coingecko rate limits. + let prices: TokenPrices = {}; + const pageCount = Math.ceil(addresses.value.length / PER_PAGE); + const pages = Array.from(Array(pageCount).keys()); + + for (const page of pages) { + if (page !== 0) await sleep(1000); + const pageAddresses = addresses.value.slice( + PER_PAGE * page, + PER_PAGE * (page + 1) + ); + console.log('Fetching', pageAddresses.length, 'prices'); + prices = { + ...prices, + ...(await coingeckoService.prices.getTokens(pageAddresses)) + }; + } + return prices; }; const queryOptions = reactive({ @@ -26,5 +51,5 @@ export default function useTokenPricesQuery( ...options }); - return useQuery(queryKey, queryFn, queryOptions); + return useQuery(queryKey, queryFn, queryOptions); } diff --git a/src/composables/queries/useUserPoolsQuery.ts b/src/composables/queries/useUserPoolsQuery.ts index be717cbde8..29e5de813d 100644 --- a/src/composables/queries/useUserPoolsQuery.ts +++ b/src/composables/queries/useUserPoolsQuery.ts @@ -1,18 +1,15 @@ import { computed, reactive } from 'vue'; import { useQuery } from 'vue-query'; import { UseQueryOptions } from 'react-query/types'; -import { useStore } from 'vuex'; -import { flatten, isEmpty, keyBy } from 'lodash'; -import { getAddress } from '@ethersproject/address'; - -import { bnum } from '@/lib/utils'; - +import { flatten, keyBy } from 'lodash'; +import { bnum, forChange } from '@/lib/utils'; import QUERY_KEYS from '@/constants/queryKeys'; - -import BalancerSubgraph from '@/services/balancer/subgraph/service'; +import { balancerSubgraphService } from '@/services/balancer/subgraph/balancer-subgraph.service'; import { DecoratedPoolWithShares } from '@/services/balancer/subgraph/types'; import useWeb3 from '@/services/web3/useWeb3'; +import useTokenLists from '../useTokenLists'; import useTokens from '../useTokens'; +import useUserSettings from '../useUserSettings'; type UserPoolsQueryResponse = { pools: DecoratedPoolWithShares[]; @@ -23,31 +20,29 @@ type UserPoolsQueryResponse = { export default function useUserPoolsQuery( options: UseQueryOptions = {} ) { - // SERVICES - const balancerSubgraph = new BalancerSubgraph(); - - // COMPOSABLES - const store = useStore(); + /** + * COMPOSABLES + */ + const { injectTokens, prices, dynamicDataLoading } = useTokens(); + const { loadingTokenLists } = useTokenLists(); const { account, isWalletReady } = useWeb3(); - const { tokens: allTokens } = useTokens(); - - // DATA - const queryKey = reactive(QUERY_KEYS.Pools.User(account)); - - // COMPUTED - const prices = computed(() => store.state.market.prices); - const isQueryEnabled = computed( - () => isWalletReady.value && account.value != null && !isEmpty(prices.value) + const { currency } = useUserSettings(); + + /** + * COMPUTED + */ + const enabled = computed( + () => + isWalletReady.value && account.value != null && !loadingTokenLists.value ); - function uninjected(tokens: string[]): string[] { - const allAddresses = Object.keys(allTokens.value); - return tokens.filter(address => !allAddresses.includes(address)); - } + /** + * QUERY PROPERTIES + */ + const queryKey = reactive(QUERY_KEYS.Pools.User(account)); - // METHODS const queryFn = async () => { - const poolShares = await balancerSubgraph.poolShares.get({ + const poolShares = await balancerSubgraphService.poolShares.get({ where: { userAddress: account.value.toLowerCase() } @@ -56,23 +51,23 @@ export default function useUserPoolsQuery( const poolSharesIds = poolShares.map(poolShare => poolShare.poolId.id); const poolSharesMap = keyBy(poolShares, poolShare => poolShare.poolId.id); - const pools = await balancerSubgraph.pools.getDecorated( + const pools = await balancerSubgraphService.pools.get({ + where: { + id_in: poolSharesIds + } + }); + + const tokens = flatten(pools.map(pool => pool.tokensList)); + await injectTokens(tokens); + await forChange(dynamicDataLoading, false); + const decoratedPools = await balancerSubgraphService.pools.decorate( + pools, '24h', prices.value, - { - where: { - id_in: poolSharesIds - } - } + currency.value ); - const tokens = flatten(pools.map(pool => pool.tokensList.map(getAddress))); - const uninjectedTokens = uninjected(tokens); - if (uninjectedTokens.length > 0) { - await store.dispatch('registry/injectTokens', uninjectedTokens); - } - - const poolsWithShares = pools.map(pool => ({ + const poolsWithShares = decoratedPools.map(pool => ({ ...pool, shares: bnum(pool.totalLiquidity) .div(pool.totalShares) @@ -93,10 +88,7 @@ export default function useUserPoolsQuery( }; const queryOptions = reactive({ - enabled: isQueryEnabled, - onSuccess: async (poolsData: UserPoolsQueryResponse) => { - await store.dispatch('registry/injectTokens', poolsData.tokens); - }, + enabled, ...options }); diff --git a/src/composables/trade/useGnosis.ts b/src/composables/trade/useGnosis.ts index fbedd431bd..8566359511 100644 --- a/src/composables/trade/useGnosis.ts +++ b/src/composables/trade/useGnosis.ts @@ -1,11 +1,9 @@ -import { computed, ComputedRef, ref, Ref } from 'vue'; +import { computed, ComputedRef, reactive, ref, Ref, toRefs } from 'vue'; import { useStore } from 'vuex'; import { BigNumber } from 'bignumber.js'; import { formatUnits } from '@ethersproject/units'; import { OrderKind } from '@gnosis.pm/gp-v2-contracts'; - import { bnum } from '@/lib/utils'; - import useWeb3 from '@/services/web3/useWeb3'; import { FeeInformation, OrderMetaData } from '@/services/gnosis/types'; import { @@ -14,16 +12,27 @@ import { UnsignedOrder } from '@/services/gnosis/signing'; import { gnosisOperator } from '@/services/gnosis/operator.service'; - import useTransactions from '../useTransactions'; - import { Token } from '@/types'; import { TradeQuote } from './types'; import useNumbers from '../useNumbers'; +import { TokenInfo } from '@/types/TokenList'; +import useTokens from '../useTokens'; // TODO: get correct app id const GNOSIS_APP_ID = 2; -const appData = '0x' + GNOSIS_APP_ID.toString(16).padStart(64, '0'); +const APP_DATA = '0x' + GNOSIS_APP_ID.toString(16).padStart(64, '0'); +const HIGH_FEE_THRESHOLD = 0.2; + +const state = reactive({ + errors: { + feeExceedsPrice: false, + priceExceedsBalance: false + }, + warnings: { + highFees: false + } +}); export type GnosisTransactionDetails = { tokenIn: Token; @@ -49,8 +58,9 @@ type Props = { tokenOutAmountInput: Ref; tokenInAmountScaled: ComputedRef; tokenOutAmountScaled: ComputedRef; - tokenIn: ComputedRef; - tokenOut: ComputedRef; + tokenIn: ComputedRef; + tokenOut: ComputedRef; + slippageBufferRate: ComputedRef; }; export default function useGnosis({ @@ -62,25 +72,20 @@ export default function useGnosis({ tokenInAmountScaled, tokenOutAmountScaled, tokenIn, - tokenOut + tokenOut, + slippageBufferRate }: Props) { // COMPOSABLES const store = useStore(); const { account, getSigner } = useWeb3(); const { addTransaction } = useTransactions(); const { fNum } = useNumbers(); + const { balanceFor } = useTokens(); // DATA const feeQuote = ref(null); - const errors = ref({ - feeExceedsPrice: false - }); const updatingQuotes = ref(false); - const trading = ref(false); - - const slippageBufferRate = computed(() => - parseFloat(store.state.app.slippage) - ); + const confirming = ref(false); // COMPUTED const appTransactionDeadline = computed( @@ -88,21 +93,11 @@ export default function useGnosis({ ); const hasErrors = computed(() => - Object.values(errors.value).some(hasError => hasError) + Object.values(state.errors).some(hasError => hasError) ); // METHODS - function resetErrors() { - errors.value = { - feeExceedsPrice: false - }; - } - - function resetFees() { - feeQuote.value = null; - } - - function getQuote(): TradeQuote { + function getFeeAmount() { const feeAmountInToken = feeQuote.value?.amount ?? '0'; const feeAmountOutToken = tokenOutAmountScaled.value .div(tokenInAmountScaled.value) @@ -110,6 +105,15 @@ export default function useGnosis({ .integerValue(BigNumber.ROUND_DOWN) .toString(); + return { + feeAmountInToken, + feeAmountOutToken + }; + } + + function getQuote(): TradeQuote { + const { feeAmountInToken, feeAmountOutToken } = getFeeAmount(); + const maximumInAmount = tokenInAmountScaled.value .plus(feeAmountInToken) .times(1 + slippageBufferRate.value) @@ -132,7 +136,7 @@ export default function useGnosis({ async function trade(successCallback?: () => void) { try { - trading.value = true; + confirming.value = true; const quote = getQuote(); const unsignedOrder: UnsignedOrder = { @@ -147,7 +151,7 @@ export default function useGnosis({ ? quote.minimumOutAmount : tokenOutAmountScaled.value.toString(), validTo: calculateValidTo(appTransactionDeadline.value), - appData, + appData: APP_DATA, feeAmount: quote.feeAmountInToken, kind: exactIn.value ? OrderKind.SELL : OrderKind.BUY, receiver: account.value, @@ -169,16 +173,25 @@ export default function useGnosis({ owner: account.value }); + const sellAmount = exactIn.value + ? tokenInAmountInput.value + : formatUnits(quote.maximumInAmount, tokenIn.value.decimals).toString(); + + const buyAmount = exactIn.value + ? formatUnits( + quote.minimumOutAmount, + tokenOut.value.decimals + ).toString() + : tokenOutAmountInput.value; + const tokenInAmountEst = exactIn.value ? '' : '~'; const tokenOutAmountEst = exactIn.value ? '~' : ''; - const summary = `${tokenInAmountEst}${fNum( - tokenInAmountInput.value, - 'token' - )} ${tokenIn.value.symbol} -> ${tokenOutAmountEst}${fNum( - tokenOutAmountInput.value, - 'token' - )} ${tokenOut.value.symbol}`; + const summary = `${tokenInAmountEst}${fNum(sellAmount, 'token')} ${ + tokenIn.value.symbol + } -> ${tokenOutAmountEst}${fNum(buyAmount, 'token')} ${ + tokenOut.value.symbol + }`; const { validTo, partiallyFillable } = unsignedOrder; @@ -207,10 +220,21 @@ export default function useGnosis({ if (successCallback != null) { successCallback(); } - trading.value = false; + confirming.value = false; } catch (e) { console.log(e); - trading.value = false; + confirming.value = false; + } + } + + function resetState(shouldResetFees = true) { + state.errors.feeExceedsPrice = false; + state.errors.priceExceedsBalance = false; + + state.warnings.highFees = false; + + if (shouldResetFees) { + feeQuote.value = null; } } @@ -231,7 +255,6 @@ export default function useGnosis({ return; } - resetErrors(); updatingQuotes.value = true; try { @@ -247,11 +270,11 @@ export default function useGnosis({ if (feeQuoteResult != null) { if (exactIn.value) { - errors.value.feeExceedsPrice = amountToExchange + state.errors.feeExceedsPrice = amountToExchange .minus(feeQuoteResult.amount) .isNegative(); } - if (!errors.value.feeExceedsPrice) { + if (!state.errors.feeExceedsPrice) { const priceQuoteResult = await gnosisOperator.getPriceQuote( queryParams ); @@ -260,15 +283,29 @@ export default function useGnosis({ feeQuote.value = feeQuoteResult; if (exactIn.value) { - tokenOutAmountInput.value = formatUnits( - priceQuoteResult.amount, - tokenOut.value.decimals - ); + tokenOutAmountInput.value = bnum( + formatUnits(priceQuoteResult.amount, tokenOut.value.decimals) + ).toFixed(6, BigNumber.ROUND_DOWN); + + const { feeAmountInToken } = getQuote(); + + state.warnings.highFees = bnum(feeAmountInToken) + .div(amountToExchange) + .gt(HIGH_FEE_THRESHOLD); } else { - tokenInAmountInput.value = formatUnits( - priceQuoteResult.amount, - tokenIn.value.decimals - ); + tokenInAmountInput.value = bnum( + formatUnits(priceQuoteResult.amount, tokenIn.value.decimals) + ).toFixed(6, BigNumber.ROUND_DOWN); + + const { feeAmountOutToken, maximumInAmount } = getQuote(); + + state.warnings.highFees = bnum(feeAmountOutToken) + .div(amountToExchange) + .gt(HIGH_FEE_THRESHOLD); + + state.errors.priceExceedsBalance = bnum( + formatUnits(maximumInAmount, tokenIn.value.decimals) + ).gt(balanceFor(tokenIn.value.address)); } } } @@ -283,15 +320,14 @@ export default function useGnosis({ // methods trade, handleAmountChange, - resetErrors, - resetFees, + resetState, // computed + ...toRefs(state), feeQuote, updatingQuotes, - errors, hasErrors, - trading, + confirming, getQuote }; } diff --git a/src/composables/trade/useSor.ts b/src/composables/trade/useSor.ts index e480cc47b7..9b4d1b80bf 100644 --- a/src/composables/trade/useSor.ts +++ b/src/composables/trade/useSor.ts @@ -1,4 +1,12 @@ -import { Ref, onMounted, ref, computed, ComputedRef } from 'vue'; +import { + Ref, + onMounted, + ref, + computed, + ComputedRef, + reactive, + toRefs +} from 'vue'; import { useStore } from 'vuex'; import { useIntervalFn } from '@vueuse/core'; import { BigNumber } from 'bignumber.js'; @@ -20,18 +28,24 @@ import { rpcProviderService } from '@/services/rpc-provider/rpc-provider.service import useFathom from '../useFathom'; import useWeb3 from '@/services/web3/useWeb3'; -import { ETHER } from '@/constants/tokenlists'; import { TransactionResponse } from '@ethersproject/providers'; import useEthers from '../useEthers'; -import { Token, TokenMap } from '@/types'; import { TradeQuote } from './types'; -import useTransactions from '../useTransactions'; +import useTransactions, { TransactionAction } from '../useTransactions'; import useNumbers from '../useNumbers'; +import { TokenInfo, TokenInfoMap } from '@/types/TokenList'; +import useTokens from '../useTokens'; const GAS_PRICE = process.env.VUE_APP_GAS_PRICE || '100000000000'; const MAX_POOLS = process.env.VUE_APP_MAX_POOLS || '4'; const SWAP_COST = process.env.VUE_APP_SWAP_COST || '100000'; const MIN_PRICE_IMPACT = 0.0001; +const HIGH_PRICE_IMPACT_THRESHOLD = 0.05; +const state = reactive({ + errors: { + highPriceImpact: false + } +}); type Props = { exactIn: Ref; @@ -39,7 +53,7 @@ type Props = { tokenInAmountInput: Ref; tokenOutAddressInput: Ref; tokenOutAmountInput: Ref; - tokens: Ref; + tokens: Ref; isWrap: Ref; isUnwrap: Ref; tokenInAmountScaled?: ComputedRef; @@ -47,10 +61,10 @@ type Props = { sorConfig?: { refetchPools: boolean; handleAmountsOnFetchPools: boolean; - enableTxHandler: boolean; }; - tokenIn?: ComputedRef; - tokenOut?: ComputedRef; + tokenIn?: ComputedRef; + tokenOut?: ComputedRef; + slippageBufferRate: ComputedRef; }; export type UseSor = ReturnType; @@ -68,11 +82,11 @@ export default function useSor({ tokenOutAmountScaled, sorConfig = { refetchPools: true, - handleAmountsOnFetchPools: true, - enableTxHandler: true + handleAmountsOnFetchPools: true }, tokenIn, - tokenOut + tokenOut, + slippageBufferRate }: Props) { let sorManager: SorManager | undefined = undefined; const pools = ref<(Pool | SubgraphPoolBase)[]>([]); @@ -98,6 +112,7 @@ export default function useSor({ } }); const trading = ref(false); + const confirming = ref(false); const priceImpact = ref(0); const latestTxHash = ref(''); const poolsLoading = ref(true); @@ -107,7 +122,8 @@ export default function useSor({ const { getProvider: getWeb3Provider, userNetworkConfig, - isV1Supported + isV1Supported, + appNetworkConfig } = useWeb3(); const provider = computed(() => getWeb3Provider()); const { trackGoal, Goals } = useFathom(); @@ -115,6 +131,7 @@ export default function useSor({ const { addTransaction } = useTransactions(); const { fNum } = useNumbers(); const { t } = useI18n(); + const { injectTokens, priceFor } = useTokens(); const liquiditySelection = computed(() => store.state.app.tradeLiquidity); @@ -126,7 +143,7 @@ export default function useSor({ if (!tokens.value[tokenOutAddressInput.value]) { unknownAssets.push(tokenOutAddressInput.value); } - await store.dispatch('registry/injectTokens', unknownAssets); + await injectTokens(unknownAssets); await initSor(); await handleAmountChange(); }); @@ -137,9 +154,9 @@ export default function useSor({ } }, 30 * 1e3); - const slippageBufferRate = computed(() => - parseFloat(store.state.app.slippage) - ); + function resetState() { + state.errors.highPriceImpact = false; + } async function initSor(): Promise { const poolsUrlV1 = `${ @@ -325,12 +342,14 @@ export default function useSor({ } pools.value = sorManager.selectedPools; + + state.errors.highPriceImpact = + priceImpact.value >= HIGH_PRICE_IMPACT_THRESHOLD; } - function txHandler( - tx: TransactionResponse, - action?: 'wrap' | 'unwrap' | 'trade' - ): void { + function txHandler(tx: TransactionResponse, action: TransactionAction): void { + confirming.value = false; + let summary = ''; const tokenInAmountFormatted = fNum(tokenInAmountInput.value, 'token'); const tokenOutAmountFormatted = fNum(tokenOutAmountInput.value, 'token'); @@ -346,7 +365,7 @@ export default function useSor({ addTransaction({ id: tx.hash, type: 'tx', - action: 'trade', + action, summary, details: { tokenIn: tokenIn?.value, @@ -377,6 +396,7 @@ export default function useSor({ async function trade(successCallback?: () => void) { trackGoal(Goals.ClickSwap); trading.value = true; + confirming.value = true; const tokenInAddress = tokenInAddressInput.value; const tokenOutAddress = tokenOutAddressInput.value; @@ -393,13 +413,16 @@ export default function useSor({ tokenInAmountScaled ); console.log('Wrap tx', tx); + + txHandler(tx, 'wrap'); + if (successCallback != null) { successCallback(); } - txHandler(tx, 'wrap'); } catch (e) { console.log(e); trading.value = false; + confirming.value = false; } return; } else if (isUnwrap.value) { @@ -410,13 +433,16 @@ export default function useSor({ tokenInAmountScaled ); console.log('Unwrap tx', tx); + + txHandler(tx, 'unwrap'); + if (successCallback != null) { successCallback(); } - txHandler(tx, 'unwrap'); } catch (e) { console.log(e); trading.value = false; + confirming.value = false; } return; } @@ -436,13 +462,16 @@ export default function useSor({ minAmount ); console.log('Swap in tx', tx); + + txHandler(tx, 'trade'); + if (successCallback != null) { successCallback(); } - txHandler(tx, 'trade'); } catch (e) { console.log(e); trading.value = false; + confirming.value = false; } } else { const tokenInAmountMax = getMaxIn(tokenInAmountScaled); @@ -462,26 +491,27 @@ export default function useSor({ tokenOutAmountScaled ); console.log('Swap out tx', tx); + + txHandler(tx, 'trade'); + if (successCallback != null) { successCallback(); } - txHandler(tx); } catch (e) { console.log(e); trading.value = false; + confirming.value = false; } } } // Uses stored market prices to calculate swap cost in token denomination function calculateSwapCost(tokenAddress: string): BigNumber { - const ethPriceUsd = - store.state.market.prices[ETHER.address.toLowerCase()]?.price || 0; - const tokenPriceUsd = - store.state.market.prices[tokenAddress.toLowerCase()]?.price || 0; + const ethPriceFiat = priceFor(appNetworkConfig.nativeAsset.address); + const tokenPriceFiat = priceFor(tokenAddress); const gasPriceWei = store.state.market.gasPrice || 0; const gasPriceScaled = scale(bnum(gasPriceWei), -18); - const ethPriceToken = bnum(Number(ethPriceUsd) / Number(tokenPriceUsd)); + const ethPriceToken = bnum(Number(ethPriceFiat) / Number(tokenPriceFiat)); const swapCost = bnum(SWAP_COST); const costSwapToken = gasPriceScaled.times(swapCost).times(ethPriceToken); return costSwapToken; @@ -540,6 +570,7 @@ export default function useSor({ } return { + ...toRefs(state), sorManager, sorReturn, pools, @@ -552,6 +583,8 @@ export default function useSor({ latestTxHash, fetchPools, poolsLoading, - getQuote + getQuote, + resetState, + confirming }; } diff --git a/src/composables/trade/useTokenApproval.ts b/src/composables/trade/useTokenApproval.ts index 111a8c4681..cff9e780f5 100644 --- a/src/composables/trade/useTokenApproval.ts +++ b/src/composables/trade/useTokenApproval.ts @@ -1,49 +1,42 @@ -import { computed, ComputedRef, Ref, ref, watch } from 'vue'; -import { useI18n } from 'vue-i18n'; +import { computed, Ref, ref, watch } from 'vue'; import { parseUnits } from '@ethersproject/units'; import { TransactionResponse } from '@ethersproject/providers'; - import { approveTokens } from '@/lib/utils/balancer/tokens'; -import { ETHER } from '@/constants/tokenlists'; - -import useWeb3 from '@/services/web3/useWeb3'; import { configService } from '@/services/config/config.service'; - -import useAllowances from '../useAllowances'; +import { TokenInfoMap } from '@/types/TokenList'; +import useTokens from '../useTokens'; +import useConfig from '../useConfig'; +import useWeb3 from '@/services/web3/useWeb3'; import useTransactions from '../useTransactions'; import useEthers from '../useEthers'; - -import { TokenMap } from '@/types'; +import { useI18n } from 'vue-i18n'; export default function useTokenApproval( tokenInAddress: Ref, amount: Ref, - tokens: ComputedRef + tokens: Ref ) { + /** + * STATE + */ const approving = ref(false); const approved = ref(false); const { addTransaction } = useTransactions(); const { t } = useI18n(); - // COMPOSABLES + /** + * COMPOSABLES + */ const { getProvider } = useWeb3(); - const { txListener } = useEthers(); + const { networkConfig } = useConfig(); + const { approvalsRequired, dynamicDataLoading } = useTokens(); - const dstList = computed(() => [ - configService.network.addresses.exchangeProxy - ]); - const allowanceTokens = computed(() => [tokenInAddress.value]); - const { - getRequiredAllowances, - isLoading: isLoadingAllowances - } = useAllowances({ - dstList, - tokens: allowanceTokens - }); - + /** + * COMPUTED + */ const allowanceState = computed(() => { - if (tokenInAddress.value === ETHER.address) { + if (tokenInAddress.value === networkConfig.nativeAsset.address) { return { isUnlockedV1: true, isUnlockedV2: true @@ -59,16 +52,16 @@ export default function useTokenApproval( const tokenInDecimals = tokens.value[tokenInAddress.value].decimals; const tokenInAmountDenorm = parseUnits(amount.value, tokenInDecimals); - const requiredAllowancesV1 = getRequiredAllowances({ - dst: configService.network.addresses.exchangeProxy, - tokens: [tokenInAddress.value], - amounts: [tokenInAmountDenorm.toString()] - }); + const requiredAllowancesV1 = approvalsRequired( + [tokenInAddress.value], + [tokenInAmountDenorm.toString()], + configService.network.addresses.exchangeProxy + ); - const requiredAllowancesV2 = getRequiredAllowances({ - tokens: [tokenInAddress.value], - amounts: [tokenInAmountDenorm.toString()] - }); + const requiredAllowancesV2 = approvalsRequired( + [tokenInAddress.value], + [tokenInAmountDenorm.toString()] + ); return { isUnlockedV1: requiredAllowancesV1.length === 0, @@ -76,6 +69,9 @@ export default function useTokenApproval( }; }); + /** + * METHODS + */ async function approveV1(): Promise { console.log('[TokenApproval] Unlock V1'); approving.value = true; @@ -133,8 +129,11 @@ export default function useTokenApproval( }); } + /** + * WATCHERS + */ watch(tokenInAddress, async () => { - if (tokenInAddress.value === ETHER.address) { + if (tokenInAddress.value === networkConfig.nativeAsset.address) { approved.value = true; } else { approved.value = false; @@ -146,6 +145,6 @@ export default function useTokenApproval( approveV1, approveV2, allowanceState, - isLoading: isLoadingAllowances + isLoading: dynamicDataLoading }; } diff --git a/src/composables/trade/useTokenApprovalGP.ts b/src/composables/trade/useTokenApprovalGP.ts index 44f7ed119a..05c1860715 100644 --- a/src/composables/trade/useTokenApprovalGP.ts +++ b/src/composables/trade/useTokenApprovalGP.ts @@ -1,44 +1,36 @@ import { computed, Ref, ref } from 'vue'; import { parseUnits } from '@ethersproject/units'; -import { useI18n } from 'vue-i18n'; - import { approveTokens } from '@/lib/utils/balancer/tokens'; - import { GP_ALLOWANCE_MANAGER_CONTRACT_ADDRESS } from '@/services/gnosis/constants'; import useWeb3 from '@/services/web3/useWeb3'; - -import useTokens from '../useTokens'; -import useAllowances from '../useAllowances'; +import useTokens from '@/composables/useTokens'; import useEthers from '../useEthers'; import useTransactions from '../useTransactions'; +import { useI18n } from 'vue-i18n'; export default function useTokenApprovalGP( tokenInAddress: Ref, amount: Ref ) { - // COMPOSABLES + /** + * STATE + */ + const approving = ref(false); + const approved = ref(false); + + /** + * COMPOSABLES + */ const { getProvider } = useWeb3(); const provider = getProvider(); - const { tokens } = useTokens(); const { txListener } = useEthers(); const { addTransaction } = useTransactions(); const { t } = useI18n(); + const { tokens, approvalsRequired, dynamicDataLoading } = useTokens(); - // DATA - const approving = ref(false); - const approved = ref(false); - - // COMPUTED - const dstList = computed(() => [GP_ALLOWANCE_MANAGER_CONTRACT_ADDRESS]); - const allowanceTokens = computed(() => [tokenInAddress.value]); - const { - getRequiredAllowances, - isLoading: isLoadingAllowances - } = useAllowances({ - dstList, - tokens: allowanceTokens - }); - + /** + * COMPUTED + */ const allowanceState = computed(() => { if (!tokenInAddress.value || !amount.value || approved.value) { return { @@ -48,18 +40,22 @@ export default function useTokenApprovalGP( const tokenInDecimals = tokens.value[tokenInAddress.value].decimals; - const requiredAllowances = getRequiredAllowances({ - dst: GP_ALLOWANCE_MANAGER_CONTRACT_ADDRESS, - tokens: [tokenInAddress.value], - amounts: [parseUnits(amount.value, tokenInDecimals).toString()] - }); + const requiredApprovals = approvalsRequired( + [tokenInAddress.value], + [parseUnits(amount.value, tokenInDecimals).toString()], + GP_ALLOWANCE_MANAGER_CONTRACT_ADDRESS + ); return { - isUnlocked: requiredAllowances.length === 0 + isUnlocked: requiredApprovals.length === 0 }; }); - // METHODS + const isUnlocked = computed(() => allowanceState.value.isUnlocked); + + /** + * METHODS + */ async function approve(): Promise { console.log('[TokenApproval] Unlock token for trading on Gnosis Protocol'); approving.value = true; @@ -96,13 +92,12 @@ export default function useTokenApprovalGP( } } - const isApproved = computed(() => allowanceState.value.isUnlocked); - return { approving, approve, + approved, allowanceState, - isApproved, - isLoading: isLoadingAllowances + isUnlocked, + isLoading: dynamicDataLoading }; } diff --git a/src/composables/trade/useTrading.ts b/src/composables/trade/useTrading.ts index 9662ae8ae6..c4ed048377 100644 --- a/src/composables/trade/useTrading.ts +++ b/src/composables/trade/useTrading.ts @@ -1,17 +1,14 @@ import { computed, Ref, watch } from 'vue'; import { useStore } from 'vuex'; - -import { ETHER } from '@/constants/tokenlists'; - import { bnum, scale } from '@/lib/utils'; - import useNumbers from '../useNumbers'; -import useTokens from '../useTokens'; import useWeb3 from '@/services/web3/useWeb3'; import useSor from './useSor'; import useGnosis from './useGnosis'; +import useTokens from '../useTokens'; +import { NATIVE_ASSET_ADDRESS } from '@/constants/tokens'; -export type TradeRoute = 'balancer' | 'gnosis'; +export type TradeRoute = 'wrapUnwrap' | 'balancer' | 'gnosis'; export type UseTrading = ReturnType; @@ -29,15 +26,21 @@ export default function useTrading( const { blockNumber, userNetworkConfig } = useWeb3(); // COMPUTED + const slippageBufferRate = computed(() => + parseFloat(store.state.app.slippage) + ); + + const liquiditySelection = computed(() => store.state.app.tradeLiquidity); + const isWrap = computed( () => - tokenInAddressInput.value === ETHER.address && + tokenInAddressInput.value === NATIVE_ASSET_ADDRESS && tokenOutAddressInput.value === userNetworkConfig.value.addresses.weth ); const isUnwrap = computed( () => - tokenOutAddressInput.value === ETHER.address && + tokenOutAddressInput.value === NATIVE_ASSET_ADDRESS && tokenInAddressInput.value === userNetworkConfig.value.addresses.weth ); @@ -46,7 +49,7 @@ export default function useTrading( const tokenOut = computed(() => tokens.value[tokenOutAddressInput.value]); const isEthTrade = computed( - () => tokenInAddressInput.value === ETHER.address + () => tokenInAddressInput.value === NATIVE_ASSET_ADDRESS ); const tokenInAmountScaled = computed(() => @@ -90,15 +93,26 @@ export default function useTrading( }; }); - const isWrapOrUnwrap = computed(() => isWrap.value || isUnwrap.value); - const tradeRoute = computed(() => { - return isWrapOrUnwrap.value || isEthTrade.value ? 'balancer' : 'gnosis'; + if (isWrap.value || isUnwrap.value) { + return 'wrapUnwrap'; + } + + return isEthTrade.value ? 'balancer' : 'gnosis'; }); const isGnosisTrade = computed(() => tradeRoute.value === 'gnosis'); + const isBalancerTrade = computed(() => tradeRoute.value === 'balancer'); + const isWrapUnwrapTrade = computed(() => tradeRoute.value === 'wrapUnwrap'); + + const hasTradeQuote = computed( + () => + parseFloat(tokenInAmountInput.value) > 0 && + parseFloat(tokenOutAmountInput.value) > 0 + ); + const sor = useSor({ exactIn, tokenInAddressInput, @@ -112,11 +126,11 @@ export default function useTrading( tokenOutAmountScaled, sorConfig: { handleAmountsOnFetchPools: false, - refetchPools: isBalancerTrade.value, - enableTxHandler: isBalancerTrade.value + refetchPools: false }, tokenIn, - tokenOut + tokenOut, + slippageBufferRate }); const gnosis = useGnosis({ @@ -128,20 +142,43 @@ export default function useTrading( tokenInAmountScaled, tokenOutAmountScaled, tokenIn, - tokenOut + tokenOut, + slippageBufferRate + }); + + const isLoading = computed(() => { + if (hasTradeQuote.value || isWrapUnwrapTrade.value) { + return false; + } + + return isBalancerTrade.value + ? sor.poolsLoading.value + : gnosis.updatingQuotes.value; }); - // initial loading - const isLoading = computed(() => - isBalancerTrade.value ? sor.poolsLoading.value : gnosis.updatingQuotes.value + const isConfirming = computed( + () => sor.confirming.value || gnosis.confirming.value ); // METHODS function trade(successCallback?: () => void) { if (isGnosisTrade.value) { - return gnosis.trade(successCallback); + return gnosis.trade(() => { + if (successCallback) { + successCallback(); + } + + gnosis.resetState(); + }); } else { - return sor.trade(successCallback); + // handles both Balancer and Wrap/Unwrap trades + return sor.trade(() => { + if (successCallback) { + successCallback(); + } + + sor.resetState(); + }); } } @@ -154,17 +191,18 @@ export default function useTrading( } function handleAmountChange() { - if (isGnosisTrade.value) { - gnosis.handleAmountChange(); + if (exactIn.value) { + tokenOutAmountInput.value = ''; } else { - sor.handleAmountChange(); + tokenInAmountInput.value = ''; } - } - function handleAssetChange() { if (isGnosisTrade.value) { - gnosis.resetErrors(); - gnosis.resetFees(); + gnosis.resetState(false); + gnosis.handleAmountChange(); + } else { + sor.resetState(); + sor.handleAmountChange(); } } @@ -172,20 +210,32 @@ export default function useTrading( watch(tokenInAddressInput, async () => { store.commit('trade/setInputAsset', tokenInAddressInput.value); - handleAssetChange(); handleAmountChange(); }); watch(tokenOutAddressInput, () => { store.commit('trade/setOutputAsset', tokenOutAddressInput.value); - handleAssetChange(); handleAmountChange(); }); watch(blockNumber, () => { - if (isGnosisTrade.value && !gnosis.hasErrors.value) { - gnosis.handleAmountChange(); + if (isGnosisTrade.value) { + if (!gnosis.hasErrors.value) { + gnosis.handleAmountChange(); + } + } else if (isBalancerTrade.value) { + sor.fetchPools(); + } + }); + + watch(slippageBufferRate, () => { + handleAmountChange(); + }); + + watch(liquiditySelection, () => { + if (isBalancerTrade.value) { + handleAmountChange(); } }); @@ -194,7 +244,6 @@ export default function useTrading( isWrap, isUnwrap, isEthTrade, - isWrapOrUnwrap, tokenIn, tokenOut, tokenInAmountScaled, @@ -209,10 +258,14 @@ export default function useTrading( sor, isGnosisTrade, isBalancerTrade, + isWrapUnwrapTrade, tokenInAddressInput, tokenInAmountInput, tokenOutAddressInput, tokenOutAmountInput, + slippageBufferRate, + isConfirming, + // methods getQuote, trade, diff --git a/src/composables/trade/useValidation.ts b/src/composables/trade/useValidation.ts index 138d555503..e11be1a8dd 100644 --- a/src/composables/trade/useValidation.ts +++ b/src/composables/trade/useValidation.ts @@ -1,9 +1,6 @@ -import { computed, ComputedRef, Ref } from 'vue'; - +import { computed, Ref } from 'vue'; import useWeb3 from '@/services/web3/useWeb3'; -import { configService } from '@/services/config/config.service'; - -import { TokenMap } from '@/types'; +import useTokens from '../useTokens'; const MIN_NATIVE_ASSET_REQUIRED = 0.0001; @@ -20,10 +17,10 @@ export default function useValidation( tokenInAddress: Ref, tokenInAmount: Ref, tokenOutAddress: Ref, - tokenOutAmount: Ref, - tokens: ComputedRef + tokenOutAmount: Ref ) { const { isWalletReady } = useWeb3(); + const { nativeAsset, balances } = useTokens(); const tokensAmountsValid = computed( () => @@ -33,19 +30,18 @@ export default function useValidation( const validationStatus = computed(() => { if (!isWalletReady) return TradeValidation.NO_ACCOUNT; - const tokenIn = tokens.value[tokenInAddress.value]; if (!tokensAmountsValid.value) return TradeValidation.EMPTY; - const nativeAsset = tokens.value[configService.network.nativeAsset.address]; - const nativeAssetBalance = parseFloat(nativeAsset.balance); + const nativeAssetBalance = parseFloat(balances.value[nativeAsset.address]); if (nativeAssetBalance < MIN_NATIVE_ASSET_REQUIRED) { return TradeValidation.NO_NATIVE_ASSET; } if ( - !tokenIn?.balance || - parseFloat(tokenIn.balance) < parseFloat(tokenInAmount.value) + !balances.value[tokenInAddress.value] || + parseFloat(balances.value[tokenInAddress.value]) < + parseFloat(tokenInAmount.value) ) return TradeValidation.NO_BALANCE; diff --git a/src/composables/useAccountBalances.ts b/src/composables/useAccountBalances.ts deleted file mode 100644 index 25c1cf1a99..0000000000 --- a/src/composables/useAccountBalances.ts +++ /dev/null @@ -1,98 +0,0 @@ -import getProvider from '@/lib/utils/provider'; -import { useQuery } from 'vue-query'; -import { computed, reactive } from 'vue'; -import { getBalances } from '@/lib/utils/balancer/tokens'; -import { formatEther, formatUnits } from '@ethersproject/units'; -import { getAddress } from '@ethersproject/address'; -import QUERY_KEYS from '@/constants/queryKeys'; -import { ETHER } from '@/constants/tokenlists'; -import useWeb3 from '@/services/web3/useWeb3'; -import useTokenStore from './useTokensStore'; - -// THE CONTENTS OF THIS WILL BE REPLACED/ALTERED WITH THE REGISTRY REFACTOR -export default function useAccountBalances() { - const { account, userNetworkConfig, isWalletReady } = useWeb3(); - const { allTokens: tokens, isLoading: isLoadingTokens } = useTokenStore(); - - const isQueryEnabled = computed( - () => - account.value !== null && - Object.keys(tokens).length !== 0 && - isWalletReady.value && - !isLoadingTokens.value - ); - - const { - data, - error, - isLoading, - isIdle, - isError, - isFetching, - refetch: refetchBalances - } = useQuery( - reactive(QUERY_KEYS.Balances.All(account, userNetworkConfig, tokens)), - () => { - return Promise.all([ - getBalances( - String(userNetworkConfig.value?.chainId), - getProvider(userNetworkConfig.value?.key), - account.value, - Object.values(tokens.value) - .map(token => token.address) - .filter(token => token !== ETHER.address) - ), - getProvider(userNetworkConfig.value?.key).getBalance( - account.value.toLowerCase() - ) - ]); - }, - reactive({ - enabled: isQueryEnabled, - keepPreviousData: isWalletReady - }) - ); - - const balances = computed(() => { - if (data.value) { - const balances = {}; - Object.keys(data.value[0]).forEach((tokenAddress: string) => { - const balance = formatUnits( - data.value[0][tokenAddress], - tokens.value[getAddress(tokenAddress)]?.decimals || 18 - ); - // not concerned with tokens which have a 0 balance - if (balance === '0.0') return; - balances[tokenAddress] = { - balance, - symbol: tokens.value[getAddress(tokenAddress)].symbol, - address: getAddress(tokenAddress) - }; - }); - - // separate case for native ether - balances[ETHER.address.toLowerCase()] = { - balance: formatEther(data.value[1] || 0), - symbol: ETHER.symbol, - address: ETHER.address - }; - return balances; - } - return null; - }); - - function hasBalance(address: string): boolean { - return !!(balances.value || {})[address]; - } - - return { - balances, - hasBalance, - error, - isLoading, - isIdle, - isError, - isFetching, - refetchBalances - }; -} diff --git a/src/composables/useAllowances.ts b/src/composables/useAllowances.ts deleted file mode 100644 index d9711b0962..0000000000 --- a/src/composables/useAllowances.ts +++ /dev/null @@ -1,93 +0,0 @@ -import useWeb3 from '@/services/web3/useWeb3'; -import { useQuery } from 'vue-query'; -import { getAllowances } from '@/lib/utils/balancer/tokens'; -import getProvider from '@/lib/utils/provider'; -import { computed, reactive, Ref, ref } from 'vue'; -import { ETHER } from '@/constants/tokenlists'; -import { isAddress } from 'web3-utils'; -import QUERY_KEYS from '@/constants/queryKeys'; -import useTokens from './useTokens'; - -type UseAccountPayload = { - tokens?: Ref; - dstList?: Ref; -}; - -const dstAllowanceMap = ref({}); - -// THE CONTENTS OF THIS WILL BE REPLACED/ALTERED WITH THE REGISTRY REFACTOR -export default function useAllowances(payload?: UseAccountPayload) { - const { userNetworkConfig, account } = useWeb3(); - const { tokens: allTokens } = useTokens(); - const provider = getProvider(String(userNetworkConfig.value?.chainId)); - // filter out ether and any bad addresses - const tokens = computed(() => - (payload?.tokens?.value || Object.keys(allTokens.value)).filter( - t => t !== ETHER.address && isAddress(t) - ) - ); - const dstList = computed(() => [ - ...(payload?.dstList?.value || []), - userNetworkConfig.value?.addresses.vault - ]); - - const isQueryEnabled = computed( - () => account.value != '' && tokens.value.length > 0 - ); - const { - data: allowances, - isLoading, - isFetching, - refetch: refetchAllowances - } = useQuery( - QUERY_KEYS.Account.Allowances(userNetworkConfig, account, dstList, tokens), - () => - Promise.all( - dstList.value.map(async dst => - getAllowances( - String(userNetworkConfig.value?.chainId), - provider, - account.value, - dst, - tokens.value - ) - ) - ), - reactive({ - enabled: isQueryEnabled, - onSuccess: allowances => { - allowances.forEach((allowance, i) => { - dstAllowanceMap.value[dstList.value[i]] = allowance; - }); - } - }) - ); - - const isLoadingOrFetching = computed( - () => isLoading.value || isFetching.value - ); - - const getRequiredAllowances = query => { - const tokens = query.tokens; - const amounts = query.amounts; - const dst = query.dst || userNetworkConfig.value?.addresses.vault; - - const requiredAllowances = tokens.filter((token, index) => { - const amount = amounts[index]; - if (parseFloat(amount) == 0) return false; - if (!dstAllowanceMap.value) return false; - if (!dstAllowanceMap.value[dst]) return true; - if (!dstAllowanceMap.value[dst][token.toLowerCase()]) return true; - return dstAllowanceMap.value[dst][token.toLowerCase()].lt(amount); - }); - - return requiredAllowances; - }; - - return { - allowances, - getRequiredAllowances, - isLoading: isLoadingOrFetching, - refetchAllowances - }; -} diff --git a/src/composables/useApp.ts b/src/composables/useApp.ts new file mode 100644 index 0000000000..aabd026d59 --- /dev/null +++ b/src/composables/useApp.ts @@ -0,0 +1,11 @@ +import { inject } from 'vue'; +import { + AppProviderResponse, + AppProviderSymbol +} from '@/providers/app.provider'; + +const defaultProviderResponse = {} as AppProviderResponse; + +export default function useApp(): AppProviderResponse { + return inject(AppProviderSymbol, defaultProviderResponse); +} diff --git a/src/composables/useConfig.ts b/src/composables/useConfig.ts index ec55d22579..33fe4db506 100644 --- a/src/composables/useConfig.ts +++ b/src/composables/useConfig.ts @@ -1,8 +1,6 @@ -import ConfigService from '@/services/config/config.service'; +import { configService } from '@/services/config/config.service'; -export default function useConfig( - configService: ConfigService = new ConfigService() -) { +export default function useConfig() { return { env: configService.env, networkConfig: configService.network diff --git a/src/composables/useEthers.ts b/src/composables/useEthers.ts index 62968452a0..c8ad86e56f 100644 --- a/src/composables/useEthers.ts +++ b/src/composables/useEthers.ts @@ -4,9 +4,9 @@ import { TransactionResponse } from '@ethersproject/providers'; -import useAccountBalances from './useAccountBalances'; import useBlocknative from './useBlocknative'; import useTransactions from './useTransactions'; +import useTokens from './useTokens'; type TxCallback = ( txData: TransactionResponse, @@ -18,8 +18,8 @@ export const processedTxs = ref>(new Set('')); export default function useEthers() { const { finalizeTransaction } = useTransactions(); - const { refetchBalances } = useAccountBalances(); const { supportsBlocknative } = useBlocknative(); + const { refetchBalances } = useTokens(); async function txListener( tx: TransactionResponse, diff --git a/src/composables/useNumbers.ts b/src/composables/useNumbers.ts index d7609c0a65..bcdc438ccf 100644 --- a/src/composables/useNumbers.ts +++ b/src/composables/useNumbers.ts @@ -1,6 +1,6 @@ import numeral from 'numeral'; import BigNumber from 'bignumber.js'; -import { useStore } from 'vuex'; +import useTokens from './useTokens'; export type Preset = | 'default' @@ -52,13 +52,12 @@ export function fNum( } export default function useNumbers() { - const store = useStore(); + const { priceFor } = useTokens(); function toFiat(amount: number | string, tokenAddress: string): number { - const rate = - store.state.market.prices[tokenAddress.toLowerCase()]?.price || 0; + const price = priceFor(tokenAddress); const tokenAmount = new BigNumber(amount); - return tokenAmount.times(rate).toNumber(); + return tokenAmount.times(price).toNumber(); } return { fNum, toFiat }; diff --git a/src/composables/usePool.ts b/src/composables/usePool.ts new file mode 100644 index 0000000000..4306949e71 --- /dev/null +++ b/src/composables/usePool.ts @@ -0,0 +1,39 @@ +import { Ref, computed } from 'vue'; +import { + Pool, + DecoratedPoolWithShares, + FullPool, + PoolType +} from '@/services/balancer/subgraph/types'; +import { TOKENS } from '@/constants/tokens'; + +type AnyPool = Pool | FullPool | DecoratedPoolWithShares; + +export function isStable(pool: AnyPool): boolean { + return pool.poolType === PoolType.Stable; +} + +export function isWeighted(pool: AnyPool): boolean { + return pool.poolType === PoolType.Weighted; +} + +export function isWeth(pool: AnyPool): boolean { + return pool.tokenAddresses.includes(TOKENS.AddressMap.WETH); +} + +export function usePool(pool: Ref) { + const isStablePool = computed(() => isStable(pool.value)); + const isWeightedPool = computed(() => isWeighted(pool.value)); + const isWethPool = computed(() => isWeth(pool.value)); + + return { + // computed + isStablePool, + isWeightedPool, + isWethPool, + // methods + isStable, + isWeighted, + isWeth + }; +} diff --git a/src/composables/useTime.ts b/src/composables/useTime.ts new file mode 100644 index 0000000000..fb9d1880dc --- /dev/null +++ b/src/composables/useTime.ts @@ -0,0 +1,18 @@ +export const oneSecondInMs = 1000; +export const oneMinInMs = 60 * oneSecondInMs; +export const oneHourInMs = 60 * oneMinInMs; + +export const twentyFourHoursInMs = 24 * oneHourInMs; +export const twentyFourHourseInSecs = twentyFourHoursInMs / oneSecondInMs; + +export const timeNowInMs = Math.floor(Date.now() / oneSecondInMs); + +export default function useTime() { + return { + oneSecondInMs, + oneMinInMs, + oneHourInMs, + twentyFourHoursInMs, + twentyFourHourseInSecs + }; +} diff --git a/src/composables/useTokenLists.ts b/src/composables/useTokenLists.ts new file mode 100644 index 0000000000..1edfc0d98c --- /dev/null +++ b/src/composables/useTokenLists.ts @@ -0,0 +1,11 @@ +import { inject } from 'vue'; +import { + TokenListsProviderResponse, + TokenListsProviderSymbol +} from '@/providers/token-lists.provider'; + +const defaultProviderResponse = {} as TokenListsProviderResponse; + +export default function useTokenLists(): TokenListsProviderResponse { + return inject(TokenListsProviderSymbol, defaultProviderResponse); +} diff --git a/src/composables/useTokenLists2.ts b/src/composables/useTokenLists2.ts deleted file mode 100644 index 22c68876da..0000000000 --- a/src/composables/useTokenLists2.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { ref, Ref, readonly, computed } from 'vue'; -import TokenListService from '@/services/token-list/token-list.service'; -import { TokenListDict } from '@/types/TokenList'; -import { pick } from 'lodash'; -import TOKEN_LISTS from '@/constants/tokenlists'; -import { lsGet, lsSet } from '@/lib/utils'; -import LS_KEYS from '@/constants/local-storage.keys'; - -// SERVICES -const tokenListService = new TokenListService(); - -// STATE -const tokenLists = ref({}); -const toggled = ref( - lsGet(LS_KEYS.TokenLists.Toggled, [TOKEN_LISTS.Balancer.Default]) -); -const loading = ref(true); -const failed = ref(false); - -// INIT STATE -(async () => { - try { - tokenLists.value = await tokenListService.getAll(); - } catch (error) { - failed.value = true; - console.error('Failed to load tokenlists', error); - } finally { - loading.value = false; - } -})(); - -export default function useTokenLists2() { - // COMPUTED - const defaultTokenList = computed( - () => - pick(tokenLists.value, TOKEN_LISTS.Balancer.Default)[ - TOKEN_LISTS.Balancer.Default - ] - ); - - const balancerTokenLists = computed(() => - pick(tokenLists.value, TOKEN_LISTS.Balancer.All) - ); - - const vettedTokenList = computed( - () => - pick(tokenLists.value, TOKEN_LISTS.Balancer.Vetted)[ - TOKEN_LISTS.Balancer.Vetted - ] - ); - - const approvedTokenLists = computed(() => - pick(tokenLists.value, TOKEN_LISTS.Approved) - ); - - const toggledLists = computed(() => pick(tokenLists.value, toggled.value)); - - // METHODS - function toggleList(uri: string): void { - if (!TOKEN_LISTS.Approved.includes(uri)) return; - - if (toggled.value.includes(uri)) { - toggled.value.splice(toggled.value.indexOf(uri), 1); - } else { - toggled.value.push(uri); - } - - lsSet(LS_KEYS.TokenLists.Toggled, toggled.value); - } - - function isToggled(uri: string): boolean { - return toggled.value.includes(uri); - } - - return { - // state - loading: readonly(loading) as Readonly>, - failed: readonly(failed) as Readonly>, - all: readonly(tokenLists) as Readonly>, - // computed - defaultTokenList, - balancerTokenLists, - approvedTokenLists, - vettedTokenList, - toggledLists, - toggled, - // methods - toggleList, - isToggled - }; -} diff --git a/src/composables/useTokens.ts b/src/composables/useTokens.ts index d8fcf8a073..92412fc223 100644 --- a/src/composables/useTokens.ts +++ b/src/composables/useTokens.ts @@ -1,73 +1,15 @@ -import { Token, TokenMap } from '@/types'; -import { getAddress } from '@ethersproject/address'; -import { keyBy, orderBy, uniqBy } from 'lodash'; -import { computed } from 'vue'; -import { useStore } from 'vuex'; -import useAccountBalances from './useAccountBalances'; -import useTokenStore from './useTokensStore'; - -type TokenRequest = { - query?: string; - queryAddress?: string; -}; - -export default function useTokens(request?: TokenRequest) { - const store = useStore(); - const prices = computed(() => store.state.market.prices); - const { allTokens: _allTokens } = useTokenStore(); - const { balances } = useAccountBalances(); - - const tokensList = computed(() => { - const _tokens = uniqBy( - orderBy( - // populate token data into list of tokens - Object.values(_allTokens.value).map(token => { - const balance = - (balances.value || {})[token.address.toLowerCase()]?.balance || '0'; - const price = prices.value[token.address.toLowerCase()]?.price || 0; - const value = balance * price; - const price24HChange = - prices.value[token.address.toLowerCase()]?.price24HChange || 0; - const value24HChange = (value / 100) * price24HChange; - return { - ...token, - address: getAddress(token.address), // Enforce that we use checksummed addresses - value, - price, - price24HChange, - balance, - value24HChange - }; - }), - ['value', 'balance'], - ['desc', 'desc'] - ), - 'address' - ); - - if (request?.queryAddress) { - const queryAddressLC = request?.queryAddress?.toLowerCase(); - - return _tokens.filter( - token => token.address?.toLowerCase() === queryAddressLC - ); - } - - // search functionality, this can be better - if (request?.query) { - const queryLC = request?.query?.toLowerCase(); - - return _tokens.filter( - token => - token.name.toLowerCase().includes(queryLC) || - token.symbol.toLowerCase().includes(queryLC) - ); - } - - return _tokens; - }); - - const tokens = computed(() => keyBy(tokensList.value, 'address') as TokenMap); - - return { tokens }; +import { inject } from 'vue'; +import { + TokensProviderResponse, + TokensProviderSymbol +} from '@/providers/tokens.provider'; + +const defaultProviderResponse = {} as TokensProviderResponse; + +/** + * useTokens Composable + * Interface to all token static and dynamic metatdata. + */ +export default function useTokens(): TokensProviderResponse { + return inject(TokensProviderSymbol, defaultProviderResponse); } diff --git a/src/composables/useTokens2.ts b/src/composables/useTokens2.ts deleted file mode 100644 index 6ac0ddd7c0..0000000000 --- a/src/composables/useTokens2.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { ref, computed } from 'vue'; -import useTokenLists2 from './useTokenLists2'; -import { getAddress } from '@ethersproject/address'; -import { TokenInfo } from '@/types/TokenList'; -import useConfig from './useConfig'; -import TokenService from '@/services/token/token.service'; -import useTokenPricesQuery from './queries/useTokenPricesQuery'; -import useAccountBalancesQuery from './queries/useAccountBalancesQuery'; - -// TYPES -type TokenDictionary = { [address: string]: TokenInfo }; - -// STATE -const injectedTokens = ref({}); - -// SERVICES -const tokenService = new TokenService(); - -export default function useTokens2() { - // COMPOSABLES - const { networkConfig } = useConfig(); - const { - loading: loadingTokenLists, - failed: tokenListsFailed, - defaultTokenList: tokenList, - all: allTokenLists - } = useTokenLists2(); - - // COMPUTED - /** - * Static metadata - * - * The baseTokens, injectedTokens and allTokens dictionaries - * provide the static metadata for each token. - */ - const baseTokens = computed( - (): TokenDictionary => { - if (loadingTokenLists.value || tokenListsFailed.value) return {}; - - const baseTokens = {}; - - tokenList.value.tokens.forEach(token => { - const address: string = getAddress(token.address); - // Don't include if already included - if (Object.keys(baseTokens).includes(address)) return; - // Don't include if not on app network - if (token.chainId !== networkConfig.chainId) return; - - baseTokens[address] = { - ...token, - address - }; - }); - - return baseTokens; - } - ); - - const ether = computed( - (): TokenDictionary => { - return { - [networkConfig.nativeAsset.address]: { - ...networkConfig.nativeAsset, - chainId: networkConfig.chainId - } - }; - } - ); - - const allTokens = computed( - (): TokenDictionary => { - return { - ...baseTokens.value, - ...injectedTokens.value - }; - } - ); - - const allAddresses = computed(() => Object.keys(allTokens.value)); - - /** - * Dynamic metadata - * - * The prices, balances and allowances dictionaries provide dynamic - * metadata for each token in allTokens. - */ - const pricesQuery = useTokenPricesQuery(allAddresses); - const accountBalancesQuery = useAccountBalancesQuery(allAddresses); - - const prices = computed(() => - pricesQuery.data.value ? pricesQuery.data.value : {} - ); - const balances = computed(() => - accountBalancesQuery.data.value - ? accountBalancesQuery.data.value.balances - : {} - ); - - // METHODS - async function injectTokens(addresses: string[]): Promise { - const tokens = await tokenService.metadata.get( - addresses, - allTokenLists.value - ); - injectedTokens.value = { ...injectedTokens.value, ...tokens }; - } - - return { - // computed - allTokens, - ether, - prices, - balances, - // methods - injectTokens - }; -} diff --git a/src/composables/useTokensStore.ts b/src/composables/useTokensStore.ts deleted file mode 100644 index 52a31690cb..0000000000 --- a/src/composables/useTokensStore.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { computed, ref } from 'vue'; -import { useStore } from 'vuex'; -import { useQuery } from 'vue-query'; -import { flatten, keyBy } from 'lodash'; - -import QUERY_KEYS from '@/constants/queryKeys'; -import TOKEN_LISTS, { ETHER } from '@/constants/tokenlists'; - -import { getTokensListURL, loadTokenlist } from '@/lib/utils/tokenlists'; -import { lsSet } from '@/lib/utils'; - -import { Token } from '@/types'; - -export type TokenListItem = { - address: string; - chainId: number; - decimals: number; - logoURI: string; - name: string; - symbol: string; - balance: string; -}; - -type TokenList = { - keywords: string[]; - logoURI: string; - name: string; - timestamp: string; - tokens: TokenListItem[]; - version: { - major: number; - minor: number; - patch: number; - }; - tokenListsURL: string; -}; - -const loadAllTokenLists = async () => { - // since a request to retrieve the list can fail - // it is best to use allSettled as we still want to - // retrieve what we can - return ( - await Promise.allSettled( - TOKEN_LISTS.Approved.map(async listURI => { - const tokenList = (await loadTokenlist(listURI)) as Omit< - TokenList, - 'tokenListsURL' - >; - - return { - ...tokenList, - tokenListsURL: getTokensListURL(listURI) - }; - }) - ) - ) - .filter(result => result.status === 'fulfilled') - .map(result => (result as PromiseFulfilledResult).value); -}; - -// THE CONTENTS OF THIS WILL BE REPLACED/ALTERED WITH THE REGISTRY REFACTOR - -// This composable retrieves all the tokens from active token lists, -// all the injected tokens from the store and the ETHER token -// for other composables to build upon -export default function useTokenStore() { - const store = useStore(); - const activeTokenLists = ref(['Balancer']); - const queryKey = QUERY_KEYS.TokenLists; - const queryFn = loadAllTokenLists; - - const getEther = () => { - const ether: any = ETHER; - ether.balance = 0; - ether.balanceDenorm = '0'; - ether.price = - store.state.market.prices[ether.address.toLowerCase()]?.price || 0; - ether.price24HChange = - store.state.market.prices[ether.address.toLowerCase()]?.price24HChange || - 0; - ether.chainId = Number(process.env.VUE_APP_NETWORK || 1); - return ether; - }; - - const { data: lists, isLoading, refetch: refreshTokenLists } = useQuery< - TokenList[] - >(queryKey, queryFn, { - refetchOnMount: false, - refetchOnWindowFocus: false - }); - - const listMap = computed(() => keyBy(lists.value, 'name')); - const injectedTokens = computed(() => store.state.registry.injected); - - const allTokens = computed(() => { - // get all the tokens from all the active lists - // get all tokens that are injected - // get the ETHER token ALWAYS - return keyBy( - flatten([ - ...Object.values(injectedTokens.value).map((t: any) => ({ ...t })), - ...activeTokenLists.value.map(name => listMap.value[name]?.tokens), - getEther() - ]) - // invalid network tokens get filtered out - .filter( - token => token?.chainId === Number(process.env.VUE_APP_NETWORK || 1) - ) as Token[], - 'address' - ); - }); - - const toggleActiveTokenList = (name: string) => { - if (activeTokenLists.value.includes(name)) { - activeTokenLists.value = activeTokenLists.value.filter( - listName => listName !== name - ); - } else { - activeTokenLists.value.push(name); - } - lsSet('activeTokenLists', activeTokenLists.value); - }; - - const isActiveList = (name: string) => { - return activeTokenLists.value.includes(name); - }; - - return { - isLoading, - lists, - toggleActiveTokenList, - isActiveList, - refreshTokenLists, - allTokens, - listMap, - activeTokenLists - }; -} diff --git a/src/composables/useTransactions.ts b/src/composables/useTransactions.ts index afc00eac2b..94af0476a7 100644 --- a/src/composables/useTransactions.ts +++ b/src/composables/useTransactions.ts @@ -21,6 +21,8 @@ import useNumbers from './useNumbers'; import { GnosisTransactionDetails } from './trade/useGnosis'; const WEEK_MS = 86_400_000 * 7; +// Please update the schema version when making changes to the transaction structure. +const TRANSACTIONS_SCHEMA_VERSION = '1.1.1'; export type TransactionStatus = | 'pending' @@ -33,6 +35,8 @@ export type TransactionAction = | 'claim' | 'approve' | 'trade' + | 'wrap' + | 'unwrap' | 'invest' | 'withdraw'; @@ -52,8 +56,12 @@ export type TxReceipt = Pick< export type OrderReceipt = OrderMetaData; +export type ReplacementReason = 'txSpeedUp' | 'txCancel'; + export type Transaction = { id: string; + originalId?: string; + replacementReason?: ReplacementReason; action: TransactionAction; type: TransactionType; receipt?: OrderReceipt | TxReceipt; @@ -79,11 +87,9 @@ export type TransactionState = { [networkId: number]: TransactionsMap; }; -const PERSIST_TRANSACTIONS = false; - // TODO: What happens if the structure changes? Either keep a version or schema validator. export const transactionsState = ref( - PERSIST_TRANSACTIONS ? lsGet(LS_KEYS.Transactions, {}) : {} + lsGet(LS_KEYS.Transactions, {}, TRANSACTIONS_SCHEMA_VERSION) ); // COMPUTED @@ -140,16 +146,18 @@ function getTransactions(): TransactionsMap { function setTransactions(transactionsMap: TransactionsMap) { transactionsState.value[networkId] = transactionsMap; - if (PERSIST_TRANSACTIONS) { - lsSet(LS_KEYS.Transactions, transactionsState.value); - } + lsSet( + LS_KEYS.Transactions, + transactionsState.value, + TRANSACTIONS_SCHEMA_VERSION + ); } function getTransaction(id: string, type: TransactionType) { const transactionsMap = getTransactions(); const txId = getId(id, type); - return transactionsMap[txId]; + return transactionsMap[txId] ?? null; } function updateTransaction( @@ -162,7 +170,17 @@ function updateTransaction( const transaction = transactionsMap[txId]; if (transaction != null) { - transactionsMap[txId] = merge(transaction, updates); + // id change requires a replacement of the transaction + if (updates.id != null) { + const newTxId = getId(updates.id, type); + + transactionsMap[newTxId] = merge({}, transaction, updates, { + originalId: id + }); + delete transactionsMap[txId]; + } else { + transactionsMap[txId] = merge({}, transaction, updates); + } setTransactions(transactionsMap); @@ -173,7 +191,10 @@ function updateTransaction( } function isSuccessfulTransaction(transaction: Transaction) { - if (transaction.status === 'confirmed') { + if ( + transaction.status === 'confirmed' && + transaction.replacementReason !== 'txCancel' + ) { if (transaction.type === 'order') { return (transaction.receipt as OrderReceipt)?.status === 'fulfilled'; } else { @@ -286,21 +307,23 @@ export default function useTransactions() { if (receipt != null) { const transaction = getTransaction(id, type); - const updateSuccessful = updateTransaction(id, type, { - receipt: - type === 'tx' - ? normalizeTxReceipt(receipt as TransactionReceipt) - : receipt, - summary: - type === 'order' - ? getSettledOrderSummary(transaction, receipt as OrderReceipt) - : transaction.summary, - status: 'confirmed', - confirmedTime: Date.now() - }); - if (updateSuccessful) { - addNotificationForTransaction(id, type); - return true; + if (transaction != null) { + const updateSuccessful = updateTransaction(id, type, { + receipt: + type === 'tx' + ? normalizeTxReceipt(receipt as TransactionReceipt) + : receipt, + summary: + type === 'order' + ? getSettledOrderSummary(transaction, receipt as OrderReceipt) + : transaction.summary, + status: 'confirmed', + confirmedTime: Date.now() + }); + if (updateSuccessful) { + addNotificationForTransaction(id, type); + return true; + } } } @@ -310,18 +333,25 @@ export default function useTransactions() { function addNotificationForTransaction(id: string, type: TransactionType) { const transaction = getTransaction(id, type); - addNotification({ - title: `${t(`transactionAction.${transaction.action}`)} ${t( - `transactionStatus.${transaction.status}` - )}`, - message: transaction.summary, - transactionMetadata: { - id: transaction.id, - status: transaction.status, - isSuccess: isSuccessfulTransaction(transaction), - explorerLink: getExplorerLink(transaction.id, transaction.type) - } - }); + if (transaction != null) { + const transactionStatus: TransactionStatus = + transaction.replacementReason === 'txCancel' + ? 'cancelled' + : transaction.status; + + addNotification({ + title: `${t(`transactionAction.${transaction.action}`)} ${t( + `transactionStatus.${transactionStatus}` + )}`, + message: transaction.summary, + transactionMetadata: { + id: transaction.id, + status: transaction.status, + isSuccess: isSuccessfulTransaction(transaction), + explorerLink: getExplorerLink(transaction.id, transaction.type) + } + }); + } } function checkOrderActivity(transaction: Transaction) { @@ -396,6 +426,7 @@ export default function useTransactions() { finalizeTransaction, getExplorerLink, isSuccessfulTransaction, + updateTransaction, // computed pendingTransactions, diff --git a/src/composables/useUrls.ts b/src/composables/useUrls.ts new file mode 100644 index 0000000000..c34bc51593 --- /dev/null +++ b/src/composables/useUrls.ts @@ -0,0 +1,12 @@ +import { configService } from '@/services/config/config.service'; + +function resolve(uri: string): string { + if (!uri) return ''; + return uri + .replace('ipfs://', `https://${configService.env.IPFS_NODE}/ipfs/`) + .replace('ipns://', `https://${configService.env.IPFS_NODE}/ipns/`); +} + +export default function useUrls() { + return { resolve }; +} diff --git a/src/composables/useUserSettings.ts b/src/composables/useUserSettings.ts new file mode 100644 index 0000000000..1634819f3c --- /dev/null +++ b/src/composables/useUserSettings.ts @@ -0,0 +1,11 @@ +import { inject } from 'vue'; +import { + UserSettingsProviderResponse, + UserSettingsProviderSymbol +} from '@/providers/user-settings.provider'; + +const defaultProviderResponse = {} as UserSettingsProviderResponse; + +export default function useUserSettings(): UserSettingsProviderResponse { + return inject(UserSettingsProviderSymbol, defaultProviderResponse); +} diff --git a/src/composables/useWeb3Watchers.ts b/src/composables/useWeb3Watchers.ts index b456de06ef..0258144294 100644 --- a/src/composables/useWeb3Watchers.ts +++ b/src/composables/useWeb3Watchers.ts @@ -1,12 +1,12 @@ import useWeb3 from '@/services/web3/useWeb3'; +import { EthereumTransactionData } from 'bnc-sdk/dist/types/src/interfaces'; import { watch } from 'vue'; import { useI18n } from 'vue-i18n'; import { useStore } from 'vuex'; -import useAccountBalances from './useAccountBalances'; -import useAllowances from './useAllowances'; import useBlocknative from './useBlocknative'; -import useTransactions from './useTransactions'; +import useTokens from './useTokens'; +import useTransactions, { ReplacementReason } from './useTransactions'; export default function useWeb3Watchers() { // COMPOSABLES @@ -21,9 +21,23 @@ export default function useWeb3Watchers() { isUnsupportedNetwork, blockNumber } = useWeb3(); - const { refetchAllowances } = useAllowances(); - const { refetchBalances } = useAccountBalances(); - const { handlePendingTransactions } = useTransactions(); + const { refetchBalances, refetchAllowances } = useTokens(); + const { handlePendingTransactions, updateTransaction } = useTransactions(); + + function handleTransactionReplacement( + tx: EthereumTransactionData, + replacementReason: ReplacementReason + ) { + const originalHash = tx.replaceHash; + + if (originalHash != null) { + updateTransaction(originalHash, 'tx', { + // new id + id: tx.hash, + replacementReason + }); + } + } // Watch for user account change: // -> Unsubscribe Blocknative from old account if exits @@ -40,6 +54,20 @@ export default function useWeb3Watchers() { refetchBalances.value(); refetchAllowances.value(); }); + + emitter.on('txSpeedUp', tx => + handleTransactionReplacement( + tx as EthereumTransactionData, + 'txSpeedUp' + ) + ); + + emitter.on('txCancel', tx => + handleTransactionReplacement( + tx as EthereumTransactionData, + 'txCancel' + ) + ); } } ); diff --git a/src/constants/currency.ts b/src/constants/currency.ts index 18f144da4d..52e240ebe7 100644 --- a/src/constants/currency.ts +++ b/src/constants/currency.ts @@ -1,3 +1,9 @@ export enum FiatCurrency { - USD = 'USD' + usd = 'usd' } + +export enum FiatSymbol { + usd = '$' +} + +export const SUPPORTED_FIAT = [FiatCurrency.usd]; diff --git a/src/constants/local-storage.keys.ts b/src/constants/local-storage.keys.ts index 98c49e132f..e734b1bbab 100644 --- a/src/constants/local-storage.keys.ts +++ b/src/constants/local-storage.keys.ts @@ -2,6 +2,9 @@ export default { App: { DarkMode: 'app.darkMode' }, + UserSettings: { + Currency: 'userSettings.currency' + }, TokenLists: { Toggled: 'tokenLists.toggled' }, diff --git a/src/constants/pools.ts b/src/constants/pools.ts index 2c9e29f7ea..0644af92af 100644 --- a/src/constants/pools.ts +++ b/src/constants/pools.ts @@ -49,7 +49,8 @@ export const POOLS = { '0xfeadd389a5c427952d8fdb8057d6c8ba1156cc56000000000000000000000066', '0x9f19a375709baf0e8e35c2c5c65aca676c4c719100000000000000000000006e', '0x06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000012', // polygon - '0xfeadd389a5c427952d8fdb8057d6c8ba1156cc5600020000000000000000001e' // polgon + '0xfeadd389a5c427952d8fdb8057d6c8ba1156cc5600020000000000000000001e', // polgon + '0x6b15a01b5d46a5321b627bd7deef1af57bc629070000000000000000000000d4' // kovan ] }, Factories: { diff --git a/src/constants/queryKeys.ts b/src/constants/queryKeys.ts index 9aa8958b1c..db441b8c0d 100644 --- a/src/constants/queryKeys.ts +++ b/src/constants/queryKeys.ts @@ -1,14 +1,16 @@ import { Config } from '@/lib/config'; -import { TokenMap } from '@/types'; import { Ref } from 'vue'; - export const POOLS_ROOT_KEY = 'pools'; export const BALANCES_ROOT_KEY = 'accountBalances'; export const CLAIMS_ROOT_KEY = 'claims'; const QUERY_KEYS = { Pools: { - All: (tokens: Ref) => [POOLS_ROOT_KEY, 'all', { tokens }], + All: (tokens: Ref, poolIds: Ref | undefined) => [ + POOLS_ROOT_KEY, + 'all', + { tokens, poolIds } + ], User: (account: Ref) => [POOLS_ROOT_KEY, 'user', { account }], Current: (id: string) => [POOLS_ROOT_KEY, 'current', { id }], Snapshot: (id: string) => [POOLS_ROOT_KEY, 'snapshot', { id }], @@ -20,14 +22,9 @@ const QUERY_KEYS = { { account, id } ] }, - Balances: { - All: ( - account: Ref, - userNetwork: Ref, - tokens?: Ref - ) => [BALANCES_ROOT_KEY, { userNetwork, account, tokens }] + TokenLists: { + All: ['tokenLists', 'all'] }, - TokenLists: ['tokenLists'], Claims: { All: (account: Ref) => [CLAIMS_ROOT_KEY, { account }] }, @@ -35,17 +32,16 @@ const QUERY_KEYS = { Prices: (tokens: Ref) => ['tokens', 'prices', { tokens }] }, Account: { - Balances: ( - account: Ref, - networkKey: Ref, - tokens: Ref - ) => ['account', 'balances', { account, networkKey, tokens }], + Balances: (account: Ref, tokens: Ref) => [ + 'account', + 'balances', + { account, tokens } + ], Allowances: ( - userNetwork: Ref, account: Ref, - dstList: Ref, + contractAddresses: Ref, tokens: Ref - ) => ['account', 'allowances', { userNetwork, account, dstList, tokens }], + ) => ['account', 'allowances', { account, contractAddresses, tokens }], Profile: (account: Ref, userNetwork: Ref) => [ 'account', 'profile', diff --git a/src/constants/symbol.keys.ts b/src/constants/symbol.keys.ts new file mode 100644 index 0000000000..8cab5d90be --- /dev/null +++ b/src/constants/symbol.keys.ts @@ -0,0 +1,7 @@ +export default { + Providers: { + App: 'providers.app', + TokenLists: 'providers.tokenLists', + Tokens: 'providers.tokens' + } +}; diff --git a/src/constants/tokenlists.ts b/src/constants/tokenlists.ts index 307f70fa8a..a8eb62bfd5 100644 --- a/src/constants/tokenlists.ts +++ b/src/constants/tokenlists.ts @@ -1,21 +1,19 @@ -import useConfig from '@/composables/useConfig'; - -const { env, networkConfig } = useConfig(); - -// TODO replace imports of ETHER throughout the app so we can remove it from here. -// It doesn't make sense to have this ETHER definition here, this file should be for tokenlist URIs only. -export const ETHER = networkConfig.nativeAsset; - -type TokenListConfig = { +export interface TokenListMap { Balancer: { Default: string; Vetted: string; }; External: string[]; -}; +} -// Mapping of the TokenLists used on each network -const TOKEN_LISTS_MAP: Record = { +interface TokenListMapByNetwork { + [networkKey: string]: TokenListMap; +} + +/** + * Mapping of the TokenLists used on each network + */ +export const TOKEN_LIST_MAP: TokenListMapByNetwork = { '1': { Balancer: { Default: @@ -28,7 +26,7 @@ const TOKEN_LISTS_MAP: Record = { 'tokenlist.zerion.eth', 'tokens.1inch.eth', 'tokenlist.aave.eth', - 'https://tokens.coingecko.com/uniswap/all.json', + // 'https://tokens.coingecko.com/uniswap/all.json', Breaks balance/allowance fetching 'https://umaproject.org/uma.tokenlist.json' ] }, @@ -44,7 +42,7 @@ const TOKEN_LISTS_MAP: Record = { 'tokenlist.zerion.eth', 'tokens.1inch.eth', 'tokenlist.aave.eth', - 'https://tokens.coingecko.com/uniswap/all.json', + // 'https://tokens.coingecko.com/uniswap/all.json', 'https://umaproject.org/uma.tokenlist.json' ] }, @@ -60,41 +58,3 @@ const TOKEN_LISTS_MAP: Record = { ] } }; - -type TokenLists = { - All: string[]; - Balancer: { - All: string[]; - // Compliant list for exchange - Default: string; - // Extended list to include LBP tokens - Vetted: string; - }; - Approved: string[]; - External: string[]; -}; - -/** - * Convert TokenList config into a more usable structure - */ -const processTokenLists = (config: TokenListConfig): TokenLists => { - const { Balancer, External } = config; - - const balancerLists = [Balancer.Default, Balancer.Vetted]; - const All = [...balancerLists, ...External]; - const Approved = [Balancer.Default, ...External]; - - return { - All, - Balancer: { - All: balancerLists, - ...Balancer - }, - Approved, - External - }; -}; - -const TOKEN_LISTS = processTokenLists(TOKEN_LISTS_MAP[env.NETWORK]); - -export default TOKEN_LISTS; diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index ca763aa98c..cf5116e607 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -1,3 +1,5 @@ +import { configService } from '@/services/config/config.service'; + export const TOKENS = { Popular: { Symbols: ['WBTC', 'DAI', 'USDC', 'BAL', 'AAVE', 'WETH'] @@ -29,4 +31,5 @@ export const TOKENS = { } }; +export const NATIVE_ASSET_ADDRESS = configService.network.nativeAsset.address; export const DEFAULT_TOKEN_DECIMALS = 18; diff --git a/src/constants/vue-query.ts b/src/constants/vue-query.ts new file mode 100644 index 0000000000..38ee82f2e3 --- /dev/null +++ b/src/constants/vue-query.ts @@ -0,0 +1,8 @@ +import { twentyFourHoursInMs } from '@/composables/useTime'; + +export const FETCH_ONCE_OPTIONS = { + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + staleTime: twentyFourHoursInMs +}; diff --git a/src/lib/utils/balancer/contentHash.ts b/src/lib/utils/balancer/contentHash.ts deleted file mode 100644 index 8939ee9df7..0000000000 --- a/src/lib/utils/balancer/contentHash.ts +++ /dev/null @@ -1,188 +0,0 @@ -// https://github.com/ensdomains/ui/blob/master/src/utils/contents.js -// https://github.com/Uniswap/uniswap-interface/blob/master/src/utils/resolveENSContentHash.ts -import contentHash from '@ensdomains/content-hash'; -import { Provider } from '@ethersproject/abstract-provider'; -import { namehash } from '@ethersproject/hash'; -import { isHexString } from '@ethersproject/bytes'; -import bs58 from 'bs58'; -import { call } from '@/lib/utils/balancer/contract'; -const supportedCodecs = ['ipns-ns', 'ipfs-ns', 'swarm-ns', 'onion', 'onion3']; - -const REGISTRAR_ABI = [ - { - constant: true, - inputs: [ - { - name: 'node', - type: 'bytes32' - } - ], - name: 'resolver', - outputs: [ - { - name: 'resolverAddress', - type: 'address' - } - ], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -const REGISTRAR_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; - -const RESOLVER_ABI = [ - { - constant: true, - inputs: [ - { - internalType: 'bytes32', - name: 'node', - type: 'bytes32' - } - ], - name: 'contenthash', - outputs: [ - { - internalType: 'bytes', - name: '', - type: 'bytes' - } - ], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -export function decodeContenthash(encoded) { - let decoded, protocolType, error; - if (encoded.error) { - return { protocolType: null, decoded: encoded.error }; - } - if (encoded) { - try { - decoded = contentHash.decode(encoded); - const codec = contentHash.getCodec(encoded); - if (codec === 'ipfs-ns') { - // convert the ipfs from base58 to base32 (url host compatible) - // if needed the hash can now be resolved through a secured origin gateway (.gateway.com) - decoded = contentHash.helpers.cidV0ToV1Base32(decoded); - - protocolType = 'ipfs'; - } else if (codec === 'ipns-ns') { - decoded = bs58 - .decode(decoded) - .slice(2) - .toString(); - protocolType = 'ipns'; - } else if (codec === 'swarm-ns') { - protocolType = 'bzz'; - } else if (codec === 'onion') { - protocolType = 'onion'; - } else if (codec === 'onion3') { - protocolType = 'onion3'; - } else { - decoded = encoded; - } - } catch (e) { - error = e.message; - } - } - return { protocolType, decoded, error }; -} - -export function validateContent(encoded) { - return ( - contentHash.isHashOfType(encoded, contentHash.Types.ipfs) || - contentHash.isHashOfType(encoded, contentHash.Types.swarm) - ); -} - -export function isValidContenthash(encoded) { - try { - const codec = contentHash.getCodec(encoded); - return isHexString(encoded) && supportedCodecs.includes(codec); - } catch (e) { - console.log(e); - } -} - -export function encodeContenthash(text) { - let content, contentType; - let encoded: any = false; - if (text) { - const matched = - text.match(/^(ipfs|ipns|bzz|onion|onion3):\/\/(.*)/) || - text.match(/\/(ipfs)\/(.*)/) || - text.match(/\/(ipns)\/(.*)/); - if (matched) { - contentType = matched[1]; - content = matched[2]; - } - try { - if (contentType === 'ipfs') { - if (content.length >= 4) { - encoded = '0x' + contentHash.encode('ipfs-ns', content); - } - } else if (contentType === 'ipns') { - const bs58content = bs58.encode( - Buffer.concat([ - Buffer.from([0, content.length]), - Buffer.from(content) - ]) - ); - encoded = '0x' + contentHash.encode('ipns-ns', bs58content); - } else if (contentType === 'bzz') { - if (content.length >= 4) { - encoded = '0x' + contentHash.fromSwarm(content); - } - } else if (contentType === 'onion') { - if (content.length == 16) { - encoded = '0x' + contentHash.encode('onion', content); - } - } else if (contentType === 'onion3') { - if (content.length == 56) { - encoded = '0x' + contentHash.encode('onion3', content); - } - } else { - console.warn('Unsupported protocol or invalid value', { - contentType, - text - }); - } - } catch (err) { - console.warn('Error encoding content hash', { text, encoded }); - //throw 'Error encoding content hash' - } - } - return encoded; -} - -/** - * Fetches and decodes the result of an ENS contenthash lookup on mainnet to a URI - * @param ensName to resolve - * @param provider provider to use to fetch the data - */ -export async function resolveENSContentHash( - ensName: string, - provider: Provider -): Promise { - const hash = namehash(ensName); - const resolverAddress = await call(provider, REGISTRAR_ABI, [ - REGISTRAR_ADDRESS, - 'resolver', - [hash] - ]); - return await call(provider, RESOLVER_ABI, [ - resolverAddress, - 'contenthash', - [hash] - ]); -} - -export async function resolveContent(provider, name) { - const contentHash = await resolveENSContentHash(name, provider); - return decodeContenthash(contentHash); -} diff --git a/src/lib/utils/balancer/helpers/sor/sorManager.ts b/src/lib/utils/balancer/helpers/sor/sorManager.ts index 329703c2ed..b7aec0987f 100644 --- a/src/lib/utils/balancer/helpers/sor/sorManager.ts +++ b/src/lib/utils/balancer/helpers/sor/sorManager.ts @@ -15,10 +15,9 @@ import { AddressZero } from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; import { scale } from '@/lib/utils'; import { Swap, Pool } from '@balancer-labs/sor/dist/types'; -import { ETHER } from '@/constants/tokenlists'; +import { NATIVE_ASSET_ADDRESS } from '@/constants/tokens'; const SWAP_COST = process.env.VUE_APP_SWAP_COST || '100000'; - export enum LiquiditySelection { Best = 'best', V1 = 'v1', @@ -108,7 +107,7 @@ export class SorManager { tokenDecimals: number, manualCost: BigNumber | null = null ): Promise { - tokenAddr = tokenAddr === ETHER.address ? this.weth : tokenAddr; + tokenAddr = tokenAddr === NATIVE_ASSET_ADDRESS ? this.weth : tokenAddr; let cost = this.sorV2.tokenCost[tokenAddr.toLowerCase()]; if (cost) { @@ -207,8 +206,8 @@ export class SorManager { // V2 uses normalised values. V1 uses scaled values. const amountNormalised = scale(amountScaled, -swapDecimals); - const v1TokenIn = tokenIn === ETHER.address ? this.weth : tokenIn; - const v1TokenOut = tokenOut === ETHER.address ? this.weth : tokenOut; + const v1TokenIn = tokenIn === NATIVE_ASSET_ADDRESS ? this.weth : tokenIn; + const v1TokenOut = tokenOut === NATIVE_ASSET_ADDRESS ? this.weth : tokenOut; let swapsV1: Swap[][] = []; let returnAmountV1 = new BigNumber(0); @@ -228,8 +227,9 @@ export class SorManager { amountScaled ); - const v2TokenIn = tokenIn === ETHER.address ? AddressZero : tokenIn; - const v2TokenOut = tokenOut === ETHER.address ? AddressZero : tokenOut; + const v2TokenIn = tokenIn === NATIVE_ASSET_ADDRESS ? AddressZero : tokenIn; + const v2TokenOut = + tokenOut === NATIVE_ASSET_ADDRESS ? AddressZero : tokenOut; const swapTypeV2: SwapTypes = swapType === 'swapExactIn' diff --git a/src/lib/utils/balancer/ipfs.ts b/src/lib/utils/balancer/ipfs.ts deleted file mode 100644 index 1920783603..0000000000 --- a/src/lib/utils/balancer/ipfs.ts +++ /dev/null @@ -1,8 +0,0 @@ -export async function ipfsGet( - gateway: string, - ipfsHash: string, - protocolType = 'ipfs' -) { - const url = `https://${gateway}/${protocolType}/${ipfsHash}`; - return fetch(url).then(res => res.json()); -} diff --git a/src/lib/utils/balancer/price.ts b/src/lib/utils/balancer/price.ts index 162a722dee..68e39a7958 100644 --- a/src/lib/utils/balancer/price.ts +++ b/src/lib/utils/balancer/price.ts @@ -1,8 +1,14 @@ +import { isStable, isWeighted } from '@/composables/usePool'; +import { FiatCurrency } from '@/constants/currency'; import { Pool } from '@/services/balancer/subgraph/types'; -import { Prices } from '@/services/coingecko'; +import { TokenPrices } from '@/services/coingecko/api/price.service'; -export function getPoolLiquidity(pool: Pool, prices: Prices) { - if (pool.poolType == 'Weighted') { +export function getPoolLiquidity( + pool: Pool, + prices: TokenPrices, + currency: FiatCurrency +) { + if (isWeighted(pool)) { const totalWeight = pool.tokens.reduce( (total, token) => total + parseFloat(token.weight), 0 @@ -15,7 +21,7 @@ export function getPoolLiquidity(pool: Pool, prices: Prices) { if (!prices[token.address]) { continue; } - const price = prices[token.address].price; + const price = prices[token.address][currency]; const balance = parseFloat(pool.tokens[i].balance); const value = balance * price; @@ -31,7 +37,7 @@ export function getPoolLiquidity(pool: Pool, prices: Prices) { } } // TODO [improvement]: if price is missing, compute spot price based on balances and amp factor - if (pool.poolType == 'Stable') { + if (isStable(pool)) { let sumBalance = 0; let sumValue = 0; @@ -42,7 +48,7 @@ export function getPoolLiquidity(pool: Pool, prices: Prices) { if (!prices[token.address]) { continue; } - const price = prices[token.address].price; + const price = prices[token.address][currency]; const balance = parseFloat(pool.tokens[i].balance); const value = balance * price; diff --git a/src/lib/utils/balancer/swapper.ts b/src/lib/utils/balancer/swapper.ts index efd6dd1f21..6079e2b89c 100644 --- a/src/lib/utils/balancer/swapper.ts +++ b/src/lib/utils/balancer/swapper.ts @@ -7,8 +7,8 @@ import { sendTransaction } from '@/lib/utils/balancer/web3'; import exchangeProxyAbi from '@/lib/abi/ExchangeProxy.json'; import vaultAbi from '@/lib/abi/Vault.json'; import configs from '@/lib/config'; -import { ETHER } from '@/constants/tokenlists'; import { SorReturn } from '@/lib/utils/balancer/helpers/sor/sorManager'; +import { NATIVE_ASSET_ADDRESS } from '@/constants/tokens'; const SWAP_KIND_IN = 0; const SWAP_KIND_OUT = 1; @@ -102,7 +102,7 @@ export async function batchSwapGivenInV1( console.log('[Swapper] batchSwapGivenInV1'); const overrides: any = {}; - if (tokenIn === ETHER.address) { + if (tokenIn === NATIVE_ASSET_ADDRESS) { overrides.value = `0x${tokenInAmount.toString(16)}`; } @@ -138,7 +138,7 @@ export async function batchSwapGivenOutV1( console.log('[Swapper] batchSwapGivenOutV1'); const overrides: any = {}; - if (tokenIn === ETHER.address) { + if (tokenIn === NATIVE_ASSET_ADDRESS) { overrides.value = `0x${tokenInAmountMax.toString(16)}`; } diff --git a/src/lib/utils/balancer/tokens.ts b/src/lib/utils/balancer/tokens.ts index 2d7ec86213..57ceed6aae 100644 --- a/src/lib/utils/balancer/tokens.ts +++ b/src/lib/utils/balancer/tokens.ts @@ -1,119 +1,8 @@ import { Web3Provider, TransactionResponse } from '@ethersproject/providers'; -import { BigNumber } from '@ethersproject/bignumber'; import { MaxUint256 } from '@ethersproject/constants'; -import { multicall, Multicaller } from '@/lib/utils/balancer/contract'; import { sendTransaction } from '@/lib/utils/balancer/web3'; -import { TokenList, TokenInfo } from '@/types/TokenList'; -import { flatten, set } from 'lodash'; -import getProvider from '@/lib/utils/provider'; -import { APP } from '@/constants/app'; import { default as abi } from '@/lib/abi/ERC20.json'; -export async function getBalances( - network: string, - provider: any, - account: string, - tokens: string[] -): Promise> { - try { - const balances: [BigNumber][] = await multicall( - network, - provider, - abi, - tokens.map(token => [token, 'balanceOf', [account]]) - ); - return Object.fromEntries( - tokens.map((token, i) => [token.toLowerCase(), balances[i][0]]) - ); - } catch (e) { - console.log(e); - } - return {}; -} - -export async function getAllowances( - network: string, - provider: any, - src: string, - dst: string, - tokens: string[] -): Promise> { - try { - const allowances: [BigNumber][] = await multicall( - network, - provider, - abi, - tokens.map(token => [token, 'allowance', [src, dst]]) - ); - return Object.fromEntries( - tokens.map((token, i) => [token.toLowerCase(), allowances[i][0]]) - ); - } catch (e) { - console.log(e); - } - return {}; -} - -export async function getOnchainTokensMeta( - network: string, - provider: any, - tokens: string[] -): Promise> { - try { - const multi = new Multicaller(network, provider, abi); - const tokensMetadata = {}; - tokens.forEach(token => { - set(tokensMetadata, `${token}.address`, token); - set(tokensMetadata, `${token}.chainId`, parseInt(network)); - set( - tokensMetadata, - `${token}.logoURI`, - `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${token}/logo.png` - ); - multi.call(`${token}.name`, token, 'name'); - multi.call(`${token}.symbol`, token, 'symbol'); - multi.call(`${token}.decimals`, token, 'decimals'); - }); - return await multi.execute(tokensMetadata); - } catch (e) { - console.log(e); - } - return {}; -} - -// Try to find tokens metadata in tokenlists first then check onchain if that fails. -export async function getTokensMeta( - tokenAddresses: string[], - tokenLists: TokenList[] -): Promise> { - const allTokensLists = Object.values(tokenLists) - .map((list: any) => list.tokens) - .filter(Boolean); - const allTokens = flatten(allTokensLists); - - const meta = {}; - tokenAddresses.forEach(async address => { - const tokenMeta = allTokens.find(token => token.address == address); - meta[address] = tokenMeta; - }); - const unknownAddresses = Object.keys(meta).filter(address => !meta[address]); - - try { - const onchainMeta = await getOnchainTokensMeta( - APP.Network, - getProvider(APP.Network), - unknownAddresses - ); - Object.keys(onchainMeta).forEach(address => { - meta[address] = onchainMeta[address]; - }); - } catch (error) { - console.error(error); - } - - return meta; -} - export async function approveTokens( web3: Web3Provider, spender: string, diff --git a/src/lib/utils/balancer/types.ts b/src/lib/utils/balancer/types.ts deleted file mode 100644 index 4753ce78f4..0000000000 --- a/src/lib/utils/balancer/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; - -export interface Pool { - address: string; - id: string; - strategy: Strategy; - tokenBalances: BigNumber[]; - poolTokens: any; - tokens: string[]; - totalSupply: BigNumber; - weights: BigNumber[]; - weightsPercent: number[]; - shares?: number; -} - -export interface Strategy { - name: string; - swapFee: BigNumber; - swapFeePercent: number; - type: number; -} diff --git a/src/lib/utils/balancer/web3.ts b/src/lib/utils/balancer/web3.ts index f666b9cfba..afa28e0a13 100644 --- a/src/lib/utils/balancer/web3.ts +++ b/src/lib/utils/balancer/web3.ts @@ -52,9 +52,8 @@ export async function sendTransaction( return await contractWithSigner[action](...params, overrides); } catch (e) { if (e.code === ErrorCode.UNPREDICTABLE_GAS_LIMIT && ENV !== 'development') { - const network = (await web3.getNetwork()).name; const sender = await web3.getSigner().getAddress(); - logFailedTx(network, sender, contract, action, params, overrides); + logFailedTx(sender, contract, action, params, overrides); } return Promise.reject(e); } diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 8a315cfcca..5443234370 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1,23 +1,11 @@ import BigNumber from 'bignumber.js'; import pkg from '@/../package.json'; +import { Ref } from 'vue'; export function shorten(str = '') { return `${str.slice(0, 6)}...${str.slice(str.length - 4)}`; } -export function jsonParse(input, fallback?) { - if (typeof input !== 'string') { - if (fallback === null) return null; - return fallback || {}; - } - try { - return JSON.parse(input); - } catch (err) { - if (fallback === null) return null; - return fallback || {}; - } -} - export async function sleep(time) { return new Promise(resolve => { setTimeout(resolve, time); @@ -28,17 +16,48 @@ export function clone(item) { return JSON.parse(JSON.stringify(item)); } -export function lsSet(key: string, value: any) { - return localStorage.setItem(`${pkg.name}.${key}`, JSON.stringify(value)); +function lsAddVersion(value: any, version: string) { + return { + data: value, + _version: version + }; } -export function lsGet(key: string, fallback?: any): T { - const item = localStorage.getItem(`${pkg.name}.${key}`); - return jsonParse(item, fallback); +function lsGetKey(key: string) { + return `${pkg.name}.${key}`; +} + +export function lsSet(key: string, value: any, version?: string) { + const data = version != null ? lsAddVersion(value, version) : value; + + return localStorage.setItem(lsGetKey(key), JSON.stringify(data)); +} + +export function lsGet( + key: string, + defaultValue: any = null, + version?: string +): T { + const rawValue = localStorage.getItem(lsGetKey(key)); + + if (rawValue != null) { + try { + const value = JSON.parse(rawValue); + + if (version != null) { + return value._version === version ? value.data : defaultValue; + } + return value; + } catch (e) { + return defaultValue; + } + } + + return defaultValue; } export function lsRemove(key: string) { - return localStorage.removeItem(`${pkg.name}.${key}`); + return localStorage.removeItem(lsGetKey(key)); } export function getCurrentTs() { @@ -68,3 +87,18 @@ export function shortenLabel(str, segLength = 4) { const lastSegment = str.substring(str.length, str.length - segLength); return `${firstSegment}...${lastSegment}`; } + +/** + * Wait for a reactive variable to change to an expected value. + */ +export async function forChange( + reactiveVar: Ref, + expected: any, + checkCount = 0, + checkDelay = 500, + checkLimit = 20 +): Promise { + if (reactiveVar.value === expected || checkCount >= checkLimit) return; + await sleep(checkDelay); + forChange(reactiveVar, expected, checkCount++); +} diff --git a/src/lib/utils/liquidityMining/index.ts b/src/lib/utils/liquidityMining/index.ts index c2781e34c7..6524cc8225 100644 --- a/src/lib/utils/liquidityMining/index.ts +++ b/src/lib/utils/liquidityMining/index.ts @@ -1,15 +1,12 @@ import { differenceInWeeks } from 'date-fns'; - import { bnum } from '@/lib/utils'; import { toUtcTime } from '@/lib/utils/date'; - import { NetworkId } from '@/constants/network'; - -import ConfigService from '@/services/config/config.service'; - -import { Prices } from '@/services/coingecko'; - +import { configService } from '@/services/config/config.service'; import MultiTokenLiquidityMining from './MultiTokenLiquidityMining.json'; +import { TokenPrices } from '@/services/coingecko/api/price.service'; +import { getAddress } from '@ethersproject/address'; +import { FiatCurrency } from '@/constants/currency'; type PoolId = string; @@ -54,7 +51,8 @@ export function computeAPRForPool( export function computeTotalAPRForPool( tokenRewards: LiquidityMiningTokenRewards[], - prices: Prices, + prices: TokenPrices, + currency: FiatCurrency, totalLiquidity: string ) { return tokenRewards @@ -63,7 +61,7 @@ export function computeTotalAPRForPool( totalRewards.plus( computeAPRForPool( amount, - prices[tokenAddress.toLowerCase()]?.price, + prices[getAddress(tokenAddress)][currency], totalLiquidity ) ), @@ -75,8 +73,6 @@ export function computeTotalAPRForPool( export function getLiquidityMiningRewards( week: number | 'current' = 'current' ) { - const configService: ConfigService = new ConfigService(); - const miningWeek = week === 'current' ? getCurrentLiquidityMiningWeek() : week; @@ -99,3 +95,10 @@ export function getLiquidityMiningRewards( } export const currentLiquidityMiningRewards = getLiquidityMiningRewards(); +let tokenAddresses = Object.values(currentLiquidityMiningRewards) + .flat() + .map(reward => reward.tokenAddress); +tokenAddresses = [...new Set(tokenAddresses)].map(address => + getAddress(address) +); +export const currentLiquidityMiningRewardTokens = tokenAddresses; diff --git a/src/lib/utils/logging.ts b/src/lib/utils/logging.ts index 4be9cefc0d..fd4db22109 100644 --- a/src/lib/utils/logging.ts +++ b/src/lib/utils/logging.ts @@ -1,11 +1,9 @@ +import { rpcProviderService } from '@/services/rpc-provider/rpc-provider.service'; import { Contract } from '@ethersproject/contracts'; import { Wallet } from '@ethersproject/wallet'; import { captureException } from '@sentry/browser'; -import { getLoggingProvider } from '@/lib/utils/provider'; - export function logFailedTx( - network: string, sender: string, contract: Contract, action: string, @@ -21,7 +19,7 @@ export function logFailedTx( overrides.gasPrice = sender; const dummyPrivateKey = '0x651bd555534625dc2fd85e13369dc61547b2e3f2cfc8b98cee868b449c17a4d6'; - const provider = getLoggingProvider(network); + const provider = rpcProviderService.alchemyProvider; const dummyWallet = new Wallet(dummyPrivateKey).connect(provider); const loggingContract = contract.connect(dummyWallet); loggingContract[action](...params, overrides); diff --git a/src/lib/utils/promise.ts b/src/lib/utils/promise.ts new file mode 100644 index 0000000000..c029cce3ed --- /dev/null +++ b/src/lib/utils/promise.ts @@ -0,0 +1,19 @@ +import { sleep } from '.'; + +export async function retryPromiseWithDelay( + promise: Promise, + retryCount: number, + delayTime: number +) { + try { + return await promise; + } catch (e) { + if (retryCount === 1) { + return Promise.reject(e); + } + console.log('retrying promise', retryCount, 'time'); + // wait for delayTime amount of time before calling this method again + await sleep(delayTime); + return retryPromiseWithDelay(promise, retryCount - 1, delayTime); + } +} diff --git a/src/lib/utils/provider.ts b/src/lib/utils/provider.ts deleted file mode 100644 index 1acd23c24f..0000000000 --- a/src/lib/utils/provider.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { JsonRpcProvider, AlchemyProvider } from '@ethersproject/providers'; -import configs from '@/lib/config'; - -const providers = {}; - -export default function getProvider(network: string) { - const url: string = configs[network]?.rpc; - if (!providers[network]) providers[network] = new JsonRpcProvider(url); - return providers[network]; -} - -export function getLoggingProvider(network: string) { - const alchemyKey = 'cQGZUiTLRCFsQS7kbRxPJK4eH4fTTu88'; - return new AlchemyProvider(network, alchemyKey); -} diff --git a/src/lib/utils/tokenlists.ts b/src/lib/utils/tokenlists.ts deleted file mode 100644 index 61a64b9fa7..0000000000 --- a/src/lib/utils/tokenlists.ts +++ /dev/null @@ -1,28 +0,0 @@ -import getProvider from '@/lib/utils/provider'; -import { ipfsGet } from '@/lib/utils/balancer/ipfs'; -import { resolveContent } from '@/lib/utils/balancer/contentHash'; - -const gateway = process.env.VUE_APP_IPFS_NODE || 'ipfs.io'; - -export async function loadTokenlist(uri: string) { - if (uri.endsWith('.eth')) { - const { protocolType, decoded } = await resolveContent( - getProvider('1'), - uri - ); - return await loadTokenlist(`${protocolType}://${decoded}`); - } - const [protocolType, path] = uri.split('://'); - if (protocolType.includes('http')) { - return fetch(uri).then(res => res.json()); - } - return await ipfsGet(gateway, path, protocolType); -} - -export function getTokensListURL(uri: string) { - const listURI = uri.startsWith('ipns') - ? `https://gateway.ipfs.io/ipns/${uri.split('://')[1]}` - : uri; - - return `https://tokenlists.org/token-list?url=${listURI}`; -} diff --git a/src/locales/default.json b/src/locales/default.json index 3a22978dd3..1258ae1ad4 100644 --- a/src/locales/default.json +++ b/src/locales/default.json @@ -40,6 +40,8 @@ "communitySwapFeeLabel": "Delegated swap fees; currently fixed: {0}", "configuration": "Configuration", "confirmTrade": "Confirm trade", + "confirmETHWrap": "Confirm ETH wrap", + "confirmETHUnwrap": "Confirm ETH unwrap", "confirming": "Confirming...", "connect": "Connect", "connecting": "Connecting...", @@ -76,8 +78,6 @@ "explore": "Explore", "explorePools": "Explore pools", "exploreBalancerV1Pools": "Explore V1 pools ->", - "expectedReceiveNoSlippage": "Expected to receive with no slippage", - "expectedSellNoSlippage": "Expected to sell with no slippage", "feesTime": "Fees ({0})", "feesManagedByGauntlet": "Liquidity providers earn dynamic swap fees on every trade utilizing the Liquidity in this pool. Dynamic swap fees are controlled by governance and are currently set by Gauntlet.", "filter": "Filter", @@ -87,7 +87,6 @@ "fixedSwapFeeLabel": "Fixed swap fees: {0}", "flashLoanFee": "Flash loan fee", "goToBalancerV1Site": "Go to the Balancer V1 site ->", - "gasCosts": "Gas costs", "highGasFees": "High gas fees? Here's a helping hand", "highPriceImpact": "High price impact", "highPriceImpactDetailed": "This trade is significantly moving the market price.", @@ -124,9 +123,7 @@ "manageLists": "Manage lists", "marketConditionsWarning": "Market conditions may change between the time your order is submitted and the time it gets executed on Ethereum. Slippage tolerance is the maximum change in price you are willing to accept. This protects you from front-running bots and miner extractable value (MEV).", "max": "Max", - "maxSoldWithSlippage": "Max sold with {0} slippage", "menu": "Menu", - "minReceivedWithSlippage": "Min received with {0} slippage", "month": "month", "months": "months", "mustBeAtLeast": "must be at least {0} characters", @@ -189,6 +186,8 @@ "popularBases": "Popular:", "preview": "Preview", "previewTrade": "Preview trade", + "previewETHWrap": "Preview ETH Wrap", + "previewETHUnwrap": "Preview ETH Unwrap", "previewTradeTransactions": "Preview trade transactions", "priceImpact": "Price impact", "priceImpactAccept": "I accept the high price impact from withdrawing single token amounts, moving the market price based on the depth of the market.", @@ -201,20 +200,22 @@ "requiresTransactions": "Requires 1 transaction | Requires {txCount} transactions", "receipt": "Receipt", "recentActivityTitle": "Recent activity", - "noRecentActivity": "Your session activity will appear here…", + "noRecentActivity": "Your recent activity will appear here…", "transactionAction": { "trade": "Trade", "approve": "Approve", "claim": "Claim", "invest": "Invest", - "withdraw": "Withdraw" + "withdraw": "Withdraw", + "wrap": "Wrap", + "unwrap": "Unwrap" }, "transactionStatus": { "pending": "Pending", "confirmed": "Confirmed", "expired": "Expired", "cancelling": "Cancelling", - "Cancelled": "Cancelled" + "cancelled": "Cancelled" }, "transactionSummary": { "investInPool": "{0} in {1}", @@ -222,8 +223,44 @@ "claimBAL": "{0} BAL", "approveForInvesting": "Approve {0} for investing", "approveForTrading": "Approve {0} for trading", - "wrapETH": "Wrap {0} ETH to WETH", - "unwrapETH": "Unwrap {0} WETH to ETH" + "wrapETH": "{0} ETH to WETH", + "unwrapETH": "{0} WETH to ETH" + }, + "tradeSummary": { + "exactIn": { + "title": "Trade from {0} details", + "tradeFees": "Trade fees", + "totalBeforeFees": "Total to receive before fees", + "totalAfterFees": "Total expected after fees", + "totalWithSlippage": "The least you’ll get at {0} slippage" + }, + "exactOut": { + "title": "Trade to {0} details", + "tradeFees": "Trade fees", + "totalBeforeFees": "Total to trade before fees", + "totalAfterFees": "Total expected to trade after fees", + "totalWithSlippage": "The most you’ll send at {0} slippage" + }, + "wrapETH": { + "title": "Summary", + "tradeFees": "Wrap fees", + "totalBeforeFees": "Amount before fees", + "totalAfterFees": "Total to receive", + "totalWithSlippage": "Zero slippage to wrap ETH" + }, + "unwrapETH": { + "title": "Summary", + "tradeFees": "Unwrap fees", + "totalBeforeFees": "Amount before fees", + "totalAfterFees": "Total to receive", + "totalWithSlippage": "Zero slippage to wrap ETH" + }, + "gasCosts": "Gas costs" + }, + "priceUpdatedAlert": { + "title": "Price updated", + "description": "The trade price has updated by more than {0}", + "actionLabel": "Accept" }, "searchBy": "Name, symbol or address", "searchByName": "Search by Name", @@ -246,7 +283,6 @@ "swapFee": "Swap fee", "swapFeeAPR": "Swap fees APR", "symbol": "Symbol", - "solverFees": "Solver fees", "theme": "Theme", "threeMonths": "3M", "token": "Token", @@ -264,10 +300,26 @@ "tradeLiquidity": "Trade liquidity", "transactionPending": "No transactions pending | {n} transaction pending | {n} transactions pending", "transactionDeadline": "Transaction deadline", - "transactionDeadlineTooltip": "Your swap expires and will not execute if it is pending for longer than the selected duration. Only executed swaps incur fees.", + "transactionDeadlineTooltip": "Your swap will expire and not execute if it is pending for more than the selected duration. Only executed swaps incur fees for trades between ERC-20 tokens.", "unavailableOnNetwork": "Sorry! Balancer is not available on this network", "unavailableOnNetworkWithName": "Sorry! Balancer is not available on {0}", "liquidityProviderCopy": "People who provide liquidity to eligible investment pools or trade on eligible token pairs receive weekly $BAL distributions as incentives. $BAL token holders own the Balancer Protocol and can help shape its future by voting on governance proposals.", + "gnosisErrors": { + "lowAmount": { + "header": "Your trade amount is too low", + "body": "The fees for this transaction exceeds the amount you want to trade." + }, + "lowBalance": { + "header": "Insufficient {0} balance", + "body": "The fees and potential slippage (up to {2}) set for this trade exceeds your {0} balance. You need at least {1} {0} to proceed." + } + }, + "gnosisWarnings": { + "highFees": { + "header": "The fees on this transaction exceed 20% of the trade amount.", + "body": "You can still proceed with the trade but a high percentage of it will be consumed by trade fees." + } + }, "unknown": "Unknown", "unknownPoolType": "Unknown pool type", "unlock": "Unlock", diff --git a/src/main.ts b/src/main.ts index a6cb87c64a..7078d9f996 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,4 @@ import { createApp } from 'vue'; -import App from '@/App.vue'; import store from '@/store'; import router from '@/plugins/router'; import mixins from '@/plugins/mixins'; @@ -8,7 +7,6 @@ import blocknative from '@/plugins/blocknative'; import vueQuery from '@/plugins/vueQuery'; import initSentry from '@/plugins/sentry'; import registerDirectives from '@/plugins/directives'; -import VueApexCharts from 'vue3-apexcharts'; import { registerGlobalComponents } from '@/plugins/components'; import Web3Plugin from '@/services/web3/web3.plugin'; import { use } from 'echarts/core'; @@ -28,6 +26,7 @@ import '@/assets/css/tailwind.css'; import '@/assets/css/index.css'; import 'vue3-virtual-scroller/dist/vue3-virtual-scroller.css'; import { Web3Provider } from '@ethersproject/providers'; +import Root from './Root'; use([ TitleComponent, @@ -41,16 +40,15 @@ use([ MarkLineComponent ]); -const app = createApp(App) +const app = createApp(Root) .use(i18n) .use(router) .use(store) .use(blocknative) - .use(VueApexCharts) .use(vueQuery) - .use(VueVirtualScroller) .use(Web3Plugin, Web3Provider) - .mixin(mixins); + .mixin(mixins) + .use(VueVirtualScroller); registerDirectives(app); registerGlobalComponents(app); diff --git a/src/pages/Home.vue b/src/pages/Home.vue index 196ecb07f8..9330fa59ea 100644 --- a/src/pages/Home.vue +++ b/src/pages/Home.vue @@ -52,14 +52,9 @@ + + diff --git a/src/pages/Pool.vue b/src/pages/Pool.vue index c78c540829..196deba81a 100644 --- a/src/pages/Pool.vue +++ b/src/pages/Pool.vue @@ -116,21 +116,19 @@ import { defineComponent, reactive, toRefs, computed, watch } from 'vue'; import * as PoolPageComponents from '@/components/pages/pool'; import GauntletIcon from '@/components/images/icons/GauntletIcon.vue'; import LiquidityMiningTooltip from '@/components/tooltips/LiquidityMiningTooltip.vue'; - -import { useStore } from 'vuex'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import { useQueryClient } from 'vue-query'; - import useNumbers from '@/composables/useNumbers'; import usePoolQuery from '@/composables/queries/usePoolQuery'; import usePoolSnapshotsQuery from '@/composables/queries/usePoolSnapshotsQuery'; import { useRouter } from 'vue-router'; - import { POOLS_ROOT_KEY } from '@/constants/queryKeys'; import { POOLS } from '@/constants/pools'; import { EXTERNAL_LINKS } from '@/constants/links'; import useWeb3 from '@/services/web3/useWeb3'; +import useTokens from '@/composables/useTokens'; +import useApp from '@/composables/useApp'; interface PoolPageData { id: string; @@ -147,31 +145,39 @@ export default defineComponent({ }, setup() { - // COMPOSABLES - const store = useStore(); + /** + * COMPOSABLES + */ + const { appLoading } = useApp(); const router = useRouter(); const { t } = useI18n(); const route = useRoute(); const { fNum } = useNumbers(); const { isWalletReady } = useWeb3(); const queryClient = useQueryClient(); + const { prices } = useTokens(); + const { blockNumber } = useWeb3(); + + /** + * QUERIES + */ const poolQuery = usePoolQuery(route.params.id as string); const poolSnapshotsQuery = usePoolSnapshotsQuery( route.params.id as string, 30 ); - const { blockNumber } = useWeb3(); - - // DATA + /** + * STATE + */ const data = reactive({ id: route.params.id as string, refetchQueriesOnBlockNumber: 0 }); - // COMPUTED - const appLoading = computed(() => store.state.app.loading); - + /** + * COMPUTED + */ const pool = computed(() => poolQuery.data.value); const noInitLiquidity = computed( @@ -253,22 +259,26 @@ export default defineComponent({ const missingPrices = computed(() => { if (pool.value) { - const tokensWithPrice = Object.keys(store.state.market.prices); - return !pool.value.tokensList.every(token => + const tokensWithPrice = Object.keys(prices.value); + return !pool.value.tokenAddresses.every(token => tokensWithPrice.includes(token) ); } return false; }); - // METHODS + /** + * METHODS + */ function onNewTx(): void { queryClient.invalidateQueries([POOLS_ROOT_KEY, 'current', data.id]); data.refetchQueriesOnBlockNumber = blockNumber.value + REFETCH_QUERIES_BLOCK_BUFFER; } - // WATCHERS + /** + * WATCHERS + */ watch(blockNumber, () => { if (data.refetchQueriesOnBlockNumber === blockNumber.value) { queryClient.invalidateQueries([POOLS_ROOT_KEY]); diff --git a/src/pages/Trade.vue b/src/pages/Trade.vue index 22bf3b4929..c00c762149 100644 --- a/src/pages/Trade.vue +++ b/src/pages/Trade.vue @@ -1,7 +1,7 @@