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 @@
+
+
+
+ .
+
+
+
+
+
+
+
+ {{ currencySymbol }}
+
+
+
+ .
+
+
+
+
+
+
+
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 @@
]
-
+
@@ -16,7 +16,7 @@ import { useStore } from 'vuex';
import TradeCard from '@/components/cards/TradeCard/TradeCard.vue';
import TradeCardGP from '@/components/cards/TradeCardGP/TradeCardGP.vue';
-import useTokenLists from '@/composables/useTokensStore';
+import useTokenLists from '@/composables/useTokenLists';
import { TradeInterface } from '@/store/modules/app';
import usePoolFilters from '@/composables/pools/usePoolFilters';
@@ -29,7 +29,7 @@ export default defineComponent({
setup() {
// COMPOSABLES
const store = useStore();
- const { isLoading: isLoadingTokens } = useTokenLists();
+ const { loadingTokenLists } = useTokenLists();
const { setSelectedTokens } = usePoolFilters();
// COMPUTED
@@ -44,7 +44,7 @@ export default defineComponent({
return {
appLoading,
tradeInterface,
- isLoadingTokens,
+ loadingTokenLists,
TradeInterface
};
}
diff --git a/src/plugins/blocknative.ts b/src/plugins/blocknative.ts
index 9f234871fe..50a3634662 100644
--- a/src/plugins/blocknative.ts
+++ b/src/plugins/blocknative.ts
@@ -7,7 +7,7 @@ export const defaultOptions: InitializationOptions = {
dappId: process.env.VUE_APP_BLOCKNATIVE_DAPP_ID || '',
networkId: Number(process.env.VUE_APP_NETWORK) || 1,
onerror: error => {
- console.log(`[Blocknative] encountered an error - ${error}`);
+ console.log(`[Blocknative] encountered an error`, error);
}
};
diff --git a/src/plugins/router.ts b/src/plugins/router.ts
index 0607f00b3e..5f0a60e1e4 100644
--- a/src/plugins/router.ts
+++ b/src/plugins/router.ts
@@ -1,6 +1,7 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import Home from '@/pages/Home.vue';
import Pool from '@/pages/Pool.vue';
+import LiquidityMining from '@/pages/LiquidityMining.vue';
import Trade from '@/pages/Trade.vue';
const routes: RouteRecordRaw[] = [
@@ -13,6 +14,11 @@ const routes: RouteRecordRaw[] = [
}
},
{ path: '/pool/:id', name: 'pool', component: Pool },
+ {
+ path: '/liquidity-mining',
+ name: 'liquidity-mining',
+ component: LiquidityMining
+ },
{ path: '/:pathMatch(.*)*', name: 'not-found', redirect: '/' }
];
diff --git a/src/providers/app.provider.ts b/src/providers/app.provider.ts
new file mode 100644
index 0000000000..d14f8bd84a
--- /dev/null
+++ b/src/providers/app.provider.ts
@@ -0,0 +1,47 @@
+import useTokenLists from '@/composables/useTokenLists';
+import useTokens from '@/composables/useTokens';
+import symbolKeys from '@/constants/symbol.keys';
+import { provide, computed, InjectionKey, ComputedRef } from 'vue';
+import { useStore } from 'vuex';
+
+/**
+ * TYPES
+ */
+export interface AppProviderResponse {
+ appLoading: ComputedRef;
+}
+
+/**
+ * SETUP
+ */
+export const AppProviderSymbol: InjectionKey = Symbol(
+ symbolKeys.Providers.App
+);
+
+/**
+ * AppProvider
+ * Provides global app level properties
+ */
+export default {
+ name: 'AppProvider',
+
+ setup(props, { slots }) {
+ const store = useStore();
+ const { loadingTokenLists } = useTokenLists();
+ const { loading: loadingTokens } = useTokens();
+
+ const appLoading = computed(
+ () =>
+ store.state.app.loading ||
+ loadingTokenLists.value ||
+ loadingTokens.value
+ );
+
+ provide(AppProviderSymbol, {
+ // computed
+ appLoading
+ });
+
+ return () => slots.default();
+ }
+};
diff --git a/src/providers/index.ts b/src/providers/index.ts
new file mode 100644
index 0000000000..6c74a9c85c
--- /dev/null
+++ b/src/providers/index.ts
@@ -0,0 +1,9 @@
+/**
+ * Order is important here!
+ * If a provider is dependent on another it must be imported/exported below
+ * the dependency.
+ */
+export { default as UserSettingsProvider } from './user-settings.provider';
+export { default as TokenListProvider } from './token-lists.provider';
+export { default as TokensProvider } from './tokens.provider';
+export { default as AppProvider } from './app.provider';
diff --git a/src/providers/token-lists.provider.ts b/src/providers/token-lists.provider.ts
new file mode 100644
index 0000000000..98cec2f68c
--- /dev/null
+++ b/src/providers/token-lists.provider.ts
@@ -0,0 +1,169 @@
+import {
+ provide,
+ reactive,
+ toRefs,
+ computed,
+ InjectionKey,
+ ComputedRef,
+ Ref
+} from 'vue';
+import symbolKeys from '@/constants/symbol.keys';
+import localStorageKeys from '@/constants/local-storage.keys';
+import { TokenList, TokenListMap } from '@/types/TokenList';
+import { tokenListService } from '@/services/token-list/token-list.service';
+import { lsSet } from '@/lib/utils';
+import { pick } from 'lodash';
+import useTokenListsQuery from '@/composables/queries/useTokenListsQuery';
+
+/** TYPES */
+export interface TokenListsState {
+ activeListKeys: string[];
+}
+
+export interface TokenListsProviderResponse {
+ activeListKeys: Ref;
+ loadingTokenLists: Ref;
+ tokenListsLoaded: ComputedRef;
+ allTokenLists: ComputedRef;
+ activeTokenLists: ComputedRef;
+ defaultTokenList: ComputedRef;
+ balancerTokenLists: ComputedRef;
+ approvedTokenLists: ComputedRef;
+ vettedTokenList: ComputedRef;
+ toggleTokenList: Function;
+ isActiveList: Function;
+}
+
+/** SETUP */
+const { uris } = tokenListService;
+export const TokenListsProviderSymbol: InjectionKey = Symbol(
+ symbolKeys.Providers.TokenLists
+);
+
+export default {
+ name: 'TokenListsProvider',
+
+ setup(props, { slots }) {
+ /**
+ * STATE
+ */
+ const state: TokenListsState = reactive({
+ activeListKeys: [uris.Balancer.Default]
+ });
+
+ /**
+ * QUERIES
+ */
+ const tokenListsQuery = useTokenListsQuery();
+
+ /**
+ * COMPUTED
+ */
+
+ /**
+ * All token lists
+ */
+ const allTokenLists = computed(
+ (): TokenListMap =>
+ tokenListsQuery.data.value ? tokenListsQuery.data.value : {}
+ );
+
+ /**
+ * Are token lists loading...
+ */
+ const loadingTokenLists = computed(
+ () => tokenListsQuery.isLoading.value || tokenListsQuery.isIdle.value
+ );
+
+ /**
+ * All available token lists are loaded
+ */
+ const tokenListsLoaded = computed(() => tokenListsQuery.isSuccess.value);
+
+ /**
+ * All active (toggled) tokenlists
+ */
+ const activeTokenLists = computed(
+ (): TokenListMap => pick(allTokenLists.value, state.activeListKeys)
+ );
+
+ /**
+ * The default Balancer token list.
+ */
+ const defaultTokenList = computed(
+ (): TokenList => allTokenLists.value[uris.Balancer.Default]
+ );
+
+ /**
+ * The Balancer vetted token list, contains LBP tokens.
+ */
+ const vettedTokenList = computed(
+ (): TokenList => allTokenLists.value[uris.Balancer.Vetted]
+ );
+
+ /**
+ * All Balancer token lists mapped by URI.
+ */
+ const balancerTokenLists = computed(
+ (): TokenListMap => pick(allTokenLists.value, uris.Balancer.All)
+ );
+
+ /**
+ * Approved token lists mapped by URI.
+ * Approved means tokens are compliant and can be presented in the UI.
+ * This excludes lists like the Balancer vetted list.
+ */
+ const approvedTokenLists = computed(
+ (): TokenListMap => pick(allTokenLists.value, uris.Approved)
+ );
+
+ /**
+ * METHODS
+ */
+
+ /**
+ * Adds a token list to the active lists which
+ * makes additonal tokens available in the token search modal.
+ */
+ function toggleTokenList(uri: string): void {
+ if (!uris.Approved.includes(uri)) return;
+
+ if (state.activeListKeys.includes(uri)) {
+ // Deactivate token list
+ state.activeListKeys.splice(state.activeListKeys.indexOf(uri), 1);
+ } else {
+ // Activate token list
+ state.activeListKeys.push(uri);
+ }
+
+ lsSet(localStorageKeys.TokenLists.Toggled, state.activeListKeys);
+ }
+
+ /**
+ * Given a token list URI checks if the related token
+ * list has been toggled via the token search modal.
+ */
+ function isActiveList(uri: string): boolean {
+ return state.activeListKeys.includes(uri);
+ }
+
+ provide(TokenListsProviderSymbol, {
+ // state
+ ...toRefs(state),
+ // computed
+ allTokenLists,
+ loadingTokenLists,
+ tokenListsLoaded,
+ activeTokenLists,
+ defaultTokenList,
+ balancerTokenLists,
+ approvedTokenLists,
+ vettedTokenList,
+ // methods
+ toggleTokenList,
+ isActiveList
+ });
+
+ return () => slots.default();
+ }
+};
diff --git a/src/providers/tokens.provider.ts b/src/providers/tokens.provider.ts
new file mode 100644
index 0000000000..7f97c7cc45
--- /dev/null
+++ b/src/providers/tokens.provider.ts
@@ -0,0 +1,364 @@
+import {
+ toRefs,
+ toRef,
+ computed,
+ reactive,
+ provide,
+ InjectionKey,
+ Ref,
+ ComputedRef,
+ onBeforeMount
+} from 'vue';
+import useTokenLists from '@/composables/useTokenLists';
+import { getAddress, isAddress } from '@ethersproject/address';
+import { TokenInfo, TokenInfoMap, TokenList } from '@/types/TokenList';
+import useConfig from '@/composables/useConfig';
+import useTokenPricesQuery from '@/composables/queries/useTokenPricesQuery';
+import useBalancesQuery from '@/composables/queries/useBalancesQuery';
+import useAllowancesQuery from '@/composables/queries/useAllowancesQuery';
+import { TokenPrices } from '@/services/coingecko/api/price.service';
+import { BalanceMap } from '@/services/token/concerns/balances.concern';
+import { ContractAllowancesMap } from '@/services/token/concerns/allowances.concern';
+import symbolKeys from '@/constants/symbol.keys';
+import { GP_ALLOWANCE_MANAGER_CONTRACT_ADDRESS } from '@/services/gnosis/constants';
+import { tokenService } from '@/services/token/token.service';
+import useUserSettings from '@/composables/useUserSettings';
+import { bnum } from '@/lib/utils';
+import { currentLiquidityMiningRewardTokens } from '@/lib/utils/liquidityMining';
+import { pick } from 'lodash';
+
+/**
+ * TYPES
+ */
+export interface TokensProviderState {
+ loading: boolean;
+ injectedTokens: TokenInfoMap;
+ allowanceContracts: string[];
+}
+
+export interface TokensProviderResponse {
+ loading: Ref;
+ tokens: ComputedRef;
+ injectedTokens: Ref;
+ allowanceContracts: Ref;
+ nativeAsset: TokenInfo;
+ activeTokenListTokens: ComputedRef;
+ prices: ComputedRef;
+ balances: ComputedRef;
+ allowances: ComputedRef;
+ dynamicDataLoaded: ComputedRef;
+ dynamicDataLoading: ComputedRef;
+ refetchPrices: Ref;
+ refetchBalances: Ref;
+ refetchAllowances: Ref;
+ injectTokens: Function;
+ searchTokens: Function;
+ hasBalance: Function;
+ approvalsRequired: Function;
+ priceFor: Function;
+ balanceFor: (address: string) => string;
+ getTokens: Function;
+}
+
+/**
+ * SETUP
+ */
+export const TokensProviderSymbol: InjectionKey = Symbol(
+ symbolKeys.Providers.Tokens
+);
+
+/**
+ * TokensProvider
+ * Provides an interface to all token static and dynamic metatdata.
+ */
+export default {
+ name: 'TokensProvider',
+
+ setup(props, { slots }) {
+ /**
+ * COMPOSABLES
+ */
+ const { networkConfig } = useConfig();
+ const { allTokenLists, activeTokenLists } = useTokenLists();
+ const { currency } = useUserSettings();
+
+ /**
+ * STATE
+ */
+ const nativeAsset: TokenInfo = {
+ ...networkConfig.nativeAsset,
+ chainId: networkConfig.chainId
+ };
+
+ const state: TokensProviderState = reactive({
+ loading: true,
+ injectedTokens: {
+ [networkConfig.nativeAsset.address]: nativeAsset
+ },
+ allowanceContracts: [
+ networkConfig.addresses.vault,
+ networkConfig.addresses.exchangeProxy,
+ GP_ALLOWANCE_MANAGER_CONTRACT_ADDRESS
+ ]
+ });
+
+ /**
+ * COMPUTED
+ */
+
+ /**
+ * All tokens from token lists that are toggled on.
+ */
+ const activeTokenListTokens = computed(
+ (): TokenInfoMap =>
+ mapTokenListTokens(Object.values(activeTokenLists.value))
+ );
+
+ /**
+ * The main tokens map
+ * A combination of activated token list tokens
+ * and any injected tokens. Static and dynamic
+ * meta data should be available for these tokens.
+ */
+ const tokens = computed(() => ({
+ ...activeTokenListTokens.value,
+ ...state.injectedTokens
+ }));
+
+ const tokenAddresses = computed(() => Object.keys(tokens.value));
+
+ /****************************************************************
+ * Dynamic metadata
+ *
+ * The prices, balances and allowances maps provide dynamic
+ * metadata for each token in the tokens state array.
+ ****************************************************************/
+ const {
+ data: priceData,
+ isSuccess: priceQuerySuccess,
+ isLoading: priceQueryLoading,
+ refetch: refetchPrices
+ } = useTokenPricesQuery(tokenAddresses);
+
+ const {
+ data: balanceData,
+ isSuccess: balanceQuerySuccess,
+ isLoading: balanceQueryLoading,
+ refetch: refetchBalances
+ } = useBalancesQuery(tokens);
+
+ const {
+ data: allowanceData,
+ isSuccess: allowanceQuerySuccess,
+ isLoading: allowanceQueryLoading,
+ refetch: refetchAllowances
+ } = useAllowancesQuery(tokens, toRef(state, 'allowanceContracts'));
+
+ const prices = computed(
+ (): TokenPrices => (priceData.value ? priceData.value : {})
+ );
+ const balances = computed(
+ (): BalanceMap => (balanceData.value ? balanceData.value : {})
+ );
+ const allowances = computed(
+ (): ContractAllowancesMap =>
+ allowanceData.value ? allowanceData.value : {}
+ );
+
+ const dynamicDataLoaded = computed(
+ () =>
+ priceQuerySuccess.value &&
+ balanceQuerySuccess.value &&
+ allowanceQuerySuccess.value
+ );
+
+ const dynamicDataLoading = computed(
+ () =>
+ priceQueryLoading.value ||
+ balanceQueryLoading.value ||
+ allowanceQueryLoading.value
+ );
+
+ /**
+ * METHODS
+ */
+ /**
+ * Create token map from a token list tokens array.
+ */
+ function mapTokenListTokens(tokenLists: TokenList[]): TokenInfoMap {
+ const tokensMap = {};
+ const tokens = tokenLists.map(list => list.tokens).flat();
+
+ tokens.forEach(token => {
+ const address: string = getAddress(token.address);
+ // Don't include if already included
+ if (Object.keys(tokensMap).includes(address)) return;
+ // Don't include if not on app network
+ if (token.chainId !== networkConfig.chainId) return;
+
+ tokensMap[address] = {
+ ...token,
+ address
+ };
+ });
+
+ return tokensMap;
+ }
+
+ /**
+ * Fetches static token metadata for given addresses and injects
+ * tokens into state tokens map.
+ */
+ async function injectTokens(addresses: string[]): Promise {
+ addresses = addresses.map(address => getAddress(address));
+
+ // Only inject tokens that aren't already in tokens
+ const injectable = addresses.filter(
+ address => !Object.keys(tokens.value).includes(address)
+ );
+ if (injectable.length === 0) return;
+
+ const newTokens = await tokenService.metadata.get(
+ injectable,
+ allTokenLists.value
+ );
+
+ state.injectedTokens = { ...state.injectedTokens, ...newTokens };
+ }
+
+ /**
+ * Given query, filters tokens map by name, symbol or address.
+ * If address is provided, search for address in tokens or injectToken
+ */
+ async function searchTokens(
+ query: string,
+ excluded: string[] = []
+ ): Promise {
+ if (!query) return removeExcluded(tokens.value, excluded);
+
+ if (isAddress(query)) {
+ const address = getAddress(query);
+ const token = tokens.value[address];
+ if (token) {
+ return { [address]: token };
+ } else {
+ await injectTokens([address]);
+ return pick(tokens.value, address);
+ }
+ } else {
+ const tokensArray = Object.entries(tokens.value);
+ const results = tokensArray.filter(
+ ([, token]) =>
+ token.name.toLowerCase().includes(query.toLowerCase()) ||
+ token.symbol.toLowerCase().includes(query.toLowerCase())
+ );
+ return removeExcluded(Object.fromEntries(results), excluded);
+ }
+ }
+
+ /**
+ * Remove excluded tokens from given token map.
+ */
+ function removeExcluded(
+ tokens: TokenInfoMap,
+ excluded: string[]
+ ): TokenInfoMap {
+ return Object.keys(tokens)
+ .filter(address => !excluded.includes(address))
+ .reduce((result, address) => {
+ result[address] = tokens[address];
+ return result;
+ }, {});
+ }
+
+ /**
+ * Check which tokens require approvals for given amounts
+ * @returns a subset of the token addresses passed in.
+ */
+ function approvalsRequired(
+ tokenAddresses: string[],
+ amounts: string[],
+ contractAddress: string = networkConfig.addresses.vault
+ ): string[] {
+ return tokenAddresses.filter((address, index) => {
+ const amount = Number(amounts[index]);
+ const allowance = bnum(allowances.value[contractAddress][address]);
+
+ if (amount === 0) return false;
+
+ return allowance.lt(amount);
+ });
+ }
+
+ /**
+ * Fetch price for a token
+ */
+ function priceFor(address: string): number {
+ try {
+ return prices.value[address][currency.value] || 0;
+ } catch {
+ return 0;
+ }
+ }
+
+ /**
+ * Fetch balance for a token
+ */
+ function balanceFor(address: string): string {
+ try {
+ return balances.value[address] || '0';
+ } catch {
+ return '0';
+ }
+ }
+
+ /**
+ * Checks if token has a balance
+ */
+ function hasBalance(address: string): boolean {
+ return Number(balances.value[address]) > 0;
+ }
+
+ /**
+ * Get subset of tokens from state
+ */
+ function getTokens(addresses: string[]): TokenInfoMap {
+ return pick(tokens.value, addresses);
+ }
+
+ /**
+ * CALLBACKS
+ */
+ onBeforeMount(async () => {
+ await injectTokens(currentLiquidityMiningRewardTokens);
+ state.loading = false;
+ });
+
+ provide(TokensProviderSymbol, {
+ // state
+ ...toRefs(state),
+ // computed
+ tokens,
+ nativeAsset,
+ activeTokenListTokens,
+ prices,
+ balances,
+ allowances,
+ dynamicDataLoaded,
+ dynamicDataLoading,
+ // methods
+ refetchPrices,
+ refetchBalances,
+ refetchAllowances,
+ injectTokens,
+ searchTokens,
+ hasBalance,
+ approvalsRequired,
+ priceFor,
+ balanceFor,
+ getTokens
+ });
+
+ return () => slots.default();
+ }
+};
diff --git a/src/providers/user-settings.provider.ts b/src/providers/user-settings.provider.ts
new file mode 100644
index 0000000000..1dae8e6468
--- /dev/null
+++ b/src/providers/user-settings.provider.ts
@@ -0,0 +1,58 @@
+import { provide, InjectionKey, reactive, Ref, toRefs } from 'vue';
+import symbolKeys from '@/constants/symbol.keys';
+import LS_KEYS from '@/constants/local-storage.keys';
+import { FiatCurrency } from '@/constants/currency';
+import { lsGet } from '@/lib/utils';
+
+/**
+ * TYPES
+ */
+export interface UserSettingsState {
+ currency: FiatCurrency;
+}
+
+export interface UserSettingsProviderResponse {
+ currency: Ref;
+ setCurrency: Function;
+}
+
+/**
+ * SETUP
+ */
+export const UserSettingsProviderSymbol: InjectionKey = Symbol(
+ symbolKeys.Providers.App
+);
+
+const lsCurrency = lsGet(LS_KEYS.UserSettings.Currency, FiatCurrency.usd);
+
+/**
+ * STATE
+ */
+const state: UserSettingsState = reactive({
+ currency: lsCurrency
+});
+
+/**
+ * UserSettingsProvider
+ * Provides global user settings interface
+ */
+export default {
+ name: 'UserSettingsProvider',
+
+ setup(props, { slots }) {
+ /**
+ * METHODS
+ */
+ function setCurrency(newCurrency: FiatCurrency): void {
+ state.currency = newCurrency;
+ }
+
+ provide(UserSettingsProviderSymbol, {
+ ...toRefs(state),
+ // methods
+ setCurrency
+ });
+
+ return () => slots.default();
+ }
+};
diff --git a/src/services/balancer/contracts/service.ts b/src/services/balancer/contracts/balancer-contracts.service.ts
similarity index 57%
rename from src/services/balancer/contracts/service.ts
rename to src/services/balancer/contracts/balancer-contracts.service.ts
index ca6781e9f5..0895e26a32 100644
--- a/src/services/balancer/contracts/service.ts
+++ b/src/services/balancer/contracts/balancer-contracts.service.ts
@@ -1,22 +1,24 @@
import Vault from './contracts/vault';
-import configs, { Config } from '@/lib/config';
+import { Config } from '@/lib/config';
import { JsonRpcProvider } from '@ethersproject/providers';
-import getProvider from '@/lib/utils/provider';
import { default as vaultAbi } from '@/lib/abi/Vault.json';
import { default as weightedPoolAbi } from '@/lib/abi/WeightedPool.json';
import { default as stablePoolAbi } from '@/lib/abi/StablePool.json';
import { default as TokenAbi } from '@/lib/abi/ERC20.json';
+import { rpcProviderService as _rpcProviderService } from '@/services/rpc-provider/rpc-provider.service';
+import { configService as _configService } from '@/services/config/config.service';
-const NETWORK = process.env.VUE_APP_NETWORK || '1';
-
-export default class Service {
+export default class BalancerContractsService {
vault: Vault;
config: Config;
provider: JsonRpcProvider;
- constructor() {
- this.provider = getProvider(NETWORK);
- this.config = configs[NETWORK];
+ constructor(
+ readonly configService = _configService,
+ readonly rpcProviderService = _rpcProviderService
+ ) {
+ this.provider = this.rpcProviderService.jsonProvider;
+ this.config = this.configService.network;
// Init contracts
this.vault = new Vault(this);
@@ -36,3 +38,5 @@ export default class Service {
);
}
}
+
+export const balancerContractsService = new BalancerContractsService();
diff --git a/src/services/balancer/contracts/contracts/vault.ts b/src/services/balancer/contracts/contracts/vault.ts
index 4a68c23202..faadcc7b49 100644
--- a/src/services/balancer/contracts/contracts/vault.ts
+++ b/src/services/balancer/contracts/contracts/vault.ts
@@ -1,12 +1,12 @@
-import Service from '../service';
+import Service from '../balancer-contracts.service';
import { default as vaultAbi } from '@/lib/abi/Vault.json';
import { Multicaller } from '@/lib/utils/balancer/contract';
import { getAddress } from '@ethersproject/address';
import { formatUnits } from '@ethersproject/units';
import { BigNumber } from '@ethersproject/bignumber';
-import { TokenMap } from '@/types';
import { OnchainPoolData, PoolType } from '../../subgraph/types';
import ConfigService from '@/services/config/config.service';
+import { TokenInfoMap } from '@/types/TokenList';
export default class Vault {
service: Service;
@@ -18,7 +18,7 @@ export default class Vault {
public async getPoolData(
id: string,
type: PoolType,
- tokens: TokenMap
+ tokens: TokenInfoMap
): Promise {
const poolAddress = getAddress(id.slice(0, 42));
let result = {} as Record;
@@ -53,7 +53,7 @@ export default class Vault {
public serializePoolData(
data,
type: PoolType,
- tokens: TokenMap
+ tokens: TokenInfoMap
): OnchainPoolData {
const _tokens = {};
const weights = this.normalizeWeights(data?.weights, type, tokens);
@@ -88,7 +88,7 @@ export default class Vault {
public normalizeWeights(
weights: BigNumber[],
type: PoolType,
- tokens: TokenMap
+ tokens: TokenInfoMap
) {
if (type == 'Weighted') {
const totalWeight = weights.reduce((a, b) => a.add(b), BigNumber.from(0));
diff --git a/src/services/balancer/subgraph/client.ts b/src/services/balancer/subgraph/balancer-subgraph.client.ts
similarity index 65%
rename from src/services/balancer/subgraph/client.ts
rename to src/services/balancer/subgraph/balancer-subgraph.client.ts
index c9d13acf03..d3ef422ebc 100644
--- a/src/services/balancer/subgraph/client.ts
+++ b/src/services/balancer/subgraph/balancer-subgraph.client.ts
@@ -1,11 +1,11 @@
import axios from 'axios';
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
-import ConfigService from '@/services/config/config.service';
+import { configService as _configService } from '@/services/config/config.service';
-export default class Client {
+export default class BalancerSubgraphClient {
url: string;
- constructor(private readonly configService = new ConfigService()) {
+ constructor(private readonly configService = _configService) {
this.url = configService.network.subgraph;
}
@@ -25,3 +25,5 @@ export default class Client {
return JSON.stringify({ query: jsonToGraphQLQuery({ query }) });
}
}
+
+export const balancerSubgraphClient = new BalancerSubgraphClient();
diff --git a/src/services/balancer/subgraph/service.ts b/src/services/balancer/subgraph/balancer-subgraph.service.ts
similarity index 81%
rename from src/services/balancer/subgraph/service.ts
rename to src/services/balancer/subgraph/balancer-subgraph.service.ts
index 8f959fa1a4..fc435d1913 100644
--- a/src/services/balancer/subgraph/service.ts
+++ b/src/services/balancer/subgraph/balancer-subgraph.service.ts
@@ -1,4 +1,4 @@
-import Client from './client';
+import { balancerSubgraphClient } from './balancer-subgraph.client';
import { rpcProviderService as _rpcProviderService } from '@/services/rpc-provider/rpc-provider.service';
import Pools from './entities/pools';
import PoolShares from './entities/poolShares';
@@ -7,19 +7,16 @@ import PoolSnapshots from './entities/poolSnapshots';
const NETWORK = process.env.VUE_APP_NETWORK || '1';
-export default class Service {
- client: Client;
+export default class BalancerSubgraphService {
pools: Pools;
poolShares: PoolShares;
poolActivities: PoolActivities;
poolSnapshots: PoolSnapshots;
constructor(
- client = new Client(),
+ readonly client = balancerSubgraphClient,
readonly rpcProviderService = _rpcProviderService
) {
- this.client = client;
-
// Init entities
this.pools = new Pools(this);
this.poolShares = new PoolShares(this);
@@ -41,3 +38,5 @@ export default class Service {
}
}
}
+
+export const balancerSubgraphService = new BalancerSubgraphService();
diff --git a/src/services/balancer/subgraph/entities/poolActivities/index.ts b/src/services/balancer/subgraph/entities/poolActivities/index.ts
index 7433cfe026..d8434df6bf 100644
--- a/src/services/balancer/subgraph/entities/poolActivities/index.ts
+++ b/src/services/balancer/subgraph/entities/poolActivities/index.ts
@@ -1,4 +1,4 @@
-import Service from '../../service';
+import Service from '../../balancer-subgraph.service';
import queryBuilder from './query';
import { PoolActivity, QueryBuilder } from '../../types';
diff --git a/src/services/balancer/subgraph/entities/poolShares/index.ts b/src/services/balancer/subgraph/entities/poolShares/index.ts
index ac229828d2..807d104f95 100644
--- a/src/services/balancer/subgraph/entities/poolShares/index.ts
+++ b/src/services/balancer/subgraph/entities/poolShares/index.ts
@@ -1,4 +1,4 @@
-import Service from '../../service';
+import Service from '../../balancer-subgraph.service';
import poolQueryBuilder from './query';
import { PoolShare, QueryBuilder } from '../../types';
diff --git a/src/services/balancer/subgraph/entities/poolSnapshots/index.ts b/src/services/balancer/subgraph/entities/poolSnapshots/index.ts
index baa445e3ca..6b74c00c18 100644
--- a/src/services/balancer/subgraph/entities/poolSnapshots/index.ts
+++ b/src/services/balancer/subgraph/entities/poolSnapshots/index.ts
@@ -1,4 +1,4 @@
-import Service from '../../service';
+import Service from '../../balancer-subgraph.service';
import poolQueryBuilder from './query';
import { PoolSnapshots, PoolSnapshot, QueryBuilder } from '../../types';
diff --git a/src/services/balancer/subgraph/entities/pools/index.ts b/src/services/balancer/subgraph/entities/pools/index.ts
index e11c1d7f68..b363d33eab 100644
--- a/src/services/balancer/subgraph/entities/pools/index.ts
+++ b/src/services/balancer/subgraph/entities/pools/index.ts
@@ -1,4 +1,4 @@
-import Service from '../../service';
+import Service from '../../balancer-subgraph.service';
import queryBuilder from './query';
import { getPoolLiquidity } from '@/lib/utils/balancer/price';
import { bnum } from '@/lib/utils';
@@ -6,16 +6,19 @@ import {
Pool,
QueryBuilder,
TimeTravelPeriod,
- DecoratedPool
+ DecoratedPool,
+ PoolToken
} from '../../types';
-import { Prices } from '@/services/coingecko';
import { getAddress } from '@ethersproject/address';
import {
currentLiquidityMiningRewards,
computeTotalAPRForPool
} from '@/lib/utils/liquidityMining';
import { NetworkId } from '@/constants/network';
-import ConfigService from '@/services/config/config.service';
+import { configService as _configService } from '@/services/config/config.service';
+import { TokenPrices } from '@/services/coingecko/api/price.service';
+import { FiatCurrency } from '@/constants/currency';
+import { isStable } from '@/composables/usePool';
const IS_LIQUIDITY_MINING_ENABLED = true;
@@ -27,7 +30,7 @@ export default class Pools {
constructor(
service: Service,
query: QueryBuilder = queryBuilder,
- private readonly configService = new ConfigService()
+ private readonly configService = _configService
) {
this.service = service;
this.query = query;
@@ -40,38 +43,35 @@ export default class Pools {
return data.pools;
}
- public async getDecorated(
+ public async decorate(
+ pools: Pool[],
period: TimeTravelPeriod,
- prices: Prices,
- args = {},
- attrs = {}
+ prices: TokenPrices,
+ currency: FiatCurrency
): Promise {
- // Get current pools
- const currentPoolsQuery = this.query(args, attrs);
- const [{ pools: currentPools }, blockNumber] = await Promise.all([
- this.service.client.get(currentPoolsQuery),
- this.timeTravelBlock(period)
- ]);
-
- // Get past state of current pools
+ // Get past state of pools
+ const blockNumber = await this.timeTravelBlock(period);
const block = { number: blockNumber };
- const isCurrentPool = { id_in: currentPools.map(pool => pool.id) };
- const pastPoolsQuery = this.query({ where: isCurrentPool, block }, attrs);
+ const isInPoolIds = { id_in: pools.map(pool => pool.id) };
+ const pastPoolsQuery = this.query({ where: isInPoolIds, block });
const { pools: pastPools } = await this.service.client.get(pastPoolsQuery);
- return this.serialize(currentPools, pastPools, period, prices);
+ return this.serialize(pools, pastPools, period, prices, currency);
}
private serialize(
pools: Pool[],
pastPools: Pool[],
period: TimeTravelPeriod,
- prices: Prices
+ prices: TokenPrices,
+ currency: FiatCurrency
): DecoratedPool[] {
return pools.map(pool => {
- pool.address = this.extractAddress(pool.id);
+ pool.address = this.addressFor(pool.id);
pool.tokenAddresses = pool.tokensList.map(t => getAddress(t));
- pool.totalLiquidity = getPoolLiquidity(pool, prices);
+ pool.tokens = this.formatPoolTokens(pool);
+ pool.totalLiquidity = getPoolLiquidity(pool, prices, currency);
+
const pastPool = pastPools.find(p => p.id === pool.id);
const volume = this.calcVolume(pool, pastPool);
const poolAPR = this.calcAPR(pool, pastPool);
@@ -79,7 +79,7 @@ export default class Pools {
const {
hasLiquidityMiningRewards,
liquidityMiningAPR
- } = this.calcLiquidityMiningAPR(pool, prices);
+ } = this.calcLiquidityMiningAPR(pool, prices, currency);
const totalAPR = this.calcTotalAPR(poolAPR, liquidityMiningAPR);
return {
@@ -99,6 +99,17 @@ export default class Pools {
});
}
+ private formatPoolTokens(pool: Pool): PoolToken[] {
+ const tokens = pool.tokens.map(token => ({
+ ...token,
+ address: getAddress(token.address)
+ }));
+
+ if (isStable(pool)) return tokens;
+
+ return tokens.sort((a, b) => parseFloat(b.weight) - parseFloat(a.weight));
+ }
+
private calcVolume(pool: Pool, pastPool: Pool | undefined): string {
if (!pastPool) return pool.totalSwapVolume;
@@ -121,7 +132,11 @@ export default class Pools {
.toString();
}
- private calcLiquidityMiningAPR(pool: Pool, prices: Prices) {
+ private calcLiquidityMiningAPR(
+ pool: Pool,
+ prices: TokenPrices,
+ currency: FiatCurrency
+ ) {
let liquidityMiningAPR = '0';
const liquidityMiningRewards = currentLiquidityMiningRewards[pool.id];
@@ -134,6 +149,7 @@ export default class Pools {
liquidityMiningAPR = computeTotalAPRForPool(
liquidityMiningRewards,
prices,
+ currency,
pool.totalLiquidity
);
}
@@ -171,7 +187,7 @@ export default class Pools {
}
}
- private extractAddress(poolId: string): string {
+ public addressFor(poolId: string): string {
return getAddress(poolId.slice(0, 42));
}
}
diff --git a/src/services/balancer/subgraph/types.ts b/src/services/balancer/subgraph/types.ts
index d18c85907b..0d470604cd 100644
--- a/src/services/balancer/subgraph/types.ts
+++ b/src/services/balancer/subgraph/types.ts
@@ -5,7 +5,10 @@ export type QueryBuilder = (
attrs?: QueryAttrs
) => Record;
-export type PoolType = 'Weighted' | 'Stable';
+export enum PoolType {
+ Weighted = 'Weighted',
+ Stable = 'Stable'
+}
export type TimeTravelPeriod = '24h';
export interface PoolToken {
diff --git a/src/services/claim.ts b/src/services/claim.ts
index 5a475af8f3..ce498e18f0 100644
--- a/src/services/claim.ts
+++ b/src/services/claim.ts
@@ -7,17 +7,15 @@ import { NetworkId } from '@/constants/network';
import { Claim } from '@/types';
-import { ipfsGet } from '@/lib/utils/balancer/ipfs';
+import { ipfsService } from './ipfs/ipfs.service';
import { call, sendTransaction } from '@/lib/utils/balancer/web3';
import { bnum } from '@/lib/utils';
import { loadTree } from '@/lib/utils/merkle';
import configs from '@/lib/config';
-import { getOriginalAddress } from '@/services/coingecko';
import { TOKENS } from '@/constants/tokens';
import merkleRedeemAbi from '@/lib/abi/MerkleRedeem.json';
-
-const gateway = process.env.VUE_APP_IPFS_NODE || 'ipfs.io';
+import { coingeckoService } from './coingecko/coingecko.service';
type Snapshot = Record;
@@ -36,7 +34,10 @@ export const constants: Record> = {
export async function getSnapshot(network: NetworkId) {
if (constants[network]?.snapshot) {
- return (await ipfsGet(gateway, constants[network].snapshot, 'ipns')) || {};
+ return (
+ (await ipfsService.get(constants[network].snapshot, 'ipns')) ||
+ {}
+ );
}
return {};
}
@@ -60,7 +61,7 @@ export type Report = Record;
export async function getReports(snapshot: Snapshot, weeks: number[]) {
const reports = await Promise.all(
- weeks.map(week => ipfsGet(gateway, snapshot[week]))
+ weeks.map(week => ipfsService.get(snapshot[week]))
);
return Object.fromEntries(reports.map((report, i) => [weeks[i], report]));
}
@@ -142,7 +143,9 @@ export async function getCurrentRewardsEstimate(
].filter(
incentive =>
incentive.token_address ==
- getOriginalAddress(network, TOKENS.AddressMap.BAL).toLowerCase()
+ coingeckoService.prices
+ .addressMapOut(TOKENS.AddressMap.BAL)
+ .toLowerCase()
);
const rewards = liquidityProviders
.reduce(
diff --git a/src/services/coingecko.ts b/src/services/coingecko.ts
deleted file mode 100644
index 71e339501f..0000000000
--- a/src/services/coingecko.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-import useConfig from '@/composables/useConfig';
-import { getNativeAssetId, getPlatformId } from './coingecko/coingecko.service';
-
-function getChainAddress(chainId: number, address: string) {
- if (!address) {
- return;
- }
- const map = {
- 1: {},
- 42: {
- '0xdfcea9088c8a88a76ff74892c1457c17dfeef9c1':
- '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
- '0x1c8e3bcb3378a443cc591f154c5ce0ebb4da9648':
- '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
- '0x41286bb1d3e870f3f750eb7e1c25d7e48c8a1ac7':
- '0xba100000625a3754423978a60c9317c58a424e3d',
- '0x8f4bebf498cc624a0797fe64114a6ff169eee078':
- '0xbc396689893d065f41bc2c6ecbee5e0085233447',
- '0xaf9ac3235be96ed496db7969f60d354fe5e426b0':
- '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
- '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115':
- '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
- '0x04df6e4121c27713ed22341e7c7df330f56f289b':
- '0x6b175474e89094c44da98b954eedeac495271d0f',
- '0xcc08220af469192c53295fdd34cfb8df29aa17ab':
- '0xdac17f958d2ee523a2206206994597c13d831ec7'
- },
- 137: {}
- };
- return map[chainId][address.toLowerCase()] || address;
-}
-
-export function getOriginalAddress(chainId: number, address: string) {
- if (!address) {
- return;
- }
- const map = {
- // WETH
- '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': {
- 42: '0xdfcea9088c8a88a76ff74892c1457c17dfeef9c1',
- 137: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619'
- },
- // WBTC
- '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599': {
- 42: '0x1c8e3bcb3378a443cc591f154c5ce0ebb4da9648'
- },
- // BAL
- '0xba100000625a3754423978a60c9317c58a424e3d': {
- 42: '0x41286bb1d3e870f3f750eb7e1c25d7e48c8a1ac7',
- 137: '0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3'
- },
- // PERP
- '0xbc396689893d065f41bc2c6ecbee5e0085233447': {
- 42: '0x8f4bebf498cc624a0797fe64114a6ff169eee078'
- },
- // MKR
- '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2': {
- 42: '0xaf9ac3235be96ed496db7969f60d354fe5e426b0'
- },
- // USDC
- '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': {
- 42: '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115'
- },
- // DAI
- '0x6b175474e89094c44da98b954eedeac495271d0f': {
- 42: '0x04df6e4121c27713ed22341e7c7df330f56f289b'
- },
- '0xdac17f958d2ee523a2206206994597c13d831ec7': {
- 42: '0xcc08220af469192c53295fdd34cfb8df29aa17ab'
- }
- };
- if (!map[address] || !map[address][chainId]) {
- return address.toLowerCase();
- }
- return map[address][chainId];
-}
-
-interface Price {
- price: number;
- price24HChange: number;
-}
-
-export type Prices = Record;
-
-export type HistoricalPrices = Record;
-
-export async function getEtherPrice(): Promise {
- const { env } = useConfig();
- const nativeAssetId = getNativeAssetId(env.NETWORK);
- const uri = `https://api.coingecko.com/api/v3/simple/price?ids=${nativeAssetId}&vs_currencies=usd&include_24hr_change=true`;
- const result = await fetch(uri).then(res => res.json());
- return {
- price: result[nativeAssetId].usd,
- price24HChange: result[nativeAssetId].usd_24h_change
- };
-}
-
-export async function getTokensPrice(
- chainId: number,
- addresses: string[]
-): Promise {
- const { env } = useConfig();
-
- const max = 175;
- const pages = Math.ceil(addresses.length / max);
- const promises = [];
- Array.from(Array(pages)).forEach((x, i) => {
- const addressString = addresses
- .slice(max * i, max * (i + 1))
- .map(address => getChainAddress(chainId, address));
- const uri = `https://api.coingecko.com/api/v3/simple/token_price/${getPlatformId(
- env.NETWORK
- )}?contract_addresses=${addressString}&vs_currencies=usd&include_24hr_change=true`;
- // @ts-ignore
- promises.push(fetch(uri).then(res => res.json()));
- });
- const results = await Promise.all(promises);
- const prices = results.reduce(
- (obj, result: any) => ({ ...obj, ...result }),
- {}
- );
- return Object.fromEntries(
- Object.entries(prices).map((token: any) => [
- getOriginalAddress(chainId, token[0]),
- {
- price: token[1].usd,
- price24HChange: token[1].usd_24h_change
- }
- ])
- );
-}
-
-export async function getTokensHistoricalPrice(
- chainId: number,
- addresses: string[],
- days: number
-): Promise {
- const { env } = useConfig();
-
- const DAY = 60 * 60 * 24;
- const now = Math.floor(Date.now() / 1000);
- const end = now - (now % DAY);
- const start = end - days * DAY;
- const priceRequests = addresses.map(address => {
- const chainAddress = getChainAddress(chainId, address);
- const url = `https://api.coingecko.com/api/v3/coins/${getPlatformId(
- env.NETWORK
- )}/contract/${chainAddress}/market_chart/range?vs_currency=usd&from=${start}&to=${end}`;
- const request = fetch(url).then(res => res.json());
- return request;
- });
- const results = await Promise.all(priceRequests);
-
- const assetPrices = Object.fromEntries(
- addresses.map((chainAddress, index) => {
- const address = getOriginalAddress(chainId, chainAddress);
- const result = (results[index] as any).prices as number[][];
- const prices = {};
- let dayTimestamp = start;
- for (const key in result) {
- const value = result[key];
- const [timestamp, price] = value;
- if (timestamp > dayTimestamp * 1000) {
- prices[dayTimestamp * 1000] = price;
- dayTimestamp += DAY;
- }
- }
- return [address, prices];
- })
- );
-
- const prices = {};
- for (const asset in assetPrices) {
- const assetPrice = assetPrices[asset];
- for (const timestamp in assetPrice) {
- const price = assetPrice[timestamp];
- if (!(timestamp in prices)) {
- prices[timestamp] = [];
- }
- prices[timestamp].push(price);
- }
- }
- return prices;
-}
diff --git a/src/services/coingecko/api/price.service.ts b/src/services/coingecko/api/price.service.ts
index ed1ea905bf..002b45784f 100644
--- a/src/services/coingecko/api/price.service.ts
+++ b/src/services/coingecko/api/price.service.ts
@@ -5,66 +5,137 @@ import {
getPlatformId
} from '../coingecko.service';
import { TOKENS } from '@/constants/tokens';
-import ConfigService from '@/services/config/config.service';
+import { configService as _configService } from '@/services/config/config.service';
import { invert } from 'lodash';
import { returnChecksum } from '@/lib/decorators/return-checksum.decorator';
+import { retryPromiseWithDelay } from '@/lib/utils/promise';
+import { twentyFourHourseInSecs } from '@/composables/useTime';
-// TYPES
+/**
+ * TYPES
+ */
export type Price = { [fiat: string]: number };
export type PriceResponse = { [id: string]: Price };
export type TokenPrices = { [address: string]: Price };
+export interface HistoricalPriceResponse {
+ market_caps: number[][];
+ prices: number[][];
+ total_volumes: number[][];
+}
+export type HistoricalPrices = { [timestamp: string]: number[] };
export class PriceService {
client: CoingeckoClient;
fiatParam: string;
- baseEndpoint: string;
appNetwork: string;
platformId: string;
nativeAssetId: string;
+ nativeAssetAddress: string;
constructor(
service: CoingeckoService,
- private readonly configService = new ConfigService()
+ private readonly configService = _configService
) {
this.client = service.client;
this.fiatParam = service.supportedFiat;
- this.baseEndpoint = '/simple';
- this.appNetwork = configService.network.key;
+ this.appNetwork = this.configService.network.key;
this.platformId = getPlatformId(this.appNetwork);
this.nativeAssetId = getNativeAssetId(this.appNetwork);
+ this.nativeAssetAddress = this.configService.network.nativeAsset.address;
}
- async getEther(): Promise {
+ async getNativeAssetPrice(): Promise {
try {
- const { ethereum: price } = await this.client.get(
- `${this.baseEndpoint}/price?ids=${this.nativeAssetId}&vs_currencies=${this.fiatParam}`
+ const response = await this.client.get(
+ `/simple/price?ids=${this.nativeAssetId}&vs_currencies=${this.fiatParam}`
);
- return price;
+ return response[this.nativeAssetId];
} catch (error) {
console.error('Unable to fetch Ether price', error);
throw error;
}
}
- async getTokens(addresses: string[]): Promise {
+ /**
+ * Rate limit for the CoinGecko API is 10 calls each second per IP address.
+ */
+ async getTokens(
+ addresses: string[],
+ addressesPerRequest = 100
+ ): Promise {
try {
+ if (addresses.length / addressesPerRequest > 10)
+ throw new Error('To many requests for rate limit.');
+
addresses = addresses.map(address => this.addressMapIn(address));
- const max = 100;
- const pages = Math.ceil(addresses.length / max);
+ const pageCount = Math.ceil(addresses.length / addressesPerRequest);
+ const pages = Array.from(Array(pageCount).keys());
const requests: Promise[] = [];
- Array.from(Array(pages).keys()).forEach(page => {
- const addressString = addresses.slice(max * page, max * (page + 1));
- const endpoint = `${this.baseEndpoint}/token_price/${this.platformId}?contract_addresses=${addressString}&vs_currencies=${this.fiatParam}`;
- requests.push(this.client.get(endpoint));
+
+ pages.forEach(page => {
+ const addressString = addresses.slice(
+ addressesPerRequest * page,
+ addressesPerRequest * (page + 1)
+ );
+ const endpoint = `/simple/token_price/${this.platformId}?contract_addresses=${addressString}&vs_currencies=${this.fiatParam}`;
+ const request = retryPromiseWithDelay(
+ this.client.get(endpoint),
+ 3,
+ 2000
+ );
+ requests.push(request);
});
+
const paginatedResults = await Promise.all(requests);
- return this.parsePaginatedTokens(paginatedResults);
+ const results = this.parsePaginatedTokens(paginatedResults);
+
+ // Inject native asset price if included in requested addresses
+ if (addresses.includes(this.nativeAssetAddress)) {
+ results[this.nativeAssetAddress] = await this.getNativeAssetPrice();
+ }
+
+ return results;
} catch (error) {
console.error('Unable to fetch token prices', addresses, error);
throw error;
}
}
+ async getTokensHistorical(
+ addresses: string[],
+ days: number,
+ addressesPerRequest = 1
+ ) {
+ if (addresses.length / addressesPerRequest > 10)
+ throw new Error('To many requests for rate limit.');
+
+ const now = Math.floor(Date.now() / 1000);
+ const end = now - (now % twentyFourHourseInSecs);
+ const start = end - days * twentyFourHourseInSecs;
+
+ addresses = addresses.map(address => this.addressMapIn(address));
+ const requests: Promise[] = [];
+
+ addresses.forEach(address => {
+ const endpoint = `/coins/${this.platformId}/contract/${address}/market_chart/range?vs_currency=${this.fiatParam}&from=${start}&to=${end}`;
+ const request = retryPromiseWithDelay(
+ this.client.get(endpoint),
+ 3, // retryCount
+ 2000 // delayTime
+ );
+ requests.push(request);
+ });
+
+ const paginatedResults = await Promise.all(requests);
+ const results = this.parseHistoricalPrices(
+ paginatedResults,
+ addresses,
+ start
+ );
+
+ return results;
+ }
+
private parsePaginatedTokens(paginatedResults: TokenPrices[]): TokenPrices {
const results = paginatedResults.reduce(
(result, page) => ({ ...result, ...page }),
@@ -78,11 +149,48 @@ export class PriceService {
return Object.fromEntries(parsedEntries);
}
+ private parseHistoricalPrices(
+ results: HistoricalPriceResponse[],
+ addresses: string[],
+ start: number
+ ): HistoricalPrices {
+ const assetPrices = Object.fromEntries(
+ addresses.map((address, index) => {
+ address = this.addressMapOut(address);
+ const result = results[index].prices;
+ const prices = {};
+ let dayTimestamp = start;
+ for (const key in result) {
+ const value = result[key];
+ const [timestamp, price] = value;
+ if (timestamp > dayTimestamp * 1000) {
+ prices[dayTimestamp * 1000] = price;
+ dayTimestamp += twentyFourHourseInSecs;
+ }
+ }
+ return [address, prices];
+ })
+ );
+
+ const prices = {};
+ for (const asset in assetPrices) {
+ const assetPrice = assetPrices[asset];
+ for (const timestamp in assetPrice) {
+ const price = assetPrice[timestamp];
+ if (!(timestamp in prices)) {
+ prices[timestamp] = [];
+ }
+ prices[timestamp].push(price);
+ }
+ }
+ return prices;
+ }
+
/**
* Map address to mainnet address if app network is a testnet
*/
@returnChecksum()
- private addressMapIn(address: string): string {
+ public addressMapIn(address: string): string {
const addressMap = TOKENS.Prices.ChainMap[this.appNetwork];
if (!addressMap) return address;
return addressMap[address.toLowerCase()] || address;
@@ -92,7 +200,7 @@ export class PriceService {
* Map mainnet address back to testnet address
*/
@returnChecksum()
- private addressMapOut(address: string): string {
+ public addressMapOut(address: string): string {
const addressMap = TOKENS.Prices.ChainMap[this.appNetwork];
if (!addressMap) return address;
return invert(addressMap)[address.toLowerCase()] || address;
diff --git a/src/services/coingecko/coingecko.client.ts b/src/services/coingecko/coingecko.client.ts
index fb20ab588c..c74ed8234c 100644
--- a/src/services/coingecko/coingecko.client.ts
+++ b/src/services/coingecko/coingecko.client.ts
@@ -12,3 +12,5 @@ export class CoingeckoClient {
return data;
}
}
+
+export const coingeckoClient = new CoingeckoClient();
diff --git a/src/services/coingecko/coingecko.service.ts b/src/services/coingecko/coingecko.service.ts
index 3587ad17c5..6e592e7d3b 100644
--- a/src/services/coingecko/coingecko.service.ts
+++ b/src/services/coingecko/coingecko.service.ts
@@ -1,7 +1,6 @@
+import { SUPPORTED_FIAT } from '@/constants/currency';
import { PriceService } from './api/price.service';
-import { CoingeckoClient } from './coingecko.client';
-
-const SUPPORTED_FIAT = ['usd'];
+import { coingeckoClient } from './coingecko.client';
export const getNativeAssetId = (chainId: string): string => {
const mapping = {
@@ -28,10 +27,12 @@ export class CoingeckoService {
prices: PriceService;
constructor(
- public readonly client = new CoingeckoClient(),
+ public readonly client = coingeckoClient,
priceServiceClass = PriceService
) {
this.supportedFiat = SUPPORTED_FIAT.join(',');
this.prices = new priceServiceClass(this);
}
}
+
+export const coingeckoService = new CoingeckoService();
diff --git a/src/services/config/config.service.ts b/src/services/config/config.service.ts
index 9768d3e4c1..1a9a55c733 100644
--- a/src/services/config/config.service.ts
+++ b/src/services/config/config.service.ts
@@ -7,6 +7,7 @@ interface Env {
APP_DOMAIN: string;
IPFS_NODE: string;
BLOCKNATIVE_DAPP_ID: string;
+ ALCHEMY_KEY: string;
PORTIS_DAPP_ID: string;
ENABLE_STABLE_POOLS: boolean;
}
@@ -19,6 +20,8 @@ export default class ConfigService {
APP_DOMAIN: process.env.VUE_APP_DOMAIN || 'app.balancer.fi',
IPFS_NODE: process.env.VUE_APP_IPFS_NODE || 'ipfs.io',
BLOCKNATIVE_DAPP_ID: process.env.VUE_APP_BLOCKNATIVE_DAPP_ID || 'xxx',
+ ALCHEMY_KEY:
+ process.env.VUE_APP_ALCHEMY_KEY || 'cQGZUiTLRCFsQS7kbRxPJK4eH4fTTu88',
ENABLE_STABLE_POOLS: process.env.VUE_APP_ENABLE_STABLE_POOLS === 'true',
PORTIS_DAPP_ID:
process.env.PORTIS_DAPP_ID || '3f1c3cfc-7dd5-4e8a-aa03-71ff7396d9fe'
diff --git a/src/services/gnosis/utils.ts b/src/services/gnosis/utils.ts
index 2de84ebc6f..ad42dd24e5 100644
--- a/src/services/gnosis/utils.ts
+++ b/src/services/gnosis/utils.ts
@@ -1,10 +1,10 @@
-import { APP_NETWORK_ID } from '@/constants/network';
-import { ETHER } from '@/constants/tokenlists';
-import configs from '@/lib/config';
+import { configService } from '../config/config.service';
export function normalizeTokenAddress(tokenAddress: string) {
- if (tokenAddress.toLowerCase() === ETHER.address.toLowerCase()) {
- return configs[APP_NETWORK_ID].addresses.weth;
+ const nativeAssetAddress = configService.network.nativeAsset.address;
+
+ if (tokenAddress.toLowerCase() === nativeAssetAddress.toLowerCase()) {
+ return configService.network.addresses.weth;
}
return tokenAddress;
diff --git a/src/services/ipfs/ipfs.service.ts b/src/services/ipfs/ipfs.service.ts
index fa4c77105e..6a38ca64dd 100644
--- a/src/services/ipfs/ipfs.service.ts
+++ b/src/services/ipfs/ipfs.service.ts
@@ -8,7 +8,7 @@ export default class IpfsService {
this.gateway = this.configService.env.IPFS_NODE;
}
- async get(hash: string, protocol = 'ipfs'): Promise {
+ async get(hash: string, protocol = 'ipfs'): Promise {
const { data } = await axios.get(
`https://${this.gateway}/${protocol}/${hash}`
);
diff --git a/src/services/pool/calculator/calculator.sevice.ts b/src/services/pool/calculator/calculator.sevice.ts
index bc7277ae6f..3722ae74ca 100644
--- a/src/services/pool/calculator/calculator.sevice.ts
+++ b/src/services/pool/calculator/calculator.sevice.ts
@@ -1,14 +1,13 @@
import BigNumber from 'bignumber.js';
import { parseUnits, formatUnits } from '@ethersproject/units';
import { BigNumberish } from '@ethersproject/bignumber';
-import { FixedPointNumber } from '@balancer-labs/sor2/dist/math/FixedPointNumber';
-
import { FullPool } from '@/services/balancer/subgraph/types';
-
-import { TokenMap } from '@/types';
-
import Weighted from './weighted';
import Stable from './stable';
+import { TokenInfoMap } from '@/types/TokenList';
+import { BalanceMap } from '@/services/token/concerns/balances.concern';
+import { ComputedRef } from 'vue';
+import { isStable } from '@/composables/usePool';
interface Amounts {
send: string[];
@@ -25,7 +24,8 @@ type PoolAction = 'join' | 'exit';
export default class CalculatorService {
pool: FullPool;
- allTokens: TokenMap;
+ allTokens: TokenInfoMap;
+ balances: ComputedRef;
action: PoolAction;
types = ['send', 'receive'];
weighted: Weighted;
@@ -33,19 +33,21 @@ export default class CalculatorService {
constructor(
pool: FullPool,
- allTokens: TokenMap,
+ allTokens: TokenInfoMap,
+ balances: ComputedRef,
action: PoolAction,
weightedClass = Weighted,
stableClass = Stable
) {
this.pool = pool;
this.allTokens = allTokens;
+ this.balances = balances;
this.action = action;
this.weighted = new weightedClass(this);
this.stable = new stableClass(this);
}
- public setAllTokens(tokens: TokenMap): void {
+ public setAllTokens(tokens: TokenInfoMap): void {
this.allTokens = tokens;
}
@@ -63,7 +65,7 @@ export default class CalculatorService {
return this.weighted.priceImpact(tokenAmounts, opts);
}
- public exactTokensInForBPTOut(tokenAmounts: string[]): FixedPointNumber {
+ public exactTokensInForBPTOut(tokenAmounts: string[]): BigNumber {
if (this.isStablePool) {
return this.stable.exactTokensInForBPTOut(tokenAmounts);
}
@@ -73,17 +75,14 @@ export default class CalculatorService {
public exactBPTInForTokenOut(
bptAmount: string,
tokenIndex: number
- ): FixedPointNumber {
+ ): BigNumber {
if (this.isStablePool) {
return this.stable.exactBPTInForTokenOut(bptAmount, tokenIndex);
}
return this.weighted.exactBPTInForTokenOut(bptAmount, tokenIndex);
}
- public bptInForExactTokenOut(
- amount: string,
- tokenIndex: number
- ): FixedPointNumber {
+ public bptInForExactTokenOut(amount: string, tokenIndex: number): BigNumber {
if (this.isStablePool) {
return this.stable.bptInForExactTokenOut(amount, tokenIndex);
}
@@ -100,13 +99,13 @@ export default class CalculatorService {
this.pool.tokenAddresses.forEach((token, tokenIndex) => {
let hasBalance = true;
- const balance = this.allTokens[token].balance.toString();
+ const balance = this.balances.value[token];
const amounts = this.propAmountsGiven(balance, tokenIndex, type);
amounts.send.forEach((amount, amountIndex) => {
const greaterThanBalance =
Number(amount) >
- Number(this.allTokens[this.tokenOf(type, amountIndex)].balance);
+ Number(this.balances.value[this.tokenOf(type, amountIndex)]);
if (greaterThanBalance) hasBalance = false;
});
@@ -205,11 +204,11 @@ export default class CalculatorService {
}
public get bptBalance(): string {
- return this.allTokens[this.pool.address].balance;
+ return this.balances.value[this.pool.address];
}
public get isStablePool(): boolean {
- return this.pool.poolType === 'Stable';
+ return isStable(this.pool);
}
public get sendTokens(): string[] {
diff --git a/src/services/pool/calculator/stable.ts b/src/services/pool/calculator/stable.ts
index 120c3a68a0..944bc10ec1 100644
--- a/src/services/pool/calculator/stable.ts
+++ b/src/services/pool/calculator/stable.ts
@@ -4,15 +4,9 @@ import { parseUnits, formatUnits } from '@ethersproject/units';
import { bnum } from '@/lib/utils';
import BigNumber from 'bignumber.js';
-import {
- _exactTokensInForBPTOut,
- _exactBPTInForTokenOut,
- _bptInForExactTokensOut
-} from '@balancer-labs/sor2/dist/pools/stablePool/stableMathEvm';
import { BPTForTokensZeroPriceImpact as _bptForTokensZeroPriceImpact } from '@balancer-labs/sor2/dist/frontendHelpers/stableHelpers';
-import { fnum } from '@balancer-labs/sor2/dist/math/lib/fixedPoint';
-import { FixedPointNumber } from '@balancer-labs/sor2/dist/math/FixedPointNumber';
import { BigNumberish } from '@ethersproject/bignumber';
+import * as SDK from '@georgeroman/balancer-v2-pools';
/**
* The stableMathEvm works with all values scaled to 18 decimals,
@@ -20,25 +14,27 @@ import { BigNumberish } from '@ethersproject/bignumber';
*/
export default class Stable {
calc: Calculator;
+ AMP_PRECISION = bnum(1000);
constructor(calculator) {
this.calc = calculator;
}
- public exactTokensInForBPTOut(tokenAmounts: string[]): FixedPointNumber {
- const amp = fnum(this.calc.pool.onchain.amp?.toString() || '0');
+ public exactTokensInForBPTOut(tokenAmounts: string[]): BigNumber {
+ const amp = bnum(this.calc.pool.onchain.amp?.toString() || '0');
+ const ampAdjusted = this.adjustAmp(amp);
const denormAmounts = this.calc.denormAmounts(
tokenAmounts,
this.calc.poolTokenDecimals.map(() => 18)
);
- const amounts = denormAmounts.map(a => fnum(a.toString()));
+ const amounts = denormAmounts.map(a => bnum(a.toString()));
- const bptOut = _exactTokensInForBPTOut(
+ const bptOut = SDK.StableMath._calcBptOutGivenExactTokensIn(
+ ampAdjusted,
this.scaledBalances,
- amp,
amounts,
this.scaledPoolTotalSupply,
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolSwapFee.toString())
);
return this.scaleOutput(
@@ -48,20 +44,22 @@ export default class Stable {
);
}
- public bptInForExactTokensOut(tokenAmounts: string[]): FixedPointNumber {
- const amp = fnum(this.calc.pool.onchain.amp?.toString() || '0');
+ public bptInForExactTokensOut(tokenAmounts: string[]): BigNumber {
+ const amp = bnum(this.calc.pool.onchain.amp?.toString() || '0');
+ const ampAdjusted = this.adjustAmp(amp);
+
const denormAmounts = this.calc.denormAmounts(
tokenAmounts,
this.calc.poolTokenDecimals.map(() => 18)
);
- const amounts = denormAmounts.map(a => fnum(a.toString()));
+ const amounts = denormAmounts.map(a => bnum(a.toString()));
- const bptIn = _bptInForExactTokensOut(
+ const bptIn = SDK.StableMath._calcBptInGivenExactTokensOut(
+ ampAdjusted,
this.scaledBalances,
- amp,
amounts,
this.scaledPoolTotalSupply,
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolSwapFee.toString())
);
return this.scaleOutput(
@@ -71,22 +69,20 @@ export default class Stable {
);
}
- public bptInForExactTokenOut(
- amount: string,
- tokenIndex: number
- ): FixedPointNumber {
- const amp = fnum(this.calc.pool.onchain.amp?.toString() || '0');
+ public bptInForExactTokenOut(amount: string, tokenIndex: number): BigNumber {
+ const amp = bnum(this.calc.pool.onchain.amp?.toString() || '0');
+ const ampAdjusted = this.adjustAmp(amp);
const amounts = this.calc.pool.tokenAddresses.map((address, i) => {
- if (i === tokenIndex) return fnum(parseUnits(amount, 18).toString());
- return fnum('0');
+ if (i === tokenIndex) return bnum(parseUnits(amount, 18).toString());
+ return bnum('0');
});
- const bptIn = _bptInForExactTokensOut(
+ const bptIn = SDK.StableMath._calcBptInGivenExactTokensOut(
+ ampAdjusted,
this.scaledBalances,
- amp,
amounts,
this.scaledPoolTotalSupply,
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolSwapFee.toString())
);
return this.scaleOutput(
@@ -99,17 +95,18 @@ export default class Stable {
public exactBPTInForTokenOut(
bptAmount: string,
tokenIndex: number
- ): FixedPointNumber {
- const amp = fnum(this.calc.pool.onchain.amp?.toString() || '0');
- const bptAmountIn = fnum(parseUnits(bptAmount, 18).toString());
+ ): BigNumber {
+ const amp = bnum(this.calc.pool.onchain.amp?.toString() || '0');
+ const ampAdjusted = this.adjustAmp(amp);
+ const bptAmountIn = bnum(parseUnits(bptAmount, 18).toString());
- const tokenAmountOut = _exactBPTInForTokenOut(
- tokenIndex,
+ const tokenAmountOut = SDK.StableMath._calcTokenOutGivenExactBptIn(
+ ampAdjusted,
this.scaledBalances,
- amp,
+ tokenIndex,
bptAmountIn,
this.scaledPoolTotalSupply,
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolSwapFee.toString())
);
return this.scaleOutput(
@@ -120,7 +117,7 @@ export default class Stable {
}
public priceImpact(tokenAmounts: string[], opts: PiOptions): BigNumber {
- let bptAmount: FixedPointNumber | BigNumberish;
+ let bptAmount: BigNumber | BigNumberish;
let bptZeroPriceImpact: BigNumber;
if (this.calc.action === 'join') {
@@ -162,7 +159,7 @@ export default class Stable {
// PRIVATE FUNCTIONS
private bptForTokensZeroPriceImpact(tokenAmounts: string[]): BigNumber {
- const amp = fnum(this.calc.pool.onchain.amp?.toString() || '0');
+ const amp = bnum(this.calc.pool.onchain.amp?.toString() || '0');
const denormAmounts = this.calc.denormAmounts(
tokenAmounts,
this.calc.poolTokenDecimals
@@ -182,37 +179,42 @@ export default class Stable {
return bptZeroImpact;
}
- private get scaledBalances(): FixedPointNumber[] {
+ private get scaledBalances(): BigNumber[] {
return this.calc.poolTokenBalances.map((balance, i) => {
const normalizedBalance = formatUnits(
balance,
this.calc.poolTokenDecimals[i]
);
const scaledBalance = parseUnits(normalizedBalance, 18);
- return fnum(scaledBalance.toString());
+ return bnum(scaledBalance.toString());
});
}
- private get scaledPoolTotalSupply(): FixedPointNumber {
+ private get scaledPoolTotalSupply(): BigNumber {
const normalizedSupply = formatUnits(
this.calc.poolTotalSupply,
this.calc.poolDecimals
);
const scaledSupply = parseUnits(normalizedSupply, 18);
- return fnum(scaledSupply.toString());
+ return bnum(scaledSupply.toString());
}
private scaleOutput(
amount: string,
decimals: number,
rounding: BigNumber.RoundingMode
- ): FixedPointNumber {
+ ): BigNumber {
const normalizedAmount = bnum(formatUnits(amount, 18)).toFixed(
decimals,
rounding
);
const scaledAmount = parseUnits(normalizedAmount, decimals);
- return fnum(scaledAmount.toString());
+ return bnum(scaledAmount.toString());
+ }
+
+ // Solidity maths uses precison method for amp that must be replicated
+ private adjustAmp(amp: BigNumber): BigNumber {
+ return amp.times(this.AMP_PRECISION);
}
}
diff --git a/src/services/pool/calculator/weighted.ts b/src/services/pool/calculator/weighted.ts
index bd39f04ddf..f94775fe3d 100644
--- a/src/services/pool/calculator/weighted.ts
+++ b/src/services/pool/calculator/weighted.ts
@@ -4,15 +4,8 @@ import { parseUnits, formatUnits } from '@ethersproject/units';
import { bnum } from '@/lib/utils';
import BigNumber from 'bignumber.js';
-import {
- _exactTokensInForBPTOut,
- _exactBPTInForTokenOut,
- _bptInForExactTokenOut,
- _bptInForExactTokensOut
-} from '@balancer-labs/sor2/dist/pools/weightedPool/weightedMathEvm';
+import * as SDK from '@georgeroman/balancer-v2-pools';
import { BPTForTokensZeroPriceImpact as _bptForTokensZeroPriceImpact } from '@balancer-labs/sor2/dist/frontendHelpers/weightedHelpers';
-import { fnum } from '@balancer-labs/sor2/dist/math/lib/fixedPoint';
-import { FixedPointNumber } from '@balancer-labs/sor2/dist/math/FixedPointNumber';
export default class Weighted {
calc: Calculator;
@@ -21,59 +14,56 @@ export default class Weighted {
this.calc = calculator;
}
- public exactTokensInForBPTOut(tokenAmounts: string[]): FixedPointNumber {
- const balances = this.calc.poolTokenBalances.map(b => fnum(b.toString()));
- const weights = this.calc.poolTokenWeights.map(w => fnum(w.toString()));
+ public exactTokensInForBPTOut(tokenAmounts: string[]): BigNumber {
+ const balances = this.calc.poolTokenBalances.map(b => bnum(b.toString()));
+ const weights = this.calc.poolTokenWeights.map(w => bnum(w.toString()));
const denormAmounts = this.calc.denormAmounts(
tokenAmounts,
this.calc.poolTokenDecimals
);
- const amounts = denormAmounts.map(a => fnum(a.toString()));
+ const amounts = denormAmounts.map(a => bnum(a.toString()));
- return _exactTokensInForBPTOut(
+ return SDK.WeightedMath._calcBptOutGivenExactTokensIn(
balances,
weights,
amounts,
- fnum(this.calc.poolTotalSupply.toString()),
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolTotalSupply.toString()),
+ bnum(this.calc.poolSwapFee.toString())
);
}
- public bptInForExactTokensOut(tokenAmounts: string[]): FixedPointNumber {
- const balances = this.calc.poolTokenBalances.map(b => fnum(b.toString()));
- const weights = this.calc.poolTokenWeights.map(w => fnum(w.toString()));
+ public bptInForExactTokensOut(tokenAmounts: string[]): BigNumber {
+ const balances = this.calc.poolTokenBalances.map(b => bnum(b.toString()));
+ const weights = this.calc.poolTokenWeights.map(w => bnum(w.toString()));
const denormAmounts = this.calc.denormAmounts(
tokenAmounts,
this.calc.poolTokenDecimals
);
- const amounts = denormAmounts.map(a => fnum(a.toString()));
+ const amounts = denormAmounts.map(a => bnum(a.toString()));
- return _bptInForExactTokensOut(
+ return SDK.WeightedMath._calcBptInGivenExactTokensOut(
balances,
weights,
amounts,
- fnum(this.calc.poolTotalSupply.toString()),
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolTotalSupply.toString()),
+ bnum(this.calc.poolSwapFee.toString())
);
}
- public bptInForExactTokenOut(
- amount: string,
- tokenIndex: number
- ): FixedPointNumber {
- const tokenBalance = fnum(
+ public bptInForExactTokenOut(amount: string, tokenIndex: number): BigNumber {
+ const tokenBalance = bnum(
this.calc.poolTokenBalances[tokenIndex].toString()
);
- const tokenNormalizedWeight = fnum(
+ const tokenNormalizedWeight = bnum(
this.calc.poolTokenWeights[tokenIndex].toString()
);
- const bptAmountIn = fnum(
+ const bptAmountIn = bnum(
parseUnits(amount, this.calc.poolTokenDecimals[tokenIndex]).toString()
);
- const bptTotalSupply = fnum(this.calc.poolTotalSupply.toString());
- const swapFee = fnum(this.calc.poolSwapFee.toString());
+ const bptTotalSupply = bnum(this.calc.poolTotalSupply.toString());
+ const swapFee = bnum(this.calc.poolSwapFee.toString());
- return _bptInForExactTokenOut(
+ return SDK.WeightedMath._calcBptInGivenExactTokenOut(
tokenBalance,
tokenNormalizedWeight,
bptAmountIn,
@@ -85,23 +75,23 @@ export default class Weighted {
public exactBPTInForTokenOut(
bptAmount: string,
tokenIndex: number
- ): FixedPointNumber {
- const tokenBalance = fnum(
+ ): BigNumber {
+ const tokenBalance = bnum(
this.calc.poolTokenBalances[tokenIndex].toString()
);
- const tokenNormalizedWeight = fnum(
+ const tokenNormalizedWeight = bnum(
this.calc.poolTokenWeights[tokenIndex].toString()
);
- const bptAmountIn = fnum(
+ const bptAmountIn = bnum(
parseUnits(bptAmount, this.calc.poolDecimals).toString()
);
- return _exactBPTInForTokenOut(
+ return SDK.WeightedMath._calcTokenOutGivenExactBptIn(
tokenBalance,
tokenNormalizedWeight,
bptAmountIn,
- fnum(this.calc.poolTotalSupply.toString()),
- fnum(this.calc.poolSwapFee.toString())
+ bnum(this.calc.poolTotalSupply.toString()),
+ bnum(this.calc.poolSwapFee.toString())
);
}
diff --git a/src/services/pool/exchange/index.ts b/src/services/pool/exchange/index.ts
index f7ccbf616f..01ff0e00c8 100644
--- a/src/services/pool/exchange/index.ts
+++ b/src/services/pool/exchange/index.ts
@@ -3,20 +3,20 @@ import configs from '@/lib/config';
import { callStatic, sendTransaction } from '@/lib/utils/balancer/web3';
import { default as vaultAbi } from '@/lib/abi/Vault.json';
import { default as helpersAbi } from '@/lib/abi/BalancerHelpers.json';
-import { TokenMap } from '@/types';
import JoinParams from './serializers/JoinParams';
import ExitParams from './serializers/ExitParams';
import { FullPool } from '@/services/balancer/subgraph/types';
import { JsonRpcProvider, Web3Provider } from '@ethersproject/providers';
+import { TokenInfoMap } from '@/types/TokenList';
export default class Exchange {
pool: FullPool;
network: string;
vaultAddress: string;
helpersAddress: string;
- tokens: TokenMap;
+ tokens: TokenInfoMap;
- constructor(pool: FullPool, network: string, tokens: TokenMap) {
+ constructor(pool: FullPool, network: string, tokens: TokenInfoMap) {
this.pool = pool;
this.network = network;
this.tokens = tokens;
diff --git a/src/services/pool/exchange/serializers/ExitParams.ts b/src/services/pool/exchange/serializers/ExitParams.ts
index 8954ad3644..349d3941e7 100644
--- a/src/services/pool/exchange/serializers/ExitParams.ts
+++ b/src/services/pool/exchange/serializers/ExitParams.ts
@@ -3,6 +3,7 @@ import { encodeExitStablePool } from '@/lib/utils/balancer/stablePoolEncoding';
import { encodeExitWeightedPool } from '@/lib/utils/balancer/weightedPoolEncoding';
import { parseUnits } from '@ethersproject/units';
import { BigNumberish } from '@ethersproject/bignumber';
+import { isStable } from '@/composables/usePool';
export default class ExitParams {
private exchange: PoolExchange;
@@ -12,7 +13,7 @@ export default class ExitParams {
constructor(exchange) {
this.exchange = exchange;
- this.isStablePool = exchange.pool.poolType === 'Stable';
+ this.isStablePool = isStable(exchange.pool);
this.dataEncodeFn = this.isStablePool
? encodeExitStablePool
: encodeExitWeightedPool;
diff --git a/src/services/pool/exchange/serializers/JoinParams.ts b/src/services/pool/exchange/serializers/JoinParams.ts
index 7b240d600c..79885e8b77 100644
--- a/src/services/pool/exchange/serializers/JoinParams.ts
+++ b/src/services/pool/exchange/serializers/JoinParams.ts
@@ -3,6 +3,7 @@ import { encodeJoinStablePool } from '@/lib/utils/balancer/stablePoolEncoding';
import { encodeJoinWeightedPool } from '@/lib/utils/balancer/weightedPoolEncoding';
import { parseUnits } from '@ethersproject/units';
import { BigNumberish } from '@ethersproject/bignumber';
+import { isStable } from '@/composables/usePool';
export default class JoinParams {
private exchange: PoolExchange;
@@ -12,7 +13,7 @@ export default class JoinParams {
constructor(exchange) {
this.exchange = exchange;
- this.isStablePool = exchange.pool.poolType === 'Stable';
+ this.isStablePool = isStable(exchange.pool);
this.dataEncodeFn = this.isStablePool
? encodeJoinStablePool
: encodeJoinWeightedPool;
diff --git a/src/services/rpc-provider/rpc-provider.service.ts b/src/services/rpc-provider/rpc-provider.service.ts
index 939b2c9fc6..06689c297b 100644
--- a/src/services/rpc-provider/rpc-provider.service.ts
+++ b/src/services/rpc-provider/rpc-provider.service.ts
@@ -1,10 +1,15 @@
import { WebSocketProvider, JsonRpcProvider } from '@ethersproject/providers';
import ConfigService, { configService } from '@/services/config/config.service';
+import { AlchemyProvider } from '@ethersproject/providers';
type NewBlockHandler = (blockNumber: number) => any;
const _wsProvider = new WebSocketProvider(configService.network.ws);
const _jsonProvider = new JsonRpcProvider(configService.network.rpc);
+const _alchemyProvider = new AlchemyProvider(
+ configService.network.chainId,
+ configService.env.ALCHEMY_KEY
+);
export default class RpcProviderService {
network: string;
@@ -12,7 +17,8 @@ export default class RpcProviderService {
constructor(
private readonly config: ConfigService = configService,
readonly wsProvider: WebSocketProvider = _wsProvider,
- readonly jsonProvider: JsonRpcProvider = _jsonProvider
+ readonly jsonProvider: JsonRpcProvider = _jsonProvider,
+ readonly alchemyProvider: AlchemyProvider = _alchemyProvider
) {
this.network = this.config.network.shortName;
}
diff --git a/src/services/token-list/token-list.service.ts b/src/services/token-list/token-list.service.ts
index 2c44b13059..84ccda5f59 100644
--- a/src/services/token-list/token-list.service.ts
+++ b/src/services/token-list/token-list.service.ts
@@ -1,26 +1,68 @@
import axios from 'axios';
import { JsonRpcProvider } from '@ethersproject/providers';
-import { TokenList, TokenListDict } from '@/types/TokenList';
-import TOKEN_LISTS from '@/constants/tokenlists';
+import { TokenList, TokenListMap } from '@/types/TokenList';
import { rpcProviderService as _rpcProviderService } from '@/services/rpc-provider/rpc-provider.service';
import { ipfsService as _ipfsService } from '../ipfs/ipfs.service';
+import { TOKEN_LIST_MAP } from '@/constants/tokenlists';
+import { configService as _configService } from '../config/config.service';
+
+interface TokenListUris {
+ All: string[];
+ Balancer: {
+ All: string[];
+ // Compliant list for exchange
+ Default: string;
+ // Extended list to include LBP tokens
+ Vetted: string;
+ };
+ Approved: string[];
+ External: string[];
+}
export default class TokenListService {
provider: JsonRpcProvider;
+ appNetworkKey: string;
constructor(
+ private readonly configService = _configService,
private readonly rpcProviderService = _rpcProviderService,
private readonly ipfsService = _ipfsService
) {
this.provider = this.rpcProviderService.jsonProvider;
+ this.appNetworkKey = this.configService.network.key;
}
- async getAll(): Promise {
- const allFetchFns = TOKEN_LISTS.All.map(uri => this.get(uri));
+ /**
+ * Return all token list URIs for the app network in
+ * a structured object.
+ */
+ public get uris(): TokenListUris {
+ const { Balancer, External } = TOKEN_LIST_MAP[this.appNetworkKey];
+
+ const balancerLists = [Balancer.Default, Balancer.Vetted];
+ const All = [...balancerLists, ...External];
+ const Approved = [Balancer.Default, ...External];
+
+ return {
+ All,
+ Balancer: {
+ All: balancerLists,
+ ...Balancer
+ },
+ Approved,
+ External
+ };
+ }
+
+ /**
+ * Fetch all token list json and return mapped to URI
+ */
+ async getAll(uris: string[] = this.uris.All): Promise {
+ const allFetchFns = uris.map(uri => this.get(uri));
const lists = await Promise.all(
allFetchFns.map(fetchList => fetchList.catch(e => e))
);
- const listsWithKey = lists.map((list, i) => [TOKEN_LISTS.All[i], list]);
+ const listsWithKey = lists.map((list, i) => [uris[i], list]);
const validLists = listsWithKey.filter(list => !(list[1] instanceof Error));
if (validLists.length === 0) {
@@ -41,9 +83,7 @@ export default class TokenListService {
const { data } = await axios.get(uri);
return data;
} else if (protocol === 'ipns') {
- return (await this.ipfsService.get(path, protocol)) as Promise<
- TokenList
- >;
+ return await this.ipfsService.get(path, protocol);
} else {
console.error('Unhandled TokenList protocol', uri);
throw new Error('Unhandled TokenList protocol');
@@ -57,6 +97,8 @@ export default class TokenListService {
private async getByEns(ensName: string): Promise {
const resolver = await this.provider.getResolver(ensName);
const [, ipfsHash] = (await resolver.getContentHash()).split('://');
- return (await this.ipfsService.get(ipfsHash)) as Promise;
+ return await this.ipfsService.get(ipfsHash);
}
}
+
+export const tokenListService = new TokenListService();
diff --git a/src/services/token/concerns/allowances.concern.ts b/src/services/token/concerns/allowances.concern.ts
new file mode 100644
index 0000000000..074e10c159
--- /dev/null
+++ b/src/services/token/concerns/allowances.concern.ts
@@ -0,0 +1,77 @@
+import TokenService from '../token.service';
+import { default as erc20Abi } from '@/lib/abi/ERC20.json';
+import { multicall } from '@/lib/utils/balancer/contract';
+import { BigNumber } from '@ethersproject/bignumber';
+import { getAddress } from '@ethersproject/address';
+import { formatUnits } from '@ethersproject/units';
+import { TokenInfoMap } from '@/types/TokenList';
+
+// TYPES
+export type AllowanceMap = { [address: string]: string };
+export type ContractAllowancesMap = { [address: string]: AllowanceMap };
+
+export default class AllowancesConcern {
+ nativeAssetAddress: string;
+
+ constructor(private readonly service: TokenService) {
+ this.nativeAssetAddress = this.service.configService.network.nativeAsset.address;
+ }
+
+ async get(
+ account: string,
+ contractAddresses: string[],
+ tokens: TokenInfoMap
+ ): Promise {
+ try {
+ // Filter out eth (or native asset) since it's not relevant for allowances.
+ const tokenAddresses = Object.keys(tokens).filter(
+ address => address !== this.nativeAssetAddress
+ );
+
+ const allContractAllowances = await Promise.all(
+ contractAddresses.map(contractAddress =>
+ this.getForContract(account, contractAddress, tokenAddresses, tokens)
+ )
+ );
+
+ const result = Object.fromEntries(
+ contractAddresses.map((contract, i) => [
+ getAddress(contract),
+ allContractAllowances[i]
+ ])
+ );
+ return result;
+ } catch (error) {
+ console.error('Failed to fetch allowances for:', account, error);
+ return {};
+ }
+ }
+
+ async getForContract(
+ account: string,
+ contractAddress: string,
+ tokenAddresses: string[],
+ tokens: TokenInfoMap
+ ): Promise {
+ const network = this.service.configService.network.key;
+ const provider = this.service.rpcProviderService.jsonProvider;
+
+ const allowances: [BigNumber][] = await multicall(
+ network,
+ provider,
+ erc20Abi,
+ tokenAddresses.map(token => [
+ token,
+ 'allowance',
+ [account, contractAddress]
+ ])
+ );
+
+ return Object.fromEntries(
+ tokenAddresses.map((token, i) => [
+ getAddress(token),
+ formatUnits(allowances[i][0].toString(), tokens[token].decimals)
+ ])
+ );
+ }
+}
diff --git a/src/services/token/concerns/balance.concern.ts b/src/services/token/concerns/balance.concern.ts
deleted file mode 100644
index 509378fbd4..0000000000
--- a/src/services/token/concerns/balance.concern.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import TokenService from '../token.service';
-import { default as erc20Abi } from '@/lib/abi/ERC20.json';
-import { multicall } from '@/lib/utils/balancer/contract';
-import { BigNumber } from '@ethersproject/bignumber';
-import { getAddress } from '@ethersproject/address';
-
-// TYPES
-export type BalanceDictionary = { [address: string]: BigNumber };
-
-export default class BalanceConcern {
- constructor(private readonly service: TokenService) {}
-
- async getMany(
- account: string,
- userNetwork: string,
- addresses: string[]
- ): Promise {
- try {
- const provider = this.service.rpcProviderService.getJsonProvider(
- userNetwork
- );
- const balances: [BigNumber][] = await multicall(
- userNetwork,
- provider,
- erc20Abi,
- addresses.map(token => [token, 'balanceOf', [account]])
- );
- return this.associateBalances(balances, addresses);
- } catch (error) {
- console.error('Failed to fetch balances', account, error);
- return {};
- }
- }
-
- private associateBalances(
- balances: [BigNumber][],
- addresses: string[]
- ): BalanceDictionary {
- return Object.fromEntries(
- addresses.map((address, i) => [getAddress(address), balances[i][0]])
- );
- }
-}
diff --git a/src/services/token/concerns/balances.concern.ts b/src/services/token/concerns/balances.concern.ts
new file mode 100644
index 0000000000..a9795108fb
--- /dev/null
+++ b/src/services/token/concerns/balances.concern.ts
@@ -0,0 +1,99 @@
+import TokenService from '../token.service';
+import { default as erc20Abi } from '@/lib/abi/ERC20.json';
+import { multicall } from '@/lib/utils/balancer/contract';
+import { BigNumber } from '@ethersproject/bignumber';
+import { getAddress } from '@ethersproject/address';
+import { TokenInfoMap } from '@/types/TokenList';
+import { formatUnits } from '@ethersproject/units';
+import { chunk } from 'lodash';
+import { JsonRpcProvider } from '@ethersproject/providers';
+
+// TYPES
+export type BalanceMap = { [address: string]: string };
+
+export default class BalancesConcern {
+ network: string;
+ provider: JsonRpcProvider;
+ nativeAssetAddress: string;
+ nativeAssetDecimals: number;
+
+ constructor(private readonly service: TokenService) {
+ this.network = this.service.configService.network.key;
+ this.provider = this.service.rpcProviderService.jsonProvider;
+ this.nativeAssetAddress = this.service.configService.network.nativeAsset.address;
+ this.nativeAssetDecimals = this.service.configService.network.nativeAsset.decimals;
+ }
+
+ async get(account: string, tokens: TokenInfoMap): Promise {
+ const paginatedAddresses = chunk(Object.keys(tokens), 1000);
+ const multicalls: Promise[] = [];
+
+ paginatedAddresses.forEach(addresses => {
+ const request = this.fetchBalances(account, addresses, tokens);
+ multicalls.push(request);
+ });
+
+ const paginatedBalances = await Promise.all(multicalls);
+ const validPages = paginatedBalances.filter(
+ page => !(page instanceof Error)
+ );
+
+ return validPages.reduce((result, current) =>
+ Object.assign(result, current)
+ );
+ }
+
+ private async fetchBalances(
+ account: string,
+ addresses: string[],
+ tokens: TokenInfoMap
+ ): Promise {
+ try {
+ const balanceMap = {};
+
+ // If native asset included in addresses, filter out for
+ // multicall, but fetch indpendently and inject.
+ if (addresses.includes(this.nativeAssetAddress)) {
+ addresses = addresses.filter(
+ address => address !== this.nativeAssetAddress
+ );
+ balanceMap[this.nativeAssetAddress] = await this.fetchNativeBalance(
+ account
+ );
+ }
+
+ const balances: [BigNumber][] = await multicall(
+ this.network,
+ this.provider,
+ erc20Abi,
+ addresses.map(address => [address, 'balanceOf', [account]])
+ );
+
+ return {
+ ...this.associateBalances(balances, addresses, tokens),
+ ...balanceMap
+ };
+ } catch (error) {
+ console.error('Failed to fetch balances for:', addresses);
+ throw error;
+ }
+ }
+
+ private async fetchNativeBalance(account: string): Promise {
+ const balance = await this.provider.getBalance(account);
+ return formatUnits(balance.toString(), this.nativeAssetDecimals);
+ }
+
+ private associateBalances(
+ balances: [BigNumber][],
+ addresses: string[],
+ tokens: TokenInfoMap
+ ): BalanceMap {
+ return Object.fromEntries(
+ addresses.map((address, i) => [
+ getAddress(address),
+ formatUnits(balances[i][0].toString(), tokens[address].decimals)
+ ])
+ );
+ }
+}
diff --git a/src/services/token/concerns/metadata.concern.ts b/src/services/token/concerns/metadata.concern.ts
index 96c632f560..b26e66d41f 100644
--- a/src/services/token/concerns/metadata.concern.ts
+++ b/src/services/token/concerns/metadata.concern.ts
@@ -1,9 +1,9 @@
import TokenService from '../token.service';
import {
TokenInfo,
- TokenInfoDict,
+ TokenInfoMap,
TokenList,
- TokenListDict
+ TokenListMap
} from '@/types/TokenList';
import { getAddress } from '@ethersproject/address';
import { Multicaller } from '@/lib/utils/balancer/contract';
@@ -20,8 +20,8 @@ export default class MetadataConcern {
*/
async get(
addresses: string[],
- tokenLists: TokenListDict
- ): Promise {
+ tokenLists: TokenListMap
+ ): Promise {
addresses = addresses.map(address => getAddress(address));
const tokenListTokens = this.tokenListsTokensFrom(tokenLists);
let metaDict = this.getMetaFromLists(addresses, tokenListTokens);
@@ -38,7 +38,7 @@ export default class MetadataConcern {
return metaDict;
}
- private tokenListsTokensFrom(lists: TokenListDict): TokenInfo[] {
+ private tokenListsTokensFrom(lists: TokenListMap): TokenInfo[] {
return Object.values(lists)
.map(list => list.tokens)
.flat();
@@ -47,7 +47,7 @@ export default class MetadataConcern {
private getMetaFromLists(
addresses: string[],
tokens: TokenInfo[]
- ): TokenInfoDict {
+ ): TokenInfoMap {
const metaDict = {};
addresses.forEach(async address => {
@@ -64,7 +64,7 @@ export default class MetadataConcern {
return metaDict;
}
- private async getMetaOnchain(addresses: string[]): Promise {
+ private async getMetaOnchain(addresses: string[]): Promise {
try {
const network = this.service.configService.network.key;
const multi = new Multicaller(network, this.service.provider, erc20Abi);
diff --git a/src/services/token/token.service.ts b/src/services/token/token.service.ts
index 4316f6cbb8..2eb55ed295 100644
--- a/src/services/token/token.service.ts
+++ b/src/services/token/token.service.ts
@@ -2,21 +2,27 @@ import { JsonRpcProvider } from '@ethersproject/providers';
import { rpcProviderService as _rpcProviderService } from '../rpc-provider/rpc-provider.service';
import { configService as _configService } from '../config/config.service';
import MetadataConcern from './concerns/metadata.concern';
-import BalanceConcern from './concerns/balance.concern';
+import BalancesConcern from './concerns/balances.concern';
+import AllowancesConcern from './concerns/allowances.concern';
export default class TokenService {
provider: JsonRpcProvider;
metadata: MetadataConcern;
- balance: BalanceConcern;
+ balances: BalancesConcern;
+ allowances: AllowancesConcern;
constructor(
readonly metadataConcernClass = MetadataConcern,
- readonly balanceConcernClass = BalanceConcern,
+ readonly balancesConcernClass = BalancesConcern,
+ readonly allowancesConcernClass = AllowancesConcern,
readonly rpcProviderService = _rpcProviderService,
readonly configService = _configService
) {
this.provider = this.rpcProviderService.jsonProvider;
this.metadata = new metadataConcernClass(this);
- this.balance = new balanceConcernClass(this);
+ this.balances = new balancesConcernClass(this);
+ this.allowances = new allowancesConcernClass(this);
}
}
+
+export const tokenService = new TokenService();
diff --git a/src/services/web3/web3.plugin.ts b/src/services/web3/web3.plugin.ts
index 4a06694d29..bd823c7555 100644
--- a/src/services/web3/web3.plugin.ts
+++ b/src/services/web3/web3.plugin.ts
@@ -12,7 +12,6 @@ import {
import { WalletLinkConnector } from './connectors/walletlink/walletlink.connector';
import { PortisConnector } from './connectors/portis/portis.connector';
import useFathom from '@/composables/useFathom';
-import getProvider from '@/lib/utils/provider';
import { configService } from '../config/config.service';
import { switchToAppNetwork } from './utils/helpers';
@@ -27,6 +26,7 @@ import trustwalletLogo from '@/assets/images/connectors/trustwallet.svg';
import walletconnectLogo from '@/assets/images/connectors/walletconnect.svg';
import walletlinkLogo from '@/assets/images/connectors/walletlink.svg';
import i18n from '@/plugins/i18n';
+import { rpcProviderService } from '../rpc-provider/rpc-provider.service';
export type Wallet = 'metamask' | 'walletconnect' | 'walletlink' | 'portis';
export const SupportedWallets = [
@@ -97,7 +97,8 @@ export default {
const provider = computed(
() =>
- pluginState.connector?.provider ?? getProvider(chainId.value.toString())
+ pluginState.connector?.provider ??
+ rpcProviderService.getJsonProvider(chainId.value.toString())
);
const signer = computed(() => pluginState.connector?.provider?.getSigner());
diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts
index 4ac3af89ac..d048af0ad1 100644
--- a/src/store/modules/app.ts
+++ b/src/store/modules/app.ts
@@ -42,8 +42,6 @@ const actions = {
);
// Fetch init data
- await dispatch('registry/get', null, { root: true });
- await dispatch('market/loadPrices', [], { root: true });
await dispatch('market/getGasPrice', [], { root: true });
// Fetch initial trade tokens
diff --git a/src/store/modules/market.ts b/src/store/modules/market.ts
index b0f9877650..f43b7cabc7 100644
--- a/src/store/modules/market.ts
+++ b/src/store/modules/market.ts
@@ -1,11 +1,8 @@
-import { getEtherPrice, getTokensPrice } from '@/services/coingecko';
-import { ETHER } from '@/constants/tokenlists';
import GasPriceService from '@/services/gas-price/gas-price.service';
type Prices = Record;
interface MarketState {
- prices: Prices;
gasPrice: number;
loading: boolean;
}
@@ -13,27 +10,11 @@ interface MarketState {
const gasPriceService = new GasPriceService();
const state: MarketState = {
- prices: {},
gasPrice: 0,
loading: false
};
const actions = {
- async loadPrices({ commit, rootGetters }, tokens: string[] = []) {
- if (tokens.length === 0)
- tokens = Object.values(rootGetters['registry/getTokens']()).map(
- (token: any) => token.address
- );
- commit('setLoading', true);
- const chainId = Number(process.env.VUE_APP_NETWORK || 1);
- const [prices, etherPrice] = await Promise.all([
- getTokensPrice(chainId, tokens),
- getEtherPrice()
- ]);
- prices[ETHER.address.toLowerCase()] = etherPrice;
- commit('addPrices', prices);
- commit('setLoading', false);
- },
async getGasPrice({ commit }) {
const price = await gasPriceService.getLatest();
commit('setGasPrice', price);
@@ -41,12 +22,6 @@ const actions = {
};
const mutations = {
- addPrices(_state: MarketState, prices: Prices) {
- for (const asset in prices) {
- _state.prices[asset] = prices[asset];
- }
- },
-
setGasPrice(_state: MarketState, price: number) {
_state.gasPrice = price;
},
diff --git a/src/store/modules/registry.ts b/src/store/modules/registry.ts
deleted file mode 100644
index 7717b2abe5..0000000000
--- a/src/store/modules/registry.ts
+++ /dev/null
@@ -1,263 +0,0 @@
-import { formatUnits } from '@ethersproject/units';
-import { getAddress, isAddress } from '@ethersproject/address';
-import orderBy from 'lodash/orderBy';
-import { loadTokenlist } from '@/lib/utils/tokenlists';
-import TOKEN_LISTS, { ETHER } from '@/constants/tokenlists';
-import { clone, lsGet, lsSet } from '@/lib/utils';
-import injected from '@/constants/injected.json';
-import { TokenList, TokenInfo } from '@/types/TokenList';
-import { getTokensMeta } from '@/lib/utils/balancer/tokens';
-
-const defaultActiveLists = {};
-defaultActiveLists[TOKEN_LISTS.Balancer.Default] = true;
-
-interface RegistryState {
- activeLists: Record;
- tokenLists: Record;
- eligibleList: TokenList | {};
- injected: TokenInfo[];
- loading: boolean;
-}
-
-const state: RegistryState = {
- activeLists: lsGet('tokenLists', defaultActiveLists),
- tokenLists: Object.fromEntries(
- TOKEN_LISTS.Approved.map(tokenList => [tokenList, {}])
- ),
- eligibleList: {},
- injected,
- loading: true
-};
-
-const getters = {
- getEther: (state, getters, rootState) => () => {
- const ether: any = ETHER;
- ether.balance = 0;
- ether.balanceDenorm = '0';
- ether.price =
- rootState.market.prices[ether.address.toLowerCase()]?.price || 0;
- ether.price24HChange =
- rootState.market.prices[ether.address.toLowerCase()]?.price24HChange || 0;
- ether.chainId = Number(process.env.VUE_APP_NETWORK || 1);
- if (rootState.web3.account) {
- ether.balanceDenorm = rootState.account.balances.ether || '0';
- ether.balance = formatUnits(ether.balanceDenorm, ether.decimals);
- ether.value = ether.balance * ether.price;
- ether.value24HChange =
- (parseFloat(ether.value) / 100) * ether.price24HChange;
- }
- return ether;
- },
-
- getTokens: (state, getters, rootState) => (query: any = {}) => {
- const { q, addresses, not, withBalance, limit, includeEther } = query;
-
- const activeLists = Object.keys(state.tokenLists)
- .filter(name => state.activeLists[name])
- .reverse();
-
- let tokens: any = {};
- clone(state.injected).forEach(
- token => (tokens[getAddress(token.address)] = token)
- );
- activeLists.forEach(name => {
- clone(state.tokenLists[name])?.tokens?.map(
- token => (tokens[getAddress(token.address)] = token)
- );
- });
- tokens = Object.values(tokens);
-
- tokens = tokens.filter(
- token => token.chainId === Number(process.env.VUE_APP_NETWORK || 1)
- );
-
- tokens = tokens.map(token => {
- token.balance = 0;
- token.balanceDenorm = '0';
- return token;
- });
-
- tokens = tokens.map(token => {
- const address = token.address.toLowerCase();
- token.price = rootState.market.prices[address]?.price || 0;
- token.price24HChange =
- rootState.market.prices[address]?.price24HChange || 0;
- return token;
- });
-
- if (rootState?.web3?.account) {
- tokens = tokens.map(token => {
- const address = token.address.toLowerCase();
- token.balanceDenorm = rootState.account.balances[address] || '0';
- token.balance = formatUnits(token.balanceDenorm, token.decimals);
- token.value = token.balance * token.price;
- token.value24HChange =
- (parseFloat(token.value) / 100) * token.price24HChange;
- return token;
- });
- tokens = orderBy(tokens, ['value', 'balance'], ['desc', 'desc']);
- }
-
- // Query filters
-
- if (includeEther) {
- tokens = [getters.getEther(), ...tokens];
- }
-
- if (q) {
- tokens = tokens.filter(token =>
- JSON.stringify([token.address, token.symbol, token.name])
- .toLowerCase()
- .includes(q.toLowerCase())
- );
- }
-
- if (addresses) {
- tokens = addresses.map(
- (address: any) =>
- tokens.filter(
- token => token.address.toLowerCase() === address.toLowerCase()
- )[0]
- );
- }
-
- if (limit) {
- tokens = tokens.slice(0, limit);
- }
-
- if (not) tokens = tokens.filter(token => !not.includes(token.address));
- if (withBalance) tokens = tokens.filter(token => token.balance > 0);
-
- return Object.fromEntries(tokens.map(token => [token.address, token]));
- },
-
- getTokenLists: (state, getters, rootState) => ({ q, active }) => {
- const tokenLists = clone(state.tokenLists);
- const bingbot = Object.fromEntries(
- Object.entries(tokenLists)
- .map((tokenList: any) => {
- tokenList[1].tokens = tokenList[1].tokens
- ? tokenList[1].tokens.filter(
- token => token.chainId === rootState.web3.config.chainId
- )
- : [];
- tokenList[1].active = state.activeLists[tokenList[0]] ? 1 : 0;
- return tokenList;
- })
- .filter(
- tokenList =>
- tokenList[1].tokens.length > 0 &&
- (!active || (active && tokenList[1].active))
- )
- .filter(tokenList =>
- q
- ? `${tokenList[0]} ${tokenList[1].name}`
- .toLowerCase()
- .includes(q.toLowerCase())
- : true
- )
- .sort((a, b): any => b[1].active - a[1].active)
- );
- return bingbot;
- },
-
- getInjected(_state: RegistryState) {
- return _state.injected;
- }
-};
-
-const actions = {
- async get({ dispatch, commit }) {
- const loadAllLists = TOKEN_LISTS.Approved.map(url =>
- dispatch('loadTokenlist', url)
- );
- const results = await Promise.all(loadAllLists.map(p => p.catch(e => e)));
- const validResults = results.filter(result => !(result instanceof Error));
- if (validResults.length === 0) {
- throw new Error('Failed to load any TokenLists');
- }
- const eligibleList = await loadTokenlist(TOKEN_LISTS.Balancer.Vetted);
- commit('setEligibleList', eligibleList);
- commit('setLoading', false);
- },
-
- async loadTokenlist({ commit }, url) {
- url = url || TOKEN_LISTS.Balancer.Default;
- try {
- const tokenList = await loadTokenlist(url);
- const tokenLists = clone(state.tokenLists);
- tokenLists[url] = tokenList;
- commit('setTokenLists', tokenLists);
- } catch (error) {
- console.error('Failed to load TokenList', url, error);
- throw error;
- }
- },
-
- async injectTokens({ commit, dispatch, state }, tokens: string[]) {
- tokens = tokens.filter(
- token => token !== ETHER.address && isAddress(token)
- );
- if (tokens.length === 0) return;
- const lists = {
- ...state.tokenLists,
- [TOKEN_LISTS.Balancer.Vetted]: state.eligibleList
- };
- const tokensMeta = await getTokensMeta(tokens, lists);
- const injected = clone(state.injected);
- Object.values(tokensMeta).forEach((meta: TokenInfo) => {
- if (meta) injected.push({ ...meta, injected: true });
- });
- commit('setInjected', injected);
- await dispatch('market/loadPrices', tokens, { root: true });
- },
-
- toggleList({ commit }, name) {
- const activeLists = clone(state.activeLists);
- if (activeLists[name]) {
- delete activeLists[name];
- } else {
- activeLists[name] = true;
- }
- if (Object.keys(activeLists).length > 0) {
- lsSet('tokenLists', activeLists);
- commit('setActiveLists', activeLists);
- }
- }
-};
-
-const mutations = {
- setLoading(_state: RegistryState, val: boolean): void {
- _state.loading = val;
- },
-
- setTokenLists(
- _state: RegistryState,
- tokenLists: Record
- ): void {
- _state.tokenLists = tokenLists;
- },
-
- setInjected(_state: RegistryState, injected: TokenInfo[]): void {
- _state.injected = injected;
- },
-
- setActiveLists(
- _state: RegistryState,
- activeLists: Record
- ): void {
- _state.activeLists = activeLists;
- },
-
- setEligibleList(_state: RegistryState, list: TokenList): void {
- _state.eligibleList = list;
- }
-};
-
-export default {
- namespaced: true,
- state,
- mutations,
- getters,
- actions
-};
diff --git a/src/types/TokenList.ts b/src/types/TokenList.ts
index 64bef13a74..3bca3ae0ed 100644
--- a/src/types/TokenList.ts
+++ b/src/types/TokenList.ts
@@ -34,5 +34,5 @@ export interface TokenList {
readonly logoURI?: string;
}
-export type TokenListDict = { [address: string]: TokenList };
-export type TokenInfoDict = { [address: string]: TokenInfo };
+export type TokenListMap = { [address: string]: TokenList };
+export type TokenInfoMap = { [address: string]: TokenInfo };