From 443782864ec7da9ce923ff70a8494d4a3681149b Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 09:31:02 +0100 Subject: [PATCH 001/175] add ankreth apr to op --- modules/network/optimism.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index 88b9d890e..bed1cce39 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -118,6 +118,16 @@ const optimismNetworkData: NetworkData = { }, }, ibAprConfig: { + ankr: { + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + tokens: { + ankrETH: { + address: '0xe05a08226c49b636acf99c40da8dc6af83ce5bb3', + serviceName: 'eth', + isIbYield: true, + }, + }, + }, beefy: { sourceUrl: 'https://api.beefy.finance/apy/breakdown?_=', tokens: { From ebf6b89ed203ae5204181d8fde336c4300f4267b Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 09:31:11 +0100 Subject: [PATCH 002/175] update sentry --- package.json | 4 +-- yarn.lock | 95 +++++++++++++++++++++++----------------------------- 2 files changed, 44 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index ffaf2d896..5c354047f 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,8 @@ "@graphql-tools/merge": "^8.2.1", "@prisma/client": "^5.3.1", "@sanity/client": "^2.23.1", - "@sentry/node": "^7.56.0", - "@sentry/tracing": "^7.56.0", + "@sentry/node": "^7.77.0", + "@sentry/tracing": "^7.77.0", "apollo-server-core": "^3.5.0", "apollo-server-express": "^3.5.0", "axios": "^0.24.0", diff --git a/yarn.lock b/yarn.lock index 6bbde0c7a..7bc0728bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3883,58 +3883,52 @@ resolved "https://registry.yarnpkg.com/@sanity/timed-out/-/timed-out-4.0.2.tgz#c9f61f9a1609baa1eb3e4235a24ea2a775022cdf" integrity sha512-NBDKGj14g9Z+bopIvZcQKWCzJq5JSrdmzRR1CS+iyA3Gm8SnIWBfZa7I3mTg2X6Nu8LQXG0EPKXdOGozLS4i3w== -"@sentry-internal/tracing@7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.56.0.tgz#ba709258f2f0f3d8a36f9740403088b39212b843" - integrity sha512-OKI4Pz/O13gng8hT9rNc+gRV3+P7nnk1HnHlV8fgaQydS6DsRxoDL1sHa42tZGbh7K9jqNAP3TC6VjBOsr2tXA== - dependencies: - "@sentry/core" "7.56.0" - "@sentry/types" "7.56.0" - "@sentry/utils" "7.56.0" - tslib "^1.9.3" - -"@sentry/core@7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.56.0.tgz#f4253e0d61f55444180a63e5278b62e57303f7cf" - integrity sha512-Nuyyfh09Yz27kPo74fXHlrdmZeK6zrlJVtxQ6LkwuoaTBcNcesNXVaOtr6gjvUGUmsfriVPP3Jero5LXufV7GQ== - dependencies: - "@sentry/types" "7.56.0" - "@sentry/utils" "7.56.0" - tslib "^1.9.3" - -"@sentry/node@^7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.56.0.tgz#ddeb34a848c8a544d0dbb5f2c3031615be040d2b" - integrity sha512-QXbWy/ypRxfFd8iP6zLvHInYZyjGKPrkVNYt43mhKAZHm764NxX/29vDfj1FztgG9Z6lVLIG2eyqTvLruYmsWw== - dependencies: - "@sentry-internal/tracing" "7.56.0" - "@sentry/core" "7.56.0" - "@sentry/types" "7.56.0" - "@sentry/utils" "7.56.0" - cookie "^0.4.1" +"@sentry-internal/tracing@7.77.0": + version "7.77.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.77.0.tgz#f3d82486f8934a955b3dd2aa54c8d29586e42a37" + integrity sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw== + dependencies: + "@sentry/core" "7.77.0" + "@sentry/types" "7.77.0" + "@sentry/utils" "7.77.0" + +"@sentry/core@7.77.0": + version "7.77.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.77.0.tgz#21100843132beeeff42296c8370cdcc7aa1d8510" + integrity sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg== + dependencies: + "@sentry/types" "7.77.0" + "@sentry/utils" "7.77.0" + +"@sentry/node@^7.77.0": + version "7.77.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.77.0.tgz#a247452779a5bcb55724457707286e3e4a29dbbe" + integrity sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g== + dependencies: + "@sentry-internal/tracing" "7.77.0" + "@sentry/core" "7.77.0" + "@sentry/types" "7.77.0" + "@sentry/utils" "7.77.0" https-proxy-agent "^5.0.0" - lru_map "^0.3.3" - tslib "^1.9.3" -"@sentry/tracing@^7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.56.0.tgz#f119c2b04c06718fa3a0d00b2781c56005e9dd80" - integrity sha512-Qy7lJdC2YBk9T8JFt4da7xHB3pTZH6yUiIwo5edmSBv2cY6MQ0QZgLzsjJurjf47+/WecVYYKdye9q4twsBlDA== +"@sentry/tracing@^7.77.0": + version "7.77.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.77.0.tgz#39d7c30834f503fe9eb20ce1c8c8bd28f7d7c9ce" + integrity sha512-zr6eSCW3NJ124uj4Fy/6hh77cziy43dpYE1WpgvO/yhl1+kdrY2XVJ0bXGqwHU0KBm/eSgHD7yecUxmZUXiabA== dependencies: - "@sentry-internal/tracing" "7.56.0" + "@sentry-internal/tracing" "7.77.0" -"@sentry/types@7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.56.0.tgz#9042a099cf9e8816d081886d24b88082a5d9f87a" - integrity sha512-5WjhVOQm75ItOytOx2jTx+5yw8/qJ316+g1Di8dS9+kgIi1zniqdMcX00C2yYe3FMUgFB49PegCUYulm9Evapw== +"@sentry/types@7.77.0": + version "7.77.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.77.0.tgz#c5d00fe547b89ccde59cdea59143bf145cee3144" + integrity sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA== -"@sentry/utils@7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.56.0.tgz#e60e4935d17b2197584abf6ce61b522ad055352c" - integrity sha512-wgeX7bufxc//TjjSIE+gCMm8hVId7Jzvc+f441bYrWnNZBuzPIDW2BummCcPrKzSYe5GeYZDTZGV8YZGMLGBjw== +"@sentry/utils@7.77.0": + version "7.77.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.77.0.tgz#1f88501f0b8777de31b371cf859d13c82ebe1379" + integrity sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g== dependencies: - "@sentry/types" "7.56.0" - tslib "^1.9.3" + "@sentry/types" "7.77.0" "@sinclair/typebox@^0.24.1": version "0.24.43" @@ -5971,7 +5965,7 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -cookie@^0.4.1, cookie@^0.4.2: +cookie@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== @@ -8894,11 +8888,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru_map@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" - integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== - magic-string@^0.30.0: version "0.30.1" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.1.tgz#ce5cd4b0a81a5d032bd69aab4522299b2166284d" @@ -10835,7 +10824,7 @@ ts-node@^9: source-map-support "^0.5.17" yn "3.1.1" -tslib@^1.11.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.11.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== From 78f73f1b8c7b00da94d86949697128253ebd25ea Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 11:00:54 +0100 Subject: [PATCH 003/175] do not get price for tokens to update --- .../swaps-price-handler.service.ts | 12 ++---------- modules/token/token-types.ts | 4 ++-- prisma/prisma-types.ts | 5 ----- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/modules/token/lib/token-price-handlers/swaps-price-handler.service.ts b/modules/token/lib/token-price-handlers/swaps-price-handler.service.ts index 219fc3a33..b5cd89d55 100644 --- a/modules/token/lib/token-price-handlers/swaps-price-handler.service.ts +++ b/modules/token/lib/token-price-handlers/swaps-price-handler.service.ts @@ -1,5 +1,5 @@ import { TokenPriceHandler } from '../../token-types'; -import { PrismaTokenWithTypes, PrismaTokenWithTypesAndPrices } from '../../../../prisma/prisma-types'; +import { PrismaTokenWithTypes } from '../../../../prisma/prisma-types'; import { prisma } from '../../../../prisma/prisma-client'; import { timestampRoundedUpToNearestHour } from '../../../common/time'; import moment from 'moment-timezone'; @@ -10,13 +10,7 @@ export class SwapsPriceHandlerService implements TokenPriceHandler { public readonly exitIfFails = false; public readonly id = 'SwapsPriceHandlerService'; - public async getAcceptedTokens(tokens: PrismaTokenWithTypesAndPrices[]): Promise { - // also update any tokens with a coingecko ID that haven't been updated for three hours. This is a fall back to coingecko pricing. - const threeHoursAgo = moment().subtract(3, 'hours').utc().unix(); - - // get the most recent price in front - tokens.forEach((token) => (token.prices = _.orderBy(token.prices, 'timestamp', 'desc'))); - + public async getAcceptedTokens(tokens: PrismaTokenWithTypes[]): Promise { return tokens .filter( (token) => @@ -24,8 +18,6 @@ export class SwapsPriceHandlerService implements TokenPriceHandler { !token.types.includes('PHANTOM_BPT') && !token.types.includes('LINEAR_WRAPPED_TOKEN') && (!token.coingeckoTokenId || - !token.prices.length || - token.prices[0].timestamp < threeHoursAgo || networkContext.data.coingecko.excludedTokenAddresses.includes(token.address)), ) .map((token) => token.address); diff --git a/modules/token/token-types.ts b/modules/token/token-types.ts index 56a6d2400..1268a5210 100644 --- a/modules/token/token-types.ts +++ b/modules/token/token-types.ts @@ -1,4 +1,4 @@ -import { PrismaTokenWithTypesAndPrices, PrismaTokenWithTypes } from '../../prisma/prisma-types'; +import { PrismaTokenWithTypes } from '../../prisma/prisma-types'; import { Chain } from '@prisma/client'; export interface TokenPriceHandler { @@ -9,7 +9,7 @@ export interface TokenPriceHandler { * Determines what tokens this price handler is capable of fetching a price for * @param tokens tokens needing prices */ - getAcceptedTokens(tokens: PrismaTokenWithTypesAndPrices[]): Promise; + getAcceptedTokens(tokens: PrismaTokenWithTypes[]): Promise; /** * Updates prices for the provided tokens, returning an array of addresses of the tokens diff --git a/prisma/prisma-types.ts b/prisma/prisma-types.ts index d986de64e..56663de91 100644 --- a/prisma/prisma-types.ts +++ b/prisma/prisma-types.ts @@ -205,11 +205,6 @@ export type PrismaTokenWithTypes = PrismaToken & { types: PrismaTokenTypeOption[]; }; -export type PrismaTokenWithTypesAndPrices = PrismaToken & { - types: PrismaTokenTypeOption[]; - prices: PrismaTokenPrice[]; -}; - export const prismaPoolMinimal = Prisma.validator()({ include: { dynamicData: true, From 470cd3abc3c21127bf55b75599ac9a9b6fc79c06 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 11:04:40 +0100 Subject: [PATCH 004/175] downgrade prisma --- package.json | 4 ++-- yarn.lock | 36 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 5c354047f..514a145ba 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@gnosis.pm/safe-core-sdk": "^1.2.0", "@graphql-tools/load-files": "^6.5.2", "@graphql-tools/merge": "^8.2.1", - "@prisma/client": "^5.3.1", + "@prisma/client": "^4.1.1", "@sanity/client": "^2.23.1", "@sentry/node": "^7.77.0", "@sentry/tracing": "^7.77.0", @@ -50,7 +50,7 @@ "memory-cache": "^0.2.0", "moment-timezone": "^0.5.34", "node-cron": "^3.0.0", - "prisma": "^5.3.1", + "prisma": "^4.1.1", "safe-compare": "^1.1.4", "stellate": "^1.15.2", "ts-dotenv": "^0.8.1", diff --git a/yarn.lock b/yarn.lock index 7bc0728bf..8f6bdaac2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3762,22 +3762,22 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== -"@prisma/client@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.3.1.tgz#fc7fc2d91e814cc4fe18a4bc5e78bf851c26985e" - integrity sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q== +"@prisma/client@^4.1.1": + version "4.16.2" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.16.2.tgz#3bb9ebd49b35c8236b3d468d0215192267016e2b" + integrity sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ== dependencies: - "@prisma/engines-version" "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" + "@prisma/engines-version" "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81" -"@prisma/engines-version@5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59": - version "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz#7eb6f5c6b7628b8b39df55c903f411528a6f761c" - integrity sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w== +"@prisma/engines-version@4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81": + version "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz#d3b5dcf95b6d220e258cbf6ae19b06d30a7e9f14" + integrity sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg== -"@prisma/engines@5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.3.1.tgz#53cc72a5ed176dc27d22305fe5569c64cc78b381" - integrity sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA== +"@prisma/engines@4.16.2": + version "4.16.2" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.16.2.tgz#5ec8dd672c2173d597e469194916ad4826ce2e5f" + integrity sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -9657,12 +9657,12 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prisma@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.3.1.tgz#a0932c1c1a5ed4ff449d064b193d9c7e94e8bf77" - integrity sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A== +prisma@^4.1.1: + version "4.16.2" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.16.2.tgz#469e0a0991c6ae5bcde289401726bb012253339e" + integrity sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g== dependencies: - "@prisma/engines" "5.3.1" + "@prisma/engines" "4.16.2" process-nextick-args@~2.0.0: version "2.0.1" From cce5fb60a2e7433cf7e104cad853da2bccc3f8d0 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 11:06:13 +0100 Subject: [PATCH 005/175] fix token price handler --- modules/token/lib/token-price.service.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/token/lib/token-price.service.ts b/modules/token/lib/token-price.service.ts index caf7ae810..1d5b77fa9 100644 --- a/modules/token/lib/token-price.service.ts +++ b/modules/token/lib/token-price.service.ts @@ -145,14 +145,10 @@ export class TokenPriceService { where: { chain: networkContext.chain }, include: { types: true, - //fetch the last price stored - prices: { take: 1, orderBy: { timestamp: 'desc' } }, }, }); - //order by timestamp ascending, so the tokens at the front of the list are the ones with the oldest timestamp - //this is for instances where a query gets rate limited and does not finish - let tokensWithTypes = _.sortBy(tokens, (token) => token.prices[0]?.timestamp || 0).map((token) => ({ + let tokensWithTypes = tokens.map((token) => ({ ...token, types: token.types.map((type) => type.type), })); From 264104dc7378394062013e29aa10e761f2396688 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 11:18:54 +0100 Subject: [PATCH 006/175] Revert "downgrade prisma" This reverts commit 470cd3abc3c21127bf55b75599ac9a9b6fc79c06. --- package.json | 4 ++-- yarn.lock | 36 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 514a145ba..5c354047f 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@gnosis.pm/safe-core-sdk": "^1.2.0", "@graphql-tools/load-files": "^6.5.2", "@graphql-tools/merge": "^8.2.1", - "@prisma/client": "^4.1.1", + "@prisma/client": "^5.3.1", "@sanity/client": "^2.23.1", "@sentry/node": "^7.77.0", "@sentry/tracing": "^7.77.0", @@ -50,7 +50,7 @@ "memory-cache": "^0.2.0", "moment-timezone": "^0.5.34", "node-cron": "^3.0.0", - "prisma": "^4.1.1", + "prisma": "^5.3.1", "safe-compare": "^1.1.4", "stellate": "^1.15.2", "ts-dotenv": "^0.8.1", diff --git a/yarn.lock b/yarn.lock index 8f6bdaac2..7bc0728bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3762,22 +3762,22 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== -"@prisma/client@^4.1.1": - version "4.16.2" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.16.2.tgz#3bb9ebd49b35c8236b3d468d0215192267016e2b" - integrity sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ== +"@prisma/client@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.3.1.tgz#fc7fc2d91e814cc4fe18a4bc5e78bf851c26985e" + integrity sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q== dependencies: - "@prisma/engines-version" "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81" + "@prisma/engines-version" "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" -"@prisma/engines-version@4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81": - version "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz#d3b5dcf95b6d220e258cbf6ae19b06d30a7e9f14" - integrity sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg== +"@prisma/engines-version@5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59": + version "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz#7eb6f5c6b7628b8b39df55c903f411528a6f761c" + integrity sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w== -"@prisma/engines@4.16.2": - version "4.16.2" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.16.2.tgz#5ec8dd672c2173d597e469194916ad4826ce2e5f" - integrity sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw== +"@prisma/engines@5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.3.1.tgz#53cc72a5ed176dc27d22305fe5569c64cc78b381" + integrity sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -9657,12 +9657,12 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prisma@^4.1.1: - version "4.16.2" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.16.2.tgz#469e0a0991c6ae5bcde289401726bb012253339e" - integrity sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g== +prisma@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.3.1.tgz#a0932c1c1a5ed4ff449d064b193d9c7e94e8bf77" + integrity sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A== dependencies: - "@prisma/engines" "4.16.2" + "@prisma/engines" "5.3.1" process-nextick-args@~2.0.0: version "2.0.1" From 1ceeae694242429b1294daa2851883b84a82411b Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 2 Nov 2023 13:41:10 +0100 Subject: [PATCH 007/175] downgrade prisma and typescript --- package.json | 10 ++-- yarn.lock | 139 +++++++++++++++++++++++++++------------------------ 2 files changed, 80 insertions(+), 69 deletions(-) diff --git a/package.json b/package.json index 5c354047f..d44899b0e 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,10 @@ "@gnosis.pm/safe-core-sdk": "^1.2.0", "@graphql-tools/load-files": "^6.5.2", "@graphql-tools/merge": "^8.2.1", - "@prisma/client": "^5.3.1", + "@prisma/client": "^4.1.1", "@sanity/client": "^2.23.1", - "@sentry/node": "^7.77.0", - "@sentry/tracing": "^7.77.0", + "@sentry/node": "^7.56.0", + "@sentry/tracing": "^7.56.0", "apollo-server-core": "^3.5.0", "apollo-server-express": "^3.5.0", "axios": "^0.24.0", @@ -50,7 +50,7 @@ "memory-cache": "^0.2.0", "moment-timezone": "^0.5.34", "node-cron": "^3.0.0", - "prisma": "^5.3.1", + "prisma": "^4.1.1", "safe-compare": "^1.1.4", "stellate": "^1.15.2", "ts-dotenv": "^0.8.1", @@ -88,7 +88,7 @@ "testcontainers": "^8.0.0", "ts-jest": "^28.0.7", "ts-node": "^10.4.0", - "typescript": "^5.2.2", + "typescript": "^4.5.3", "vitest": "^0.32.4", "vitest-mock-extended": "^1.1.3" } diff --git a/yarn.lock b/yarn.lock index 7bc0728bf..ccf0f16fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3762,22 +3762,22 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== -"@prisma/client@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.3.1.tgz#fc7fc2d91e814cc4fe18a4bc5e78bf851c26985e" - integrity sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q== +"@prisma/client@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.1.1.tgz#dcb1118397deb8247fbe39a1f3eee5606648adf8" + integrity sha512-2pXuIUYxHv5H9o6QTa1VIsl4yMgsAjKQOitlo8WVTB+vo73rmMJITBPavdGUZSWUc7adMkFzEV3y5rVTUQr77Q== dependencies: - "@prisma/engines-version" "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" + "@prisma/engines-version" "4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8" -"@prisma/engines-version@5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59": - version "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz#7eb6f5c6b7628b8b39df55c903f411528a6f761c" - integrity sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w== +"@prisma/engines-version@4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8": + version "4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8.tgz#ce00e6377126e491a8b1e0e2039c97e2924bd6d9" + integrity sha512-cRRJwpHFGFJZvtHbY3GZjMffNBEjjZk68ztn+S2hDgPCGB4H66IK26roK94GJxBodSehwRJ0wGyebC2GoIH1JQ== -"@prisma/engines@5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.3.1.tgz#53cc72a5ed176dc27d22305fe5569c64cc78b381" - integrity sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA== +"@prisma/engines@4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.1.1.tgz#a6a75870618bbd19ff734c51af7dbe9f362c3265" + integrity sha512-DCw8L/SS0IXqmj5IW/fMxOXiifnsfjBzDfRhf0j3NFWqvMCh9OtfjmXQZxVgI2mwvJLc/5jzXhkiWT39qS09dA== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -3883,52 +3883,58 @@ resolved "https://registry.yarnpkg.com/@sanity/timed-out/-/timed-out-4.0.2.tgz#c9f61f9a1609baa1eb3e4235a24ea2a775022cdf" integrity sha512-NBDKGj14g9Z+bopIvZcQKWCzJq5JSrdmzRR1CS+iyA3Gm8SnIWBfZa7I3mTg2X6Nu8LQXG0EPKXdOGozLS4i3w== -"@sentry-internal/tracing@7.77.0": - version "7.77.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.77.0.tgz#f3d82486f8934a955b3dd2aa54c8d29586e42a37" - integrity sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw== - dependencies: - "@sentry/core" "7.77.0" - "@sentry/types" "7.77.0" - "@sentry/utils" "7.77.0" - -"@sentry/core@7.77.0": - version "7.77.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.77.0.tgz#21100843132beeeff42296c8370cdcc7aa1d8510" - integrity sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg== - dependencies: - "@sentry/types" "7.77.0" - "@sentry/utils" "7.77.0" - -"@sentry/node@^7.77.0": - version "7.77.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.77.0.tgz#a247452779a5bcb55724457707286e3e4a29dbbe" - integrity sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g== - dependencies: - "@sentry-internal/tracing" "7.77.0" - "@sentry/core" "7.77.0" - "@sentry/types" "7.77.0" - "@sentry/utils" "7.77.0" +"@sentry-internal/tracing@7.56.0": + version "7.56.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.56.0.tgz#ba709258f2f0f3d8a36f9740403088b39212b843" + integrity sha512-OKI4Pz/O13gng8hT9rNc+gRV3+P7nnk1HnHlV8fgaQydS6DsRxoDL1sHa42tZGbh7K9jqNAP3TC6VjBOsr2tXA== + dependencies: + "@sentry/core" "7.56.0" + "@sentry/types" "7.56.0" + "@sentry/utils" "7.56.0" + tslib "^1.9.3" + +"@sentry/core@7.56.0": + version "7.56.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.56.0.tgz#f4253e0d61f55444180a63e5278b62e57303f7cf" + integrity sha512-Nuyyfh09Yz27kPo74fXHlrdmZeK6zrlJVtxQ6LkwuoaTBcNcesNXVaOtr6gjvUGUmsfriVPP3Jero5LXufV7GQ== + dependencies: + "@sentry/types" "7.56.0" + "@sentry/utils" "7.56.0" + tslib "^1.9.3" + +"@sentry/node@^7.56.0": + version "7.56.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.56.0.tgz#ddeb34a848c8a544d0dbb5f2c3031615be040d2b" + integrity sha512-QXbWy/ypRxfFd8iP6zLvHInYZyjGKPrkVNYt43mhKAZHm764NxX/29vDfj1FztgG9Z6lVLIG2eyqTvLruYmsWw== + dependencies: + "@sentry-internal/tracing" "7.56.0" + "@sentry/core" "7.56.0" + "@sentry/types" "7.56.0" + "@sentry/utils" "7.56.0" + cookie "^0.4.1" https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" -"@sentry/tracing@^7.77.0": - version "7.77.0" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.77.0.tgz#39d7c30834f503fe9eb20ce1c8c8bd28f7d7c9ce" - integrity sha512-zr6eSCW3NJ124uj4Fy/6hh77cziy43dpYE1WpgvO/yhl1+kdrY2XVJ0bXGqwHU0KBm/eSgHD7yecUxmZUXiabA== +"@sentry/tracing@^7.56.0": + version "7.56.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.56.0.tgz#f119c2b04c06718fa3a0d00b2781c56005e9dd80" + integrity sha512-Qy7lJdC2YBk9T8JFt4da7xHB3pTZH6yUiIwo5edmSBv2cY6MQ0QZgLzsjJurjf47+/WecVYYKdye9q4twsBlDA== dependencies: - "@sentry-internal/tracing" "7.77.0" + "@sentry-internal/tracing" "7.56.0" -"@sentry/types@7.77.0": - version "7.77.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.77.0.tgz#c5d00fe547b89ccde59cdea59143bf145cee3144" - integrity sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA== +"@sentry/types@7.56.0": + version "7.56.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.56.0.tgz#9042a099cf9e8816d081886d24b88082a5d9f87a" + integrity sha512-5WjhVOQm75ItOytOx2jTx+5yw8/qJ316+g1Di8dS9+kgIi1zniqdMcX00C2yYe3FMUgFB49PegCUYulm9Evapw== -"@sentry/utils@7.77.0": - version "7.77.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.77.0.tgz#1f88501f0b8777de31b371cf859d13c82ebe1379" - integrity sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g== +"@sentry/utils@7.56.0": + version "7.56.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.56.0.tgz#e60e4935d17b2197584abf6ce61b522ad055352c" + integrity sha512-wgeX7bufxc//TjjSIE+gCMm8hVId7Jzvc+f441bYrWnNZBuzPIDW2BummCcPrKzSYe5GeYZDTZGV8YZGMLGBjw== dependencies: - "@sentry/types" "7.77.0" + "@sentry/types" "7.56.0" + tslib "^1.9.3" "@sinclair/typebox@^0.24.1": version "0.24.43" @@ -5965,7 +5971,7 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -cookie@^0.4.2: +cookie@^0.4.1, cookie@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== @@ -8888,6 +8894,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + magic-string@^0.30.0: version "0.30.1" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.1.tgz#ce5cd4b0a81a5d032bd69aab4522299b2166284d" @@ -9657,12 +9668,12 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prisma@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.3.1.tgz#a0932c1c1a5ed4ff449d064b193d9c7e94e8bf77" - integrity sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A== +prisma@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.1.1.tgz#41c2e19896357f484ef21567165d762908376fca" + integrity sha512-yw50J8If2dKP4wYIi695zthsCASQFHiogGvUHHWd3falx/rpsD6Sb1LMLRV9nO3iGG3lozxNJ2PSINxK7xwdpg== dependencies: - "@prisma/engines" "5.3.1" + "@prisma/engines" "4.1.1" process-nextick-args@~2.0.0: version "2.0.1" @@ -10824,7 +10835,7 @@ ts-node@^9: source-map-support "^0.5.17" yn "3.1.1" -tslib@^1.11.1, tslib@^1.9.0: +tslib@^1.11.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -10886,10 +10897,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== +typescript@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" + integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== ua-parser-js@^0.7.30: version "0.7.31" From 3410e155b706c8d873ee5b5dc98f39b180bc7029 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 3 Nov 2023 12:08:22 +0100 Subject: [PATCH 008/175] update gnosis rpc --- modules/network/gnosis.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index 360c75540..5c4f30d84 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -54,7 +54,8 @@ const gnosisNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://rpc.gnosis.gateway.fm', + rpcUrl: + (env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' ? `https://rpc.gnosischain.com` : 'https://gnosis.drpc.org', rpcMaxBlockRange: 2000, protocolToken: 'bal', bal: { From ed042c968dbf75670e4d931a4c8142c99c5b7f31 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 3 Nov 2023 12:58:23 -0300 Subject: [PATCH 009/175] Add type and version to nested pools --- modules/pool/pool.gql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 2d08af860..af90e7f62 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -416,6 +416,9 @@ type GqlPoolLinearNested { upperTarget: BigInt! lowerTarget: BigInt! bptPriceRate: BigDecimal! + + type: String! + version: Int! } type GqlPoolPhantomStableNested { @@ -434,6 +437,9 @@ type GqlPoolPhantomStableNested { amp: BigInt! swapFee: BigDecimal! bptPriceRate: BigDecimal! + + type: String! + version: Int! } union GqlPoolUnion = From 8b27bd7b978e8e890e08a8f3363294e29d429bc8 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 6 Nov 2023 13:30:41 +0100 Subject: [PATCH 010/175] dont filter bpt in nested phantomstable --- modules/pool/lib/pool-gql-loader.service.ts | 56 ++++++++++----------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 302785ba7..b381545ee 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -1001,38 +1001,36 @@ export class PoolGqlLoaderService { __typename: 'GqlPoolPhantomStableNested', ...pool, nestingType: this.getPoolNestingType(pool), - tokens: pool.tokens - .filter((token) => token.address !== pool.address) - .map((token) => { - const nestedPool = token.nestedPool; + tokens: pool.tokens.map((token) => { + const nestedPool = token.nestedPool; - if (nestedPool && nestedPool.type === 'LINEAR') { - const totalShares = parseFloat(nestedPool.dynamicData?.totalShares || '0'); - const percentOfLinearSupplyNested = - totalShares > 0 ? parseFloat(token.dynamicData?.balance || '0') / totalShares : 0; + if (nestedPool && nestedPool.type === 'LINEAR') { + const totalShares = parseFloat(nestedPool.dynamicData?.totalShares || '0'); + const percentOfLinearSupplyNested = + totalShares > 0 ? parseFloat(token.dynamicData?.balance || '0') / totalShares : 0; - return { - ...this.mapPoolTokenToGql({ - ...token, - dynamicData: token.dynamicData - ? { - ...token.dynamicData, - balance: `${parseFloat(token.dynamicData.balance) * percentOfSupplyNested}`, - } - : null, - }), - __typename: 'GqlPoolTokenLinear', - ...this.getLinearPoolTokenData(token, nestedPool), - pool: this.mapNestedPoolToGqlPoolLinearNested( - nestedPool, - percentOfSupplyNested * percentOfLinearSupplyNested, - ), - totalBalance: token.dynamicData?.balance || '0', - }; - } + return { + ...this.mapPoolTokenToGql({ + ...token, + dynamicData: token.dynamicData + ? { + ...token.dynamicData, + balance: `${parseFloat(token.dynamicData.balance) * percentOfSupplyNested}`, + } + : null, + }), + __typename: 'GqlPoolTokenLinear', + ...this.getLinearPoolTokenData(token, nestedPool), + pool: this.mapNestedPoolToGqlPoolLinearNested( + nestedPool, + percentOfSupplyNested * percentOfLinearSupplyNested, + ), + totalBalance: token.dynamicData?.balance || '0', + }; + } - return this.mapPoolTokenToGql(token); - }), + return this.mapPoolTokenToGql(token); + }), totalLiquidity: `${pool.dynamicData?.totalLiquidity || 0}`, totalShares: pool.dynamicData?.totalShares || '0', swapFee: pool.dynamicData?.swapFee || '0', From 8ddbf9e5ace8c575be927c47f12c274dfbb681e8 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 6 Nov 2023 16:48:27 +0100 Subject: [PATCH 011/175] unused --- modules/gnosis/gnosis-safe.service.ts | 54 --------------------------- 1 file changed, 54 deletions(-) delete mode 100644 modules/gnosis/gnosis-safe.service.ts diff --git a/modules/gnosis/gnosis-safe.service.ts b/modules/gnosis/gnosis-safe.service.ts deleted file mode 100644 index 5ecff4151..000000000 --- a/modules/gnosis/gnosis-safe.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -import Safe, { ContractNetworksConfig, EthersAdapter } from '@gnosis.pm/safe-core-sdk'; -import { ethers, providers } from 'ethers'; -import { getAddress } from 'ethers/lib/utils'; -import { Cache } from 'memory-cache'; -import { networkContext } from '../network/network-context.service'; - -const CACHE_KEY_PREFIX = `gnosis-address-is-multisig_`; -const TIMEOUT = 2592000; //30 days - -const contractNetworks: ContractNetworksConfig = { - 250: { - safeProxyFactoryAddress: '0xc3C41Ab65Dabe3ae250A0A1FE4706FdB7ECEB951', - multiSendAddress: '0xd1b160Ee570632ac402Efb230d720669604918e8', - safeMasterCopyAddress: '0x87EB227FE974e9E1d3Bc4Da562e0Bd3C348c2B34', - }, -}; - -export class GnosisSafeService { - private cache = new Cache(); - - public async isAddressGnosisSafe(address: string) { - const key = `${CACHE_KEY_PREFIX}:${networkContext.chainId}:${address}`; - const cachedValue = this.cache.get(key); - if (cachedValue != null) { - return cachedValue; - } - - try { - await Safe.create({ - ethAdapter: await this.getAdapter(), - safeAddress: getAddress(address), - contractNetworks, - }); - - this.cache.put(key, true, TIMEOUT); - return true; - } catch { - this.cache.put(key, false, TIMEOUT); - return false; - } - } - - private async getAdapter() { - const provider = new providers.JsonRpcProvider(networkContext.data.rpcUrl); - const signer = ethers.Wallet.createRandom(); - - return new EthersAdapter({ - ethers, - signer: signer.connect(provider), - }); - } -} - -export const gnosisSafeService = new GnosisSafeService(); From 994f1107022565982b6ede6be3334f2da261cc97 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:29:59 +0100 Subject: [PATCH 012/175] Fix gyro fee (#509) Update pool-on-chain-data.service.ts --- modules/pool/lib/pool-on-chain-data.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 66e6bc82f..66c658053 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -189,7 +189,7 @@ export class PoolOnChainDataService { [ defaultAbiCoder.encode( ['bytes32', 'bytes32'], - [feeKey, formatBytes32String('E-CLP')] + [feeKey, formatBytes32String('ECLP')] ), ] ); @@ -386,7 +386,7 @@ export class PoolOnChainDataService { if (onchainData.gyroProtocolFee) { yieldProtocolFeePercentage = formatFixed(onchainData.gyroProtocolFee, 18); } else if (pool.type.includes('GYRO')) { - if (pool.type === 'GYROE') { + if (pool.type === 'GYROE' && gyroDefaults.eclp) { yieldProtocolFeePercentage = formatFixed(gyroDefaults.eclp, 18) || '0'; } else { yieldProtocolFeePercentage = formatFixed(gyroDefaults.default, 18) || '0'; From 05311c3804d647a80313560178fae431292af48e Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 6 Nov 2023 17:54:57 +0100 Subject: [PATCH 013/175] change poolGetPool --- modules/pool/lib/pool-gql-loader.service.ts | 7 ++++--- modules/pool/pool.gql | 2 +- modules/pool/pool.resolvers.ts | 11 +++++++++-- modules/pool/pool.service.ts | 11 ++++++----- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index b381545ee..de5cd2c7a 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -11,6 +11,7 @@ import { import { GqlBalancePoolAprItem, GqlBalancePoolAprSubItem, + GqlChain, GqlPoolDynamicData, GqlPoolFeaturedPoolGroup, GqlPoolInvestConfig, @@ -33,16 +34,16 @@ import { import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; -import { Prisma, PrismaPoolAprType } from '@prisma/client'; +import { Chain, Prisma, PrismaPoolAprType } from '@prisma/client'; import { isWeightedPoolV2 } from './pool-utils'; import { oldBnum } from '../../big-number/old-big-number'; import { networkContext } from '../../network/network-context.service'; import { fixedNumber } from '../../view-helpers/fixed-number'; export class PoolGqlLoaderService { - public async getPool(id: string): Promise { + public async getPool(id: string, chain: Chain): Promise { const pool = await prisma.prismaPool.findUnique({ - where: { id_chain: { id, chain: networkContext.chain } }, + where: { id_chain: { id, chain: chain } }, include: prismaPoolWithExpandedNesting.include, }); diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index af90e7f62..c4b984cb7 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -1,5 +1,5 @@ extend type Query { - poolGetPool(id: String!): GqlPoolBase! + poolGetPool(id: String!, chain: GqlChain): GqlPoolBase! poolGetPools( first: Int skip: Int diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index d958ca087..064838421 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -3,11 +3,18 @@ import { Resolvers } from '../../schema'; import { isAdminRoute } from '../auth/auth-context'; import { prisma } from '../../prisma/prisma-client'; import { networkContext } from '../network/network-context.service'; +import { headerChain } from '../context/header-chain'; const balancerResolvers: Resolvers = { Query: { - poolGetPool: async (parent, { id }, context) => { - return poolService.getGqlPool(id); + poolGetPool: async (parent, { id, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + return poolService.getGqlPool(id, chain); }, poolGetPools: async (parent, args, context) => { return poolService.getGqlPools(args); diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 852a03bc0..39c0a93f2 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -4,6 +4,7 @@ import { Cache } from 'memory-cache'; import moment from 'moment-timezone'; import { prisma } from '../../prisma/prisma-client'; import { + GqlChain, GqlPoolBatchSwap, GqlPoolFeaturedPoolGroup, GqlPoolJoinExit, @@ -61,8 +62,8 @@ export class PoolService { return networkContext.config.contentService; } - public async getGqlPool(id: string): Promise { - return this.poolGqlLoaderService.getPool(id); + public async getGqlPool(id: string, chain: GqlChain): Promise { + return this.poolGqlLoaderService.getPool(id, chain); } public async getGqlPools(args: QueryPoolGetPoolsArgs): Promise { @@ -438,7 +439,7 @@ export class PoolService { await prisma.prismaPoolLinearData.deleteMany({ where: { chain: networkContext.chain, poolId: poolId }, }); - + await prisma.prismaPoolGyroData.deleteMany({ where: { chain: networkContext.chain, poolId: poolId }, }); @@ -481,9 +482,9 @@ export class PoolService { }, }); - if(gauge && gauge.votingGauge) + if (gauge && gauge.votingGauge) await prisma.prismaVotingGauge.deleteMany({ - where: { chain: networkContext.chain, id: gauge.votingGauge.id } + where: { chain: networkContext.chain, id: gauge.votingGauge.id }, }); await prisma.prismaPoolStakingGauge.deleteMany({ From ce7e6650950a6e374673355104df42da28834987 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 6 Nov 2023 18:24:50 +0100 Subject: [PATCH 014/175] poolGetSwaps and poolGetBatchSwaps --- modules/pool/lib/pool-swap.service.ts | 8 ++++++-- modules/pool/pool.gql | 1 + modules/pool/pool.resolvers.ts | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 83430ab05..2dd2a472c 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -85,7 +85,9 @@ export class PoolSwapService { tokenOut: { in: args.where?.tokenOutIn || undefined, }, - chain: networkContext.chain, + chain: { + in: args.where?.chainIn || undefined, + }, }, orderBy: { timestamp: 'desc' }, }); @@ -144,7 +146,9 @@ export class PoolSwapService { tokenOut: { in: args.where?.tokenOutIn || undefined, }, - chain: networkContext.chain, + chain: { + in: args.where?.chainIn || undefined, + }, }, orderBy: { timestamp: 'desc' }, include: { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index c4b984cb7..2cc0925a4 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -707,6 +707,7 @@ input GqlPoolSwapFilter { tokenInIn: [String!] tokenOutIn: [String!] poolIdIn: [String!] + chainIn: [GqlChain!] } type GqlPoolStaking { diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 064838421..c30b40b11 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -26,9 +26,21 @@ const balancerResolvers: Resolvers = { return poolService.getPoolFilters(); }, poolGetSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.where?.chainIn && currentChain) { + args.where = { ...args.where, chainIn: [currentChain] }; + } else if (!args.where?.chainIn) { + throw new Error('Chain is required'); + } return poolService.getPoolSwaps(args); }, poolGetBatchSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.where?.chainIn && currentChain) { + args.where = { ...args.where, chainIn: [currentChain] }; + } else if (!args.where?.chainIn) { + throw new Error('Chain is required'); + } return poolService.getPoolBatchSwaps(args); }, poolGetJoinExits: async (parent, args, context) => { From aca12ca64ccab11e4fd575b7a718959233d489ee Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 6 Nov 2023 18:25:06 +0100 Subject: [PATCH 015/175] cleanup --- modules/token/token.resolvers.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index 988a6111a..8af8f11d3 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -2,13 +2,12 @@ import { Resolvers } from '../../schema'; import _ from 'lodash'; import { isAdminRoute } from '../auth/auth-context'; import { tokenService } from './token.service'; -import { networkContext } from '../network/network-context.service'; import { headerChain } from '../context/header-chain'; const resolvers: Resolvers = { Query: { tokenGetTokens: async (parent, { chains }, context) => { - const currentChain = headerChain() + const currentChain = headerChain(); if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { @@ -17,7 +16,7 @@ const resolvers: Resolvers = { return tokenService.getTokenDefinitions(chains); }, tokenGetCurrentPrices: async (parent, { chains }, context) => { - const currentChain = headerChain() + const currentChain = headerChain(); if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { From bc5c4f418d2ae7ffac1ca703f45e4c1ab5bd858e Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 10:04:10 +0100 Subject: [PATCH 016/175] user swaps --- modules/pool/lib/pool-swap.service.ts | 10 ++++++++-- modules/pool/pool.gql | 2 ++ .../balancer-subgraph/balancer-subgraph.service.ts | 4 ++-- modules/user/user.gql | 2 +- modules/user/user.resolvers.ts | 14 ++++++++++---- modules/user/user.service.ts | 11 ++++++++--- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 2dd2a472c..38846075e 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -15,12 +15,13 @@ import { QueryPoolGetSwapsArgs, QueryPoolGetUserSwapVolumeArgs, } from '../../../schema'; -import { PrismaPoolSwap } from '@prisma/client'; +import { Chain, PrismaPoolSwap } from '@prisma/client'; import _ from 'lodash'; import { isSupportedInt, prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { PrismaPoolBatchSwapWithSwaps, prismaPoolMinimal } from '../../../prisma/prisma-types'; import { networkContext } from '../../network/network-context.service'; import * as Sentry from '@sentry/node'; +import { AllNetworkConfigsKeyedOnChain } from '../../network/network-config'; export class PoolSwapService { constructor( @@ -96,10 +97,14 @@ export class PoolSwapService { public async getUserSwapsForPool( userAddress: string, poolId: string, + chain: Chain, first = 10, skip = 0, ): Promise { - const result = await this.balancerSubgraphService.getSwaps({ + const balancerSubgraphService = new BalancerSubgraphService( + AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, + ); + const result = await balancerSubgraphService.getSwaps({ first, skip, where: { @@ -112,6 +117,7 @@ export class PoolSwapService { return result.swaps.map((swap) => ({ id: swap.id, + chain: chain, userAddress, poolId: swap.poolId.id, tokenIn: swap.tokenIn, diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 2cc0925a4..04c80623e 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -659,6 +659,7 @@ type GqlPoolFilterDefinition { type GqlPoolSwap { id: ID! + chain: GqlChain! poolId: String! userAddress: String! tokenIn: String! @@ -672,6 +673,7 @@ type GqlPoolSwap { type GqlPoolBatchSwap { id: ID! + chain: GqlChain! userAddress: String! tokenIn: String! tokenOut: String! diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts index 0a4023c9c..43b779b58 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts @@ -52,7 +52,7 @@ const PORTFOLIO_POOLS_CACHE_KEY = `balance-subgraph_portfolio-pools`; export class BalancerSubgraphService { private cache: CacheClass; - constructor() { + constructor(private readonly subgraphUrl = networkContext.data.subgraphs.balancer) { this.cache = new Cache(); } @@ -314,7 +314,7 @@ export class BalancerSubgraphService { } private get sdk() { - const client = new GraphQLClient(networkContext.data.subgraphs.balancer); + const client = new GraphQLClient(this.subgraphUrl); return getSdk(client); } diff --git a/modules/user/user.gql b/modules/user/user.gql index 62803f158..50223a760 100644 --- a/modules/user/user.gql +++ b/modules/user/user.gql @@ -2,7 +2,7 @@ extend type Query { userGetPoolBalances(chains: [GqlChain!], address: String): [GqlUserPoolBalance!]! userGetStaking: [GqlPoolStaking!]! userGetPoolJoinExits(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolJoinExit!]! - userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolSwap!]! + userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!, chain: GqlChain, address: String): [GqlPoolSwap!]! } extend type Mutation { diff --git a/modules/user/user.resolvers.ts b/modules/user/user.resolvers.ts index 0c7424b6f..ec2f2ba54 100644 --- a/modules/user/user.resolvers.ts +++ b/modules/user/user.resolvers.ts @@ -7,7 +7,7 @@ import { headerChain } from '../context/header-chain'; const resolvers: Resolvers = { Query: { userGetPoolBalances: async (parent, { chains, address }, context) => { - const currentChain = headerChain() + const currentChain = headerChain(); if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { @@ -27,9 +27,15 @@ const resolvers: Resolvers = { return userService.getUserPoolInvestments(accountAddress, poolId, first, skip); }, - userGetSwaps: async (parent, { first, skip, poolId }, context) => { - const accountAddress = getRequiredAccountAddress(context); - return userService.getUserSwaps(accountAddress, poolId, first, skip); + userGetSwaps: async (parent, { first, skip, poolId, chain, address }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + const accountAddress = address || getRequiredAccountAddress(context); + return userService.getUserSwaps(accountAddress, poolId, chain, first, skip); }, userGetStaking: async (parent, {}, context) => { const accountAddress = getRequiredAccountAddress(context); diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index fd603906c..22aa391a0 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -5,7 +5,6 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { PoolSnapshotService } from '../pool/lib/pool-snapshot.service'; import { PoolSwapService } from '../pool/lib/pool-swap.service'; import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; -import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { reliquarySubgraphService } from '../subgraphs/reliquary-subgraph/reliquary.service'; import { userSnapshotSubgraphService } from '../subgraphs/user-snapshot-subgraph/user-snapshot-subgraph.service'; import { tokenService } from '../token/token.service'; @@ -40,8 +39,14 @@ export class UserService { return this.poolSwapService.getUserJoinExitsForPool(address, poolId, first, skip); } - public async getUserSwaps(address: string, poolId: string, first?: number, skip?: number): Promise { - return this.poolSwapService.getUserSwapsForPool(address, poolId, first, skip); + public async getUserSwaps( + address: string, + poolId: string, + chain: Chain, + first?: number, + skip?: number, + ): Promise { + return this.poolSwapService.getUserSwapsForPool(address, poolId, chain, first, skip); } public async getUserFbeetsBalance(address: string): Promise> { From 863ff81acb13c5e47675f1f5e87ddda2b5ba394b Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 10:37:16 +0100 Subject: [PATCH 017/175] remove sanity poolFilters --- modules/content/sanity-content.service.ts | 33 ----------------------- modules/pool/pool.gql | 1 - modules/pool/pool.resolvers.ts | 3 --- 3 files changed, 37 deletions(-) diff --git a/modules/content/sanity-content.service.ts b/modules/content/sanity-content.service.ts index 00d6dd8b0..67403a1f2 100644 --- a/modules/content/sanity-content.service.ts +++ b/modules/content/sanity-content.service.ts @@ -26,11 +26,6 @@ interface SanityToken { interface SanityPoolConfig { incentivizedPools: string[]; blacklistedPools: string[]; - poolFilters: { - id: string; - title: string; - pools: string[]; - }[]; } const SANITY_TOKEN_TYPE_MAP: { [key: string]: string } = { @@ -209,13 +204,11 @@ export class SanityContentService implements ContentService { const response = await getSanityClient().fetch(`*[_type == "config" && chainId == ${networkContext.chainId}][0]{ incentivizedPools, blacklistedPools, - poolFilters }`); const config: SanityPoolConfig = { incentivizedPools: response?.incentivizedPools ?? [], blacklistedPools: response?.blacklistedPools ?? [], - poolFilters: response?.poolFilters ?? [], }; const categories = await prisma.prismaPoolCategory.findMany({ where: { chain: networkContext.chain } }); @@ -224,32 +217,6 @@ export class SanityContentService implements ContentService { await this.updatePoolCategory(incentivized, config.incentivizedPools, 'INCENTIVIZED'); await this.updatePoolCategory(blacklisted, config.blacklistedPools, 'BLACK_LISTED'); - - await prisma.$transaction([ - prisma.prismaPoolFilterMap.deleteMany({ where: { chain: networkContext.chain } }), - prisma.prismaPoolFilter.deleteMany({ where: { chain: networkContext.chain } }), - prisma.prismaPoolFilter.createMany({ - data: config.poolFilters.map((item) => ({ - id: item.id, - chain: networkContext.chain, - title: item.title, - })), - skipDuplicates: true, - }), - prisma.prismaPoolFilterMap.createMany({ - data: config.poolFilters - .map((item) => { - return item.pools.map((poolId) => ({ - id: `${item.id}-${poolId}`, - chain: networkContext.chain, - poolId, - filterId: item.id, - })); - }) - .flat(), - skipDuplicates: true, - }), - ]); } private async updatePoolCategory(currentPoolIds: string[], newPoolIds: string[], category: PrismaPoolCategoryType) { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 04c80623e..f21b0f40d 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -16,7 +16,6 @@ extend type Query { where: GqlPoolFilter textSearch: String ): Int! - poolGetPoolFilters: [GqlPoolFilterDefinition!]! poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index c30b40b11..fa646878c 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -22,9 +22,6 @@ const balancerResolvers: Resolvers = { poolGetPoolsCount: async (parent, args, context) => { return poolService.getPoolsCount(args); }, - poolGetPoolFilters: async (parent, {}, context) => { - return poolService.getPoolFilters(); - }, poolGetSwaps: async (parent, args, context) => { const currentChain = headerChain(); if (!args.where?.chainIn && currentChain) { From 5d88651b2bbb0e076023746ec000d340fc8fe145 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 12:58:07 +0100 Subject: [PATCH 018/175] joinExit --- modules/pool/lib/pool-swap.service.ts | 42 ++++++++++++++++++--------- modules/pool/pool.gql | 2 ++ modules/pool/pool.resolvers.ts | 6 ++++ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 38846075e..e230009e9 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -32,20 +32,36 @@ export class PoolSwapService { public async getJoinExits(args: QueryPoolGetJoinExitsArgs): Promise { const first = !args.first || args.first > 100 ? 10 : args.first; - const { joinExits } = await this.balancerSubgraphService.getPoolJoinExits({ - where: { pool_in: args.where?.poolIdIn }, - first, - skip: args.skip, - orderBy: JoinExit_OrderBy.Timestamp, - orderDirection: OrderDirection.Desc, - }); + const allChainsJoinExits: GqlPoolJoinExit[] = []; + + for (const chain of args.where!.chainIn!) { + const balancerSubgraphService = new BalancerSubgraphService( + AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, + ); + + const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ + where: { pool_in: args.where?.poolIdIn }, + first, + skip: args.skip, + orderBy: JoinExit_OrderBy.Timestamp, + orderDirection: OrderDirection.Desc, + }); - return joinExits.map((joinExit) => ({ - ...joinExit, - __typename: 'GqlPoolJoinExit', - poolId: joinExit.pool.id, - amounts: joinExit.amounts.map((amount, index) => ({ address: joinExit.pool.tokensList[index], amount })), - })); + const mappedJoinExits: GqlPoolJoinExit[] = joinExits.map((joinExit) => ({ + ...joinExit, + __typename: 'GqlPoolJoinExit', + chain: chain, + poolId: joinExit.pool.id, + amounts: joinExit.amounts.map((amount, index) => ({ + address: joinExit.pool.tokensList[index], + amount, + })), + })); + + allChainsJoinExits.push(...mappedJoinExits); + } + + return allChainsJoinExits; } public async getUserJoinExitsForPool( diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index f21b0f40d..d91af3d26 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -730,10 +730,12 @@ enum GqlPoolStakingGaugeStatus { input GqlPoolJoinExitFilter { poolIdIn: [String!] + chainIn: [GqlChain!] } type GqlPoolJoinExit { id: ID! + chain: GqlChain! type: GqlPoolJoinExitType! sender: String! poolId: String! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index fa646878c..012b2cd56 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -41,6 +41,12 @@ const balancerResolvers: Resolvers = { return poolService.getPoolBatchSwaps(args); }, poolGetJoinExits: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.where?.chainIn && currentChain) { + args.where = { ...args.where, chainIn: [currentChain] }; + } else if (!args.where?.chainIn) { + throw new Error('Chain is required'); + } return poolService.getPoolJoinExits(args); }, poolGetUserSwapVolume: async (parent, args, context) => { From e99ebc52782d182065709010f7dc4bb5d6f11db7 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 13:03:23 +0100 Subject: [PATCH 019/175] getUserJoinExitsForPool --- modules/pool/lib/pool-swap.service.ts | 7 ++++++- modules/user/user.gql | 8 +++++++- modules/user/user.resolvers.ts | 12 +++++++++--- modules/user/user.service.ts | 3 ++- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index e230009e9..09ae3d080 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -67,10 +67,14 @@ export class PoolSwapService { public async getUserJoinExitsForPool( userAddress: string, poolId: string, + chain: Chain, first = 10, skip = 0, ): Promise { - const { joinExits } = await this.balancerSubgraphService.getPoolJoinExits({ + const balancerSubgraphService = new BalancerSubgraphService( + AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, + ); + const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ where: { pool: poolId, user: userAddress }, first, skip: skip, @@ -82,6 +86,7 @@ export class PoolSwapService { ...joinExit, __typename: 'GqlPoolJoinExit', poolId: joinExit.pool.id, + chain: chain, amounts: joinExit.amounts.map((amount, index) => ({ address: joinExit.pool.tokensList[index], amount })), })); } diff --git a/modules/user/user.gql b/modules/user/user.gql index 50223a760..9a595a15c 100644 --- a/modules/user/user.gql +++ b/modules/user/user.gql @@ -1,7 +1,13 @@ extend type Query { userGetPoolBalances(chains: [GqlChain!], address: String): [GqlUserPoolBalance!]! userGetStaking: [GqlPoolStaking!]! - userGetPoolJoinExits(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolJoinExit!]! + userGetPoolJoinExits( + first: Int = 10 + skip: Int = 0 + poolId: String! + chain: GqlChain + address: String + ): [GqlPoolJoinExit!]! userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!, chain: GqlChain, address: String): [GqlPoolSwap!]! } diff --git a/modules/user/user.resolvers.ts b/modules/user/user.resolvers.ts index ec2f2ba54..6b391fcb6 100644 --- a/modules/user/user.resolvers.ts +++ b/modules/user/user.resolvers.ts @@ -22,10 +22,16 @@ const resolvers: Resolvers = { tokenPrice: tokenService.getPriceForToken(tokenPrices[balance.chain] || [], balance.tokenAddress), })); }, - userGetPoolJoinExits: async (parent, { first, skip, poolId }, context) => { - const accountAddress = getRequiredAccountAddress(context); + userGetPoolJoinExits: async (parent, { first, skip, poolId, chain, address }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + const accountAddress = address || getRequiredAccountAddress(context); - return userService.getUserPoolInvestments(accountAddress, poolId, first, skip); + return userService.getUserPoolInvestments(accountAddress, poolId, chain, first, skip); }, userGetSwaps: async (parent, { first, skip, poolId, chain, address }, context) => { const currentChain = headerChain(); diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index 22aa391a0..cb7028c39 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -33,10 +33,11 @@ export class UserService { public async getUserPoolInvestments( address: string, poolId: string, + chain: Chain, first?: number, skip?: number, ): Promise { - return this.poolSwapService.getUserJoinExitsForPool(address, poolId, first, skip); + return this.poolSwapService.getUserJoinExitsForPool(address, poolId, chain, first, skip); } public async getUserSwaps( From 922a0d51aa06b8cfe1ef2d54260f6ce6b3689fd6 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 13:36:26 +0100 Subject: [PATCH 020/175] remove unused userSwapVolume --- modules/pool/lib/pool-swap.service.ts | 22 ---------------------- modules/pool/pool.gql | 1 - modules/pool/pool.resolvers.ts | 3 --- modules/pool/pool.service.ts | 5 ----- 4 files changed, 31 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 09ae3d080..b3330e68a 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -13,7 +13,6 @@ import { QueryPoolGetBatchSwapsArgs, QueryPoolGetJoinExitsArgs, QueryPoolGetSwapsArgs, - QueryPoolGetUserSwapVolumeArgs, } from '../../../schema'; import { Chain, PrismaPoolSwap } from '@prisma/client'; import _ from 'lodash'; @@ -184,27 +183,6 @@ export class PoolSwapService { }); } - public async getUserSwapVolume(args: QueryPoolGetUserSwapVolumeArgs) { - const yesterday = moment().subtract(1, 'day').unix(); - const take = !args.first || args.first > 100 ? 10 : args.first; - - const result = await prisma.prismaPoolSwap.groupBy({ - take, - skip: args.skip || undefined, - by: ['userAddress'], - _sum: { valueUSD: true }, - orderBy: { _sum: { valueUSD: 'desc' } }, - where: { - poolId: { in: args.where?.poolIdIn || undefined }, - tokenIn: { in: args.where?.tokenInIn || undefined }, - tokenOut: { in: args.where?.tokenOutIn || undefined }, - timestamp: { gte: yesterday }, - }, - }); - - return result.map((item) => ({ userAddress: item.userAddress, swapVolumeUSD: `${item._sum.valueUSD || 0}` })); - } - /** * Syncs all swaps for the last 48 hours. We fetch the timestamp of the last stored swap to avoid * duplicate effort. Return an array of poolIds with swaps added. diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index d91af3d26..58db21daa 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -19,7 +19,6 @@ extend type Query { poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! - poolGetUserSwapVolume(first: Int, skip: Int, where: GqlUserSwapVolumeFilter): [GqlPoolUserSwapVolume!]! poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! poolGetSnapshots(id: String!, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetAllPoolsSnapshots(range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 012b2cd56..3429aaf84 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -49,9 +49,6 @@ const balancerResolvers: Resolvers = { } return poolService.getPoolJoinExits(args); }, - poolGetUserSwapVolume: async (parent, args, context) => { - return poolService.getPoolUserSwapVolume(args); - }, poolGetFeaturedPoolGroups: async (parent, args, context) => { return poolService.getFeaturedPoolGroups(); }, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 39c0a93f2..3839ed2b4 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -17,7 +17,6 @@ import { QueryPoolGetJoinExitsArgs, QueryPoolGetPoolsArgs, QueryPoolGetSwapsArgs, - QueryPoolGetUserSwapVolumeArgs, } from '../../schema'; import { coingeckoService } from '../coingecko/coingecko.service'; import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; @@ -104,10 +103,6 @@ export class PoolService { return this.poolSwapService.getJoinExits(args); } - public async getPoolUserSwapVolume(args: QueryPoolGetUserSwapVolumeArgs): Promise { - return this.poolSwapService.getUserSwapVolume(args); - } - public async getFeaturedPoolGroups(): Promise { const cached: GqlPoolFeaturedPoolGroup[] = await this.cache.get( `${FEATURED_POOL_GROUPS_CACHE_KEY}:${networkContext.chainId}`, From 1eb702fe2d03525590ed18f20ca7cf5161fc4ea5 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 14:23:54 +0100 Subject: [PATCH 021/175] poolSnapshot --- modules/beethoven/beets.gql | 2 +- modules/beethoven/beets.resolvers.ts | 3 ++- modules/pool/lib/pool-snapshot.service.ts | 11 +++++------ modules/pool/pool.gql | 5 +++-- modules/pool/pool.resolvers.ts | 20 ++++++++++++++++---- modules/pool/pool.service.ts | 10 +++++----- modules/user/lib/user-snapshot.service.ts | 14 +++++++++++--- modules/user/user.service.ts | 3 ++- 8 files changed, 45 insertions(+), 23 deletions(-) diff --git a/modules/beethoven/beets.gql b/modules/beethoven/beets.gql index 445412974..caa15efbb 100644 --- a/modules/beethoven/beets.gql +++ b/modules/beethoven/beets.gql @@ -5,7 +5,7 @@ extend type Query { userGetFbeetsBalance: GqlUserFbeetsBalance! - userGetPoolSnapshots(poolId: String!, range: GqlUserSnapshotDataRange!): [GqlUserPoolSnapshot!]! + userGetPoolSnapshots(poolId: String!, chain: GqlChain!, range: GqlUserSnapshotDataRange!): [GqlUserPoolSnapshot!]! userGetRelicSnapshots(farmId: String!, range: GqlUserSnapshotDataRange!): [GqlUserRelicSnapshot!]! userGetPortfolioSnapshots(days: Int!): [GqlUserPortfolioSnapshot!]! diff --git a/modules/beethoven/beets.resolvers.ts b/modules/beethoven/beets.resolvers.ts index ecb709df7..87e705789 100644 --- a/modules/beethoven/beets.resolvers.ts +++ b/modules/beethoven/beets.resolvers.ts @@ -36,12 +36,13 @@ const beetsResolvers: Resolvers = { ...balance, }; }, - userGetPoolSnapshots: async (parent, { poolId, range }, context) => { + userGetPoolSnapshots: async (parent, { poolId, chain, range }, context) => { const accountAddress = getRequiredAccountAddress(context); return userService.getUserBalanceSnapshotsForPool( accountAddress.toLowerCase(), poolId.toLowerCase(), + chain, range, ); }, diff --git a/modules/pool/lib/pool-snapshot.service.ts b/modules/pool/lib/pool-snapshot.service.ts index e64062b73..3c51d26cc 100644 --- a/modules/pool/lib/pool-snapshot.service.ts +++ b/modules/pool/lib/pool-snapshot.service.ts @@ -1,4 +1,3 @@ -import * as Sentry from '@sentry/node'; import { balancerSubgraphService, BalancerSubgraphService, @@ -12,7 +11,7 @@ import { import { GqlPoolSnapshotDataRange } from '../../../schema'; import moment from 'moment-timezone'; import _ from 'lodash'; -import { PrismaPoolSnapshot } from '@prisma/client'; +import { Chain, PrismaPoolSnapshot } from '@prisma/client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { prismaPoolWithExpandedNesting } from '../../../prisma/prisma-types'; import { CoingeckoService } from '../../coingecko/coingecko.service'; @@ -27,11 +26,11 @@ export class PoolSnapshotService { private readonly coingeckoService: CoingeckoService, ) {} - public async getSnapshotsForPool(poolId: string, range: GqlPoolSnapshotDataRange) { + public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ - where: { poolId, timestamp: { gte: timestamp }, chain: networkContext.chain }, + where: { poolId, timestamp: { gte: timestamp }, chain: chain }, orderBy: { timestamp: 'asc' }, }); } @@ -42,7 +41,7 @@ export class PoolSnapshotService { }); } - public async getSnapshotsForAllPools(range: GqlPoolSnapshotDataRange) { + public async getSnapshotsForAllPools(chains: Chain[], range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ @@ -54,7 +53,7 @@ export class PoolSnapshotService { pool: { categories: { none: { category: 'BLACK_LISTED' } }, }, - chain: networkContext.chain, + chain: { in: chains }, }, orderBy: { timestamp: 'asc' }, }); diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 58db21daa..240fce4c1 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -20,8 +20,8 @@ extend type Query { poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! - poolGetSnapshots(id: String!, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! - poolGetAllPoolsSnapshots(range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! + poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! + poolGetAllPoolsSnapshots(chains: [GqlChain!], range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetLinearPools: [GqlPoolLinear!]! } @@ -804,6 +804,7 @@ enum GqlPoolSnapshotDataRange { type GqlPoolSnapshot { id: ID! + chain: GqlChain! poolId: String! timestamp: Int! totalLiquidity: String! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 3429aaf84..403635ab9 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -52,8 +52,14 @@ const balancerResolvers: Resolvers = { poolGetFeaturedPoolGroups: async (parent, args, context) => { return poolService.getFeaturedPoolGroups(); }, - poolGetSnapshots: async (parent, { id, range }, context) => { - const snapshots = await poolService.getSnapshotsForPool(id, range); + poolGetSnapshots: async (parent, { id, chain, range }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + const snapshots = await poolService.getSnapshotsForPool(id, chain, range); return snapshots.map((snapshot) => ({ ...snapshot, @@ -67,8 +73,14 @@ const balancerResolvers: Resolvers = { holdersCount: `${snapshot.holdersCount}`, })); }, - poolGetAllPoolsSnapshots: async (parent, { range }, context) => { - const snapshots = await poolService.getSnapshotsForAllPools(range); + poolGetAllPoolsSnapshots: async (parent, { chains, range }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + chains = []; + } + const snapshots = await poolService.getSnapshotsForAllPools(chains, range); return snapshots.map((snapshot) => ({ ...snapshot, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 3839ed2b4..29a863b85 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -1,4 +1,4 @@ -import { PrismaPoolFilter, PrismaPoolStakingType, PrismaPoolSwap } from '@prisma/client'; +import { Chain, PrismaPoolFilter, PrismaPoolStakingType, PrismaPoolSwap } from '@prisma/client'; import _, { chain, includes } from 'lodash'; import { Cache } from 'memory-cache'; import moment from 'moment-timezone'; @@ -123,12 +123,12 @@ export class PoolService { return featuredPoolGroups; } - public async getSnapshotsForAllPools(range: GqlPoolSnapshotDataRange) { - return this.poolSnapshotService.getSnapshotsForAllPools(range); + public async getSnapshotsForAllPools(chains: Chain[], range: GqlPoolSnapshotDataRange) { + return this.poolSnapshotService.getSnapshotsForAllPools(chains, range); } - public async getSnapshotsForPool(poolId: string, range: GqlPoolSnapshotDataRange) { - return this.poolSnapshotService.getSnapshotsForPool(poolId, range); + public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { + return this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); } public async getSnapshotsForReliquaryFarm(id: number, range: GqlPoolSnapshotDataRange) { diff --git a/modules/user/lib/user-snapshot.service.ts b/modules/user/lib/user-snapshot.service.ts index c31e9c2bc..f5d262761 100644 --- a/modules/user/lib/user-snapshot.service.ts +++ b/modules/user/lib/user-snapshot.service.ts @@ -4,7 +4,14 @@ import moment from 'moment-timezone'; import { UserPoolSnapshot, UserRelicSnapshot } from '../user-types'; import { GqlUserSnapshotDataRange } from '../../../schema'; import { PoolSnapshotService } from '../../pool/lib/pool-snapshot.service'; -import { Prisma, PrismaPool, PrismaPoolSnapshot, PrismaPoolStaking, PrismaUserRelicSnapshot } from '@prisma/client'; +import { + Chain, + Prisma, + PrismaPool, + PrismaPoolSnapshot, + PrismaPoolStaking, + PrismaUserRelicSnapshot, +} from '@prisma/client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { oneDayInSeconds, secondsPerDay } from '../../common/time'; import { UserBalanceSnapshotFragment } from '../../subgraphs/user-snapshot-subgraph/generated/user-snapshot-subgraph-types'; @@ -309,6 +316,7 @@ export class UserSnapshotService { public async getUserPoolBalanceSnapshotsForPool( userAddress: string, poolId: string, + chain: Chain, range: GqlUserSnapshotDataRange, ): Promise { const oldestRequestedSnapshotTimestamp = this.getTimestampForRange(range); @@ -374,7 +382,7 @@ export class UserSnapshotService { const prismaInput: Prisma.PrismaUserPoolBalanceSnapshotCreateManyInput[] = []; - poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, range); + poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); /* For each snapshot from the subgraph, this will get the poolSnapshot for the same timestamp and enrich with $ value data @@ -436,7 +444,7 @@ export class UserSnapshotService { // Only get them if we didn't get them above if (poolSnapshots.length === 0) { - poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, range); + poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); } /* diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index cb7028c39..716785074 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -61,9 +61,10 @@ export class UserService { public async getUserBalanceSnapshotsForPool( accountAddress: string, poolId: string, + chain: Chain, days: GqlUserSnapshotDataRange, ): Promise { - return this.userSnapshotService.getUserPoolBalanceSnapshotsForPool(accountAddress, poolId, days); + return this.userSnapshotService.getUserPoolBalanceSnapshotsForPool(accountAddress, poolId, chain, days); } public async getUserRelicSnapshots(accountAddress: string, farmId: string, days: GqlUserSnapshotDataRange) { From cfb936864dfb6149ca5e0701cfba8ebfd274c673 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 16:24:34 +0100 Subject: [PATCH 022/175] add veQi --- modules/vebal/special-pools/ve-pools.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/vebal/special-pools/ve-pools.ts b/modules/vebal/special-pools/ve-pools.ts index bb9ad5816..ed3f00905 100644 --- a/modules/vebal/special-pools/ve-pools.ts +++ b/modules/vebal/special-pools/ve-pools.ts @@ -1,10 +1,12 @@ /* The following hardcoded pools are missing in the subgraph but we have their pool data so we just need to provide a match between poolId and veVotingGauge address + This is needed for any singleRecipientGauge */ export const vePools: Record = { '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014': '0xb78543e00712c3abba10d0852f6e38fde2aaba4d', //veBAL '0x9232a548dd9e81bac65500b5e0d918f8ba93675c000200000000000000000423': '0x56124eb16441a1ef12a4ccaeabdd3421281b795a', //veLIT '0xd689abc77b82803f22c49de5c8a0049cc74d11fd000200000000000000000524': '0x5b79494824bc256cd663648ee1aad251b32693a9', //veUSH + '0x39eb558131e5ebeb9f76a6cbf6898f6e6dce5e4e0002000000000000000005c8': '0x8E891A7b048A594592e9F0de70Dc223143B4F1E6 ', //veQi }; export const veGauges = Object.values(vePools).map((v) => v.toLowerCase()); From dee68da3bda72684ee8100b9ed96d183eaacfc60 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 16:39:07 +0100 Subject: [PATCH 023/175] linearPools --- modules/pool/lib/pool-gql-loader.service.ts | 4 +- modules/pool/pool.gql | 2 +- modules/pool/pool.resolvers.ts | 12 +++- modules/pool/pool.service.ts | 4 +- modules/user/user-snapshot.service.test.ts | 61 ++++++++++++++++++--- 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index de5cd2c7a..85e841c54 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -67,9 +67,9 @@ export class PoolGqlLoaderService { return pools.map((pool) => this.mapToMinimalGqlPool(pool)); } - public async getLinearPools(): Promise { + public async getLinearPools(chains: Chain[]): Promise { const pools = await prisma.prismaPool.findMany({ - where: { type: 'LINEAR', chain: networkContext.chain }, + where: { type: 'LINEAR', chain: { in: chains } }, orderBy: { dynamicData: { totalLiquidity: 'desc' } }, include: prismaPoolWithExpandedNesting.include, }); diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 240fce4c1..f744fde6d 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -22,7 +22,7 @@ extend type Query { poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetAllPoolsSnapshots(chains: [GqlChain!], range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! - poolGetLinearPools: [GqlPoolLinear!]! + poolGetLinearPools(chains: [GqlChain!]): [GqlPoolLinear!]! } extend type Mutation { diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 403635ab9..3a88f526c 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -78,7 +78,7 @@ const balancerResolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } const snapshots = await poolService.getSnapshotsForAllPools(chains, range); @@ -94,8 +94,14 @@ const balancerResolvers: Resolvers = { holdersCount: `${snapshot.holdersCount}`, })); }, - poolGetLinearPools: async () => { - return poolService.getGqlLinearPools(); + poolGetLinearPools: async (parent, { chains }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('Chain is required'); + } + return poolService.getGqlLinearPools(chains); }, }, Mutation: { diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 29a863b85..a6a69bd96 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -69,8 +69,8 @@ export class PoolService { return this.poolGqlLoaderService.getPools(args); } - public async getGqlLinearPools(): Promise { - return this.poolGqlLoaderService.getLinearPools(); + public async getGqlLinearPools(chains: Chain[]): Promise { + return this.poolGqlLoaderService.getLinearPools(chains); } public async getPoolsCount(args: QueryPoolGetPoolsArgs): Promise { diff --git a/modules/user/user-snapshot.service.test.ts b/modules/user/user-snapshot.service.test.ts index 47d8da75b..f97fc4d57 100644 --- a/modules/user/user-snapshot.service.test.ts +++ b/modules/user/user-snapshot.service.test.ts @@ -13,6 +13,7 @@ import { mockServer } from '../tests-helper/mocks/mockHttpServer'; import { userService } from './user.service'; import { secondsPerDay } from '../common/time'; import { networkContext } from '../network/network-context.service'; +import { Chain } from '@prisma/client'; /* TEST SETUP: @@ -182,7 +183,12 @@ test('The user requests the user stats for the first time, requesting from subgr ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -298,6 +304,7 @@ test('User in in the pool for a very long time, requests various different time const thirtySnapshotsFromService = await userService.getUserBalanceSnapshotsForPool( userAddress, poolId1, + Chain.FANTOM, 'THIRTY_DAYS', ); @@ -316,6 +323,7 @@ test('User in in the pool for a very long time, requests various different time const ninetySnapshotsFromService = await userService.getUserBalanceSnapshotsForPool( userAddress, poolId1, + Chain.FANTOM, 'NINETY_DAYS', ); //also includes the one from today @@ -412,7 +420,12 @@ Mock data for user-balance-subgraph (important that timestamps are ASC, as this ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -535,7 +548,12 @@ Mock data for user-balance-subgraph (important that timestamps are ASC, as this ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(2); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -627,7 +645,12 @@ test('Return a snapshot with 0 valueUSD if there is no pool snapshot for the giv ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, pool2Id, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + pool2Id, + Chain.FANTOM, + 'THIRTY_DAYS', + ); // should get all 4 snapshots expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -661,6 +684,7 @@ test('Return a snapshot with 0 valueUSD if there is no pool snapshot for the giv const snapshotsAfterAdditionalPoolSnapshot = await userService.getUserBalanceSnapshotsForPool( userAddress, pool2Id, + Chain.FANTOM, 'THIRTY_DAYS', ); //expect still the same results here as above @@ -804,7 +828,12 @@ test('User snapshots in the database must be picked up and synced by the sync pr expect(snapshotsFromDb.length).toBe(3); // after the sync, all 4 snapshots should be present - const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(snapshotsAfterSync.length).toBe(4); // check if balances are calculated correctly @@ -1000,7 +1029,12 @@ test('User has left and re-entered the pool. Make sure the sync does not persist // after the sync, 5 snapshots should be present. //Sevendaysago, sixdaysago (inferred), fivedaysago (0 balance), fourdays ago (0 balance), threedaysago and twodaysago (0 balance) - const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(snapshotsAfterSync.length).toBe(6); const snapshotsFromDbAfterGet = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -1115,7 +1149,12 @@ test('Todays user snapshot must be gradually updated based on an updated pool sn ], ); - const userSnapshotsBefore = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const userSnapshotsBefore = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(userSnapshotsBefore.length).toBe(1); // check if balances are calculated correctly @@ -1166,7 +1205,12 @@ test('Todays user snapshot must be gradually updated based on an updated pool sn await userService.syncUserBalanceSnapshots(); // check numbers again - const userSnapshotsAfter = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const userSnapshotsAfter = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(userSnapshotsBefore.length).toBe(1); // check if balances are calculated correctly @@ -1292,6 +1336,7 @@ test('User requests pool snapshots for Fidelio Duetto Pool. Make sure fBeets are const userBalanceSnapshots = await userService.getUserBalanceSnapshotsForPool( userAddress, fidelioPoolId, + Chain.FANTOM, 'THIRTY_DAYS', ); expect(userBalanceSnapshots.length).toBe(4); From a99c49acf6843d8459b54ce86c2c389e5016d731 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 16:44:11 +0100 Subject: [PATCH 024/175] userstaking --- modules/pool/pool.gql | 1 + modules/token/token.resolvers.ts | 2 +- modules/user/lib/user-balance.service.ts | 4 ++-- modules/user/user.gql | 2 +- modules/user/user.resolvers.ts | 14 ++++++++++---- modules/user/user.service.ts | 4 ++-- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index f744fde6d..b033434ef 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -712,6 +712,7 @@ input GqlPoolSwapFilter { type GqlPoolStaking { id: ID! + chain: GqlChain! type: GqlPoolStakingType! address: String! gauge: GqlPoolStakingGauge diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index 8af8f11d3..1fd73b5c9 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -11,7 +11,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } return tokenService.getTokenDefinitions(chains); }, diff --git a/modules/user/lib/user-balance.service.ts b/modules/user/lib/user-balance.service.ts index 200f7837b..fb98fb1f2 100644 --- a/modules/user/lib/user-balance.service.ts +++ b/modules/user/lib/user-balance.service.ts @@ -74,12 +74,12 @@ export class UserBalanceService { }; } - public async getUserStaking(address: string): Promise { + public async getUserStaking(address: string, chains: Chain[]): Promise { const user = await prisma.prismaUser.findUnique({ where: { address }, include: { stakedBalances: { - where: { chain: networkContext.chain, balanceNum: { gt: 0 } }, + where: { chain: { in: chains }, balanceNum: { gt: 0 } }, include: { staking: { include: { diff --git a/modules/user/user.gql b/modules/user/user.gql index 9a595a15c..bdeabede4 100644 --- a/modules/user/user.gql +++ b/modules/user/user.gql @@ -1,6 +1,6 @@ extend type Query { userGetPoolBalances(chains: [GqlChain!], address: String): [GqlUserPoolBalance!]! - userGetStaking: [GqlPoolStaking!]! + userGetStaking(chains: [GqlChain!], address: String): [GqlPoolStaking!]! userGetPoolJoinExits( first: Int = 10 skip: Int = 0 diff --git a/modules/user/user.resolvers.ts b/modules/user/user.resolvers.ts index 6b391fcb6..e94480d62 100644 --- a/modules/user/user.resolvers.ts +++ b/modules/user/user.resolvers.ts @@ -11,7 +11,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } const accountAddress = address || getRequiredAccountAddress(context); const tokenPrices = await tokenService.getTokenPricesForChains(chains); @@ -43,10 +43,16 @@ const resolvers: Resolvers = { const accountAddress = address || getRequiredAccountAddress(context); return userService.getUserSwaps(accountAddress, poolId, chain, first, skip); }, - userGetStaking: async (parent, {}, context) => { - const accountAddress = getRequiredAccountAddress(context); + userGetStaking: async (parent, { chains, address }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('Chain is required'); + } + const accountAddress = address || getRequiredAccountAddress(context); - return userService.getUserStaking(accountAddress); + return userService.getUserStaking(accountAddress, chains); }, }, Mutation: { diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index 716785074..f58c6104f 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -54,8 +54,8 @@ export class UserService { return this.userBalanceService.getUserFbeetsBalance(address); } - public async getUserStaking(address: string): Promise { - return this.userBalanceService.getUserStaking(address); + public async getUserStaking(address: string, chains: Chain[]): Promise { + return this.userBalanceService.getUserStaking(address, chains); } public async getUserBalanceSnapshotsForPool( From 78d36c128b124aa889e14ae8f7ef3da5d33707c1 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 16:52:04 +0100 Subject: [PATCH 025/175] throw error --- modules/token/token.resolvers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index 1fd73b5c9..b5c54cafe 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -20,7 +20,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } const prices = await tokenService.getWhiteListedTokenPrices(chains); From 50db8d904ad855f629f2edfe69beb00e23b39c29 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 16:53:54 +0100 Subject: [PATCH 026/175] lowercase gauge addr --- modules/vebal/special-pools/ve-pools.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vebal/special-pools/ve-pools.ts b/modules/vebal/special-pools/ve-pools.ts index ed3f00905..309ad7983 100644 --- a/modules/vebal/special-pools/ve-pools.ts +++ b/modules/vebal/special-pools/ve-pools.ts @@ -6,7 +6,7 @@ export const vePools: Record = { '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014': '0xb78543e00712c3abba10d0852f6e38fde2aaba4d', //veBAL '0x9232a548dd9e81bac65500b5e0d918f8ba93675c000200000000000000000423': '0x56124eb16441a1ef12a4ccaeabdd3421281b795a', //veLIT '0xd689abc77b82803f22c49de5c8a0049cc74d11fd000200000000000000000524': '0x5b79494824bc256cd663648ee1aad251b32693a9', //veUSH - '0x39eb558131e5ebeb9f76a6cbf6898f6e6dce5e4e0002000000000000000005c8': '0x8E891A7b048A594592e9F0de70Dc223143B4F1E6 ', //veQi + '0x39eb558131e5ebeb9f76a6cbf6898f6e6dce5e4e0002000000000000000005c8': '0x8e891a7b048a594592e9f0de70dc223143b4f1e6 ', //veQi }; export const veGauges = Object.values(vePools).map((v) => v.toLowerCase()); From 73792e3f5759896860613cef6389754fa72a7133 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 17:11:08 +0100 Subject: [PATCH 027/175] remove blank --- modules/vebal/special-pools/ve-pools.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vebal/special-pools/ve-pools.ts b/modules/vebal/special-pools/ve-pools.ts index 309ad7983..a6fbad812 100644 --- a/modules/vebal/special-pools/ve-pools.ts +++ b/modules/vebal/special-pools/ve-pools.ts @@ -6,7 +6,7 @@ export const vePools: Record = { '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014': '0xb78543e00712c3abba10d0852f6e38fde2aaba4d', //veBAL '0x9232a548dd9e81bac65500b5e0d918f8ba93675c000200000000000000000423': '0x56124eb16441a1ef12a4ccaeabdd3421281b795a', //veLIT '0xd689abc77b82803f22c49de5c8a0049cc74d11fd000200000000000000000524': '0x5b79494824bc256cd663648ee1aad251b32693a9', //veUSH - '0x39eb558131e5ebeb9f76a6cbf6898f6e6dce5e4e0002000000000000000005c8': '0x8e891a7b048a594592e9f0de70dc223143b4f1e6 ', //veQi + '0x39eb558131e5ebeb9f76a6cbf6898f6e6dce5e4e0002000000000000000005c8': '0x8e891a7b048a594592e9f0de70dc223143b4f1e6', //veQi }; export const veGauges = Object.values(vePools).map((v) => v.toLowerCase()); From 7497f1c0b939f321aad8cb5679c9a9ed8df7718f Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 8 Nov 2023 10:06:27 +0800 Subject: [PATCH 028/175] Add user address filter to pools query --- modules/pool/lib/pool-gql-loader.service.ts | 40 +++++++++++++++++++-- modules/pool/pool.gql | 2 ++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index b381545ee..4ed99b862 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -131,6 +131,7 @@ export class PoolGqlLoaderService { private mapQueryArgsToPoolQuery(args: QueryPoolGetPoolsArgs): Prisma.PrismaPoolFindManyArgs { let orderBy: Prisma.PrismaPoolOrderByWithRelationInput = {}; const orderDirection = args.orderDirection || undefined; + const userAddress = args.userAddress; switch (args.orderBy) { case 'totalLiquidity': @@ -206,6 +207,35 @@ export class PoolGqlLoaderService { }); } + const userArgs: Prisma.PrismaPoolWhereInput = userAddress + ? { + OR: [ + { + userWalletBalances: { + some: { + userAddress: { + equals: userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, + }, + }, + { + userStakedBalances: { + some: { + userAddress: { + equals: userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, + }, + }, + ], + } + : {}; + const filterArgs: Prisma.PrismaPoolWhereInput = { dynamicData: { totalSharesNum: { @@ -271,7 +301,10 @@ export class PoolGqlLoaderService { if (!textSearch) { return { ...baseQuery, - where: filterArgs, + where: { + ...filterArgs, + ...userArgs, + }, }; } @@ -279,10 +312,11 @@ export class PoolGqlLoaderService { ...baseQuery, where: { OR: [ - { name: textSearch, ...filterArgs }, - { symbol: textSearch, ...filterArgs }, + { name: textSearch, ...filterArgs, ...userArgs }, + { symbol: textSearch, ...filterArgs, ...userArgs }, { ...filterArgs, + ...userArgs, allTokens: { some: { OR: [ diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index af90e7f62..0457a378d 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -7,6 +7,7 @@ extend type Query { orderDirection: GqlPoolOrderDirection where: GqlPoolFilter textSearch: String + userAddress: String ): [GqlPoolMinimal!]! poolGetPoolsCount( first: Int @@ -15,6 +16,7 @@ extend type Query { orderDirection: GqlPoolOrderDirection where: GqlPoolFilter textSearch: String + userAddress: String ): Int! poolGetPoolFilters: [GqlPoolFilterDefinition!]! poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! From ba7d0578892b5c597b06d8c3c4fbbe5ac023f663 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 10:03:38 +0100 Subject: [PATCH 029/175] change fantom rpc --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 07c7ed9bf..352d12494 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://rpc.fantom.network', + rpcUrl: 'https://rpc2.fantom.network', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From 3af9cd253ee91b4b275312efd241c3a312604ad9 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 10:22:17 +0100 Subject: [PATCH 030/175] change fantom rpc again --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 352d12494..0dccfbc63 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -102,7 +102,7 @@ const fantomNetworkData: NetworkData = { protocolToken: 'beets', beets: { address: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', - beetsPriceProviderRpcUrl: 'https://rpc.ftm.tools', + beetsPriceProviderRpcUrl: 'https://fantom.drpc.org', }, fbeets: { address: '0xfcef8a994209d6916eb2c86cdd2afd60aa6f54b1', From b79a29a8f983a6d0eb5bdcc3cd4fcaa24a1094ea Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 10:37:36 +0100 Subject: [PATCH 031/175] fix rpc --- modules/network/fantom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 0dccfbc63..783b1ac59 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://rpc2.fantom.network', + rpcUrl: 'https://fantom.drpc.org', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', @@ -102,7 +102,7 @@ const fantomNetworkData: NetworkData = { protocolToken: 'beets', beets: { address: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', - beetsPriceProviderRpcUrl: 'https://fantom.drpc.org', + beetsPriceProviderRpcUrl: 'https://rpc.ftm.tools', }, fbeets: { address: '0xfcef8a994209d6916eb2c86cdd2afd60aa6f54b1', From 90ecb4b7b9dcd10c7ee3d637e9bf4822cbe1c6bb Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 14:41:54 +0100 Subject: [PATCH 032/175] change rpc --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 783b1ac59..059107105 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://fantom.drpc.org', + rpcUrl: 'https://rpc.fantom.gateway.fm', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From a1371657ac0aa213362a4e00d1963c5f56a556a9 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 15:00:36 +0100 Subject: [PATCH 033/175] change rpc --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 783b1ac59..059107105 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://fantom.drpc.org', + rpcUrl: 'https://rpc.fantom.gateway.fm', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From 623b5615ea145160f6a4de1b3289cb17169f377a Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 15:38:39 +0100 Subject: [PATCH 034/175] logging --- .../pool/lib/staking/gauge-staking.service.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/pool/lib/staking/gauge-staking.service.ts b/modules/pool/lib/staking/gauge-staking.service.ts index 99ac67a8b..5b56899df 100644 --- a/modules/pool/lib/staking/gauge-staking.service.ts +++ b/modules/pool/lib/staking/gauge-staking.service.ts @@ -83,10 +83,13 @@ export class GaugeStakingService implements PoolStakingService { async syncStakingForPools(pools?: { id: string }[]): Promise { // Getting data from the DB and subgraph - const poolIds = (pools ?? await prisma.prismaPool.findMany({ - select: { id: true }, - where: { chain: networkContext.chain }, - })).map((pool) => pool.id); + const poolIds = ( + pools ?? + (await prisma.prismaPool.findMany({ + select: { id: true }, + where: { chain: networkContext.chain }, + })) + ).map((pool) => pool.id); const { pools: subgraphPoolsWithGauges } = await this.gaugeSubgraphService.getPoolsWithGauges(poolIds); const subgraphGauges = subgraphPoolsWithGauges @@ -180,7 +183,7 @@ export class GaugeStakingService implements PoolStakingService { if (!token) { const poolId = subgraphGauges.find((gauge) => gauge.id === gaugeId)?.poolId; console.error( - `Could not find reward token (${tokenAddress}) in DB for gauge ${gaugeId} of pool ${poolId}`, + `Could not find reward token (${tokenAddress}) in DB for gauge ${gaugeId} of pool ${poolId} on chain ${networkContext.chain}`, ); continue; } @@ -205,7 +208,9 @@ export class GaugeStakingService implements PoolStakingService { await prismaBulkExecuteOperations(operations, true, undefined); } - private async getOnchainRewardTokensData(gauges: { id: string; version: 1 | 2; tokens: { id: string, decimals: number }[] }[]) { + private async getOnchainRewardTokensData( + gauges: { id: string; version: 1 | 2; tokens: { id: string; decimals: number }[] }[], + ) { // Get onchain data for BAL rewards const currentWeek = Math.floor(Date.now() / 1000 / 604800); for (const gauge of gauges) { @@ -283,7 +288,9 @@ export class GaugeStakingService implements PoolStakingService { const id = `${gaugeAddress}-${tokenAddress}`.toLowerCase(); const { rate, period_finish } = rewardsData[gaugeAddress].rewardData[tokenAddress]; const rewardPerSecond = - period_finish && period_finish.toNumber() > now ? formatUnits(rate!, decimals[tokenAddress]) : '0.0'; + period_finish && period_finish.toNumber() > now + ? formatUnits(rate!, decimals[tokenAddress]) + : '0.0'; const { totalSupply } = balData[gaugeAddress]; return { From b6c88a1b8c21cc5bde0137380bb8893e6eb85b9a Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 10:37:16 +0100 Subject: [PATCH 035/175] remove sanity poolFilters --- modules/content/sanity-content.service.ts | 33 ----------------------- modules/pool/pool.gql | 1 - modules/pool/pool.resolvers.ts | 3 --- 3 files changed, 37 deletions(-) diff --git a/modules/content/sanity-content.service.ts b/modules/content/sanity-content.service.ts index 00d6dd8b0..67403a1f2 100644 --- a/modules/content/sanity-content.service.ts +++ b/modules/content/sanity-content.service.ts @@ -26,11 +26,6 @@ interface SanityToken { interface SanityPoolConfig { incentivizedPools: string[]; blacklistedPools: string[]; - poolFilters: { - id: string; - title: string; - pools: string[]; - }[]; } const SANITY_TOKEN_TYPE_MAP: { [key: string]: string } = { @@ -209,13 +204,11 @@ export class SanityContentService implements ContentService { const response = await getSanityClient().fetch(`*[_type == "config" && chainId == ${networkContext.chainId}][0]{ incentivizedPools, blacklistedPools, - poolFilters }`); const config: SanityPoolConfig = { incentivizedPools: response?.incentivizedPools ?? [], blacklistedPools: response?.blacklistedPools ?? [], - poolFilters: response?.poolFilters ?? [], }; const categories = await prisma.prismaPoolCategory.findMany({ where: { chain: networkContext.chain } }); @@ -224,32 +217,6 @@ export class SanityContentService implements ContentService { await this.updatePoolCategory(incentivized, config.incentivizedPools, 'INCENTIVIZED'); await this.updatePoolCategory(blacklisted, config.blacklistedPools, 'BLACK_LISTED'); - - await prisma.$transaction([ - prisma.prismaPoolFilterMap.deleteMany({ where: { chain: networkContext.chain } }), - prisma.prismaPoolFilter.deleteMany({ where: { chain: networkContext.chain } }), - prisma.prismaPoolFilter.createMany({ - data: config.poolFilters.map((item) => ({ - id: item.id, - chain: networkContext.chain, - title: item.title, - })), - skipDuplicates: true, - }), - prisma.prismaPoolFilterMap.createMany({ - data: config.poolFilters - .map((item) => { - return item.pools.map((poolId) => ({ - id: `${item.id}-${poolId}`, - chain: networkContext.chain, - poolId, - filterId: item.id, - })); - }) - .flat(), - skipDuplicates: true, - }), - ]); } private async updatePoolCategory(currentPoolIds: string[], newPoolIds: string[], category: PrismaPoolCategoryType) { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index af90e7f62..dfa3a7786 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -16,7 +16,6 @@ extend type Query { where: GqlPoolFilter textSearch: String ): Int! - poolGetPoolFilters: [GqlPoolFilterDefinition!]! poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index d958ca087..9f29bf8f2 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -15,9 +15,6 @@ const balancerResolvers: Resolvers = { poolGetPoolsCount: async (parent, args, context) => { return poolService.getPoolsCount(args); }, - poolGetPoolFilters: async (parent, {}, context) => { - return poolService.getPoolFilters(); - }, poolGetSwaps: async (parent, args, context) => { return poolService.getPoolSwaps(args); }, From 9c8abfe1ec27e502b1c435fba44c68f4ab6e7d1c Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 7 Nov 2023 13:36:26 +0100 Subject: [PATCH 036/175] remove unused userSwapVolume --- modules/pool/lib/pool-swap.service.ts | 22 ---------------------- modules/pool/pool.gql | 1 - modules/pool/pool.resolvers.ts | 3 --- modules/pool/pool.service.ts | 5 ----- 4 files changed, 31 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 83430ab05..7de0530db 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -13,7 +13,6 @@ import { QueryPoolGetBatchSwapsArgs, QueryPoolGetJoinExitsArgs, QueryPoolGetSwapsArgs, - QueryPoolGetUserSwapVolumeArgs, } from '../../../schema'; import { PrismaPoolSwap } from '@prisma/client'; import _ from 'lodash'; @@ -153,27 +152,6 @@ export class PoolSwapService { }); } - public async getUserSwapVolume(args: QueryPoolGetUserSwapVolumeArgs) { - const yesterday = moment().subtract(1, 'day').unix(); - const take = !args.first || args.first > 100 ? 10 : args.first; - - const result = await prisma.prismaPoolSwap.groupBy({ - take, - skip: args.skip || undefined, - by: ['userAddress'], - _sum: { valueUSD: true }, - orderBy: { _sum: { valueUSD: 'desc' } }, - where: { - poolId: { in: args.where?.poolIdIn || undefined }, - tokenIn: { in: args.where?.tokenInIn || undefined }, - tokenOut: { in: args.where?.tokenOutIn || undefined }, - timestamp: { gte: yesterday }, - }, - }); - - return result.map((item) => ({ userAddress: item.userAddress, swapVolumeUSD: `${item._sum.valueUSD || 0}` })); - } - /** * Syncs all swaps for the last 48 hours. We fetch the timestamp of the last stored swap to avoid * duplicate effort. Return an array of poolIds with swaps added. diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index dfa3a7786..061eaa2ab 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -19,7 +19,6 @@ extend type Query { poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! - poolGetUserSwapVolume(first: Int, skip: Int, where: GqlUserSwapVolumeFilter): [GqlPoolUserSwapVolume!]! poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! poolGetSnapshots(id: String!, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetAllPoolsSnapshots(range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 9f29bf8f2..c3f80f032 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -24,9 +24,6 @@ const balancerResolvers: Resolvers = { poolGetJoinExits: async (parent, args, context) => { return poolService.getPoolJoinExits(args); }, - poolGetUserSwapVolume: async (parent, args, context) => { - return poolService.getPoolUserSwapVolume(args); - }, poolGetFeaturedPoolGroups: async (parent, args, context) => { return poolService.getFeaturedPoolGroups(); }, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 852a03bc0..97e2d37f1 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -16,7 +16,6 @@ import { QueryPoolGetJoinExitsArgs, QueryPoolGetPoolsArgs, QueryPoolGetSwapsArgs, - QueryPoolGetUserSwapVolumeArgs, } from '../../schema'; import { coingeckoService } from '../coingecko/coingecko.service'; import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; @@ -103,10 +102,6 @@ export class PoolService { return this.poolSwapService.getJoinExits(args); } - public async getPoolUserSwapVolume(args: QueryPoolGetUserSwapVolumeArgs): Promise { - return this.poolSwapService.getUserSwapVolume(args); - } - public async getFeaturedPoolGroups(): Promise { const cached: GqlPoolFeaturedPoolGroup[] = await this.cache.get( `${FEATURED_POOL_GROUPS_CACHE_KEY}:${networkContext.chainId}`, From 1d79b07bdabdedca7d326fb45b9454cbb01bd60b Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 16:25:41 +0100 Subject: [PATCH 037/175] Revert "remove unused userSwapVolume" This reverts commit 922a0d51aa06b8cfe1ef2d54260f6ce6b3689fd6. --- modules/pool/lib/pool-swap.service.ts | 22 ++++++++++++++++++++++ modules/pool/pool.gql | 1 + modules/pool/pool.resolvers.ts | 3 +++ modules/pool/pool.service.ts | 5 +++++ 4 files changed, 31 insertions(+) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index b3330e68a..09ae3d080 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -13,6 +13,7 @@ import { QueryPoolGetBatchSwapsArgs, QueryPoolGetJoinExitsArgs, QueryPoolGetSwapsArgs, + QueryPoolGetUserSwapVolumeArgs, } from '../../../schema'; import { Chain, PrismaPoolSwap } from '@prisma/client'; import _ from 'lodash'; @@ -183,6 +184,27 @@ export class PoolSwapService { }); } + public async getUserSwapVolume(args: QueryPoolGetUserSwapVolumeArgs) { + const yesterday = moment().subtract(1, 'day').unix(); + const take = !args.first || args.first > 100 ? 10 : args.first; + + const result = await prisma.prismaPoolSwap.groupBy({ + take, + skip: args.skip || undefined, + by: ['userAddress'], + _sum: { valueUSD: true }, + orderBy: { _sum: { valueUSD: 'desc' } }, + where: { + poolId: { in: args.where?.poolIdIn || undefined }, + tokenIn: { in: args.where?.tokenInIn || undefined }, + tokenOut: { in: args.where?.tokenOutIn || undefined }, + timestamp: { gte: yesterday }, + }, + }); + + return result.map((item) => ({ userAddress: item.userAddress, swapVolumeUSD: `${item._sum.valueUSD || 0}` })); + } + /** * Syncs all swaps for the last 48 hours. We fetch the timestamp of the last stored swap to avoid * duplicate effort. Return an array of poolIds with swaps added. diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index b033434ef..148eecd3d 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -19,6 +19,7 @@ extend type Query { poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! + poolGetUserSwapVolume(first: Int, skip: Int, where: GqlUserSwapVolumeFilter): [GqlPoolUserSwapVolume!]! poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetAllPoolsSnapshots(chains: [GqlChain!], range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 3a88f526c..8301488c8 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -49,6 +49,9 @@ const balancerResolvers: Resolvers = { } return poolService.getPoolJoinExits(args); }, + poolGetUserSwapVolume: async (parent, args, context) => { + return poolService.getPoolUserSwapVolume(args); + }, poolGetFeaturedPoolGroups: async (parent, args, context) => { return poolService.getFeaturedPoolGroups(); }, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index a6a69bd96..d75daab8d 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -17,6 +17,7 @@ import { QueryPoolGetJoinExitsArgs, QueryPoolGetPoolsArgs, QueryPoolGetSwapsArgs, + QueryPoolGetUserSwapVolumeArgs, } from '../../schema'; import { coingeckoService } from '../coingecko/coingecko.service'; import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; @@ -103,6 +104,10 @@ export class PoolService { return this.poolSwapService.getJoinExits(args); } + public async getPoolUserSwapVolume(args: QueryPoolGetUserSwapVolumeArgs): Promise { + return this.poolSwapService.getUserSwapVolume(args); + } + public async getFeaturedPoolGroups(): Promise { const cached: GqlPoolFeaturedPoolGroup[] = await this.cache.get( `${FEATURED_POOL_GROUPS_CACHE_KEY}:${networkContext.chainId}`, From 381183d0bca4d7c93fa89dc6c0b685d081af0a33 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 16:25:53 +0100 Subject: [PATCH 038/175] Revert "remove sanity poolFilters" This reverts commit 863ff81acb13c5e47675f1f5e87ddda2b5ba394b. --- modules/content/sanity-content.service.ts | 33 +++++++++++++++++++++++ modules/pool/pool.gql | 1 + modules/pool/pool.resolvers.ts | 3 +++ 3 files changed, 37 insertions(+) diff --git a/modules/content/sanity-content.service.ts b/modules/content/sanity-content.service.ts index 67403a1f2..00d6dd8b0 100644 --- a/modules/content/sanity-content.service.ts +++ b/modules/content/sanity-content.service.ts @@ -26,6 +26,11 @@ interface SanityToken { interface SanityPoolConfig { incentivizedPools: string[]; blacklistedPools: string[]; + poolFilters: { + id: string; + title: string; + pools: string[]; + }[]; } const SANITY_TOKEN_TYPE_MAP: { [key: string]: string } = { @@ -204,11 +209,13 @@ export class SanityContentService implements ContentService { const response = await getSanityClient().fetch(`*[_type == "config" && chainId == ${networkContext.chainId}][0]{ incentivizedPools, blacklistedPools, + poolFilters }`); const config: SanityPoolConfig = { incentivizedPools: response?.incentivizedPools ?? [], blacklistedPools: response?.blacklistedPools ?? [], + poolFilters: response?.poolFilters ?? [], }; const categories = await prisma.prismaPoolCategory.findMany({ where: { chain: networkContext.chain } }); @@ -217,6 +224,32 @@ export class SanityContentService implements ContentService { await this.updatePoolCategory(incentivized, config.incentivizedPools, 'INCENTIVIZED'); await this.updatePoolCategory(blacklisted, config.blacklistedPools, 'BLACK_LISTED'); + + await prisma.$transaction([ + prisma.prismaPoolFilterMap.deleteMany({ where: { chain: networkContext.chain } }), + prisma.prismaPoolFilter.deleteMany({ where: { chain: networkContext.chain } }), + prisma.prismaPoolFilter.createMany({ + data: config.poolFilters.map((item) => ({ + id: item.id, + chain: networkContext.chain, + title: item.title, + })), + skipDuplicates: true, + }), + prisma.prismaPoolFilterMap.createMany({ + data: config.poolFilters + .map((item) => { + return item.pools.map((poolId) => ({ + id: `${item.id}-${poolId}`, + chain: networkContext.chain, + poolId, + filterId: item.id, + })); + }) + .flat(), + skipDuplicates: true, + }), + ]); } private async updatePoolCategory(currentPoolIds: string[], newPoolIds: string[], category: PrismaPoolCategoryType) { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 148eecd3d..697ae197d 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -16,6 +16,7 @@ extend type Query { where: GqlPoolFilter textSearch: String ): Int! + poolGetPoolFilters: [GqlPoolFilterDefinition!]! poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 8301488c8..7d8319f3a 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -22,6 +22,9 @@ const balancerResolvers: Resolvers = { poolGetPoolsCount: async (parent, args, context) => { return poolService.getPoolsCount(args); }, + poolGetPoolFilters: async (parent, {}, context) => { + return poolService.getPoolFilters(); + }, poolGetSwaps: async (parent, args, context) => { const currentChain = headerChain(); if (!args.where?.chainIn && currentChain) { From ce035ba5247e3a42a7c1a224b9fe6ae07aead2ff Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 16:48:16 +0100 Subject: [PATCH 039/175] add chunks to wallet balancen fetching --- .../lib/user-sync-wallet-balance.service.ts | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 4e8251294..98fafe612 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -123,25 +123,32 @@ export class UserSyncWalletBalanceService { console.log( `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools`, ); - for (const poolAddress of poolAddresses) { - logPromises.push( - networkContext.provider.getLogs({ - //ERC20 Transfer topic - topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], - fromBlock, - toBlock, - address: poolAddress, - }), - ); - } + const chunks = _.chunk(poolAddresses, 100); + let i = 1; + for (const chunk of chunks) { + for (const poolAddress of poolAddresses) { + logPromises.push( + networkContext.provider.getLogs({ + //ERC20 Transfer topic + topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], + fromBlock, + toBlock, + address: poolAddress, + }), + ); + } - const allResponses = await Promise.all(logPromises); - console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools done.`, - ); + const allResponses = await Promise.all(logPromises); + console.log( + `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${ + chunk.length * i + } pools done.`, + ); - for (const response of allResponses) { - events.push(...response); + for (const response of allResponses) { + events.push(...response); + } + i++; } const relevantERC20Addresses = poolAddresses; From cefe3f6ab86b8e573306273d96d8ab6eb19de12b Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 17:19:41 +0100 Subject: [PATCH 040/175] use different rpc for canary and main on fantom --- modules/network/fantom.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 059107105..38ba6a11c 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,10 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://rpc.fantom.gateway.fm', + rpcUrl: + (env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' + ? `https://rpc.fantom.gateway.fm` + : `https://rpc.fantom.network`, rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From d1e7941909d1cf2dde1250a6df1c12b9935687a8 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 17:40:28 +0100 Subject: [PATCH 041/175] change beets address for OP --- modules/network/optimism.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index bed1cce39..c36471992 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -65,7 +65,7 @@ const optimismNetworkData: NetworkData = { }, protocolToken: 'beets', beets: { - address: '0x97513e975a7fa9072c72c92d8000b0db90b163c5', + address: '0xb4bc46bc6cb217b59ea8f4530bae26bf69f677f0', beetsPriceProviderRpcUrl: 'https://rpc.ftm.tools', }, bal: { From 245ab2e8a933038869d6cafde102c8c7d74fc4d3 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 17:46:36 +0100 Subject: [PATCH 042/175] increase chunk size --- modules/user/lib/user-sync-wallet-balance.service.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 98fafe612..52b1591ae 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -123,7 +123,7 @@ export class UserSyncWalletBalanceService { console.log( `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools`, ); - const chunks = _.chunk(poolAddresses, 100); + const chunks = _.chunk(poolAddresses, 400); let i = 1; for (const chunk of chunks) { for (const poolAddress of poolAddresses) { @@ -140,9 +140,7 @@ export class UserSyncWalletBalanceService { const allResponses = await Promise.all(logPromises); console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${ - chunk.length * i - } pools done.`, + `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${chunk.length} pools done.`, ); for (const response of allResponses) { From 04f62448d725324b4f75982b15be1e93fcdb0bfd Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 18:00:04 +0100 Subject: [PATCH 043/175] change rpc --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 059107105..d663b1bc1 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://rpc.fantom.gateway.fm', + rpcUrl: 'https://ftm.obsidianrpc.com', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From 8645fc8656c7e8ca51f30907c3b5a9ebe1f4a1d0 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 18:05:07 +0100 Subject: [PATCH 044/175] update beets address for pricing --- modules/network/optimism.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index bed1cce39..c36471992 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -65,7 +65,7 @@ const optimismNetworkData: NetworkData = { }, protocolToken: 'beets', beets: { - address: '0x97513e975a7fa9072c72c92d8000b0db90b163c5', + address: '0xb4bc46bc6cb217b59ea8f4530bae26bf69f677f0', beetsPriceProviderRpcUrl: 'https://rpc.ftm.tools', }, bal: { From 9f7079caa5605b5a33e61e115900aa7c3d6b08a1 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 18:13:03 +0100 Subject: [PATCH 045/175] change fantom rpc --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index d663b1bc1..24a4804ef 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://ftm.obsidianrpc.com', + rpcUrl: 'https://rpc.ankr.com/fantom', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From 63a7b0b7f7270fc86445922f9eb3db7c375f79d5 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 16:48:16 +0100 Subject: [PATCH 046/175] add chunks to wallet balancen fetching --- .../lib/user-sync-wallet-balance.service.ts | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 4e8251294..98fafe612 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -123,25 +123,32 @@ export class UserSyncWalletBalanceService { console.log( `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools`, ); - for (const poolAddress of poolAddresses) { - logPromises.push( - networkContext.provider.getLogs({ - //ERC20 Transfer topic - topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], - fromBlock, - toBlock, - address: poolAddress, - }), - ); - } + const chunks = _.chunk(poolAddresses, 100); + let i = 1; + for (const chunk of chunks) { + for (const poolAddress of poolAddresses) { + logPromises.push( + networkContext.provider.getLogs({ + //ERC20 Transfer topic + topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], + fromBlock, + toBlock, + address: poolAddress, + }), + ); + } - const allResponses = await Promise.all(logPromises); - console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools done.`, - ); + const allResponses = await Promise.all(logPromises); + console.log( + `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${ + chunk.length * i + } pools done.`, + ); - for (const response of allResponses) { - events.push(...response); + for (const response of allResponses) { + events.push(...response); + } + i++; } const relevantERC20Addresses = poolAddresses; From e9a146500c7eac05f0b5a1e4fe93ec607489757e Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 8 Nov 2023 18:23:11 +0100 Subject: [PATCH 047/175] increase chunks --- modules/user/lib/user-sync-wallet-balance.service.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 98fafe612..52b1591ae 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -123,7 +123,7 @@ export class UserSyncWalletBalanceService { console.log( `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools`, ); - const chunks = _.chunk(poolAddresses, 100); + const chunks = _.chunk(poolAddresses, 400); let i = 1; for (const chunk of chunks) { for (const poolAddress of poolAddresses) { @@ -140,9 +140,7 @@ export class UserSyncWalletBalanceService { const allResponses = await Promise.all(logPromises); console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${ - chunk.length * i - } pools done.`, + `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${chunk.length} pools done.`, ); for (const response of allResponses) { From 23cccd0e08040afaa77ad4af4d505ab79370750e Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 9 Nov 2023 08:26:15 +0100 Subject: [PATCH 048/175] change fantom rpc --- modules/network/fantom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 24a4804ef..07c7ed9bf 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -93,7 +93,7 @@ const fantomNetworkData: NetworkData = { tokenPrices: { maxHourlyPriceHistoryNumDays: 100, }, - rpcUrl: 'https://rpc.ankr.com/fantom', + rpcUrl: 'https://rpc.fantom.network', rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From 1877f36c3759c41e084fb5f442f44c33c7afa6b2 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:48:56 +0100 Subject: [PATCH 049/175] onchain data refactoring * simpler data fetching and parsing * splitting into separate responsibilities (state, data, gyro) * fixing protocol fee for pools in recovery - should be 0 * fixing BPT price rate --- .../pool/lib/pool-on-chain-data.service.ts | 427 ++++-------------- modules/pool/lib/pool-onchain-data.ts | 211 +++++++++ modules/pool/lib/pool-onchain-gyro-fee.ts | 86 ++++ modules/pool/lib/pool-onchain-state.ts | 56 +++ 4 files changed, 451 insertions(+), 329 deletions(-) create mode 100644 modules/pool/lib/pool-onchain-data.ts create mode 100644 modules/pool/lib/pool-onchain-gyro-fee.ts create mode 100644 modules/pool/lib/pool-onchain-state.ts diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 66c658053..4ff0162c0 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -1,61 +1,14 @@ -// TODO: let's make it simpler, it's already refactored in the SDK -import ElementPoolAbi from '../abi/ConvergentCurvePool.json'; -import LinearPoolAbi from '../abi/LinearPool.json'; -import LiquidityBootstrappingPoolAbi from '../abi/LiquidityBootstrappingPool.json'; -import ComposableStablePoolAbi from '../abi/ComposableStablePool.json'; -import GyroEV2Abi from '../abi/GyroEV2.json'; -import VaultAbi from '../abi/Vault.json'; -import aTokenRateProvider from '../abi/StaticATokenRateProvider.json'; -import WeightedPoolAbi from '../abi/WeightedPool.json'; -import StablePoolAbi from '../abi/StablePool.json'; -import MetaStablePoolAbi from '../abi/MetaStablePool.json'; -import StablePhantomPoolAbi from '../abi/StablePhantomPool.json'; -import { BigNumber } from 'ethers'; import { formatFixed } from '@ethersproject/bignumber'; import { PrismaPoolType } from '@prisma/client'; import { isSameAddress } from '@balancer-labs/sdk'; import { prisma } from '../../../prisma/prisma-client'; -import { isComposableStablePool, isGyroEV2, isGyroPool, isStablePool, isWeightedPoolV2 } from './pool-utils'; +import { isStablePool } from './pool-utils'; import { TokenService } from '../../token/token.service'; import { networkContext } from '../../network/network-context.service'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; -import { Multicaller3 } from '../../web3/multicaller3'; -import { formatBytes32String } from '@ethersproject/strings'; -import { keccak256 } from '@ethersproject/solidity'; -import { defaultAbiCoder } from '@ethersproject/abi'; -import GyroConfigAbi from '../abi/GyroConfig.json'; - -interface PoolStatusResult { - [key: string]: { - isPaused: boolean; - inRecoveryMode: boolean; - }; -} - -interface MulticallPoolStateExecuteResult { - inRecoveryMode: boolean; - pausedState: { - paused: boolean; - }; -} -interface MulticallExecuteResult { - amp?: string[] | undefined; - swapFee: string | undefined; - totalSupply: string | undefined; - weights?: string[] | undefined; - targets?: string[] | undefined; - poolTokens: { - tokens: string[]; - balances: string[]; - }; - wrappedTokenRate?: BigNumber | undefined; - rate?: BigNumber | undefined; - swapEnabled?: boolean | undefined; - protocolFeePercentageCache?: number | undefined; - tokenRates?: BigNumber[] | undefined; - metaPriceRateCache?: [BigNumber, BigNumber, BigNumber][] | undefined; - gyroProtocolFee?: BigNumber | undefined; -} +import { fetchOnChainPoolState } from './pool-onchain-state'; +import { fetchOnChainPoolData } from './pool-onchain-data'; +import { fetchOnChainGyroFees } from './pool-onchain-gyro-fee'; const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ 'WEIGHTED', @@ -72,8 +25,6 @@ const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ ]; export class PoolOnChainDataService { - gyroDefaultFee = 0; - gyroPoolTypeFee = 0; constructor(private readonly tokenService: TokenService) {} public async updateOnChainStatus(poolIds: string[]): Promise { @@ -90,37 +41,12 @@ export class PoolOnChainDataService { }, }); - const multicall = new Multicaller3(ComposableStablePoolAbi); - - filteredPools.forEach((pool) => { - if (!(pool.type === 'ELEMENT')) { - multicall.call(`${pool.id}.pausedState`, pool.address, 'getPausedState'); - } - if ( - pool.type !== 'LIQUIDITY_BOOTSTRAPPING' && // exclude all LBP - pool.type !== 'META_STABLE' && // exclude meta stable - pool.type !== 'ELEMENT' // exclude element - ) { - multicall.call(`${pool.id}.inRecoveryMode`, pool.address, 'inRecoveryMode'); - } - }); - - const multicallResult = (await multicall.execute()) as Record; - - const poolStatusResults: PoolStatusResult = {}; + const state = await fetchOnChainPoolState(filteredPools, 1024); const operations = []; for (const pool of filteredPools) { + const { isPaused, isInRecoveryMode } = state[pool.id]; if (pool.dynamicData) { - let isPaused = false; - let isInRecoveryMode = false; - - if (multicallResult[pool.id] && multicallResult[pool.id].inRecoveryMode) { - isInRecoveryMode = multicallResult[pool.id].inRecoveryMode; - } - if (multicallResult[pool.id] && multicallResult[pool.id].pausedState) { - isPaused = multicallResult[pool.id].pausedState.paused; - } operations.push( prisma.prismaPoolDynamicData.update({ where: { id_chain: { id: pool.id, chain: networkContext.chain } }, @@ -155,195 +81,40 @@ export class PoolOnChainDataService { }, }); - const tokenPrices = await this.tokenService.getTokenPrices(); - - const abis: any = Object.values( - // Remove duplicate entries using their names - Object.fromEntries( - [ - ...ElementPoolAbi, - ...LinearPoolAbi, - ...LiquidityBootstrappingPoolAbi, - ...ComposableStablePoolAbi, - ...GyroEV2Abi, - ...GyroConfigAbi, - ...VaultAbi, - ...aTokenRateProvider, - ...WeightedPoolAbi, - ...StablePoolAbi, - ...StablePhantomPoolAbi, - ...MetaStablePoolAbi, - ...ComposableStablePoolAbi, - //...WeightedPoolV2Abi, - ].map((row) => [row.name, row]), - ), - ); - - const multiPool = new Multicaller3(abis); - - // Adding Gyro default fee calls - const feeKey = formatBytes32String('PROTOCOL_SWAP_FEE_PERC'); - if (networkContext.data.gyro?.config) { - const eclpKey = keccak256( - ['bytes'], - [ - defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [feeKey, formatBytes32String('ECLP')] - ), - ] - ); - multiPool.call('gyro.default', networkContext.data.gyro.config, 'getUint', [feeKey]); - multiPool.call('gyro.eclp', networkContext.data.gyro.config, 'getUint', [eclpKey]); - } - - filteredPools.forEach((pool) => { - if (!SUPPORTED_POOL_TYPES.includes(pool.type || '')) { - console.error(`Unknown pool type: ${pool.type} ${pool.id}`); - return; - } - - // get per pool yield protocol fee (type 2) - if ( - networkContext.data.balancer.factoriesWithpoolSpecificProtocolFeePercentagesProvider?.includes( - pool.factory || '', - ) - ) { - multiPool.call( - `${pool.id}.protocolYieldFeePercentageCache`, - pool.address, - 'getProtocolFeePercentageCache', - [2], - ); - } - - multiPool.call( - `${pool.id}.poolTokens`, - networkContext.data.balancer.vault, - 'getPoolTokens', - [pool.id], - false, - ); - - if (pool.type === 'WEIGHTED' || pool.type === 'LIQUIDITY_BOOTSTRAPPING' || pool.type === 'INVESTMENT') { - multiPool.call(`${pool.id}.weights`, pool.address, 'getNormalizedWeights'); - multiPool.call(`${pool.id}.swapFee`, pool.address, 'getSwapFeePercentage'); - } else if (isStablePool(pool.type)) { - // MetaStable & StablePhantom is the same as Stable for multicall purposes - multiPool.call(`${pool.id}.amp`, pool.address, 'getAmplificationParameter'); - multiPool.call(`${pool.id}.swapFee`, pool.address, 'getSwapFeePercentage'); - } else if (pool.type === 'ELEMENT') { - multiPool.call(`${pool.id}.swapFee`, pool.address, 'percentFee'); - } else if (pool.type === 'LINEAR') { - multiPool.call(`${pool.id}.swapFee`, pool.address, 'getSwapFeePercentage'); - multiPool.call(`${pool.id}.targets`, pool.address, 'getTargets'); - multiPool.call(`${pool.id}.rate`, pool.address, 'getRate'); - multiPool.call(`${pool.id}.wrappedTokenRate`, pool.address, 'getWrappedTokenRate'); - } else if (isGyroPool(pool)) { - multiPool.call(`${pool.id}.swapFee`, pool.address, 'getSwapFeePercentage'); - multiPool.call(`${pool.id}.rate`, pool.address, 'getRate'); - - // Get fee from Gyro config pool - if (networkContext.data.gyro?.config) { - const poolFeeKey = keccak256( - ['bytes'], - [ - defaultAbiCoder.encode( - ['bytes32', 'uint256'], - [feeKey, pool.address] - ), - ] - ); - multiPool.call(`${pool.id}.gyroProtocolFee`, networkContext.data.gyro.config, 'getUint', [poolFeeKey]); - } - } - - if (pool.type === 'LIQUIDITY_BOOTSTRAPPING' || pool.type === 'INVESTMENT') { - multiPool.call(`${pool.id}.swapEnabled`, pool.address, 'getSwapEnabled'); - } - - if (pool.type === 'META_STABLE') { - multiPool.call(`${pool.id}.rate`, pool.address, 'getRate'); - - const tokenAddresses = pool.tokens.map((token) => token.address); - - tokenAddresses.forEach((token, i) => { - multiPool.call(`${pool.id}.metaPriceRateCache[${i}]`, pool.address, 'getPriceRateCache', [token]); - }); - } - - if (isComposableStablePool(pool) || isWeightedPoolV2(pool)) { - multiPool.call(`${pool.id}.totalSupply`, pool.address, 'getActualSupply'); - } else if (pool.type === 'LINEAR' || pool.type === 'PHANTOM_STABLE') { - // the old phantom stable and linear pool does not have this and expose the actual supply as virtualSupply - multiPool.call(`${pool.id}.totalSupply`, pool.address, 'getVirtualSupply'); - } else { - //default to totalSupply for any other pool type - multiPool.call(`${pool.id}.totalSupply`, pool.address, 'totalSupply'); - } - - if (pool.type === 'PHANTOM_STABLE') { - //we retrieve token rates for phantom stable and composable stable pools - const tokenAddresses = pool.tokens.map((token) => token.address); - - tokenAddresses.forEach((token, i) => { - multiPool.call(`${pool.id}.tokenRates[${i}]`, pool.address, 'getTokenRate', [token]); - }); - } - - // gyro pool returns uint[] for rates - if (isGyroEV2(pool)) { - multiPool.call(`${pool.id}.tokenRates`, pool.address, 'getTokenRates'); - } - }); - - let poolsOnChainData = {} as Record; - let gyroDefaults = { - default: 0, - eclp: 0, - } + const gyroPools = filteredPools.filter((pool) => pool.type.includes('GYRO')); - try { - const { gyro, ..._poolsOnChainData } = (await multiPool.execute()); - poolsOnChainData = _poolsOnChainData; - gyroDefaults = gyro; - } catch (err: any) { - console.error(err); - throw `Issue with multicall execution. ${err}`; - } - - const poolsOnChainDataArray = Object.entries(poolsOnChainData); + const tokenPrices = await this.tokenService.getTokenPrices(); + const onchainResults = await fetchOnChainPoolData(filteredPools, networkContext.data.balancer.vault, 1024); + const gyroFees = await fetchOnChainGyroFees(gyroPools, networkContext.data.gyro?.config, 1024); - for (let index = 0; index < poolsOnChainDataArray.length; index++) { - const [poolId, onchainData] = poolsOnChainDataArray[index]; - const pool = filteredPools.find((pool) => pool.id === poolId)!; - const { poolTokens } = onchainData; + const operations = []; + for (const pool of filteredPools) { + const onchainData = onchainResults[pool.id]; + const { amp, poolTokens } = onchainData; try { if (isStablePool(pool.type)) { - if (!onchainData.amp) { + if (!amp) { console.log('onchain data', onchainData); - console.error(`Stable Pool Missing Amp: ${poolId}`); + console.error(`Stable Pool Missing Amp: ${pool.id}`); continue; } - // Need to scale amp by precision to match expected Subgraph scale - // amp is stored with 3 decimals of precision - const amp = formatFixed(onchainData.amp[0], 3); - //only update if amp has changed if (!pool.stableDynamicData || pool.stableDynamicData.amp !== amp) { - await prisma.prismaPoolStableDynamicData.upsert({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, - create: { id: pool.id, chain: networkContext.chain, poolId: pool.id, amp, blockNumber }, - update: { amp, blockNumber }, - }); + operations.push( + prisma.prismaPoolStableDynamicData.upsert({ + where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + create: { id: pool.id, chain: networkContext.chain, poolId: pool.id, amp, blockNumber }, + update: { amp, blockNumber }, + }) + ); } } if (pool.type === 'LINEAR') { if (!onchainData.targets) { - console.error(`Linear Pool Missing Targets: ${poolId}`); + console.error(`Linear Pool Missing Targets: ${pool.id}`); continue; } else { const lowerTarget = formatFixed(onchainData.targets[0], 18); @@ -354,44 +125,34 @@ export class PoolOnChainDataService { pool.linearDynamicData.lowerTarget !== lowerTarget || pool.linearDynamicData.upperTarget !== upperTarget ) { - await prisma.prismaPoolLinearDynamicData.upsert({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, - create: { - id: pool.id, - chain: networkContext.chain, - poolId: pool.id, - upperTarget, - lowerTarget, - blockNumber, - }, - update: { upperTarget, lowerTarget, blockNumber }, - }); + operations.push( + prisma.prismaPoolLinearDynamicData.upsert({ + where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + create: { + id: pool.id, + chain: networkContext.chain, + poolId: pool.id, + upperTarget, + lowerTarget, + blockNumber, + }, + update: { upperTarget, lowerTarget, blockNumber }, + }) + ); } } } - const swapFee = formatFixed(onchainData.swapFee || '0', 18); - const totalShares = formatFixed(onchainData.totalSupply || '0', 18); + const { swapFee, totalShares } = onchainData; const swapEnabled = typeof onchainData.swapEnabled !== 'undefined' ? onchainData.swapEnabled : pool.dynamicData?.swapEnabled; - let yieldProtocolFeePercentage = - typeof onchainData.protocolFeePercentageCache !== 'undefined' - ? formatFixed(onchainData.protocolFeePercentageCache, 18) - : `${networkContext.data.balancer.yieldProtocolFeePercentage}`; - - // Setting Gyro protocol fee - if (onchainData.gyroProtocolFee) { - yieldProtocolFeePercentage = formatFixed(onchainData.gyroProtocolFee, 18); - } else if (pool.type.includes('GYRO')) { - if (pool.type === 'GYROE' && gyroDefaults.eclp) { - yieldProtocolFeePercentage = formatFixed(gyroDefaults.eclp, 18) || '0'; - } else { - yieldProtocolFeePercentage = formatFixed(gyroDefaults.default, 18) || '0'; - } - } + const yieldProtocolFeePercentage = + gyroFees[pool.id] || + onchainData.protocolYieldFeePercentageCache || + String(networkContext.data.balancer.yieldProtocolFeePercentage); if ( pool.dynamicData && @@ -400,17 +161,19 @@ export class PoolOnChainDataService { pool.dynamicData.swapEnabled !== swapEnabled || pool.dynamicData.protocolYieldFee !== yieldProtocolFeePercentage) ) { - await prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, - data: { - swapFee, - totalShares, - totalSharesNum: parseFloat(totalShares), - swapEnabled: typeof swapEnabled !== 'undefined' ? swapEnabled : true, - protocolYieldFee: yieldProtocolFeePercentage, - blockNumber, - }, - }); + operations.push( + prisma.prismaPoolDynamicData.update({ + where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + data: { + swapFee, + totalShares, + totalSharesNum: parseFloat(totalShares), + swapEnabled: typeof swapEnabled !== 'undefined' ? swapEnabled : true, + protocolYieldFee: yieldProtocolFeePercentage, + blockNumber, + }, + }) + ); } for (let i = 0; i < poolTokens.tokens.length; i++) { @@ -418,34 +181,34 @@ export class PoolOnChainDataService { const poolToken = pool.tokens.find((token) => isSameAddress(token.address, tokenAddress)); if (!poolToken) { - throw `Pool Missing Expected Token: ${poolId} ${tokenAddress}`; + throw `Pool Missing Expected Token: ${pool.id} ${tokenAddress}`; } if (poolToken.index !== i) { throw `Pooltoken index mismatch! "poolToken.index": ${poolToken.index} vs "i": ${i} on pool ${pool.id}`; } - const balance = formatFixed(poolTokens.balances[i], poolToken.token.decimals); - const weight = onchainData.weights ? formatFixed(onchainData.weights[i], 18) : null; + const balance = poolTokens.balances[i]; + const weight = onchainData.weights ? onchainData.weights[i] : null; // set token price rate for various rate types // top level token rates, e.g. LSTs in pools - let priceRate = onchainData.tokenRates ? formatFixed(onchainData.tokenRates[i], 18) : '1.0'; + let priceRate = poolTokens.rates[i] || (onchainData.tokenRates[i] ?? '1.0'); // metastable pools - if (onchainData.metaPriceRateCache && onchainData.metaPriceRateCache[i][0].gt('0')) { - priceRate = formatFixed(onchainData.metaPriceRateCache[i][0], 18); + if (onchainData.metaPriceRateCache && onchainData.metaPriceRateCache[i]) { + priceRate = onchainData.metaPriceRateCache[i]; } // bpt price rate if (onchainData.rate && isSameAddress(poolToken.address, pool.address)) { - priceRate = formatFixed(onchainData.rate, 18); + priceRate = onchainData.rate; } // linear wrapped token rate if (onchainData.wrappedTokenRate && pool.linearData?.wrappedIndex === poolToken.index) { - priceRate = formatFixed(onchainData.wrappedTokenRate, 18); + priceRate = onchainData.wrappedTokenRate; } if ( @@ -454,39 +217,45 @@ export class PoolOnChainDataService { poolToken.dynamicData.priceRate !== priceRate || poolToken.dynamicData.weight !== weight ) { - await prisma.prismaPoolTokenDynamicData.upsert({ - where: { id_chain: { id: poolToken.id, chain: networkContext.chain } }, - create: { - id: poolToken.id, - chain: networkContext.chain, - poolTokenId: poolToken.id, - blockNumber, - priceRate, - weight, - balance, - balanceUSD: - poolToken.address === pool.address - ? 0 - : this.tokenService.getPriceForToken(tokenPrices, poolToken.address) * - parseFloat(balance), - }, - update: { - blockNumber, - priceRate, - weight, - balance, - balanceUSD: - poolToken.address === pool.address - ? 0 - : this.tokenService.getPriceForToken(tokenPrices, poolToken.address) * - parseFloat(balance), - }, - }); + operations.push( + prisma.prismaPoolTokenDynamicData.upsert({ + where: { id_chain: { id: poolToken.id, chain: networkContext.chain } }, + create: { + id: poolToken.id, + chain: networkContext.chain, + poolTokenId: poolToken.id, + blockNumber, + priceRate, + weight, + balance, + balanceUSD: + poolToken.address === pool.address + ? 0 + : this.tokenService.getPriceForToken(tokenPrices, poolToken.address) * + parseFloat(balance), + }, + update: { + blockNumber, + priceRate, + weight, + balance, + balanceUSD: + poolToken.address === pool.address + ? 0 + : this.tokenService.getPriceForToken(tokenPrices, poolToken.address) * + parseFloat(balance), + }, + }) + ); } } } catch (e) { console.log('error syncing on chain data', e); } } + + Promise.all(operations).then((r) => console.log(r)) + + await prismaBulkExecuteOperations(operations, false); } } diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts new file mode 100644 index 000000000..ecd379d67 --- /dev/null +++ b/modules/pool/lib/pool-onchain-data.ts @@ -0,0 +1,211 @@ +import { formatEther, formatUnits } from 'ethers/lib/utils'; +import { Multicaller3 } from '../../web3/multicaller3'; +import { PrismaPoolType } from '@prisma/client'; +import { BigNumber, formatFixed } from '@ethersproject/bignumber'; +import ElementPoolAbi from '../abi/ConvergentCurvePool.json'; +import LinearPoolAbi from '../abi/LinearPool.json'; +import LiquidityBootstrappingPoolAbi from '../abi/LiquidityBootstrappingPool.json'; +import ComposableStablePoolAbi from '../abi/ComposableStablePool.json'; +import GyroEV2Abi from '../abi/GyroEV2.json'; +import VaultAbi from '../abi/Vault.json'; +import aTokenRateProvider from '../abi/StaticATokenRateProvider.json'; +import WeightedPoolAbi from '../abi/WeightedPool.json'; +import StablePoolAbi from '../abi/StablePool.json'; +import MetaStablePoolAbi from '../abi/MetaStablePool.json'; +import StablePhantomPoolAbi from '../abi/StablePhantomPool.json'; +import { JsonFragment } from '@ethersproject/abi'; + +const abi: JsonFragment[] = Object.values( + // Remove duplicate entries using their names + Object.fromEntries( + [ + ...ElementPoolAbi, + ...LinearPoolAbi, + ...LiquidityBootstrappingPoolAbi, + ...ComposableStablePoolAbi, + ...GyroEV2Abi, + ...VaultAbi, + ...aTokenRateProvider, + ...WeightedPoolAbi, + ...StablePoolAbi, + ...StablePhantomPoolAbi, + ...MetaStablePoolAbi, + ...ComposableStablePoolAbi, + //...WeightedPoolV2Abi, + ].map((row) => [row.name, row]), + ), +); + +const getSwapFeeFn = (type: string) => { + if (type === 'ELEMENT') { + return 'percentFee'; + } else if (type === 'FX') { + return 'protocolPercentFee'; + } else { + return 'getSwapFeePercentage'; + } +}; + +interface PoolInput { + id: string; + address: string; + type: PrismaPoolType; + tokens: { + address: string, + token: { + decimals: number, + } + }[]; + version?: number; +} + +interface OnchainData { + poolTokens: [string[], BigNumber[]]; + totalSupply: BigNumber; + virtualSupply?: BigNumber; + actualSupply?: BigNumber; + swapFee: BigNumber; + swapEnabled?: boolean; + protocolYieldFeePercentageCache?: BigNumber; + rate?: BigNumber; + weights?: BigNumber[]; + targets?: [BigNumber, BigNumber]; + wrappedTokenRate?: BigNumber; + amp?: [BigNumber, boolean, BigNumber]; + tokenRates?: [BigNumber, BigNumber]; + tokenRate?: BigNumber[]; + metaPriceRateCache?: [BigNumber, BigNumber, BigNumber][]; +} + +const defaultCalls = ( + { id, address, type }: PoolInput, + vaultAddress: string, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.poolTokens`, vaultAddress, 'getPoolTokens', [id]); + multicaller.call(`${id}.totalSupply`, address, 'totalSupply'); + multicaller.call(`${id}.virtualSupply`, address, 'getVirtualSupply'); + multicaller.call(`${id}.actualSupply`, address, 'getActualSupply'); + multicaller.call(`${id}.swapFee`, address, getSwapFeeFn(type)); + multicaller.call(`${id}.rate`, address, 'getRate'); + multicaller.call(`${id}.protocolYieldFeePercentageCache`, address, 'getProtocolFeePercentageCache', [2]); +}; + +const weightedCalls = ( + { id, address }: PoolInput, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.weights`, address, 'getNormalizedWeights'); +}; + +const lbpAndInvestmentCalls =( + { id, address }: PoolInput, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.weights`, address, 'getNormalizedWeights'); + multicaller.call(`${id}.swapEnabled`, address, 'getSwapEnabled'); +}; + +const linearCalls = ( + { id, address }: PoolInput, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.targets`, address, 'getTargets'); + multicaller.call(`${id}.wrappedTokenRate`, address, 'getWrappedTokenRate'); +}; + +const stableCalls = ( + { id, address, tokens }: PoolInput, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); + + tokens.forEach(({ address: tokenAddress }, i) => { + multicaller.call(`${id}.tokenRate[${i}]`, address, 'getTokenRate', [tokenAddress]); + multicaller.call(`${id}.metaPriceRateCache[${i}]`, address, 'getPriceRateCache', [tokenAddress]); + }); +}; + +const gyroECalls = ( + { id, address }: PoolInput, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.tokenRates`, address, 'getTokenRates'); +}; + +const poolTypeCalls = (type: PrismaPoolType, version = 1) => { + const do_nothing = () => ({}); + switch (type) { + case 'WEIGHTED': + return weightedCalls; + case 'LIQUIDITY_BOOTSTRAPPING': + case 'INVESTMENT': + return lbpAndInvestmentCalls; + case 'STABLE': + case 'PHANTOM_STABLE': + case 'META_STABLE': + return stableCalls; + case 'GYROE': + if (version === 2) { + return gyroECalls; + } else { + return do_nothing; + } + case 'LINEAR': + return linearCalls; + default: + return do_nothing; + } +}; + +const parse = (result: OnchainData, decimalsLookup: { [address: string]: number }) => ({ + amp: result.amp ? formatFixed(result.amp[0], String(result.amp[2]).length - 1) : undefined, + swapFee: formatEther(result.swapFee ?? '0'), + totalShares: formatEther(result.actualSupply || result.virtualSupply || result.totalSupply || '0'), + weights: result.weights?.map(formatEther), + targets: result.targets?.map(String), + poolTokens: result.poolTokens ? { + tokens: result.poolTokens[0].map((t) => t.toLocaleLowerCase()), + balances: result.poolTokens[1].map((w, i) => formatUnits(w, decimalsLookup[result.poolTokens[0][i].toLocaleLowerCase()])), + rates: result.poolTokens[0].map((_, i) => result.tokenRate && result.tokenRate[i] ? formatEther(result.tokenRate[i]) : undefined) + } : { tokens: [], balances: [], rates: [] }, + wrappedTokenRate: result.wrappedTokenRate ? formatEther(result.wrappedTokenRate) : '1.0', + rate: result.rate ? formatEther(result.rate) : '1.0', + swapEnabled: result.swapEnabled, + protocolYieldFeePercentageCache: result.protocolYieldFeePercentageCache ? formatEther(result.protocolYieldFeePercentageCache) : undefined, + tokenRates: result.tokenRates?.map((tokenRate) => tokenRate ? formatEther(tokenRate) : '1.0') || [], + metaPriceRateCache: result.metaPriceRateCache?.length ? result.metaPriceRateCache.map((r) => r ? r[0].gt(0) ? formatEther(r[0]) : '1.0' : r) : undefined +}); + +export const fetchOnChainPoolData = async ( + pools: PoolInput[], + vaultAddress: string, + batchSize = 1024 +) => { + if (pools.length === 0) { + return {}; + } + + const multicaller = new Multicaller3(abi, batchSize); + + pools.forEach((pool) => { + defaultCalls(pool, vaultAddress, multicaller); + poolTypeCalls(pool.type, pool.version)(pool, multicaller); + }); + + const results = (await multicaller.execute()) as { + [id: string]: OnchainData; + }; + + const decimalsLookup = Object.fromEntries( + pools.flatMap((pool) => + pool.tokens.map(({ address, token }) => [address, token.decimals]) + ) + ); + + const parsed = Object.fromEntries( + Object.entries(results).map(([key, result]) => [key, parse(result, decimalsLookup)]) + ); + + return parsed; +}; diff --git a/modules/pool/lib/pool-onchain-gyro-fee.ts b/modules/pool/lib/pool-onchain-gyro-fee.ts new file mode 100644 index 000000000..04afb95d7 --- /dev/null +++ b/modules/pool/lib/pool-onchain-gyro-fee.ts @@ -0,0 +1,86 @@ +import { Multicaller3 } from '../../web3/multicaller3'; +import { PrismaPoolType } from '@prisma/client'; +import abi from '../abi/GyroConfig.json'; +import { defaultAbiCoder } from '@ethersproject/abi'; +import { formatBytes32String } from '@ethersproject/strings'; +import { keccak256 } from '@ethersproject/solidity'; +import { formatEther } from 'ethers/lib/utils'; + +interface PoolInput { + id: string; + address: string; + type: PrismaPoolType; + version?: number; +} + +interface OnchainGyroFees { + eclpFee?: string; + defaultFee?: string; + pools?: { + [id: string]: { + poolFee?: string; + }; + } +} + +export const fetchOnChainGyroFees = async ( + pools: PoolInput[], + gyroConfigAddress?: string, + batchSize = 1024 +) => { + if (pools.length === 0 || !gyroConfigAddress) { + return {}; + } + + const multicaller = new Multicaller3(abi, batchSize); + + const feeKey = formatBytes32String('PROTOCOL_SWAP_FEE_PERC'); + + const eclpKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'bytes32'], + [feeKey, formatBytes32String('ECLP')] + ), + ] + ); + multicaller.call('defaultFee', gyroConfigAddress, 'getUint', [feeKey]); + multicaller.call('eclpFee', gyroConfigAddress, 'getUint', [eclpKey]); + + let poolTypeLookup: { [id: string]: PrismaPoolType } = {}; + pools.forEach(({ id, type, address }) => { + if (type.includes('GYRO')) { + const poolFeeKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'uint256'], + [feeKey, address] + ), + ] + ); + + multicaller.call(`pools.${id}.poolFee`, gyroConfigAddress, 'getUint', [poolFeeKey]); + + poolTypeLookup[id] = type; + } + }); + + const results = (await multicaller.execute()) as OnchainGyroFees; + const defaultFee = results.defaultFee ?? '0'; + const eclpFee = results.eclpFee ?? '0'; + + let parsed: { [address: string]: string } = {}; + if (results.pools) { + parsed = Object.fromEntries( + Object.entries(results.pools).map(([id, { poolFee }]) => [id, + formatEther( + poolFee ? poolFee : poolTypeLookup[id].includes('GYROE') ? eclpFee : defaultFee + ) + ]) + ); + } + + return parsed; +}; diff --git a/modules/pool/lib/pool-onchain-state.ts b/modules/pool/lib/pool-onchain-state.ts new file mode 100644 index 000000000..8301f8c23 --- /dev/null +++ b/modules/pool/lib/pool-onchain-state.ts @@ -0,0 +1,56 @@ +import { Multicaller3 } from '../../web3/multicaller3'; +import { PrismaPoolType } from '@prisma/client'; +import abi from '../abi/WeightedPoolV2.json'; + +interface PoolInput { + id: string; + address: string; + type: PrismaPoolType; + version?: number; +} + +interface OnchainState { + pausedState?: { + paused: boolean; + }; + inRecoveryMode?: boolean; +} + +const parse = (result: OnchainState) => ({ + isPaused: result.pausedState?.paused ? result.pausedState.paused : false, + isInRecoveryMode: result.inRecoveryMode ? result.inRecoveryMode : false, +}); + +export const fetchOnChainPoolState = async ( + pools: PoolInput[], + batchSize = 1024 +) => { + if (pools.length === 0) { + return {}; + } + + const multicaller = new Multicaller3(abi, batchSize); + + pools.forEach(({ id, type, address }) => { + if (!(type === 'ELEMENT')) { + multicaller.call(`${id}.pausedState`, address, 'getPausedState'); + } + if ( + type !== 'LIQUIDITY_BOOTSTRAPPING' && // exclude all LBP + type !== 'META_STABLE' && // exclude meta stable + type !== 'ELEMENT' // exclude element + ) { + multicaller.call(`${id}.inRecoveryMode`, address, 'inRecoveryMode'); + } + }); + + const results = (await multicaller.execute()) as { + [id: string]: OnchainState; + }; + + const parsed = Object.fromEntries( + Object.entries(results).map(([key, result]) => [key, parse(result)]) + ); + + return parsed; +}; From f1d058c7312a11455b5835868827c7231474afd0 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 9 Nov 2023 13:01:04 +0100 Subject: [PATCH 050/175] return timestamp --- modules/balancer/balancer.resolvers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/balancer/balancer.resolvers.ts b/modules/balancer/balancer.resolvers.ts index ba97d0c87..00723d41e 100644 --- a/modules/balancer/balancer.resolvers.ts +++ b/modules/balancer/balancer.resolvers.ts @@ -1,9 +1,10 @@ +import moment from 'moment'; import { Resolvers } from '../../schema'; const balancerResolvers: Resolvers = { Query: { balancerQueryTest: async (parent, {}, context) => { - return 'test'; + return `${moment().utc().valueOf()}`; }, }, Mutation: { From 1e0cecb95429421b5b4e53f2b2d196b952c01bb2 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 9 Nov 2023 13:45:08 +0100 Subject: [PATCH 051/175] include blacklisted pools by default --- modules/pool/lib/pool-gql-loader.service.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 4ed99b862..06b567f3e 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -261,11 +261,15 @@ export class PoolGqlLoaderService { mode: 'insensitive', }, categories: { - every: { - category: { - notIn: ['BLACK_LISTED', ...(where?.categoryNotIn || [])], - }, - }, + ...(where?.categoryNotIn + ? { + every: { + category: { + notIn: where.categoryNotIn, + }, + }, + } + : {}), ...(where?.categoryIn ? { some: { From e98e5ab20078bfe79fda60584342402bfcdaed8f Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 9 Nov 2023 19:32:22 +0100 Subject: [PATCH 052/175] updates --- .../pool/lib/pool-on-chain-data.service.ts | 43 +++++++++++-------- modules/pool/lib/pool-onchain-data.ts | 4 +- modules/pool/lib/pool-onchain-gyro-fee.ts | 37 +++++++++++++++- modules/pool/pool.service.ts | 2 +- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 4ff0162c0..e457f05a2 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -1,10 +1,9 @@ import { formatFixed } from '@ethersproject/bignumber'; -import { PrismaPoolType } from '@prisma/client'; +import { Chain, PrismaPoolType } from '@prisma/client'; import { isSameAddress } from '@balancer-labs/sdk'; import { prisma } from '../../../prisma/prisma-client'; import { isStablePool } from './pool-utils'; import { TokenService } from '../../token/token.service'; -import { networkContext } from '../../network/network-context.service'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { fetchOnChainPoolState } from './pool-onchain-state'; import { fetchOnChainPoolData } from './pool-onchain-data'; @@ -25,7 +24,13 @@ const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ ]; export class PoolOnChainDataService { - constructor(private readonly tokenService: TokenService) {} + constructor( + private readonly tokenService: TokenService, + private chain: Chain, + private readonly vaultAddress: string, + private readonly yieldProtocolFeePercentage: number, + private readonly gyroConfig?: string, + ) {} public async updateOnChainStatus(poolIds: string[]): Promise { if (poolIds.length === 0) return; @@ -33,7 +38,7 @@ export class PoolOnChainDataService { const filteredPools = await prisma.prismaPool.findMany({ where: { id: { in: poolIds }, - chain: networkContext.chain, + chain: this.chain, type: { in: SUPPORTED_POOL_TYPES }, }, include: { @@ -49,7 +54,7 @@ export class PoolOnChainDataService { if (pool.dynamicData) { operations.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: this.chain } }, data: { isPaused, isInRecoveryMode, @@ -69,7 +74,7 @@ export class PoolOnChainDataService { const filteredPools = await prisma.prismaPool.findMany({ where: { id: { in: poolIds }, - chain: networkContext.chain, + chain: this.chain, type: { in: SUPPORTED_POOL_TYPES }, }, include: { @@ -84,8 +89,12 @@ export class PoolOnChainDataService { const gyroPools = filteredPools.filter((pool) => pool.type.includes('GYRO')); const tokenPrices = await this.tokenService.getTokenPrices(); - const onchainResults = await fetchOnChainPoolData(filteredPools, networkContext.data.balancer.vault, 1024); - const gyroFees = await fetchOnChainGyroFees(gyroPools, networkContext.data.gyro?.config, 1024); + const onchainResults = await fetchOnChainPoolData(filteredPools, this.vaultAddress, 1024); + const gyroFees = await ( + this.gyroConfig + ? fetchOnChainGyroFees(gyroPools, this.gyroConfig, 1024) + : Promise.resolve({} as { [address: string]: string }) + ); const operations = []; for (const pool of filteredPools) { @@ -104,8 +113,8 @@ export class PoolOnChainDataService { if (!pool.stableDynamicData || pool.stableDynamicData.amp !== amp) { operations.push( prisma.prismaPoolStableDynamicData.upsert({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, - create: { id: pool.id, chain: networkContext.chain, poolId: pool.id, amp, blockNumber }, + where: { id_chain: { id: pool.id, chain: this.chain } }, + create: { id: pool.id, chain: this.chain, poolId: pool.id, amp, blockNumber }, update: { amp, blockNumber }, }) ); @@ -127,10 +136,10 @@ export class PoolOnChainDataService { ) { operations.push( prisma.prismaPoolLinearDynamicData.upsert({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: this.chain } }, create: { id: pool.id, - chain: networkContext.chain, + chain: this.chain, poolId: pool.id, upperTarget, lowerTarget, @@ -152,7 +161,7 @@ export class PoolOnChainDataService { const yieldProtocolFeePercentage = gyroFees[pool.id] || onchainData.protocolYieldFeePercentageCache || - String(networkContext.data.balancer.yieldProtocolFeePercentage); + String(this.yieldProtocolFeePercentage); if ( pool.dynamicData && @@ -163,7 +172,7 @@ export class PoolOnChainDataService { ) { operations.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: this.chain } }, data: { swapFee, totalShares, @@ -219,10 +228,10 @@ export class PoolOnChainDataService { ) { operations.push( prisma.prismaPoolTokenDynamicData.upsert({ - where: { id_chain: { id: poolToken.id, chain: networkContext.chain } }, + where: { id_chain: { id: poolToken.id, chain: this.chain } }, create: { id: poolToken.id, - chain: networkContext.chain, + chain: this.chain, poolTokenId: poolToken.id, blockNumber, priceRate, @@ -254,8 +263,6 @@ export class PoolOnChainDataService { } } - Promise.all(operations).then((r) => console.log(r)) - await prismaBulkExecuteOperations(operations, false); } } diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index ecd379d67..31118eab2 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -165,8 +165,8 @@ const parse = (result: OnchainData, decimalsLookup: { [address: string]: number weights: result.weights?.map(formatEther), targets: result.targets?.map(String), poolTokens: result.poolTokens ? { - tokens: result.poolTokens[0].map((t) => t.toLocaleLowerCase()), - balances: result.poolTokens[1].map((w, i) => formatUnits(w, decimalsLookup[result.poolTokens[0][i].toLocaleLowerCase()])), + tokens: result.poolTokens[0].map((token) => token.toLowerCase()), + balances: result.poolTokens[1].map((balance, i) => formatUnits(balance, decimalsLookup[result.poolTokens[0][i].toLowerCase()])), rates: result.poolTokens[0].map((_, i) => result.tokenRate && result.tokenRate[i] ? formatEther(result.tokenRate[i]) : undefined) } : { tokens: [], balances: [], rates: [] }, wrappedTokenRate: result.wrappedTokenRate ? formatEther(result.wrappedTokenRate) : '1.0', diff --git a/modules/pool/lib/pool-onchain-gyro-fee.ts b/modules/pool/lib/pool-onchain-gyro-fee.ts index 04afb95d7..d59d51efe 100644 --- a/modules/pool/lib/pool-onchain-gyro-fee.ts +++ b/modules/pool/lib/pool-onchain-gyro-fee.ts @@ -15,6 +15,8 @@ interface PoolInput { interface OnchainGyroFees { eclpFee?: string; + twoClpFee?: string; + threeClpFee?: string; defaultFee?: string; pools?: { [id: string]: { @@ -45,8 +47,31 @@ export const fetchOnChainGyroFees = async ( ), ] ); + + const twoClpKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'bytes32'], + [feeKey, formatBytes32String('2CLP')] + ), + ] + ); + + const threeClpKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'bytes32'], + [feeKey, formatBytes32String('3CLP')] + ), + ] + ); + multicaller.call('defaultFee', gyroConfigAddress, 'getUint', [feeKey]); multicaller.call('eclpFee', gyroConfigAddress, 'getUint', [eclpKey]); + multicaller.call('twoClpFee', gyroConfigAddress, 'getUint', [twoClpKey]); + multicaller.call('threeClpFee', gyroConfigAddress, 'getUint', [threeClpKey]); let poolTypeLookup: { [id: string]: PrismaPoolType } = {}; pools.forEach(({ id, type, address }) => { @@ -70,13 +95,23 @@ export const fetchOnChainGyroFees = async ( const results = (await multicaller.execute()) as OnchainGyroFees; const defaultFee = results.defaultFee ?? '0'; const eclpFee = results.eclpFee ?? '0'; + const twoClpFee = results.twoClpFee ?? '0'; + const threeClpFee = results.threeClpFee ?? '0'; let parsed: { [address: string]: string } = {}; if (results.pools) { parsed = Object.fromEntries( Object.entries(results.pools).map(([id, { poolFee }]) => [id, formatEther( - poolFee ? poolFee : poolTypeLookup[id].includes('GYROE') ? eclpFee : defaultFee + poolFee + ? poolFee + : poolTypeLookup[id] == 'GYROE' + ? eclpFee + : poolTypeLookup[id] == 'GYRO' + ? twoClpFee + : poolTypeLookup[id] == 'GYRO3' + ? threeClpFee + : defaultFee ) ]) ); diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 852a03bc0..015aed022 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -530,7 +530,7 @@ export class PoolService { export const poolService = new PoolService( new PoolCreatorService(userService), - new PoolOnChainDataService(tokenService), + new PoolOnChainDataService(tokenService, networkContext.chain, networkContext.data.balancer.vault, networkContext.data.balancer.yieldProtocolFeePercentage, networkContext.data.gyro?.config), new PoolUsdDataService(tokenService, blocksSubgraphService, balancerSubgraphService), new PoolGqlLoaderService(), new PoolAprUpdaterService(), From a465433fc2d1cd3467b5307fbcb8cbb78f2b16b0 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 9 Nov 2023 21:16:12 +0100 Subject: [PATCH 053/175] new composable stable factory on zkevm (#525) --- modules/network/zkevm.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index cc45ad53e..80a53b2ef 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -72,6 +72,7 @@ const zkevmNetworkData: NetworkData = { composableStablePoolFactories: [ '0x8ea89804145c007e7d226001a96955ad53836087', '0x956ccab09898c0af2aca5e6c229c3ad4e93d9288', + '0x577e5993b9cc480f07f98b5ebd055604bd9071c4' ], weightedPoolV2Factories: ['0x03f3fb107e74f2eac9358862e91ad3c692712054'], swapProtocolFeePercentage: 0.5, From c6f9f8cbc29b4c4a43e36ab86ec5d9de1e1ee630 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:47:58 +0100 Subject: [PATCH 054/175] Revert "Feature/multichain filter" (#526) --- modules/beethoven/beets.gql | 2 +- modules/beethoven/beets.resolvers.ts | 3 +- modules/gnosis/gnosis-safe.service.ts | 54 +++++++++++++++ modules/pool/lib/pool-gql-loader.service.ts | 11 ++- modules/pool/lib/pool-snapshot.service.ts | 11 +-- modules/pool/lib/pool-swap.service.ts | 67 +++++-------------- modules/pool/pool.gql | 15 ++--- modules/pool/pool.resolvers.ts | 59 +++------------- modules/pool/pool.service.ts | 25 ++++--- .../balancer-subgraph.service.ts | 4 +- modules/token/token.resolvers.ts | 9 +-- modules/user/lib/user-balance.service.ts | 4 +- modules/user/lib/user-snapshot.service.ts | 14 +--- modules/user/user-snapshot.service.test.ts | 61 +++-------------- modules/user/user.gql | 12 +--- modules/user/user.resolvers.ts | 40 +++-------- modules/user/user.service.ts | 21 ++---- 17 files changed, 150 insertions(+), 262 deletions(-) create mode 100644 modules/gnosis/gnosis-safe.service.ts diff --git a/modules/beethoven/beets.gql b/modules/beethoven/beets.gql index caa15efbb..445412974 100644 --- a/modules/beethoven/beets.gql +++ b/modules/beethoven/beets.gql @@ -5,7 +5,7 @@ extend type Query { userGetFbeetsBalance: GqlUserFbeetsBalance! - userGetPoolSnapshots(poolId: String!, chain: GqlChain!, range: GqlUserSnapshotDataRange!): [GqlUserPoolSnapshot!]! + userGetPoolSnapshots(poolId: String!, range: GqlUserSnapshotDataRange!): [GqlUserPoolSnapshot!]! userGetRelicSnapshots(farmId: String!, range: GqlUserSnapshotDataRange!): [GqlUserRelicSnapshot!]! userGetPortfolioSnapshots(days: Int!): [GqlUserPortfolioSnapshot!]! diff --git a/modules/beethoven/beets.resolvers.ts b/modules/beethoven/beets.resolvers.ts index 87e705789..ecb709df7 100644 --- a/modules/beethoven/beets.resolvers.ts +++ b/modules/beethoven/beets.resolvers.ts @@ -36,13 +36,12 @@ const beetsResolvers: Resolvers = { ...balance, }; }, - userGetPoolSnapshots: async (parent, { poolId, chain, range }, context) => { + userGetPoolSnapshots: async (parent, { poolId, range }, context) => { const accountAddress = getRequiredAccountAddress(context); return userService.getUserBalanceSnapshotsForPool( accountAddress.toLowerCase(), poolId.toLowerCase(), - chain, range, ); }, diff --git a/modules/gnosis/gnosis-safe.service.ts b/modules/gnosis/gnosis-safe.service.ts new file mode 100644 index 000000000..5ecff4151 --- /dev/null +++ b/modules/gnosis/gnosis-safe.service.ts @@ -0,0 +1,54 @@ +import Safe, { ContractNetworksConfig, EthersAdapter } from '@gnosis.pm/safe-core-sdk'; +import { ethers, providers } from 'ethers'; +import { getAddress } from 'ethers/lib/utils'; +import { Cache } from 'memory-cache'; +import { networkContext } from '../network/network-context.service'; + +const CACHE_KEY_PREFIX = `gnosis-address-is-multisig_`; +const TIMEOUT = 2592000; //30 days + +const contractNetworks: ContractNetworksConfig = { + 250: { + safeProxyFactoryAddress: '0xc3C41Ab65Dabe3ae250A0A1FE4706FdB7ECEB951', + multiSendAddress: '0xd1b160Ee570632ac402Efb230d720669604918e8', + safeMasterCopyAddress: '0x87EB227FE974e9E1d3Bc4Da562e0Bd3C348c2B34', + }, +}; + +export class GnosisSafeService { + private cache = new Cache(); + + public async isAddressGnosisSafe(address: string) { + const key = `${CACHE_KEY_PREFIX}:${networkContext.chainId}:${address}`; + const cachedValue = this.cache.get(key); + if (cachedValue != null) { + return cachedValue; + } + + try { + await Safe.create({ + ethAdapter: await this.getAdapter(), + safeAddress: getAddress(address), + contractNetworks, + }); + + this.cache.put(key, true, TIMEOUT); + return true; + } catch { + this.cache.put(key, false, TIMEOUT); + return false; + } + } + + private async getAdapter() { + const provider = new providers.JsonRpcProvider(networkContext.data.rpcUrl); + const signer = ethers.Wallet.createRandom(); + + return new EthersAdapter({ + ethers, + signer: signer.connect(provider), + }); + } +} + +export const gnosisSafeService = new GnosisSafeService(); diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 85e841c54..b381545ee 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -11,7 +11,6 @@ import { import { GqlBalancePoolAprItem, GqlBalancePoolAprSubItem, - GqlChain, GqlPoolDynamicData, GqlPoolFeaturedPoolGroup, GqlPoolInvestConfig, @@ -34,16 +33,16 @@ import { import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; -import { Chain, Prisma, PrismaPoolAprType } from '@prisma/client'; +import { Prisma, PrismaPoolAprType } from '@prisma/client'; import { isWeightedPoolV2 } from './pool-utils'; import { oldBnum } from '../../big-number/old-big-number'; import { networkContext } from '../../network/network-context.service'; import { fixedNumber } from '../../view-helpers/fixed-number'; export class PoolGqlLoaderService { - public async getPool(id: string, chain: Chain): Promise { + public async getPool(id: string): Promise { const pool = await prisma.prismaPool.findUnique({ - where: { id_chain: { id, chain: chain } }, + where: { id_chain: { id, chain: networkContext.chain } }, include: prismaPoolWithExpandedNesting.include, }); @@ -67,9 +66,9 @@ export class PoolGqlLoaderService { return pools.map((pool) => this.mapToMinimalGqlPool(pool)); } - public async getLinearPools(chains: Chain[]): Promise { + public async getLinearPools(): Promise { const pools = await prisma.prismaPool.findMany({ - where: { type: 'LINEAR', chain: { in: chains } }, + where: { type: 'LINEAR', chain: networkContext.chain }, orderBy: { dynamicData: { totalLiquidity: 'desc' } }, include: prismaPoolWithExpandedNesting.include, }); diff --git a/modules/pool/lib/pool-snapshot.service.ts b/modules/pool/lib/pool-snapshot.service.ts index 3c51d26cc..e64062b73 100644 --- a/modules/pool/lib/pool-snapshot.service.ts +++ b/modules/pool/lib/pool-snapshot.service.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/node'; import { balancerSubgraphService, BalancerSubgraphService, @@ -11,7 +12,7 @@ import { import { GqlPoolSnapshotDataRange } from '../../../schema'; import moment from 'moment-timezone'; import _ from 'lodash'; -import { Chain, PrismaPoolSnapshot } from '@prisma/client'; +import { PrismaPoolSnapshot } from '@prisma/client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { prismaPoolWithExpandedNesting } from '../../../prisma/prisma-types'; import { CoingeckoService } from '../../coingecko/coingecko.service'; @@ -26,11 +27,11 @@ export class PoolSnapshotService { private readonly coingeckoService: CoingeckoService, ) {} - public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { + public async getSnapshotsForPool(poolId: string, range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ - where: { poolId, timestamp: { gte: timestamp }, chain: chain }, + where: { poolId, timestamp: { gte: timestamp }, chain: networkContext.chain }, orderBy: { timestamp: 'asc' }, }); } @@ -41,7 +42,7 @@ export class PoolSnapshotService { }); } - public async getSnapshotsForAllPools(chains: Chain[], range: GqlPoolSnapshotDataRange) { + public async getSnapshotsForAllPools(range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ @@ -53,7 +54,7 @@ export class PoolSnapshotService { pool: { categories: { none: { category: 'BLACK_LISTED' } }, }, - chain: { in: chains }, + chain: networkContext.chain, }, orderBy: { timestamp: 'asc' }, }); diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index b3330e68a..7de0530db 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -14,13 +14,12 @@ import { QueryPoolGetJoinExitsArgs, QueryPoolGetSwapsArgs, } from '../../../schema'; -import { Chain, PrismaPoolSwap } from '@prisma/client'; +import { PrismaPoolSwap } from '@prisma/client'; import _ from 'lodash'; import { isSupportedInt, prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { PrismaPoolBatchSwapWithSwaps, prismaPoolMinimal } from '../../../prisma/prisma-types'; import { networkContext } from '../../network/network-context.service'; import * as Sentry from '@sentry/node'; -import { AllNetworkConfigsKeyedOnChain } from '../../network/network-config'; export class PoolSwapService { constructor( @@ -31,49 +30,29 @@ export class PoolSwapService { public async getJoinExits(args: QueryPoolGetJoinExitsArgs): Promise { const first = !args.first || args.first > 100 ? 10 : args.first; - const allChainsJoinExits: GqlPoolJoinExit[] = []; - - for (const chain of args.where!.chainIn!) { - const balancerSubgraphService = new BalancerSubgraphService( - AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, - ); - - const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ - where: { pool_in: args.where?.poolIdIn }, - first, - skip: args.skip, - orderBy: JoinExit_OrderBy.Timestamp, - orderDirection: OrderDirection.Desc, - }); - - const mappedJoinExits: GqlPoolJoinExit[] = joinExits.map((joinExit) => ({ - ...joinExit, - __typename: 'GqlPoolJoinExit', - chain: chain, - poolId: joinExit.pool.id, - amounts: joinExit.amounts.map((amount, index) => ({ - address: joinExit.pool.tokensList[index], - amount, - })), - })); - - allChainsJoinExits.push(...mappedJoinExits); - } + const { joinExits } = await this.balancerSubgraphService.getPoolJoinExits({ + where: { pool_in: args.where?.poolIdIn }, + first, + skip: args.skip, + orderBy: JoinExit_OrderBy.Timestamp, + orderDirection: OrderDirection.Desc, + }); - return allChainsJoinExits; + return joinExits.map((joinExit) => ({ + ...joinExit, + __typename: 'GqlPoolJoinExit', + poolId: joinExit.pool.id, + amounts: joinExit.amounts.map((amount, index) => ({ address: joinExit.pool.tokensList[index], amount })), + })); } public async getUserJoinExitsForPool( userAddress: string, poolId: string, - chain: Chain, first = 10, skip = 0, ): Promise { - const balancerSubgraphService = new BalancerSubgraphService( - AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, - ); - const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ + const { joinExits } = await this.balancerSubgraphService.getPoolJoinExits({ where: { pool: poolId, user: userAddress }, first, skip: skip, @@ -85,7 +64,6 @@ export class PoolSwapService { ...joinExit, __typename: 'GqlPoolJoinExit', poolId: joinExit.pool.id, - chain: chain, amounts: joinExit.amounts.map((amount, index) => ({ address: joinExit.pool.tokensList[index], amount })), })); } @@ -106,9 +84,7 @@ export class PoolSwapService { tokenOut: { in: args.where?.tokenOutIn || undefined, }, - chain: { - in: args.where?.chainIn || undefined, - }, + chain: networkContext.chain, }, orderBy: { timestamp: 'desc' }, }); @@ -117,14 +93,10 @@ export class PoolSwapService { public async getUserSwapsForPool( userAddress: string, poolId: string, - chain: Chain, first = 10, skip = 0, ): Promise { - const balancerSubgraphService = new BalancerSubgraphService( - AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, - ); - const result = await balancerSubgraphService.getSwaps({ + const result = await this.balancerSubgraphService.getSwaps({ first, skip, where: { @@ -137,7 +109,6 @@ export class PoolSwapService { return result.swaps.map((swap) => ({ id: swap.id, - chain: chain, userAddress, poolId: swap.poolId.id, tokenIn: swap.tokenIn, @@ -172,9 +143,7 @@ export class PoolSwapService { tokenOut: { in: args.where?.tokenOutIn || undefined, }, - chain: { - in: args.where?.chainIn || undefined, - }, + chain: networkContext.chain, }, orderBy: { timestamp: 'desc' }, include: { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index b033434ef..061eaa2ab 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -1,5 +1,5 @@ extend type Query { - poolGetPool(id: String!, chain: GqlChain): GqlPoolBase! + poolGetPool(id: String!): GqlPoolBase! poolGetPools( first: Int skip: Int @@ -20,9 +20,9 @@ extend type Query { poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! - poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! - poolGetAllPoolsSnapshots(chains: [GqlChain!], range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! - poolGetLinearPools(chains: [GqlChain!]): [GqlPoolLinear!]! + poolGetSnapshots(id: String!, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! + poolGetAllPoolsSnapshots(range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! + poolGetLinearPools: [GqlPoolLinear!]! } extend type Mutation { @@ -657,7 +657,6 @@ type GqlPoolFilterDefinition { type GqlPoolSwap { id: ID! - chain: GqlChain! poolId: String! userAddress: String! tokenIn: String! @@ -671,7 +670,6 @@ type GqlPoolSwap { type GqlPoolBatchSwap { id: ID! - chain: GqlChain! userAddress: String! tokenIn: String! tokenOut: String! @@ -707,12 +705,10 @@ input GqlPoolSwapFilter { tokenInIn: [String!] tokenOutIn: [String!] poolIdIn: [String!] - chainIn: [GqlChain!] } type GqlPoolStaking { id: ID! - chain: GqlChain! type: GqlPoolStakingType! address: String! gauge: GqlPoolStakingGauge @@ -730,12 +726,10 @@ enum GqlPoolStakingGaugeStatus { input GqlPoolJoinExitFilter { poolIdIn: [String!] - chainIn: [GqlChain!] } type GqlPoolJoinExit { id: ID! - chain: GqlChain! type: GqlPoolJoinExitType! sender: String! poolId: String! @@ -805,7 +799,6 @@ enum GqlPoolSnapshotDataRange { type GqlPoolSnapshot { id: ID! - chain: GqlChain! poolId: String! timestamp: Int! totalLiquidity: String! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 3a88f526c..c3f80f032 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -3,18 +3,11 @@ import { Resolvers } from '../../schema'; import { isAdminRoute } from '../auth/auth-context'; import { prisma } from '../../prisma/prisma-client'; import { networkContext } from '../network/network-context.service'; -import { headerChain } from '../context/header-chain'; const balancerResolvers: Resolvers = { Query: { - poolGetPool: async (parent, { id, chain }, context) => { - const currentChain = headerChain(); - if (!chain && currentChain) { - chain = currentChain; - } else if (!chain) { - throw new Error('Chain is required'); - } - return poolService.getGqlPool(id, chain); + poolGetPool: async (parent, { id }, context) => { + return poolService.getGqlPool(id); }, poolGetPools: async (parent, args, context) => { return poolService.getGqlPools(args); @@ -23,43 +16,19 @@ const balancerResolvers: Resolvers = { return poolService.getPoolsCount(args); }, poolGetSwaps: async (parent, args, context) => { - const currentChain = headerChain(); - if (!args.where?.chainIn && currentChain) { - args.where = { ...args.where, chainIn: [currentChain] }; - } else if (!args.where?.chainIn) { - throw new Error('Chain is required'); - } return poolService.getPoolSwaps(args); }, poolGetBatchSwaps: async (parent, args, context) => { - const currentChain = headerChain(); - if (!args.where?.chainIn && currentChain) { - args.where = { ...args.where, chainIn: [currentChain] }; - } else if (!args.where?.chainIn) { - throw new Error('Chain is required'); - } return poolService.getPoolBatchSwaps(args); }, poolGetJoinExits: async (parent, args, context) => { - const currentChain = headerChain(); - if (!args.where?.chainIn && currentChain) { - args.where = { ...args.where, chainIn: [currentChain] }; - } else if (!args.where?.chainIn) { - throw new Error('Chain is required'); - } return poolService.getPoolJoinExits(args); }, poolGetFeaturedPoolGroups: async (parent, args, context) => { return poolService.getFeaturedPoolGroups(); }, - poolGetSnapshots: async (parent, { id, chain, range }, context) => { - const currentChain = headerChain(); - if (!chain && currentChain) { - chain = currentChain; - } else if (!chain) { - throw new Error('Chain is required'); - } - const snapshots = await poolService.getSnapshotsForPool(id, chain, range); + poolGetSnapshots: async (parent, { id, range }, context) => { + const snapshots = await poolService.getSnapshotsForPool(id, range); return snapshots.map((snapshot) => ({ ...snapshot, @@ -73,14 +42,8 @@ const balancerResolvers: Resolvers = { holdersCount: `${snapshot.holdersCount}`, })); }, - poolGetAllPoolsSnapshots: async (parent, { chains, range }, context) => { - const currentChain = headerChain(); - if (!chains && currentChain) { - chains = [currentChain]; - } else if (!chains) { - throw new Error('Chain is required'); - } - const snapshots = await poolService.getSnapshotsForAllPools(chains, range); + poolGetAllPoolsSnapshots: async (parent, { range }, context) => { + const snapshots = await poolService.getSnapshotsForAllPools(range); return snapshots.map((snapshot) => ({ ...snapshot, @@ -94,14 +57,8 @@ const balancerResolvers: Resolvers = { holdersCount: `${snapshot.holdersCount}`, })); }, - poolGetLinearPools: async (parent, { chains }, context) => { - const currentChain = headerChain(); - if (!chains && currentChain) { - chains = [currentChain]; - } else if (!chains) { - throw new Error('Chain is required'); - } - return poolService.getGqlLinearPools(chains); + poolGetLinearPools: async () => { + return poolService.getGqlLinearPools(); }, }, Mutation: { diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index a6a69bd96..97e2d37f1 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -1,10 +1,9 @@ -import { Chain, PrismaPoolFilter, PrismaPoolStakingType, PrismaPoolSwap } from '@prisma/client'; +import { PrismaPoolFilter, PrismaPoolStakingType, PrismaPoolSwap } from '@prisma/client'; import _, { chain, includes } from 'lodash'; import { Cache } from 'memory-cache'; import moment from 'moment-timezone'; import { prisma } from '../../prisma/prisma-client'; import { - GqlChain, GqlPoolBatchSwap, GqlPoolFeaturedPoolGroup, GqlPoolJoinExit, @@ -61,16 +60,16 @@ export class PoolService { return networkContext.config.contentService; } - public async getGqlPool(id: string, chain: GqlChain): Promise { - return this.poolGqlLoaderService.getPool(id, chain); + public async getGqlPool(id: string): Promise { + return this.poolGqlLoaderService.getPool(id); } public async getGqlPools(args: QueryPoolGetPoolsArgs): Promise { return this.poolGqlLoaderService.getPools(args); } - public async getGqlLinearPools(chains: Chain[]): Promise { - return this.poolGqlLoaderService.getLinearPools(chains); + public async getGqlLinearPools(): Promise { + return this.poolGqlLoaderService.getLinearPools(); } public async getPoolsCount(args: QueryPoolGetPoolsArgs): Promise { @@ -123,12 +122,12 @@ export class PoolService { return featuredPoolGroups; } - public async getSnapshotsForAllPools(chains: Chain[], range: GqlPoolSnapshotDataRange) { - return this.poolSnapshotService.getSnapshotsForAllPools(chains, range); + public async getSnapshotsForAllPools(range: GqlPoolSnapshotDataRange) { + return this.poolSnapshotService.getSnapshotsForAllPools(range); } - public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { - return this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); + public async getSnapshotsForPool(poolId: string, range: GqlPoolSnapshotDataRange) { + return this.poolSnapshotService.getSnapshotsForPool(poolId, range); } public async getSnapshotsForReliquaryFarm(id: number, range: GqlPoolSnapshotDataRange) { @@ -434,7 +433,7 @@ export class PoolService { await prisma.prismaPoolLinearData.deleteMany({ where: { chain: networkContext.chain, poolId: poolId }, }); - + await prisma.prismaPoolGyroData.deleteMany({ where: { chain: networkContext.chain, poolId: poolId }, }); @@ -477,9 +476,9 @@ export class PoolService { }, }); - if (gauge && gauge.votingGauge) + if(gauge && gauge.votingGauge) await prisma.prismaVotingGauge.deleteMany({ - where: { chain: networkContext.chain, id: gauge.votingGauge.id }, + where: { chain: networkContext.chain, id: gauge.votingGauge.id } }); await prisma.prismaPoolStakingGauge.deleteMany({ diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts index 43b779b58..0a4023c9c 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts @@ -52,7 +52,7 @@ const PORTFOLIO_POOLS_CACHE_KEY = `balance-subgraph_portfolio-pools`; export class BalancerSubgraphService { private cache: CacheClass; - constructor(private readonly subgraphUrl = networkContext.data.subgraphs.balancer) { + constructor() { this.cache = new Cache(); } @@ -314,7 +314,7 @@ export class BalancerSubgraphService { } private get sdk() { - const client = new GraphQLClient(this.subgraphUrl); + const client = new GraphQLClient(networkContext.data.subgraphs.balancer); return getSdk(client); } diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index b5c54cafe..988a6111a 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -2,25 +2,26 @@ import { Resolvers } from '../../schema'; import _ from 'lodash'; import { isAdminRoute } from '../auth/auth-context'; import { tokenService } from './token.service'; +import { networkContext } from '../network/network-context.service'; import { headerChain } from '../context/header-chain'; const resolvers: Resolvers = { Query: { tokenGetTokens: async (parent, { chains }, context) => { - const currentChain = headerChain(); + const currentChain = headerChain() if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + chains = []; } return tokenService.getTokenDefinitions(chains); }, tokenGetCurrentPrices: async (parent, { chains }, context) => { - const currentChain = headerChain(); + const currentChain = headerChain() if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + chains = []; } const prices = await tokenService.getWhiteListedTokenPrices(chains); diff --git a/modules/user/lib/user-balance.service.ts b/modules/user/lib/user-balance.service.ts index fb98fb1f2..200f7837b 100644 --- a/modules/user/lib/user-balance.service.ts +++ b/modules/user/lib/user-balance.service.ts @@ -74,12 +74,12 @@ export class UserBalanceService { }; } - public async getUserStaking(address: string, chains: Chain[]): Promise { + public async getUserStaking(address: string): Promise { const user = await prisma.prismaUser.findUnique({ where: { address }, include: { stakedBalances: { - where: { chain: { in: chains }, balanceNum: { gt: 0 } }, + where: { chain: networkContext.chain, balanceNum: { gt: 0 } }, include: { staking: { include: { diff --git a/modules/user/lib/user-snapshot.service.ts b/modules/user/lib/user-snapshot.service.ts index f5d262761..c31e9c2bc 100644 --- a/modules/user/lib/user-snapshot.service.ts +++ b/modules/user/lib/user-snapshot.service.ts @@ -4,14 +4,7 @@ import moment from 'moment-timezone'; import { UserPoolSnapshot, UserRelicSnapshot } from '../user-types'; import { GqlUserSnapshotDataRange } from '../../../schema'; import { PoolSnapshotService } from '../../pool/lib/pool-snapshot.service'; -import { - Chain, - Prisma, - PrismaPool, - PrismaPoolSnapshot, - PrismaPoolStaking, - PrismaUserRelicSnapshot, -} from '@prisma/client'; +import { Prisma, PrismaPool, PrismaPoolSnapshot, PrismaPoolStaking, PrismaUserRelicSnapshot } from '@prisma/client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { oneDayInSeconds, secondsPerDay } from '../../common/time'; import { UserBalanceSnapshotFragment } from '../../subgraphs/user-snapshot-subgraph/generated/user-snapshot-subgraph-types'; @@ -316,7 +309,6 @@ export class UserSnapshotService { public async getUserPoolBalanceSnapshotsForPool( userAddress: string, poolId: string, - chain: Chain, range: GqlUserSnapshotDataRange, ): Promise { const oldestRequestedSnapshotTimestamp = this.getTimestampForRange(range); @@ -382,7 +374,7 @@ export class UserSnapshotService { const prismaInput: Prisma.PrismaUserPoolBalanceSnapshotCreateManyInput[] = []; - poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); + poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, range); /* For each snapshot from the subgraph, this will get the poolSnapshot for the same timestamp and enrich with $ value data @@ -444,7 +436,7 @@ export class UserSnapshotService { // Only get them if we didn't get them above if (poolSnapshots.length === 0) { - poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); + poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, range); } /* diff --git a/modules/user/user-snapshot.service.test.ts b/modules/user/user-snapshot.service.test.ts index f97fc4d57..47d8da75b 100644 --- a/modules/user/user-snapshot.service.test.ts +++ b/modules/user/user-snapshot.service.test.ts @@ -13,7 +13,6 @@ import { mockServer } from '../tests-helper/mocks/mockHttpServer'; import { userService } from './user.service'; import { secondsPerDay } from '../common/time'; import { networkContext } from '../network/network-context.service'; -import { Chain } from '@prisma/client'; /* TEST SETUP: @@ -183,12 +182,7 @@ test('The user requests the user stats for the first time, requesting from subgr ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -304,7 +298,6 @@ test('User in in the pool for a very long time, requests various different time const thirtySnapshotsFromService = await userService.getUserBalanceSnapshotsForPool( userAddress, poolId1, - Chain.FANTOM, 'THIRTY_DAYS', ); @@ -323,7 +316,6 @@ test('User in in the pool for a very long time, requests various different time const ninetySnapshotsFromService = await userService.getUserBalanceSnapshotsForPool( userAddress, poolId1, - Chain.FANTOM, 'NINETY_DAYS', ); //also includes the one from today @@ -420,12 +412,7 @@ Mock data for user-balance-subgraph (important that timestamps are ASC, as this ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -548,12 +535,7 @@ Mock data for user-balance-subgraph (important that timestamps are ASC, as this ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(2); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -645,12 +627,7 @@ test('Return a snapshot with 0 valueUSD if there is no pool snapshot for the giv ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( - userAddress, - pool2Id, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, pool2Id, 'THIRTY_DAYS'); // should get all 4 snapshots expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -684,7 +661,6 @@ test('Return a snapshot with 0 valueUSD if there is no pool snapshot for the giv const snapshotsAfterAdditionalPoolSnapshot = await userService.getUserBalanceSnapshotsForPool( userAddress, pool2Id, - Chain.FANTOM, 'THIRTY_DAYS', ); //expect still the same results here as above @@ -828,12 +804,7 @@ test('User snapshots in the database must be picked up and synced by the sync pr expect(snapshotsFromDb.length).toBe(3); // after the sync, all 4 snapshots should be present - const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); expect(snapshotsAfterSync.length).toBe(4); // check if balances are calculated correctly @@ -1029,12 +1000,7 @@ test('User has left and re-entered the pool. Make sure the sync does not persist // after the sync, 5 snapshots should be present. //Sevendaysago, sixdaysago (inferred), fivedaysago (0 balance), fourdays ago (0 balance), threedaysago and twodaysago (0 balance) - const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); expect(snapshotsAfterSync.length).toBe(6); const snapshotsFromDbAfterGet = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -1149,12 +1115,7 @@ test('Todays user snapshot must be gradually updated based on an updated pool sn ], ); - const userSnapshotsBefore = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const userSnapshotsBefore = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); expect(userSnapshotsBefore.length).toBe(1); // check if balances are calculated correctly @@ -1205,12 +1166,7 @@ test('Todays user snapshot must be gradually updated based on an updated pool sn await userService.syncUserBalanceSnapshots(); // check numbers again - const userSnapshotsAfter = await userService.getUserBalanceSnapshotsForPool( - userAddress, - poolId1, - Chain.FANTOM, - 'THIRTY_DAYS', - ); + const userSnapshotsAfter = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); expect(userSnapshotsBefore.length).toBe(1); // check if balances are calculated correctly @@ -1336,7 +1292,6 @@ test('User requests pool snapshots for Fidelio Duetto Pool. Make sure fBeets are const userBalanceSnapshots = await userService.getUserBalanceSnapshotsForPool( userAddress, fidelioPoolId, - Chain.FANTOM, 'THIRTY_DAYS', ); expect(userBalanceSnapshots.length).toBe(4); diff --git a/modules/user/user.gql b/modules/user/user.gql index bdeabede4..62803f158 100644 --- a/modules/user/user.gql +++ b/modules/user/user.gql @@ -1,14 +1,8 @@ extend type Query { userGetPoolBalances(chains: [GqlChain!], address: String): [GqlUserPoolBalance!]! - userGetStaking(chains: [GqlChain!], address: String): [GqlPoolStaking!]! - userGetPoolJoinExits( - first: Int = 10 - skip: Int = 0 - poolId: String! - chain: GqlChain - address: String - ): [GqlPoolJoinExit!]! - userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!, chain: GqlChain, address: String): [GqlPoolSwap!]! + userGetStaking: [GqlPoolStaking!]! + userGetPoolJoinExits(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolJoinExit!]! + userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolSwap!]! } extend type Mutation { diff --git a/modules/user/user.resolvers.ts b/modules/user/user.resolvers.ts index e94480d62..0c7424b6f 100644 --- a/modules/user/user.resolvers.ts +++ b/modules/user/user.resolvers.ts @@ -7,11 +7,11 @@ import { headerChain } from '../context/header-chain'; const resolvers: Resolvers = { Query: { userGetPoolBalances: async (parent, { chains, address }, context) => { - const currentChain = headerChain(); + const currentChain = headerChain() if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + chains = []; } const accountAddress = address || getRequiredAccountAddress(context); const tokenPrices = await tokenService.getTokenPricesForChains(chains); @@ -22,37 +22,19 @@ const resolvers: Resolvers = { tokenPrice: tokenService.getPriceForToken(tokenPrices[balance.chain] || [], balance.tokenAddress), })); }, - userGetPoolJoinExits: async (parent, { first, skip, poolId, chain, address }, context) => { - const currentChain = headerChain(); - if (!chain && currentChain) { - chain = currentChain; - } else if (!chain) { - throw new Error('Chain is required'); - } - const accountAddress = address || getRequiredAccountAddress(context); + userGetPoolJoinExits: async (parent, { first, skip, poolId }, context) => { + const accountAddress = getRequiredAccountAddress(context); - return userService.getUserPoolInvestments(accountAddress, poolId, chain, first, skip); + return userService.getUserPoolInvestments(accountAddress, poolId, first, skip); }, - userGetSwaps: async (parent, { first, skip, poolId, chain, address }, context) => { - const currentChain = headerChain(); - if (!chain && currentChain) { - chain = currentChain; - } else if (!chain) { - throw new Error('Chain is required'); - } - const accountAddress = address || getRequiredAccountAddress(context); - return userService.getUserSwaps(accountAddress, poolId, chain, first, skip); + userGetSwaps: async (parent, { first, skip, poolId }, context) => { + const accountAddress = getRequiredAccountAddress(context); + return userService.getUserSwaps(accountAddress, poolId, first, skip); }, - userGetStaking: async (parent, { chains, address }, context) => { - const currentChain = headerChain(); - if (!chains && currentChain) { - chains = [currentChain]; - } else if (!chains) { - throw new Error('Chain is required'); - } - const accountAddress = address || getRequiredAccountAddress(context); + userGetStaking: async (parent, {}, context) => { + const accountAddress = getRequiredAccountAddress(context); - return userService.getUserStaking(accountAddress, chains); + return userService.getUserStaking(accountAddress); }, }, Mutation: { diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index f58c6104f..fd603906c 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -5,6 +5,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { PoolSnapshotService } from '../pool/lib/pool-snapshot.service'; import { PoolSwapService } from '../pool/lib/pool-swap.service'; import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; +import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { reliquarySubgraphService } from '../subgraphs/reliquary-subgraph/reliquary.service'; import { userSnapshotSubgraphService } from '../subgraphs/user-snapshot-subgraph/user-snapshot-subgraph.service'; import { tokenService } from '../token/token.service'; @@ -33,38 +34,30 @@ export class UserService { public async getUserPoolInvestments( address: string, poolId: string, - chain: Chain, first?: number, skip?: number, ): Promise { - return this.poolSwapService.getUserJoinExitsForPool(address, poolId, chain, first, skip); + return this.poolSwapService.getUserJoinExitsForPool(address, poolId, first, skip); } - public async getUserSwaps( - address: string, - poolId: string, - chain: Chain, - first?: number, - skip?: number, - ): Promise { - return this.poolSwapService.getUserSwapsForPool(address, poolId, chain, first, skip); + public async getUserSwaps(address: string, poolId: string, first?: number, skip?: number): Promise { + return this.poolSwapService.getUserSwapsForPool(address, poolId, first, skip); } public async getUserFbeetsBalance(address: string): Promise> { return this.userBalanceService.getUserFbeetsBalance(address); } - public async getUserStaking(address: string, chains: Chain[]): Promise { - return this.userBalanceService.getUserStaking(address, chains); + public async getUserStaking(address: string): Promise { + return this.userBalanceService.getUserStaking(address); } public async getUserBalanceSnapshotsForPool( accountAddress: string, poolId: string, - chain: Chain, days: GqlUserSnapshotDataRange, ): Promise { - return this.userSnapshotService.getUserPoolBalanceSnapshotsForPool(accountAddress, poolId, chain, days); + return this.userSnapshotService.getUserPoolBalanceSnapshotsForPool(accountAddress, poolId, days); } public async getUserRelicSnapshots(accountAddress: string, farmId: string, days: GqlUserSnapshotDataRange) { From 5158418ca5395159017bd0705a0667801c102bb3 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:11:55 +0100 Subject: [PATCH 055/175] Revert "Revert "Feature/multichain filter" (#526)" This reverts commit c6f9f8cbc29b4c4a43e36ab86ec5d9de1e1ee630. --- modules/beethoven/beets.gql | 2 +- modules/beethoven/beets.resolvers.ts | 3 +- modules/gnosis/gnosis-safe.service.ts | 54 --------------- modules/pool/lib/pool-gql-loader.service.ts | 11 +-- modules/pool/lib/pool-snapshot.service.ts | 11 ++- modules/pool/lib/pool-swap.service.ts | 67 ++++++++++++++----- modules/pool/pool.gql | 15 +++-- modules/pool/pool.resolvers.ts | 59 +++++++++++++--- modules/pool/pool.service.ts | 25 +++---- .../balancer-subgraph.service.ts | 4 +- modules/token/token.resolvers.ts | 9 ++- modules/user/lib/user-balance.service.ts | 4 +- modules/user/lib/user-snapshot.service.ts | 14 +++- modules/user/user-snapshot.service.test.ts | 61 ++++++++++++++--- modules/user/user.gql | 12 +++- modules/user/user.resolvers.ts | 40 ++++++++--- modules/user/user.service.ts | 21 ++++-- 17 files changed, 262 insertions(+), 150 deletions(-) delete mode 100644 modules/gnosis/gnosis-safe.service.ts diff --git a/modules/beethoven/beets.gql b/modules/beethoven/beets.gql index 445412974..caa15efbb 100644 --- a/modules/beethoven/beets.gql +++ b/modules/beethoven/beets.gql @@ -5,7 +5,7 @@ extend type Query { userGetFbeetsBalance: GqlUserFbeetsBalance! - userGetPoolSnapshots(poolId: String!, range: GqlUserSnapshotDataRange!): [GqlUserPoolSnapshot!]! + userGetPoolSnapshots(poolId: String!, chain: GqlChain!, range: GqlUserSnapshotDataRange!): [GqlUserPoolSnapshot!]! userGetRelicSnapshots(farmId: String!, range: GqlUserSnapshotDataRange!): [GqlUserRelicSnapshot!]! userGetPortfolioSnapshots(days: Int!): [GqlUserPortfolioSnapshot!]! diff --git a/modules/beethoven/beets.resolvers.ts b/modules/beethoven/beets.resolvers.ts index ecb709df7..87e705789 100644 --- a/modules/beethoven/beets.resolvers.ts +++ b/modules/beethoven/beets.resolvers.ts @@ -36,12 +36,13 @@ const beetsResolvers: Resolvers = { ...balance, }; }, - userGetPoolSnapshots: async (parent, { poolId, range }, context) => { + userGetPoolSnapshots: async (parent, { poolId, chain, range }, context) => { const accountAddress = getRequiredAccountAddress(context); return userService.getUserBalanceSnapshotsForPool( accountAddress.toLowerCase(), poolId.toLowerCase(), + chain, range, ); }, diff --git a/modules/gnosis/gnosis-safe.service.ts b/modules/gnosis/gnosis-safe.service.ts deleted file mode 100644 index 5ecff4151..000000000 --- a/modules/gnosis/gnosis-safe.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -import Safe, { ContractNetworksConfig, EthersAdapter } from '@gnosis.pm/safe-core-sdk'; -import { ethers, providers } from 'ethers'; -import { getAddress } from 'ethers/lib/utils'; -import { Cache } from 'memory-cache'; -import { networkContext } from '../network/network-context.service'; - -const CACHE_KEY_PREFIX = `gnosis-address-is-multisig_`; -const TIMEOUT = 2592000; //30 days - -const contractNetworks: ContractNetworksConfig = { - 250: { - safeProxyFactoryAddress: '0xc3C41Ab65Dabe3ae250A0A1FE4706FdB7ECEB951', - multiSendAddress: '0xd1b160Ee570632ac402Efb230d720669604918e8', - safeMasterCopyAddress: '0x87EB227FE974e9E1d3Bc4Da562e0Bd3C348c2B34', - }, -}; - -export class GnosisSafeService { - private cache = new Cache(); - - public async isAddressGnosisSafe(address: string) { - const key = `${CACHE_KEY_PREFIX}:${networkContext.chainId}:${address}`; - const cachedValue = this.cache.get(key); - if (cachedValue != null) { - return cachedValue; - } - - try { - await Safe.create({ - ethAdapter: await this.getAdapter(), - safeAddress: getAddress(address), - contractNetworks, - }); - - this.cache.put(key, true, TIMEOUT); - return true; - } catch { - this.cache.put(key, false, TIMEOUT); - return false; - } - } - - private async getAdapter() { - const provider = new providers.JsonRpcProvider(networkContext.data.rpcUrl); - const signer = ethers.Wallet.createRandom(); - - return new EthersAdapter({ - ethers, - signer: signer.connect(provider), - }); - } -} - -export const gnosisSafeService = new GnosisSafeService(); diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index b381545ee..85e841c54 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -11,6 +11,7 @@ import { import { GqlBalancePoolAprItem, GqlBalancePoolAprSubItem, + GqlChain, GqlPoolDynamicData, GqlPoolFeaturedPoolGroup, GqlPoolInvestConfig, @@ -33,16 +34,16 @@ import { import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; -import { Prisma, PrismaPoolAprType } from '@prisma/client'; +import { Chain, Prisma, PrismaPoolAprType } from '@prisma/client'; import { isWeightedPoolV2 } from './pool-utils'; import { oldBnum } from '../../big-number/old-big-number'; import { networkContext } from '../../network/network-context.service'; import { fixedNumber } from '../../view-helpers/fixed-number'; export class PoolGqlLoaderService { - public async getPool(id: string): Promise { + public async getPool(id: string, chain: Chain): Promise { const pool = await prisma.prismaPool.findUnique({ - where: { id_chain: { id, chain: networkContext.chain } }, + where: { id_chain: { id, chain: chain } }, include: prismaPoolWithExpandedNesting.include, }); @@ -66,9 +67,9 @@ export class PoolGqlLoaderService { return pools.map((pool) => this.mapToMinimalGqlPool(pool)); } - public async getLinearPools(): Promise { + public async getLinearPools(chains: Chain[]): Promise { const pools = await prisma.prismaPool.findMany({ - where: { type: 'LINEAR', chain: networkContext.chain }, + where: { type: 'LINEAR', chain: { in: chains } }, orderBy: { dynamicData: { totalLiquidity: 'desc' } }, include: prismaPoolWithExpandedNesting.include, }); diff --git a/modules/pool/lib/pool-snapshot.service.ts b/modules/pool/lib/pool-snapshot.service.ts index e64062b73..3c51d26cc 100644 --- a/modules/pool/lib/pool-snapshot.service.ts +++ b/modules/pool/lib/pool-snapshot.service.ts @@ -1,4 +1,3 @@ -import * as Sentry from '@sentry/node'; import { balancerSubgraphService, BalancerSubgraphService, @@ -12,7 +11,7 @@ import { import { GqlPoolSnapshotDataRange } from '../../../schema'; import moment from 'moment-timezone'; import _ from 'lodash'; -import { PrismaPoolSnapshot } from '@prisma/client'; +import { Chain, PrismaPoolSnapshot } from '@prisma/client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { prismaPoolWithExpandedNesting } from '../../../prisma/prisma-types'; import { CoingeckoService } from '../../coingecko/coingecko.service'; @@ -27,11 +26,11 @@ export class PoolSnapshotService { private readonly coingeckoService: CoingeckoService, ) {} - public async getSnapshotsForPool(poolId: string, range: GqlPoolSnapshotDataRange) { + public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ - where: { poolId, timestamp: { gte: timestamp }, chain: networkContext.chain }, + where: { poolId, timestamp: { gte: timestamp }, chain: chain }, orderBy: { timestamp: 'asc' }, }); } @@ -42,7 +41,7 @@ export class PoolSnapshotService { }); } - public async getSnapshotsForAllPools(range: GqlPoolSnapshotDataRange) { + public async getSnapshotsForAllPools(chains: Chain[], range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ @@ -54,7 +53,7 @@ export class PoolSnapshotService { pool: { categories: { none: { category: 'BLACK_LISTED' } }, }, - chain: networkContext.chain, + chain: { in: chains }, }, orderBy: { timestamp: 'asc' }, }); diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 7de0530db..b3330e68a 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -14,12 +14,13 @@ import { QueryPoolGetJoinExitsArgs, QueryPoolGetSwapsArgs, } from '../../../schema'; -import { PrismaPoolSwap } from '@prisma/client'; +import { Chain, PrismaPoolSwap } from '@prisma/client'; import _ from 'lodash'; import { isSupportedInt, prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { PrismaPoolBatchSwapWithSwaps, prismaPoolMinimal } from '../../../prisma/prisma-types'; import { networkContext } from '../../network/network-context.service'; import * as Sentry from '@sentry/node'; +import { AllNetworkConfigsKeyedOnChain } from '../../network/network-config'; export class PoolSwapService { constructor( @@ -30,29 +31,49 @@ export class PoolSwapService { public async getJoinExits(args: QueryPoolGetJoinExitsArgs): Promise { const first = !args.first || args.first > 100 ? 10 : args.first; - const { joinExits } = await this.balancerSubgraphService.getPoolJoinExits({ - where: { pool_in: args.where?.poolIdIn }, - first, - skip: args.skip, - orderBy: JoinExit_OrderBy.Timestamp, - orderDirection: OrderDirection.Desc, - }); + const allChainsJoinExits: GqlPoolJoinExit[] = []; - return joinExits.map((joinExit) => ({ - ...joinExit, - __typename: 'GqlPoolJoinExit', - poolId: joinExit.pool.id, - amounts: joinExit.amounts.map((amount, index) => ({ address: joinExit.pool.tokensList[index], amount })), - })); + for (const chain of args.where!.chainIn!) { + const balancerSubgraphService = new BalancerSubgraphService( + AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, + ); + + const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ + where: { pool_in: args.where?.poolIdIn }, + first, + skip: args.skip, + orderBy: JoinExit_OrderBy.Timestamp, + orderDirection: OrderDirection.Desc, + }); + + const mappedJoinExits: GqlPoolJoinExit[] = joinExits.map((joinExit) => ({ + ...joinExit, + __typename: 'GqlPoolJoinExit', + chain: chain, + poolId: joinExit.pool.id, + amounts: joinExit.amounts.map((amount, index) => ({ + address: joinExit.pool.tokensList[index], + amount, + })), + })); + + allChainsJoinExits.push(...mappedJoinExits); + } + + return allChainsJoinExits; } public async getUserJoinExitsForPool( userAddress: string, poolId: string, + chain: Chain, first = 10, skip = 0, ): Promise { - const { joinExits } = await this.balancerSubgraphService.getPoolJoinExits({ + const balancerSubgraphService = new BalancerSubgraphService( + AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, + ); + const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ where: { pool: poolId, user: userAddress }, first, skip: skip, @@ -64,6 +85,7 @@ export class PoolSwapService { ...joinExit, __typename: 'GqlPoolJoinExit', poolId: joinExit.pool.id, + chain: chain, amounts: joinExit.amounts.map((amount, index) => ({ address: joinExit.pool.tokensList[index], amount })), })); } @@ -84,7 +106,9 @@ export class PoolSwapService { tokenOut: { in: args.where?.tokenOutIn || undefined, }, - chain: networkContext.chain, + chain: { + in: args.where?.chainIn || undefined, + }, }, orderBy: { timestamp: 'desc' }, }); @@ -93,10 +117,14 @@ export class PoolSwapService { public async getUserSwapsForPool( userAddress: string, poolId: string, + chain: Chain, first = 10, skip = 0, ): Promise { - const result = await this.balancerSubgraphService.getSwaps({ + const balancerSubgraphService = new BalancerSubgraphService( + AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, + ); + const result = await balancerSubgraphService.getSwaps({ first, skip, where: { @@ -109,6 +137,7 @@ export class PoolSwapService { return result.swaps.map((swap) => ({ id: swap.id, + chain: chain, userAddress, poolId: swap.poolId.id, tokenIn: swap.tokenIn, @@ -143,7 +172,9 @@ export class PoolSwapService { tokenOut: { in: args.where?.tokenOutIn || undefined, }, - chain: networkContext.chain, + chain: { + in: args.where?.chainIn || undefined, + }, }, orderBy: { timestamp: 'desc' }, include: { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 061eaa2ab..b033434ef 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -1,5 +1,5 @@ extend type Query { - poolGetPool(id: String!): GqlPoolBase! + poolGetPool(id: String!, chain: GqlChain): GqlPoolBase! poolGetPools( first: Int skip: Int @@ -20,9 +20,9 @@ extend type Query { poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! poolGetFeaturedPoolGroups: [GqlPoolFeaturedPoolGroup!]! - poolGetSnapshots(id: String!, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! - poolGetAllPoolsSnapshots(range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! - poolGetLinearPools: [GqlPoolLinear!]! + poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! + poolGetAllPoolsSnapshots(chains: [GqlChain!], range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! + poolGetLinearPools(chains: [GqlChain!]): [GqlPoolLinear!]! } extend type Mutation { @@ -657,6 +657,7 @@ type GqlPoolFilterDefinition { type GqlPoolSwap { id: ID! + chain: GqlChain! poolId: String! userAddress: String! tokenIn: String! @@ -670,6 +671,7 @@ type GqlPoolSwap { type GqlPoolBatchSwap { id: ID! + chain: GqlChain! userAddress: String! tokenIn: String! tokenOut: String! @@ -705,10 +707,12 @@ input GqlPoolSwapFilter { tokenInIn: [String!] tokenOutIn: [String!] poolIdIn: [String!] + chainIn: [GqlChain!] } type GqlPoolStaking { id: ID! + chain: GqlChain! type: GqlPoolStakingType! address: String! gauge: GqlPoolStakingGauge @@ -726,10 +730,12 @@ enum GqlPoolStakingGaugeStatus { input GqlPoolJoinExitFilter { poolIdIn: [String!] + chainIn: [GqlChain!] } type GqlPoolJoinExit { id: ID! + chain: GqlChain! type: GqlPoolJoinExitType! sender: String! poolId: String! @@ -799,6 +805,7 @@ enum GqlPoolSnapshotDataRange { type GqlPoolSnapshot { id: ID! + chain: GqlChain! poolId: String! timestamp: Int! totalLiquidity: String! diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index c3f80f032..3a88f526c 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -3,11 +3,18 @@ import { Resolvers } from '../../schema'; import { isAdminRoute } from '../auth/auth-context'; import { prisma } from '../../prisma/prisma-client'; import { networkContext } from '../network/network-context.service'; +import { headerChain } from '../context/header-chain'; const balancerResolvers: Resolvers = { Query: { - poolGetPool: async (parent, { id }, context) => { - return poolService.getGqlPool(id); + poolGetPool: async (parent, { id, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + return poolService.getGqlPool(id, chain); }, poolGetPools: async (parent, args, context) => { return poolService.getGqlPools(args); @@ -16,19 +23,43 @@ const balancerResolvers: Resolvers = { return poolService.getPoolsCount(args); }, poolGetSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.where?.chainIn && currentChain) { + args.where = { ...args.where, chainIn: [currentChain] }; + } else if (!args.where?.chainIn) { + throw new Error('Chain is required'); + } return poolService.getPoolSwaps(args); }, poolGetBatchSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.where?.chainIn && currentChain) { + args.where = { ...args.where, chainIn: [currentChain] }; + } else if (!args.where?.chainIn) { + throw new Error('Chain is required'); + } return poolService.getPoolBatchSwaps(args); }, poolGetJoinExits: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.where?.chainIn && currentChain) { + args.where = { ...args.where, chainIn: [currentChain] }; + } else if (!args.where?.chainIn) { + throw new Error('Chain is required'); + } return poolService.getPoolJoinExits(args); }, poolGetFeaturedPoolGroups: async (parent, args, context) => { return poolService.getFeaturedPoolGroups(); }, - poolGetSnapshots: async (parent, { id, range }, context) => { - const snapshots = await poolService.getSnapshotsForPool(id, range); + poolGetSnapshots: async (parent, { id, chain, range }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + const snapshots = await poolService.getSnapshotsForPool(id, chain, range); return snapshots.map((snapshot) => ({ ...snapshot, @@ -42,8 +73,14 @@ const balancerResolvers: Resolvers = { holdersCount: `${snapshot.holdersCount}`, })); }, - poolGetAllPoolsSnapshots: async (parent, { range }, context) => { - const snapshots = await poolService.getSnapshotsForAllPools(range); + poolGetAllPoolsSnapshots: async (parent, { chains, range }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('Chain is required'); + } + const snapshots = await poolService.getSnapshotsForAllPools(chains, range); return snapshots.map((snapshot) => ({ ...snapshot, @@ -57,8 +94,14 @@ const balancerResolvers: Resolvers = { holdersCount: `${snapshot.holdersCount}`, })); }, - poolGetLinearPools: async () => { - return poolService.getGqlLinearPools(); + poolGetLinearPools: async (parent, { chains }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('Chain is required'); + } + return poolService.getGqlLinearPools(chains); }, }, Mutation: { diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 97e2d37f1..a6a69bd96 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -1,9 +1,10 @@ -import { PrismaPoolFilter, PrismaPoolStakingType, PrismaPoolSwap } from '@prisma/client'; +import { Chain, PrismaPoolFilter, PrismaPoolStakingType, PrismaPoolSwap } from '@prisma/client'; import _, { chain, includes } from 'lodash'; import { Cache } from 'memory-cache'; import moment from 'moment-timezone'; import { prisma } from '../../prisma/prisma-client'; import { + GqlChain, GqlPoolBatchSwap, GqlPoolFeaturedPoolGroup, GqlPoolJoinExit, @@ -60,16 +61,16 @@ export class PoolService { return networkContext.config.contentService; } - public async getGqlPool(id: string): Promise { - return this.poolGqlLoaderService.getPool(id); + public async getGqlPool(id: string, chain: GqlChain): Promise { + return this.poolGqlLoaderService.getPool(id, chain); } public async getGqlPools(args: QueryPoolGetPoolsArgs): Promise { return this.poolGqlLoaderService.getPools(args); } - public async getGqlLinearPools(): Promise { - return this.poolGqlLoaderService.getLinearPools(); + public async getGqlLinearPools(chains: Chain[]): Promise { + return this.poolGqlLoaderService.getLinearPools(chains); } public async getPoolsCount(args: QueryPoolGetPoolsArgs): Promise { @@ -122,12 +123,12 @@ export class PoolService { return featuredPoolGroups; } - public async getSnapshotsForAllPools(range: GqlPoolSnapshotDataRange) { - return this.poolSnapshotService.getSnapshotsForAllPools(range); + public async getSnapshotsForAllPools(chains: Chain[], range: GqlPoolSnapshotDataRange) { + return this.poolSnapshotService.getSnapshotsForAllPools(chains, range); } - public async getSnapshotsForPool(poolId: string, range: GqlPoolSnapshotDataRange) { - return this.poolSnapshotService.getSnapshotsForPool(poolId, range); + public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { + return this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); } public async getSnapshotsForReliquaryFarm(id: number, range: GqlPoolSnapshotDataRange) { @@ -433,7 +434,7 @@ export class PoolService { await prisma.prismaPoolLinearData.deleteMany({ where: { chain: networkContext.chain, poolId: poolId }, }); - + await prisma.prismaPoolGyroData.deleteMany({ where: { chain: networkContext.chain, poolId: poolId }, }); @@ -476,9 +477,9 @@ export class PoolService { }, }); - if(gauge && gauge.votingGauge) + if (gauge && gauge.votingGauge) await prisma.prismaVotingGauge.deleteMany({ - where: { chain: networkContext.chain, id: gauge.votingGauge.id } + where: { chain: networkContext.chain, id: gauge.votingGauge.id }, }); await prisma.prismaPoolStakingGauge.deleteMany({ diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts index 0a4023c9c..43b779b58 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts @@ -52,7 +52,7 @@ const PORTFOLIO_POOLS_CACHE_KEY = `balance-subgraph_portfolio-pools`; export class BalancerSubgraphService { private cache: CacheClass; - constructor() { + constructor(private readonly subgraphUrl = networkContext.data.subgraphs.balancer) { this.cache = new Cache(); } @@ -314,7 +314,7 @@ export class BalancerSubgraphService { } private get sdk() { - const client = new GraphQLClient(networkContext.data.subgraphs.balancer); + const client = new GraphQLClient(this.subgraphUrl); return getSdk(client); } diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index 988a6111a..b5c54cafe 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -2,26 +2,25 @@ import { Resolvers } from '../../schema'; import _ from 'lodash'; import { isAdminRoute } from '../auth/auth-context'; import { tokenService } from './token.service'; -import { networkContext } from '../network/network-context.service'; import { headerChain } from '../context/header-chain'; const resolvers: Resolvers = { Query: { tokenGetTokens: async (parent, { chains }, context) => { - const currentChain = headerChain() + const currentChain = headerChain(); if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } return tokenService.getTokenDefinitions(chains); }, tokenGetCurrentPrices: async (parent, { chains }, context) => { - const currentChain = headerChain() + const currentChain = headerChain(); if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } const prices = await tokenService.getWhiteListedTokenPrices(chains); diff --git a/modules/user/lib/user-balance.service.ts b/modules/user/lib/user-balance.service.ts index 200f7837b..fb98fb1f2 100644 --- a/modules/user/lib/user-balance.service.ts +++ b/modules/user/lib/user-balance.service.ts @@ -74,12 +74,12 @@ export class UserBalanceService { }; } - public async getUserStaking(address: string): Promise { + public async getUserStaking(address: string, chains: Chain[]): Promise { const user = await prisma.prismaUser.findUnique({ where: { address }, include: { stakedBalances: { - where: { chain: networkContext.chain, balanceNum: { gt: 0 } }, + where: { chain: { in: chains }, balanceNum: { gt: 0 } }, include: { staking: { include: { diff --git a/modules/user/lib/user-snapshot.service.ts b/modules/user/lib/user-snapshot.service.ts index c31e9c2bc..f5d262761 100644 --- a/modules/user/lib/user-snapshot.service.ts +++ b/modules/user/lib/user-snapshot.service.ts @@ -4,7 +4,14 @@ import moment from 'moment-timezone'; import { UserPoolSnapshot, UserRelicSnapshot } from '../user-types'; import { GqlUserSnapshotDataRange } from '../../../schema'; import { PoolSnapshotService } from '../../pool/lib/pool-snapshot.service'; -import { Prisma, PrismaPool, PrismaPoolSnapshot, PrismaPoolStaking, PrismaUserRelicSnapshot } from '@prisma/client'; +import { + Chain, + Prisma, + PrismaPool, + PrismaPoolSnapshot, + PrismaPoolStaking, + PrismaUserRelicSnapshot, +} from '@prisma/client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { oneDayInSeconds, secondsPerDay } from '../../common/time'; import { UserBalanceSnapshotFragment } from '../../subgraphs/user-snapshot-subgraph/generated/user-snapshot-subgraph-types'; @@ -309,6 +316,7 @@ export class UserSnapshotService { public async getUserPoolBalanceSnapshotsForPool( userAddress: string, poolId: string, + chain: Chain, range: GqlUserSnapshotDataRange, ): Promise { const oldestRequestedSnapshotTimestamp = this.getTimestampForRange(range); @@ -374,7 +382,7 @@ export class UserSnapshotService { const prismaInput: Prisma.PrismaUserPoolBalanceSnapshotCreateManyInput[] = []; - poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, range); + poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); /* For each snapshot from the subgraph, this will get the poolSnapshot for the same timestamp and enrich with $ value data @@ -436,7 +444,7 @@ export class UserSnapshotService { // Only get them if we didn't get them above if (poolSnapshots.length === 0) { - poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, range); + poolSnapshots = await this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); } /* diff --git a/modules/user/user-snapshot.service.test.ts b/modules/user/user-snapshot.service.test.ts index 47d8da75b..f97fc4d57 100644 --- a/modules/user/user-snapshot.service.test.ts +++ b/modules/user/user-snapshot.service.test.ts @@ -13,6 +13,7 @@ import { mockServer } from '../tests-helper/mocks/mockHttpServer'; import { userService } from './user.service'; import { secondsPerDay } from '../common/time'; import { networkContext } from '../network/network-context.service'; +import { Chain } from '@prisma/client'; /* TEST SETUP: @@ -182,7 +183,12 @@ test('The user requests the user stats for the first time, requesting from subgr ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -298,6 +304,7 @@ test('User in in the pool for a very long time, requests various different time const thirtySnapshotsFromService = await userService.getUserBalanceSnapshotsForPool( userAddress, poolId1, + Chain.FANTOM, 'THIRTY_DAYS', ); @@ -316,6 +323,7 @@ test('User in in the pool for a very long time, requests various different time const ninetySnapshotsFromService = await userService.getUserBalanceSnapshotsForPool( userAddress, poolId1, + Chain.FANTOM, 'NINETY_DAYS', ); //also includes the one from today @@ -412,7 +420,12 @@ Mock data for user-balance-subgraph (important that timestamps are ASC, as this ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -535,7 +548,12 @@ Mock data for user-balance-subgraph (important that timestamps are ASC, as this ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); //check if 4th snapshot has been inferred from three present ones expect(snapshotsFromService.length).toBe(2); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -627,7 +645,12 @@ test('Return a snapshot with 0 valueUSD if there is no pool snapshot for the giv ], ); - const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool(userAddress, pool2Id, 'THIRTY_DAYS'); + const snapshotsFromService = await userService.getUserBalanceSnapshotsForPool( + userAddress, + pool2Id, + Chain.FANTOM, + 'THIRTY_DAYS', + ); // should get all 4 snapshots expect(snapshotsFromService.length).toBe(4); const snapshotsFromDb = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -661,6 +684,7 @@ test('Return a snapshot with 0 valueUSD if there is no pool snapshot for the giv const snapshotsAfterAdditionalPoolSnapshot = await userService.getUserBalanceSnapshotsForPool( userAddress, pool2Id, + Chain.FANTOM, 'THIRTY_DAYS', ); //expect still the same results here as above @@ -804,7 +828,12 @@ test('User snapshots in the database must be picked up and synced by the sync pr expect(snapshotsFromDb.length).toBe(3); // after the sync, all 4 snapshots should be present - const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(snapshotsAfterSync.length).toBe(4); // check if balances are calculated correctly @@ -1000,7 +1029,12 @@ test('User has left and re-entered the pool. Make sure the sync does not persist // after the sync, 5 snapshots should be present. //Sevendaysago, sixdaysago (inferred), fivedaysago (0 balance), fourdays ago (0 balance), threedaysago and twodaysago (0 balance) - const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const snapshotsAfterSync = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(snapshotsAfterSync.length).toBe(6); const snapshotsFromDbAfterGet = await prisma.prismaUserPoolBalanceSnapshot.findMany({ @@ -1115,7 +1149,12 @@ test('Todays user snapshot must be gradually updated based on an updated pool sn ], ); - const userSnapshotsBefore = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const userSnapshotsBefore = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(userSnapshotsBefore.length).toBe(1); // check if balances are calculated correctly @@ -1166,7 +1205,12 @@ test('Todays user snapshot must be gradually updated based on an updated pool sn await userService.syncUserBalanceSnapshots(); // check numbers again - const userSnapshotsAfter = await userService.getUserBalanceSnapshotsForPool(userAddress, poolId1, 'THIRTY_DAYS'); + const userSnapshotsAfter = await userService.getUserBalanceSnapshotsForPool( + userAddress, + poolId1, + Chain.FANTOM, + 'THIRTY_DAYS', + ); expect(userSnapshotsBefore.length).toBe(1); // check if balances are calculated correctly @@ -1292,6 +1336,7 @@ test('User requests pool snapshots for Fidelio Duetto Pool. Make sure fBeets are const userBalanceSnapshots = await userService.getUserBalanceSnapshotsForPool( userAddress, fidelioPoolId, + Chain.FANTOM, 'THIRTY_DAYS', ); expect(userBalanceSnapshots.length).toBe(4); diff --git a/modules/user/user.gql b/modules/user/user.gql index 62803f158..bdeabede4 100644 --- a/modules/user/user.gql +++ b/modules/user/user.gql @@ -1,8 +1,14 @@ extend type Query { userGetPoolBalances(chains: [GqlChain!], address: String): [GqlUserPoolBalance!]! - userGetStaking: [GqlPoolStaking!]! - userGetPoolJoinExits(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolJoinExit!]! - userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!): [GqlPoolSwap!]! + userGetStaking(chains: [GqlChain!], address: String): [GqlPoolStaking!]! + userGetPoolJoinExits( + first: Int = 10 + skip: Int = 0 + poolId: String! + chain: GqlChain + address: String + ): [GqlPoolJoinExit!]! + userGetSwaps(first: Int = 10, skip: Int = 0, poolId: String!, chain: GqlChain, address: String): [GqlPoolSwap!]! } extend type Mutation { diff --git a/modules/user/user.resolvers.ts b/modules/user/user.resolvers.ts index 0c7424b6f..e94480d62 100644 --- a/modules/user/user.resolvers.ts +++ b/modules/user/user.resolvers.ts @@ -7,11 +7,11 @@ import { headerChain } from '../context/header-chain'; const resolvers: Resolvers = { Query: { userGetPoolBalances: async (parent, { chains, address }, context) => { - const currentChain = headerChain() + const currentChain = headerChain(); if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - chains = []; + throw new Error('Chain is required'); } const accountAddress = address || getRequiredAccountAddress(context); const tokenPrices = await tokenService.getTokenPricesForChains(chains); @@ -22,19 +22,37 @@ const resolvers: Resolvers = { tokenPrice: tokenService.getPriceForToken(tokenPrices[balance.chain] || [], balance.tokenAddress), })); }, - userGetPoolJoinExits: async (parent, { first, skip, poolId }, context) => { - const accountAddress = getRequiredAccountAddress(context); + userGetPoolJoinExits: async (parent, { first, skip, poolId, chain, address }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + const accountAddress = address || getRequiredAccountAddress(context); - return userService.getUserPoolInvestments(accountAddress, poolId, first, skip); + return userService.getUserPoolInvestments(accountAddress, poolId, chain, first, skip); }, - userGetSwaps: async (parent, { first, skip, poolId }, context) => { - const accountAddress = getRequiredAccountAddress(context); - return userService.getUserSwaps(accountAddress, poolId, first, skip); + userGetSwaps: async (parent, { first, skip, poolId, chain, address }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('Chain is required'); + } + const accountAddress = address || getRequiredAccountAddress(context); + return userService.getUserSwaps(accountAddress, poolId, chain, first, skip); }, - userGetStaking: async (parent, {}, context) => { - const accountAddress = getRequiredAccountAddress(context); + userGetStaking: async (parent, { chains, address }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('Chain is required'); + } + const accountAddress = address || getRequiredAccountAddress(context); - return userService.getUserStaking(accountAddress); + return userService.getUserStaking(accountAddress, chains); }, }, Mutation: { diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index fd603906c..f58c6104f 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -5,7 +5,6 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { PoolSnapshotService } from '../pool/lib/pool-snapshot.service'; import { PoolSwapService } from '../pool/lib/pool-swap.service'; import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; -import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { reliquarySubgraphService } from '../subgraphs/reliquary-subgraph/reliquary.service'; import { userSnapshotSubgraphService } from '../subgraphs/user-snapshot-subgraph/user-snapshot-subgraph.service'; import { tokenService } from '../token/token.service'; @@ -34,30 +33,38 @@ export class UserService { public async getUserPoolInvestments( address: string, poolId: string, + chain: Chain, first?: number, skip?: number, ): Promise { - return this.poolSwapService.getUserJoinExitsForPool(address, poolId, first, skip); + return this.poolSwapService.getUserJoinExitsForPool(address, poolId, chain, first, skip); } - public async getUserSwaps(address: string, poolId: string, first?: number, skip?: number): Promise { - return this.poolSwapService.getUserSwapsForPool(address, poolId, first, skip); + public async getUserSwaps( + address: string, + poolId: string, + chain: Chain, + first?: number, + skip?: number, + ): Promise { + return this.poolSwapService.getUserSwapsForPool(address, poolId, chain, first, skip); } public async getUserFbeetsBalance(address: string): Promise> { return this.userBalanceService.getUserFbeetsBalance(address); } - public async getUserStaking(address: string): Promise { - return this.userBalanceService.getUserStaking(address); + public async getUserStaking(address: string, chains: Chain[]): Promise { + return this.userBalanceService.getUserStaking(address, chains); } public async getUserBalanceSnapshotsForPool( accountAddress: string, poolId: string, + chain: Chain, days: GqlUserSnapshotDataRange, ): Promise { - return this.userSnapshotService.getUserPoolBalanceSnapshotsForPool(accountAddress, poolId, days); + return this.userSnapshotService.getUserPoolBalanceSnapshotsForPool(accountAddress, poolId, chain, days); } public async getUserRelicSnapshots(accountAddress: string, farmId: string, days: GqlUserSnapshotDataRange) { From a222cd695b719a3d71468189794f6af8515ca268 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 10 Nov 2023 12:20:52 +0100 Subject: [PATCH 056/175] balancerSubgraphService per network --- modules/network/arbitrum.ts | 4 + modules/network/avalanche.ts | 4 + modules/network/base.ts | 4 + modules/network/fantom.ts | 4 + modules/network/gnosis.ts | 4 + modules/network/mainnet.ts | 28 +++-- modules/network/network-config-types.ts | 7 ++ modules/network/network-context.service.ts | 4 + modules/network/optimism.ts | 4 + modules/network/polygon.ts | 4 + modules/network/zkevm.ts | 4 + modules/pool/lib/pool-creator.service.ts | 13 +- modules/pool/lib/pool-snapshot.service.ts | 39 +++--- modules/pool/lib/pool-swap.service.ts | 45 +++---- modules/pool/lib/pool-usd-data.service.ts | 6 +- modules/pool/pool.service.ts | 113 ++++++++++-------- .../balancer-subgraph.service.ts | 21 ++-- .../lib/user-sync-wallet-balance.service.ts | 14 ++- modules/user/user.service.ts | 5 +- 19 files changed, 197 insertions(+), 130 deletions(-) diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 5a5264910..20bb523f5 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -17,6 +17,7 @@ import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/ import { coingeckoService } from '../coingecko/coingecko.service'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; import { env } from '../../app/env'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const arbitrumNetworkData: NetworkData = { chain: { @@ -249,6 +250,9 @@ export const arbitrumNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(arbitrumNetworkData.subgraphs.balancer, arbitrumNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 5725ea5b6..f696e5005 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -17,6 +17,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { env } from '../../app/env'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const avalancheNetworkData: NetworkData = { chain: { @@ -239,6 +240,9 @@ export const avalancheNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(avalancheNetworkData.subgraphs.balancer, avalancheNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/base.ts b/modules/network/base.ts index 51e43a18b..c25d9b91e 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -16,6 +16,7 @@ import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/ import { coingeckoService } from '../coingecko/coingecko.service'; import { env } from '../../app/env'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const baseNetworkData: NetworkData = { chain: { @@ -137,6 +138,9 @@ export const baseNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(baseNetworkData.subgraphs.balancer, baseNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 38ba6a11c..a266d331c 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -26,6 +26,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { env } from '../../app/env'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; import { BeetswarsGaugeVotingAprService } from '../pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const fantomNetworkData: NetworkData = { chain: { @@ -345,6 +346,9 @@ export const fantomNetworkConfig: NetworkConfig = { ), new UserSyncReliquaryFarmBalanceService(fantomNetworkData.reliquary!.address), ], + services: { + balancerSubgraphService: new BalancerSubgraphService(fantomNetworkData.subgraphs.balancer, fantomNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index 5c4f30d84..d00aa09f0 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -17,6 +17,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { env } from '../../app/env'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const gnosisNetworkData: NetworkData = { chain: { @@ -166,6 +167,9 @@ export const gnosisNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(gnosisNetworkData.subgraphs.balancer, gnosisNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index e693631bb..8e77f13ff 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -17,6 +17,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; import { env } from '../../app/env'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const underlyingTokens = { USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', @@ -25,7 +26,7 @@ const underlyingTokens = { wETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', }; -export const mainnetNetworkData: NetworkData = { +const data: NetworkData = { chain: { slug: 'ethereum', id: 1, @@ -367,25 +368,25 @@ export const mainnetNetworkData: NetworkData = { }; export const mainnetNetworkConfig: NetworkConfig = { - data: mainnetNetworkData, + data, contentService: new GithubContentService(), - provider: new ethers.providers.JsonRpcProvider({ url: mainnetNetworkData.rpcUrl, timeout: 60000 }), + provider: new ethers.providers.JsonRpcProvider({ url: data.rpcUrl, timeout: 60000 }), poolAprServices: [ new IbTokensAprService( - mainnetNetworkData.ibAprConfig, - mainnetNetworkData.chain.prismaId, - mainnetNetworkData.balancer.yieldProtocolFeePercentage, - mainnetNetworkData.balancer.swapProtocolFeePercentage, + data.ibAprConfig, + data.chain.prismaId, + data.balancer.yieldProtocolFeePercentage, + data.balancer.swapProtocolFeePercentage, ), new PhantomStableAprService( - mainnetNetworkData.chain.prismaId, - mainnetNetworkData.balancer.yieldProtocolFeePercentage, + data.chain.prismaId, + data.balancer.yieldProtocolFeePercentage, ), new BoostedPoolAprService(), - new SwapFeeAprService(mainnetNetworkData.balancer.swapProtocolFeePercentage), - new GaugeAprService(tokenService, [mainnetNetworkData.bal!.address]), + new SwapFeeAprService(data.balancer.swapProtocolFeePercentage), + new GaugeAprService(tokenService, [data.bal!.address]), ], - poolStakingServices: [new GaugeStakingService(gaugeSubgraphService, mainnetNetworkData.bal!.address)], + poolStakingServices: [new GaugeStakingService(gaugeSubgraphService, data.bal!.address)], tokenPriceHandlers: [ new CoingeckoPriceHandlerService(coingeckoService), new BptPriceHandlerService(), @@ -393,6 +394,9 @@ export const mainnetNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(data.subgraphs.balancer, 1), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index a34c742e2..10c8e33dd 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -7,6 +7,7 @@ import { BaseProvider } from '@ethersproject/providers'; import { GqlChain } from '../../schema'; import { ContentService } from '../content/content-types'; import { AaveAprConfig, IbAprConfig } from './apr-config-types'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; export interface NetworkConfig { data: NetworkData; @@ -17,7 +18,13 @@ export interface NetworkConfig { tokenPriceHandlers: TokenPriceHandler[]; provider: BaseProvider; workerJobs: WorkerJob[]; + services: NetworkServices; } + +interface NetworkServices { + balancerSubgraphService: BalancerSubgraphService; +} + export interface WorkerJob { name: string; interval: number; diff --git a/modules/network/network-context.service.ts b/modules/network/network-context.service.ts index 3522175c8..6eef167f6 100644 --- a/modules/network/network-context.service.ts +++ b/modules/network/network-context.service.ts @@ -57,6 +57,10 @@ export class NetworkContextService { public get protocolSupportedChainIds(): string[] { return this.isBalancerChain ? BalancerChainIds : BeethovenChainIds; } + + public get services() { + return this.config.services; + } } export const networkContext = new NetworkContextService(env.DEFAULT_CHAIN_ID); diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index c36471992..af9a1a00d 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -18,6 +18,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; import { env } from '../../app/env'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const optimismNetworkData: NetworkData = { chain: { @@ -294,6 +295,9 @@ export const optimismNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(optimismNetworkData.subgraphs.balancer, optimismNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index cf3c9227f..8fd44b1fd 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -17,6 +17,7 @@ import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; import { env } from '../../app/env'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const polygonNetworkData: NetworkData = { chain: { @@ -284,6 +285,9 @@ export const polygonNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(polygonNetworkData.subgraphs.balancer, polygonNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 80a53b2ef..77ed05875 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -17,6 +17,7 @@ import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/ import { coingeckoService } from '../coingecko/coingecko.service'; import { env } from '../../app/env'; import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const zkevmNetworkData: NetworkData = { chain: { @@ -185,6 +186,9 @@ export const zkevmNetworkConfig: NetworkConfig = { new SwapsPriceHandlerService(), ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], + services: { + balancerSubgraphService: new BalancerSubgraphService(zkevmNetworkData.subgraphs.balancer, zkevmNetworkData.chain.id), + }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. This is needed because the minimum alarm period is 1 minute and we want the alarm to trigger already after 1 minute instead of 3. diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index e9f296e58..fb22dc4fb 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -1,4 +1,3 @@ -import { balancerSubgraphService } from '../../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; import { prisma } from '../../../prisma/prisma-client'; import { ZERO_ADDRESS } from '@gnosis.pm/safe-core-sdk/dist/src/utils/constants'; @@ -12,9 +11,13 @@ import { networkContext } from '../../network/network-context.service'; export class PoolCreatorService { constructor(private readonly userService: UserService) {} + private get balancerSubgraphService() { + return networkContext.services.balancerSubgraphService; + } + public async syncAllPoolsFromSubgraph(blockNumber: number): Promise { const existingPools = await prisma.prismaPool.findMany({ where: { chain: networkContext.chain } }); - const subgraphPools = await balancerSubgraphService.getAllPools({}, false); + const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); const sortedSubgraphPools = this.sortSubgraphPools(subgraphPools); const poolIds: string[] = []; @@ -43,7 +46,7 @@ export class PoolCreatorService { where: { chain: networkContext.chain }, }); - const subgraphPools = await balancerSubgraphService.getAllPools( + const subgraphPools = await this.balancerSubgraphService.getAllPools( { where: { createTime_gte: latest?.createTime || 0 }, }, @@ -66,7 +69,7 @@ export class PoolCreatorService { } public async reloadPoolNestedTokens(poolId: string): Promise { - const subgraphPools = await balancerSubgraphService.getAllPools({}, false); + const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); const poolToLoad = subgraphPools.find((pool) => pool.id === poolId); if (!poolToLoad) { @@ -335,7 +338,7 @@ export class PoolCreatorService { } public async reloadPoolTokenIndexes(poolId: string): Promise { - const { pool: subgraphPool } = await balancerSubgraphService.getPool({ id: poolId }); + const { pool: subgraphPool } = await this.balancerSubgraphService.getPool({ id: poolId }); if (!subgraphPool) { throw new Error('Pool with id does not exist'); diff --git a/modules/pool/lib/pool-snapshot.service.ts b/modules/pool/lib/pool-snapshot.service.ts index 3c51d26cc..ac54bacc0 100644 --- a/modules/pool/lib/pool-snapshot.service.ts +++ b/modules/pool/lib/pool-snapshot.service.ts @@ -1,7 +1,3 @@ -import { - balancerSubgraphService, - BalancerSubgraphService, -} from '../../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { prisma } from '../../../prisma/prisma-client'; import { BalancerPoolSnapshotFragment, @@ -22,10 +18,17 @@ import { TokenHistoricalPrices } from '../../coingecko/coingecko-types'; export class PoolSnapshotService { constructor( - private readonly balancerSubgraphService: BalancerSubgraphService, private readonly coingeckoService: CoingeckoService, ) {} + private get balancerSubgraphService() { + return networkContext.config.services.balancerSubgraphService; + } + + private get chain() { + return networkContext.chain; + } + public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { const timestamp = this.getTimestampForRange(range); @@ -37,7 +40,7 @@ export class PoolSnapshotService { public async getSnapshotForPool(poolId: string, timestamp: number) { return prisma.prismaPoolSnapshot.findUnique({ - where: { id_chain: { id: `${poolId}-${timestamp}`, chain: networkContext.chain } }, + where: { id_chain: { id: `${poolId}-${timestamp}`, chain: this.chain } }, }); } @@ -82,14 +85,14 @@ export class PoolSnapshotService { where: { // there is no guarantee that a pool receives a swap per day, so we get the last day with a swap timestamp: { lte: moment().utc().startOf('day').subtract(daysToSync, 'days').unix() }, - chain: networkContext.chain, + chain: this.chain, }, orderBy: { timestamp: 'desc' }, distinct: 'poolId', }); const poolIds = _.uniq(allSnapshots.map((snapshot) => snapshot.pool.id)); - const pools = await prisma.prismaPool.findMany({ where: { id: { in: poolIds }, chain: networkContext.chain } }); + const pools = await prisma.prismaPool.findMany({ where: { id: { in: poolIds }, chain: this.chain } }); for (const pool of pools) { const snapshots = allSnapshots.filter((snapshot) => snapshot.pool.id === pool.id); @@ -116,7 +119,7 @@ export class PoolSnapshotService { ); return prisma.prismaPoolSnapshot.upsert({ - where: { id_chain: { id: snapshot.id, chain: networkContext.chain } }, + where: { id_chain: { id: snapshot.id, chain: this.chain } }, create: data, update: data, }); @@ -129,8 +132,8 @@ export class PoolSnapshotService { const poolsWithoutSnapshots = await prisma.prismaPool.findMany({ where: { OR: [ - { type: 'PHANTOM_STABLE', chain: networkContext.chain }, - { tokens: { some: { nestedPoolId: { not: null } } }, chain: networkContext.chain }, + { type: 'PHANTOM_STABLE', chain: this.chain }, + { tokens: { some: { nestedPoolId: { not: null } } }, chain: this.chain }, ], }, include: { tokens: true }, @@ -168,7 +171,7 @@ export class PoolSnapshotService { public async createPoolSnapshotsForPoolsMissingSubgraphData(poolId: string, numDays = -1) { const pool = await prisma.prismaPool.findUniqueOrThrow({ - where: { id_chain: { id: poolId, chain: networkContext.chain } }, + where: { id_chain: { id: poolId, chain: this.chain } }, include: prismaPoolWithExpandedNesting.include, }); @@ -183,7 +186,7 @@ export class PoolSnapshotService { throw new Error('Unsupported pool type'); } - const swaps = await balancerSubgraphService.getAllSwapsWithPaging({ where: { poolId }, startTimestamp }); + const swaps = await this.balancerSubgraphService.getAllSwapsWithPaging({ where: { poolId }, startTimestamp }); const tokenPriceMap: TokenHistoricalPrices = {}; @@ -194,7 +197,7 @@ export class PoolSnapshotService { if (token.nestedPoolId && token.nestedPool) { const snapshots = await prisma.prismaPoolSnapshot.findMany({ - where: { poolId: token.nestedPoolId, chain: networkContext.chain }, + where: { poolId: token.nestedPoolId, chain: this.chain }, }); tokenPriceMap[token.address] = snapshots.map((snapshot) => ({ @@ -207,7 +210,7 @@ export class PoolSnapshotService { where: { tokenAddress: token.address, timestamp: { gte: startTimestamp }, - chain: networkContext.chain, + chain: this.chain, }, }); if (priceForDays.length === 0) { @@ -277,7 +280,7 @@ export class PoolSnapshotService { const id = `${poolId}-${startTimestamp}`; const data = { id, - chain: networkContext.chain, + chain: this.chain, poolId, timestamp: startTimestamp, totalLiquidity: totalLiquidity || 0, @@ -297,7 +300,7 @@ export class PoolSnapshotService { try { await prisma.prismaPoolSnapshot.upsert({ - where: { id_chain: { id, chain: networkContext.chain } }, + where: { id_chain: { id, chain: this.chain } }, create: data, update: data, }); @@ -318,7 +321,7 @@ export class PoolSnapshotService { return { id: snapshot.id, - chain: networkContext.chain, + chain: this.chain, poolId: snapshot.pool.id, timestamp: snapshot.timestamp, totalLiquidity: parseFloat(snapshot.liquidity), diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index b3330e68a..58bf051ea 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -6,7 +6,6 @@ import { Swap_OrderBy, } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; import { tokenService, TokenService } from '../../token/token.service'; -import { BalancerSubgraphService } from '../../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { GqlPoolJoinExit, GqlPoolSwap, @@ -25,18 +24,24 @@ import { AllNetworkConfigsKeyedOnChain } from '../../network/network-config'; export class PoolSwapService { constructor( private readonly tokenService: TokenService, - private readonly balancerSubgraphService: BalancerSubgraphService, ) {} + private get balancerSubgraphService() { + return networkContext.services.balancerSubgraphService; + } + + private get chain() { + return networkContext.chain; + } + public async getJoinExits(args: QueryPoolGetJoinExitsArgs): Promise { const first = !args.first || args.first > 100 ? 10 : args.first; const allChainsJoinExits: GqlPoolJoinExit[] = []; for (const chain of args.where!.chainIn!) { - const balancerSubgraphService = new BalancerSubgraphService( - AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, - ); + const balancerSubgraphService = + AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ where: { pool_in: args.where?.poolIdIn }, @@ -70,9 +75,9 @@ export class PoolSwapService { first = 10, skip = 0, ): Promise { - const balancerSubgraphService = new BalancerSubgraphService( - AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, - ); + const balancerSubgraphService = + AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; + const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ where: { pool: poolId, user: userAddress }, first, @@ -121,9 +126,9 @@ export class PoolSwapService { first = 10, skip = 0, ): Promise { - const balancerSubgraphService = new BalancerSubgraphService( - AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer, - ); + const balancerSubgraphService = + AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; + const result = await balancerSubgraphService.getSwaps({ first, skip, @@ -191,7 +196,7 @@ export class PoolSwapService { const tokenPrices = await this.tokenService.getTokenPrices(); const lastSwap = await prisma.prismaPoolSwap.findFirst({ orderBy: { timestamp: 'desc' }, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); const twoDaysAgo = moment().subtract(2, 'day').unix(); //ensure we only sync the last 48 hours worth of swaps @@ -256,7 +261,7 @@ export class PoolSwapService { return { id: swap.id, - chain: networkContext.chain, + chain: this.chain, timestamp: swap.timestamp, poolId: swap.poolId.id, userAddress: swap.userAddress.id, @@ -290,13 +295,13 @@ export class PoolSwapService { await prisma.prismaPoolSwap.deleteMany({ where: { timestamp: { lt: twoDaysAgo }, - chain: networkContext.chain, + chain: this.chain, }, }); await prisma.prismaPoolBatchSwap.deleteMany({ where: { timestamp: { lt: twoDaysAgo }, - chain: networkContext.chain, + chain: this.chain, }, }); @@ -305,14 +310,14 @@ export class PoolSwapService { private async createBatchSwaps(txs: string[]) { const tokenPrices = await this.tokenService.getTokenPrices(); - const swaps = await prisma.prismaPoolSwap.findMany({ where: { tx: { in: txs }, chain: networkContext.chain } }); + const swaps = await prisma.prismaPoolSwap.findMany({ where: { tx: { in: txs }, chain: this.chain } }); const groupedByTxAndUser = _.groupBy(swaps, (swap) => `${swap.tx}${swap.userAddress}`); let operations: any[] = [ prisma.prismaPoolSwap.updateMany({ - where: { tx: { in: txs }, chain: networkContext.chain }, + where: { tx: { in: txs }, chain: this.chain }, data: { batchSwapId: null, batchSwapIdx: null }, }), - prisma.prismaPoolBatchSwap.deleteMany({ where: { tx: { in: txs }, chain: networkContext.chain } }), + prisma.prismaPoolBatchSwap.deleteMany({ where: { tx: { in: txs }, chain: this.chain } }), ]; for (const group of Object.values(groupedByTxAndUser)) { @@ -339,7 +344,7 @@ export class PoolSwapService { prisma.prismaPoolBatchSwap.create({ data: { id: startSwap.id, - chain: networkContext.chain, + chain: this.chain, timestamp: startSwap.timestamp, userAddress: startSwap.userAddress, tokenIn: startSwap.tokenIn, @@ -354,7 +359,7 @@ export class PoolSwapService { }), ...batchSwaps.map((swap, index) => prisma.prismaPoolSwap.update({ - where: { id_chain: { id: swap.id, chain: networkContext.chain } }, + where: { id_chain: { id: swap.id, chain: this.chain } }, data: { batchSwapId: startSwap.id, batchSwapIdx: index }, }), ), diff --git a/modules/pool/lib/pool-usd-data.service.ts b/modules/pool/lib/pool-usd-data.service.ts index da03195d0..aafffbf3c 100644 --- a/modules/pool/lib/pool-usd-data.service.ts +++ b/modules/pool/lib/pool-usd-data.service.ts @@ -4,7 +4,6 @@ import moment from 'moment-timezone'; import { isSupportedInt, prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { TokenService } from '../../token/token.service'; import { BlocksSubgraphService } from '../../subgraphs/blocks-subgraph/blocks-subgraph.service'; -import { BalancerSubgraphService } from '../../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { networkContext } from '../../network/network-context.service'; import { capturesYield } from './pool-utils'; import * as Sentry from '@sentry/node'; @@ -13,9 +12,12 @@ export class PoolUsdDataService { constructor( private readonly tokenService: TokenService, private readonly blockSubgraphService: BlocksSubgraphService, - private readonly balancerSubgraphService: BalancerSubgraphService, ) {} + private get balancerSubgraphService() { + return networkContext.services.balancerSubgraphService; + } + /** * Liquidity is dependent on token prices, so the values here are constantly in flux. * When updating, the easiest is to update all pools at once. diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index a6a69bd96..6f7816f4f 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -19,7 +19,6 @@ import { QueryPoolGetSwapsArgs, } from '../../schema'; import { coingeckoService } from '../coingecko/coingecko.service'; -import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { blocksSubgraphService } from '../subgraphs/blocks-subgraph/blocks-subgraph.service'; import { tokenService } from '../token/token.service'; import { userService } from '../user/user.service'; @@ -53,6 +52,14 @@ export class PoolService { private readonly reliquarySnapshotService: ReliquarySnapshotService, ) {} + private get chain() { + return networkContext.chain; + } + + private get chainId() { + return networkContext.chainId; + } + private get poolStakingServices(): PoolStakingService[] { return networkContext.config.poolStakingServices; } @@ -61,6 +68,10 @@ export class PoolService { return networkContext.config.contentService; } + private get balancerSubgraphService() { + return networkContext.services.balancerSubgraphService; + } + public async getGqlPool(id: string, chain: GqlChain): Promise { return this.poolGqlLoaderService.getPool(id, chain); } @@ -78,7 +89,7 @@ export class PoolService { } public async getPoolFilters(): Promise { - return prisma.prismaPoolFilter.findMany({ where: { chain: networkContext.chain } }); + return prisma.prismaPoolFilter.findMany({ where: { chain: this.chain } }); } public async getPoolSwaps(args: QueryPoolGetSwapsArgs): Promise { @@ -105,7 +116,7 @@ export class PoolService { public async getFeaturedPoolGroups(): Promise { const cached: GqlPoolFeaturedPoolGroup[] = await this.cache.get( - `${FEATURED_POOL_GROUPS_CACHE_KEY}:${networkContext.chainId}`, + `${FEATURED_POOL_GROUPS_CACHE_KEY}:${this.chainId}`, ); if (cached) { @@ -115,7 +126,7 @@ export class PoolService { const featuredPoolGroups = await this.poolGqlLoaderService.getFeaturedPoolGroups(); this.cache.put( - `${FEATURED_POOL_GROUPS_CACHE_KEY}:${networkContext.chainId}`, + `${FEATURED_POOL_GROUPS_CACHE_KEY}:${this.chainId}`, featuredPoolGroups, 60 * 5 * 1000, ); @@ -154,7 +165,7 @@ export class PoolService { public async syncPoolAllTokensRelationship(): Promise { const pools = await prisma.prismaPool.findMany({ select: { id: true }, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); for (const pool of pools) { @@ -183,7 +194,7 @@ export class PoolService { categories: { none: { category: 'BLACK_LISTED' }, }, - chain: networkContext.chain, + chain: this.chain, }, }); const poolIds = result.map((item) => item.id); @@ -216,7 +227,7 @@ export class PoolService { public async loadOnChainDataForPoolsWithActiveUpdates() { const blockNumber = await networkContext.provider.getBlockNumber(); const timestamp = moment().subtract(5, 'minutes').unix(); - const poolIds = await balancerSubgraphService.getPoolsWithActiveUpdates(timestamp); + const poolIds = await this.balancerSubgraphService.getPoolsWithActiveUpdates(timestamp); await this.poolOnChainDataService.updateOnChainData(poolIds, blockNumber); } @@ -271,7 +282,7 @@ export class PoolService { public async loadSnapshotsForPools(poolIds: string[], reload: boolean) { if (reload) { await prisma.prismaPoolSnapshot.deleteMany({ - where: { chain: networkContext.chain, poolId: { in: poolIds } }, + where: { chain: this.chain, poolId: { in: poolIds } }, }); } @@ -279,7 +290,7 @@ export class PoolService { } public async loadSnapshotsForAllPools() { - await prisma.prismaPoolSnapshot.deleteMany({ where: { chain: networkContext.chain } }); + await prisma.prismaPoolSnapshot.deleteMany({ where: { chain: this.chain } }); const pools = await prisma.prismaPool.findMany({ select: { id: true }, where: { @@ -288,7 +299,7 @@ export class PoolService { gt: 0.000000000001, }, }, - chain: networkContext.chain, + chain: this.chain, }, }); const chunks = _.chunk(pools, 10); @@ -308,10 +319,10 @@ export class PoolService { } public async loadReliquarySnapshotsForAllFarms() { - await prisma.prismaReliquaryTokenBalanceSnapshot.deleteMany({ where: { chain: networkContext.chain } }); - await prisma.prismaReliquaryLevelSnapshot.deleteMany({ where: { chain: networkContext.chain } }); - await prisma.prismaReliquaryFarmSnapshot.deleteMany({ where: { chain: networkContext.chain } }); - const farms = await prisma.prismaPoolStakingReliquaryFarm.findMany({ where: { chain: networkContext.chain } }); + await prisma.prismaReliquaryTokenBalanceSnapshot.deleteMany({ where: { chain: this.chain } }); + await prisma.prismaReliquaryLevelSnapshot.deleteMany({ where: { chain: this.chain } }); + await prisma.prismaReliquaryFarmSnapshot.deleteMany({ where: { chain: this.chain } }); + const farms = await prisma.prismaPoolStakingReliquaryFarm.findMany({ where: { chain: this.chain } }); const farmIds = farms.map((farm) => parseFloat(farm.id)); for (const farmId of farmIds) { await this.reliquarySnapshotService.loadAllSnapshotsForFarm(farmId); @@ -347,10 +358,10 @@ export class PoolService { } public async syncPoolVersionForAllPools() { - const subgraphPools = await balancerSubgraphService.getAllPools({}, false); + const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); for (const subgraphPool of subgraphPools) { await prisma.prismaPool.update({ - where: { id_chain: { chain: networkContext.chain, id: subgraphPool.id } }, + where: { id_chain: { chain: this.chain, id: subgraphPool.id } }, data: { version: subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1, }, @@ -360,7 +371,7 @@ export class PoolService { public async addToBlackList(poolId: string) { const category = await prisma.prismaPoolCategory.findFirst({ - where: { poolId, chain: networkContext.chain, category: 'BLACK_LISTED' }, + where: { poolId, chain: this.chain, category: 'BLACK_LISTED' }, }); if (category) { @@ -369,9 +380,9 @@ export class PoolService { await prisma.prismaPoolCategory.create({ data: { - id: `${networkContext.chain}-${poolId}-BLACK_LISTED`, + id: `${this.chain}-${poolId}-BLACK_LISTED`, category: 'BLACK_LISTED', - chain: networkContext.chain, + chain: this.chain, poolId, }, }); @@ -381,7 +392,7 @@ export class PoolService { await prisma.prismaPoolCategory.deleteMany({ where: { category: 'BLACK_LISTED', - chain: networkContext.chain, + chain: this.chain, poolId, }, }); @@ -389,87 +400,87 @@ export class PoolService { public async deletePool(poolId: string) { const pool = await prisma.prismaPool.findUniqueOrThrow({ - where: { id_chain: { id: poolId, chain: networkContext.chain } }, + where: { id_chain: { id: poolId, chain: this.chain } }, }); const poolTokens = await prisma.prismaPoolToken.findMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); const poolTokenIds = poolTokens.map((poolToken) => poolToken.id); const poolTokenAddresses = poolTokens.map((poolToken) => poolToken.address); await prisma.prismaPoolSnapshot.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaTokenType.deleteMany({ - where: { chain: networkContext.chain, tokenAddress: pool.address }, + where: { chain: this.chain, tokenAddress: pool.address }, }); await prisma.prismaUserWalletBalance.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolTokenDynamicData.deleteMany({ - where: { chain: networkContext.chain, poolTokenId: { in: poolTokenIds } }, + where: { chain: this.chain, poolTokenId: { in: poolTokenIds } }, }); await prisma.prismaTokenDynamicData.deleteMany({ - where: { chain: networkContext.chain, tokenAddress: { in: poolTokenAddresses } }, + where: { chain: this.chain, tokenAddress: { in: poolTokenAddresses } }, }); await prisma.prismaPoolToken.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolDynamicData.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolToken.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolLinearData.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolGyroData.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolExpandedTokens.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolLinearDynamicData.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolAprItem.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolSwap.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); const poolStaking = await prisma.prismaPoolStaking.findMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); for (const staking of poolStaking) { switch (staking.type) { case 'GAUGE': await prisma.prismaPoolStakingGaugeReward.deleteMany({ - where: { chain: networkContext.chain, gaugeId: staking.id }, + where: { chain: this.chain, gaugeId: staking.id }, }); // delete votingGauge entry before deleting the staking gauge let gauge = await prisma.prismaPoolStakingGauge.findFirst({ where: { - chain: networkContext.chain, + chain: this.chain, stakingId: staking.id, }, select: { @@ -479,30 +490,30 @@ export class PoolService { if (gauge && gauge.votingGauge) await prisma.prismaVotingGauge.deleteMany({ - where: { chain: networkContext.chain, id: gauge.votingGauge.id }, + where: { chain: this.chain, id: gauge.votingGauge.id }, }); await prisma.prismaPoolStakingGauge.deleteMany({ - where: { chain: networkContext.chain, stakingId: staking.id }, + where: { chain: this.chain, stakingId: staking.id }, }); break; case 'MASTER_CHEF': await prisma.prismaPoolStakingMasterChefFarmRewarder.deleteMany({ - where: { chain: networkContext.chain, farmId: staking.id }, + where: { chain: this.chain, farmId: staking.id }, }); await prisma.prismaPoolStakingMasterChefFarm.deleteMany({ - where: { chain: networkContext.chain, stakingId: staking.id }, + where: { chain: this.chain, stakingId: staking.id }, }); break; case 'RELIQUARY': await prisma.prismaPoolStakingReliquaryFarmLevel.deleteMany({ - where: { chain: networkContext.chain, farmId: staking.id.split('-')[1] }, + where: { chain: this.chain, farmId: staking.id.split('-')[1] }, }); await prisma.prismaPoolStakingReliquaryFarm.deleteMany({ - where: { chain: networkContext.chain, stakingId: staking.id }, + where: { chain: this.chain, stakingId: staking.id }, }); break; default: @@ -511,15 +522,15 @@ export class PoolService { } await prisma.prismaUserStakedBalance.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPoolStaking.deleteMany({ - where: { chain: networkContext.chain, poolId: poolId }, + where: { chain: this.chain, poolId: poolId }, }); await prisma.prismaPool.delete({ - where: { id_chain: { id: poolId, chain: networkContext.chain } }, + where: { id_chain: { id: poolId, chain: this.chain } }, }); } } @@ -527,11 +538,11 @@ export class PoolService { export const poolService = new PoolService( new PoolCreatorService(userService), new PoolOnChainDataService(tokenService), - new PoolUsdDataService(tokenService, blocksSubgraphService, balancerSubgraphService), + new PoolUsdDataService(tokenService, blocksSubgraphService), new PoolGqlLoaderService(), new PoolAprUpdaterService(), new PoolSyncService(), - new PoolSwapService(tokenService, balancerSubgraphService), - new PoolSnapshotService(balancerSubgraphService, coingeckoService), + new PoolSwapService(tokenService), + new PoolSnapshotService(coingeckoService), new ReliquarySnapshotService(reliquarySubgraphService), ); diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts index 43b779b58..0ca49121f 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph.service.ts @@ -44,16 +44,17 @@ import { subgraphLoadAll } from '../subgraph-util'; import { Cache, CacheClass } from 'memory-cache'; import { fiveMinutesInMs, twentyFourHoursInMs } from '../../common/time'; import { BalancerUserPoolShare } from './balancer-subgraph-types'; -import { networkContext } from '../../network/network-context.service'; const ALL_POOLS_CACHE_KEY = `balance-subgraph_all-pools`; const PORTFOLIO_POOLS_CACHE_KEY = `balance-subgraph_portfolio-pools`; export class BalancerSubgraphService { private cache: CacheClass; + private sdk: ReturnType; - constructor(private readonly subgraphUrl = networkContext.data.subgraphs.balancer) { + constructor(subgraphUrl: string, private chainId: number) { this.cache = new Cache(); + this.sdk = getSdk(new GraphQLClient(subgraphUrl)); } public async getMetadata() { @@ -265,7 +266,7 @@ export class BalancerSubgraphService { public async getPortfolioPoolsData(previousBlockNumber: number): Promise { const cached = this.cache.get( - `${PORTFOLIO_POOLS_CACHE_KEY}:${networkContext.chainId}`, + `${PORTFOLIO_POOLS_CACHE_KEY}:${this.chainId}`, ) as BalancerPortfolioPoolsDataQuery | null; if (cached) { @@ -274,13 +275,13 @@ export class BalancerSubgraphService { const portfolioPools = await this.sdk.BalancerPortfolioPoolsData({ previousBlockNumber }); - this.cache.put(`${PORTFOLIO_POOLS_CACHE_KEY}:${networkContext.chainId}`, portfolioPools, fiveMinutesInMs); + this.cache.put(`${PORTFOLIO_POOLS_CACHE_KEY}:${this.chainId}`, portfolioPools, fiveMinutesInMs); return portfolioPools; } public async getAllPoolsAtBlock(block: number): Promise { - const cached = this.cache.get(`${ALL_POOLS_CACHE_KEY}:${networkContext.chainId}:${block}`) as + const cached = this.cache.get(`${ALL_POOLS_CACHE_KEY}:${this.chainId}:${block}`) as | BalancerPoolFragment[] | null; @@ -294,7 +295,7 @@ export class BalancerSubgraphService { block: { number: block }, }); - this.cache.put(`${ALL_POOLS_CACHE_KEY}:${networkContext.chainId}:${block}`, pools, twentyFourHoursInMs); + this.cache.put(`${ALL_POOLS_CACHE_KEY}:${this.chainId}:${block}`, pools, twentyFourHoursInMs); return pools; } @@ -313,12 +314,6 @@ export class BalancerSubgraphService { return [...ampUpdates, ...gradualWeightUpdates].map((item) => item.poolId.id); } - private get sdk() { - const client = new GraphQLClient(this.subgraphUrl); - - return getSdk(client); - } - private normalizeBalancerUser(user: BalancerUserFragment): BalancerUserFragment { return { ...user, @@ -330,5 +325,3 @@ export class BalancerSubgraphService { }; } } - -export const balancerSubgraphService = new BalancerSubgraphService(); diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 52b1591ae..571418aa9 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -6,7 +6,6 @@ import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { BalancerUserPoolShare } from '../../subgraphs/balancer-subgraph/balancer-subgraph-types'; -import { balancerSubgraphService } from '../../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { beetsBarService } from '../../subgraphs/beets-bar-subgraph/beets-bar.service'; import { BeetsBarUserFragment } from '../../subgraphs/beets-bar-subgraph/generated/beets-bar-subgraph-types'; import { Multicaller, MulticallUserBalance } from '../../web3/multicaller'; @@ -15,9 +14,14 @@ import { networkContext } from '../../network/network-context.service'; export class UserSyncWalletBalanceService { constructor() {} + + private get balancerSubgraphService() { + return networkContext.services.balancerSubgraphService; + } + public async initBalancesForPools() { console.log('initBalancesForPools: loading balances, pools, block...'); - const { block } = await balancerSubgraphService.getMetadata(); + const { block } = await this.balancerSubgraphService.getMetadata(); let endBlock = block.number; console.log(`Loading balances at block ${endBlock}`); @@ -32,7 +36,7 @@ export class UserSyncWalletBalanceService { where: { dynamicData: { totalSharesNum: { gt: 0.000000000001 } }, chain: networkContext.chain }, }); const poolIdsToInit = pools.map((pool) => pool.id); - const shares = await balancerSubgraphService.getAllPoolSharesWithBalance(poolIdsToInit, [ + const shares = await this.balancerSubgraphService.getAllPoolSharesWithBalance(poolIdsToInit, [ AddressZero, networkContext.data.balancer.vault, ]); @@ -227,9 +231,9 @@ export class UserSyncWalletBalanceService { } public async initBalancesForPool(poolId: string) { - const { block } = await balancerSubgraphService.getMetadata(); + const { block } = await this.balancerSubgraphService.getMetadata(); - const shares = await balancerSubgraphService.getAllPoolSharesWithBalance([poolId], [AddressZero]); + const shares = await this.balancerSubgraphService.getAllPoolSharesWithBalance([poolId], [AddressZero]); await prismaBulkExecuteOperations( [ diff --git a/modules/user/user.service.ts b/modules/user/user.service.ts index f58c6104f..872e3c505 100644 --- a/modules/user/user.service.ts +++ b/modules/user/user.service.ts @@ -4,7 +4,6 @@ import { GqlPoolJoinExit, GqlPoolSwap, GqlUserSnapshotDataRange } from '../../sc import { coingeckoService } from '../coingecko/coingecko.service'; import { PoolSnapshotService } from '../pool/lib/pool-snapshot.service'; import { PoolSwapService } from '../pool/lib/pool-swap.service'; -import { balancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { reliquarySubgraphService } from '../subgraphs/reliquary-subgraph/reliquary.service'; import { userSnapshotSubgraphService } from '../subgraphs/user-snapshot-subgraph/user-snapshot-subgraph.service'; import { tokenService } from '../token/token.service'; @@ -143,10 +142,10 @@ export class UserService { export const userService = new UserService( new UserBalanceService(), new UserSyncWalletBalanceService(), - new PoolSwapService(tokenService, balancerSubgraphService), + new PoolSwapService(tokenService), new UserSnapshotService( userSnapshotSubgraphService, reliquarySubgraphService, - new PoolSnapshotService(balancerSubgraphService, coingeckoService), + new PoolSnapshotService(coingeckoService), ), ); From 81cfdadb3ef1e83498e6df39f6a41745c81e4bd0 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 10 Nov 2023 19:59:06 +0100 Subject: [PATCH 057/175] fix pool version syncing --- modules/pool/pool.service.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 015aed022..0acaf9f53 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -352,13 +352,20 @@ export class PoolService { public async syncPoolVersionForAllPools() { const subgraphPools = await balancerSubgraphService.getAllPools({}, false); + for (const subgraphPool of subgraphPools) { - await prisma.prismaPool.update({ - where: { id_chain: { chain: networkContext.chain, id: subgraphPool.id } }, - data: { - version: subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1, - }, - }); + try { + await prisma.prismaPool.update({ + where: { id_chain: { chain: networkContext.chain, id: subgraphPool.id } }, + data: { + version: subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1, + }, + }); + } catch(e: any) { + // Some pools are filtered from the DB, like test pools, + // so we just ignore them without breaking the loop + console.error(e.meta.cause, 'Network', networkContext.chain, 'Pool ID: ', subgraphPool.id); + } } } From cfc0f19e27108b6f0fbb90006df4df597dbd857d Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 10 Nov 2023 20:00:04 +0100 Subject: [PATCH 058/175] onchain syncing depends on network config --- .../pool/lib/pool-on-chain-data.service.ts | 52 ++++++++++++------- modules/pool/pool.service.ts | 2 +- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index e457f05a2..3c1ff916a 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -8,6 +8,7 @@ import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { fetchOnChainPoolState } from './pool-onchain-state'; import { fetchOnChainPoolData } from './pool-onchain-data'; import { fetchOnChainGyroFees } from './pool-onchain-gyro-fee'; +import { networkContext } from '../../network/network-context.service'; const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ 'WEIGHTED', @@ -26,19 +27,25 @@ const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ export class PoolOnChainDataService { constructor( private readonly tokenService: TokenService, - private chain: Chain, - private readonly vaultAddress: string, - private readonly yieldProtocolFeePercentage: number, - private readonly gyroConfig?: string, ) {} + private get options() { + return { + chain: networkContext.chain, + vaultAddress: networkContext.data.balancer.vault, + yieldProtocolFeePercentage: networkContext.data.balancer.yieldProtocolFeePercentage, + gyroConfig: networkContext.data.gyro?.config, + composableStableFactories: networkContext.data.balancer.composableStablePoolFactories, + } + } + public async updateOnChainStatus(poolIds: string[]): Promise { if (poolIds.length === 0) return; const filteredPools = await prisma.prismaPool.findMany({ where: { id: { in: poolIds }, - chain: this.chain, + chain: this.options.chain, type: { in: SUPPORTED_POOL_TYPES }, }, include: { @@ -54,7 +61,7 @@ export class PoolOnChainDataService { if (pool.dynamicData) { operations.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: this.chain } }, + where: { id_chain: { id: pool.id, chain: this.options.chain } }, data: { isPaused, isInRecoveryMode, @@ -74,7 +81,7 @@ export class PoolOnChainDataService { const filteredPools = await prisma.prismaPool.findMany({ where: { id: { in: poolIds }, - chain: this.chain, + chain: this.options.chain, type: { in: SUPPORTED_POOL_TYPES }, }, include: { @@ -87,12 +94,20 @@ export class PoolOnChainDataService { }); const gyroPools = filteredPools.filter((pool) => pool.type.includes('GYRO')); + const poolsWithComposableStableType = filteredPools.map((pool) => ({ + ...pool, + type: ( + pool.factory && this.options.composableStableFactories.includes(pool.factory) + ? 'COMPOSABLE_STABLE' + : pool.type + ) as PrismaPoolType | 'COMPOSABLE_STABLE', + })); const tokenPrices = await this.tokenService.getTokenPrices(); - const onchainResults = await fetchOnChainPoolData(filteredPools, this.vaultAddress, 1024); + const onchainResults = await fetchOnChainPoolData(poolsWithComposableStableType, this.options.vaultAddress, 1024); const gyroFees = await ( - this.gyroConfig - ? fetchOnChainGyroFees(gyroPools, this.gyroConfig, 1024) + this.options.gyroConfig + ? fetchOnChainGyroFees(gyroPools, this.options.gyroConfig, 1024) : Promise.resolve({} as { [address: string]: string }) ); @@ -104,7 +119,6 @@ export class PoolOnChainDataService { try { if (isStablePool(pool.type)) { if (!amp) { - console.log('onchain data', onchainData); console.error(`Stable Pool Missing Amp: ${pool.id}`); continue; } @@ -113,8 +127,8 @@ export class PoolOnChainDataService { if (!pool.stableDynamicData || pool.stableDynamicData.amp !== amp) { operations.push( prisma.prismaPoolStableDynamicData.upsert({ - where: { id_chain: { id: pool.id, chain: this.chain } }, - create: { id: pool.id, chain: this.chain, poolId: pool.id, amp, blockNumber }, + where: { id_chain: { id: pool.id, chain: this.options.chain } }, + create: { id: pool.id, chain: this.options.chain, poolId: pool.id, amp, blockNumber }, update: { amp, blockNumber }, }) ); @@ -136,10 +150,10 @@ export class PoolOnChainDataService { ) { operations.push( prisma.prismaPoolLinearDynamicData.upsert({ - where: { id_chain: { id: pool.id, chain: this.chain } }, + where: { id_chain: { id: pool.id, chain: this.options.chain } }, create: { id: pool.id, - chain: this.chain, + chain: this.options.chain, poolId: pool.id, upperTarget, lowerTarget, @@ -161,7 +175,7 @@ export class PoolOnChainDataService { const yieldProtocolFeePercentage = gyroFees[pool.id] || onchainData.protocolYieldFeePercentageCache || - String(this.yieldProtocolFeePercentage); + String(this.options.yieldProtocolFeePercentage); if ( pool.dynamicData && @@ -172,7 +186,7 @@ export class PoolOnChainDataService { ) { operations.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: this.chain } }, + where: { id_chain: { id: pool.id, chain: this.options.chain } }, data: { swapFee, totalShares, @@ -228,10 +242,10 @@ export class PoolOnChainDataService { ) { operations.push( prisma.prismaPoolTokenDynamicData.upsert({ - where: { id_chain: { id: poolToken.id, chain: this.chain } }, + where: { id_chain: { id: poolToken.id, chain: this.options.chain } }, create: { id: poolToken.id, - chain: this.chain, + chain: this.options.chain, poolTokenId: poolToken.id, blockNumber, priceRate, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 0acaf9f53..eea337780 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -537,7 +537,7 @@ export class PoolService { export const poolService = new PoolService( new PoolCreatorService(userService), - new PoolOnChainDataService(tokenService, networkContext.chain, networkContext.data.balancer.vault, networkContext.data.balancer.yieldProtocolFeePercentage, networkContext.data.gyro?.config), + new PoolOnChainDataService(tokenService), new PoolUsdDataService(tokenService, blocksSubgraphService, balancerSubgraphService), new PoolGqlLoaderService(), new PoolAprUpdaterService(), From e12c4cfa0079613e90f6aba6745cfb0c9eb7b54c Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 10 Nov 2023 20:03:22 +0100 Subject: [PATCH 059/175] totalSupply depends on type --- modules/pool/lib/pool-onchain-data.ts | 35 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index 31118eab2..44940826f 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -46,24 +46,36 @@ const getSwapFeeFn = (type: string) => { } }; +const getTotalSupplyFn = (type: PoolInput['type'], version: number) => { + if (['LINEAR', 'PHANTOM_STABLE'].includes(type)) { + return 'getVirtualSupply'; + } else if ( + type === 'COMPOSABLE_STABLE' + || (type === 'WEIGHTED' && version > 1) + || (type === 'GYROE' && version > 1) + || (type === 'UNKNOWN' && version > 1)) { + return 'getActualSupply'; + } else { + return 'totalSupply'; + } +} + interface PoolInput { id: string; address: string; - type: PrismaPoolType; + type: PrismaPoolType | 'COMPOSABLE_STABLE'; tokens: { address: string, token: { decimals: number, } }[]; - version?: number; + version: number; } interface OnchainData { poolTokens: [string[], BigNumber[]]; totalSupply: BigNumber; - virtualSupply?: BigNumber; - actualSupply?: BigNumber; swapFee: BigNumber; swapEnabled?: boolean; protocolYieldFeePercentageCache?: BigNumber; @@ -78,14 +90,12 @@ interface OnchainData { } const defaultCalls = ( - { id, address, type }: PoolInput, + { id, address, type, version }: PoolInput, vaultAddress: string, multicaller: Multicaller3 ) => { multicaller.call(`${id}.poolTokens`, vaultAddress, 'getPoolTokens', [id]); - multicaller.call(`${id}.totalSupply`, address, 'totalSupply'); - multicaller.call(`${id}.virtualSupply`, address, 'getVirtualSupply'); - multicaller.call(`${id}.actualSupply`, address, 'getActualSupply'); + multicaller.call(`${id}.totalSupply`, address, getTotalSupplyFn(type, version)); multicaller.call(`${id}.swapFee`, address, getSwapFeeFn(type)); multicaller.call(`${id}.rate`, address, 'getRate'); multicaller.call(`${id}.protocolYieldFeePercentageCache`, address, 'getProtocolFeePercentageCache', [2]); @@ -133,7 +143,7 @@ const gyroECalls = ( multicaller.call(`${id}.tokenRates`, address, 'getTokenRates'); }; -const poolTypeCalls = (type: PrismaPoolType, version = 1) => { +const poolTypeCalls = (type: PoolInput['type'], version = 1) => { const do_nothing = () => ({}); switch (type) { case 'WEIGHTED': @@ -144,12 +154,13 @@ const poolTypeCalls = (type: PrismaPoolType, version = 1) => { case 'STABLE': case 'PHANTOM_STABLE': case 'META_STABLE': + case 'COMPOSABLE_STABLE': return stableCalls; case 'GYROE': if (version === 2) { - return gyroECalls; + return gyroECalls; } else { - return do_nothing; + return do_nothing; } case 'LINEAR': return linearCalls; @@ -161,7 +172,7 @@ const poolTypeCalls = (type: PrismaPoolType, version = 1) => { const parse = (result: OnchainData, decimalsLookup: { [address: string]: number }) => ({ amp: result.amp ? formatFixed(result.amp[0], String(result.amp[2]).length - 1) : undefined, swapFee: formatEther(result.swapFee ?? '0'), - totalShares: formatEther(result.actualSupply || result.virtualSupply || result.totalSupply || '0'), + totalShares: formatEther(result.totalSupply || '0'), weights: result.weights?.map(formatEther), targets: result.targets?.map(String), poolTokens: result.poolTokens ? { From c157f3c43190f22069c8619c3e0421a0f0739cc0 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:24:45 +0100 Subject: [PATCH 060/175] fetching metastable rates case --- .../pool/lib/pool-on-chain-data.service.ts | 7 +---- modules/pool/lib/pool-onchain-data.ts | 27 +++++++++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 3c1ff916a..0f54decb7 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -217,12 +217,7 @@ export class PoolOnChainDataService { // set token price rate for various rate types // top level token rates, e.g. LSTs in pools - let priceRate = poolTokens.rates[i] || (onchainData.tokenRates[i] ?? '1.0'); - - // metastable pools - if (onchainData.metaPriceRateCache && onchainData.metaPriceRateCache[i]) { - priceRate = onchainData.metaPriceRateCache[i]; - } + let priceRate = poolTokens.rates[i] ?? '1.0'; // bpt price rate if (onchainData.rate && isSameAddress(poolToken.address, pool.address)) { diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index 44940826f..4fbdf8e3f 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -125,15 +125,20 @@ const linearCalls = ( }; const stableCalls = ( - { id, address, tokens }: PoolInput, + { id, address, type, tokens }: PoolInput, multicaller: Multicaller3 ) => { multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); - tokens.forEach(({ address: tokenAddress }, i) => { - multicaller.call(`${id}.tokenRate[${i}]`, address, 'getTokenRate', [tokenAddress]); - multicaller.call(`${id}.metaPriceRateCache[${i}]`, address, 'getPriceRateCache', [tokenAddress]); - }); + if (type === 'META_STABLE') { + tokens.forEach(({ address: tokenAddress }, i) => { + multicaller.call(`${id}.metaPriceRateCache[${i}]`, address, 'getPriceRateCache', [tokenAddress]); + }); + } else { + tokens.forEach(({ address: tokenAddress }, i) => { + multicaller.call(`${id}.tokenRate[${i}]`, address, 'getTokenRate', [tokenAddress]); + }); + } }; const gyroECalls = ( @@ -178,14 +183,20 @@ const parse = (result: OnchainData, decimalsLookup: { [address: string]: number poolTokens: result.poolTokens ? { tokens: result.poolTokens[0].map((token) => token.toLowerCase()), balances: result.poolTokens[1].map((balance, i) => formatUnits(balance, decimalsLookup[result.poolTokens[0][i].toLowerCase()])), - rates: result.poolTokens[0].map((_, i) => result.tokenRate && result.tokenRate[i] ? formatEther(result.tokenRate[i]) : undefined) + rates: result.poolTokens[0].map((_, i) => + result.tokenRate && result.tokenRate[i] + ? formatEther(result.tokenRate[i]) + : result.tokenRates && result.tokenRates[i] + ? formatEther(result.tokenRates[i]) + : result.metaPriceRateCache && result.metaPriceRateCache[i][0].gt(0) + ? formatEther(result.metaPriceRateCache[i][0]) + : undefined + ) } : { tokens: [], balances: [], rates: [] }, wrappedTokenRate: result.wrappedTokenRate ? formatEther(result.wrappedTokenRate) : '1.0', rate: result.rate ? formatEther(result.rate) : '1.0', swapEnabled: result.swapEnabled, protocolYieldFeePercentageCache: result.protocolYieldFeePercentageCache ? formatEther(result.protocolYieldFeePercentageCache) : undefined, - tokenRates: result.tokenRates?.map((tokenRate) => tokenRate ? formatEther(tokenRate) : '1.0') || [], - metaPriceRateCache: result.metaPriceRateCache?.length ? result.metaPriceRateCache.map((r) => r ? r[0].gt(0) ? formatEther(r[0]) : '1.0' : r) : undefined }); export const fetchOnChainPoolData = async ( From 96e8e81ef14f7916bc0b2c9a20ec9f1437cd2d49 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:28:55 +0100 Subject: [PATCH 061/175] type checking cleanup --- modules/pool/lib/pool-onchain-data.ts | 86 ++++++++++++++------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index 4fbdf8e3f..2ee091af4 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -15,6 +15,35 @@ import MetaStablePoolAbi from '../abi/MetaStablePool.json'; import StablePhantomPoolAbi from '../abi/StablePhantomPool.json'; import { JsonFragment } from '@ethersproject/abi'; +interface PoolInput { + id: string; + address: string; + type: PrismaPoolType | 'COMPOSABLE_STABLE'; + tokens: { + address: string, + token: { + decimals: number, + } + }[]; + version: number; +} + +interface OnchainData { + poolTokens: [string[], BigNumber[]]; + totalSupply: BigNumber; + swapFee: BigNumber; + swapEnabled?: boolean; + protocolYieldFeePercentageCache?: BigNumber; + rate?: BigNumber; + weights?: BigNumber[]; + targets?: [BigNumber, BigNumber]; + wrappedTokenRate?: BigNumber; + amp?: [BigNumber, boolean, BigNumber]; + tokenRates?: [BigNumber, BigNumber]; + tokenRate?: BigNumber[]; + metaPriceRateCache?: [BigNumber, BigNumber, BigNumber][]; +} + const abi: JsonFragment[] = Object.values( // Remove duplicate entries using their names Object.fromEntries( @@ -60,35 +89,6 @@ const getTotalSupplyFn = (type: PoolInput['type'], version: number) => { } } -interface PoolInput { - id: string; - address: string; - type: PrismaPoolType | 'COMPOSABLE_STABLE'; - tokens: { - address: string, - token: { - decimals: number, - } - }[]; - version: number; -} - -interface OnchainData { - poolTokens: [string[], BigNumber[]]; - totalSupply: BigNumber; - swapFee: BigNumber; - swapEnabled?: boolean; - protocolYieldFeePercentageCache?: BigNumber; - rate?: BigNumber; - weights?: BigNumber[]; - targets?: [BigNumber, BigNumber]; - wrappedTokenRate?: BigNumber; - amp?: [BigNumber, boolean, BigNumber]; - tokenRates?: [BigNumber, BigNumber]; - tokenRate?: BigNumber[]; - metaPriceRateCache?: [BigNumber, BigNumber, BigNumber][]; -} - const defaultCalls = ( { id, address, type, version }: PoolInput, vaultAddress: string, @@ -125,22 +125,27 @@ const linearCalls = ( }; const stableCalls = ( - { id, address, type, tokens }: PoolInput, + { id, address, tokens }: PoolInput, multicaller: Multicaller3 ) => { multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); - if (type === 'META_STABLE') { - tokens.forEach(({ address: tokenAddress }, i) => { - multicaller.call(`${id}.metaPriceRateCache[${i}]`, address, 'getPriceRateCache', [tokenAddress]); - }); - } else { - tokens.forEach(({ address: tokenAddress }, i) => { - multicaller.call(`${id}.tokenRate[${i}]`, address, 'getTokenRate', [tokenAddress]); - }); - } + tokens.forEach(({ address: tokenAddress }, i) => { + multicaller.call(`${id}.tokenRate[${i}]`, address, 'getTokenRate', [tokenAddress]); + }); }; +const metaStableCalls = ( + { id, address, tokens }: PoolInput, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); + + tokens.forEach(({ address: tokenAddress }, i) => { + multicaller.call(`${id}.metaPriceRateCache[${i}]`, address, 'getPriceRateCache', [tokenAddress]); + }); +} + const gyroECalls = ( { id, address }: PoolInput, multicaller: Multicaller3 @@ -158,9 +163,10 @@ const poolTypeCalls = (type: PoolInput['type'], version = 1) => { return lbpAndInvestmentCalls; case 'STABLE': case 'PHANTOM_STABLE': - case 'META_STABLE': case 'COMPOSABLE_STABLE': return stableCalls; + case 'META_STABLE': + return metaStableCalls; case 'GYROE': if (version === 2) { return gyroECalls; From 18980a153d77a3dd6b5a02556dfdfc4a995c7f89 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 14 Nov 2023 16:56:12 +0100 Subject: [PATCH 062/175] vebal sync error handling --- modules/vebal/vebal-voting-list.service.ts | 20 ++++++++++++++++---- modules/vebal/voting-gauges.repository.ts | 18 ++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/modules/vebal/vebal-voting-list.service.ts b/modules/vebal/vebal-voting-list.service.ts index 8f8abec59..3f78e2061 100644 --- a/modules/vebal/vebal-voting-list.service.ts +++ b/modules/vebal/vebal-voting-list.service.ts @@ -135,8 +135,10 @@ export class VeBalVotingListService { async sync(votingGaugeAddresses: string[]) { const chunks = chunk(votingGaugeAddresses, 50); + const syncErrors: Error[] = []; for (const addressChunk of chunks) { - const votingGauges = await this.fetchVotingGauges(addressChunk); + const { votingGauges, errors } = await this.fetchVotingGauges(addressChunk); + syncErrors.push(...errors); /* We avoid saving gauges in specialVotingGaugeAddresses because they require special handling @@ -145,20 +147,30 @@ export class VeBalVotingListService { (gauge) => !specialVotingGaugeAddresses.includes(gauge.gaugeAddress), ); - await this.votingGauges.saveVotingGauges(cleanVotingGauges); + const { saveErrors } = await this.votingGauges.saveVotingGauges(cleanVotingGauges); + syncErrors.push(...saveErrors); + } + if (syncErrors.length > 0) { + throw new Error(`Errors while syncing voting gauges: ${syncErrors.map((error) => error.message)}`); } } async fetchVotingGauges(votingGaugeAddresses: string[]) { + const errors: Error[] = []; + const subgraphGauges = await this.votingGauges.fetchVotingGaugesFromSubgraph(votingGaugeAddresses); const onchainGauges = await this.votingGauges.fetchOnchainVotingGauges(votingGaugeAddresses); const votingGauges = this.votingGauges.updateOnchainGaugesWithSubgraphData(onchainGauges, subgraphGauges); - this.throwIfMissingVotingGaugeData(votingGauges); + try { + this.throwIfMissingVotingGaugeData(votingGauges); + } catch (error) { + errors.push(error as Error); + } - return votingGauges; + return { votingGauges, errors }; } throwIfMissingVotingGaugeData(votingGauges: VotingGauge[]) { diff --git a/modules/vebal/voting-gauges.repository.ts b/modules/vebal/voting-gauges.repository.ts index 4485a35bf..78d16a8c7 100644 --- a/modules/vebal/voting-gauges.repository.ts +++ b/modules/vebal/voting-gauges.repository.ts @@ -119,16 +119,22 @@ export class VotingGaugesRepository { } async saveVotingGauges(votingGauges: VotingGauge[]) { - const votingGaugesWithStakingGaugeId = Promise.all( + const saveErrors: Error[] = []; + const votingGaugesWithStakingGaugeId = await Promise.all( votingGauges.map(async (gauge) => { - const stakingId = await this.findStakingGaugeId(gauge); - gauge.stakingGaugeId = stakingId; - await this.saveVotingGauge(gauge); - return gauge; + try { + const stakingId = await this.findStakingGaugeId(gauge); + gauge.stakingGaugeId = stakingId; + await this.saveVotingGauge(gauge); + return gauge; + } catch (error) { + saveErrors.push(error as Error); + return gauge; + } }), ); - return votingGaugesWithStakingGaugeId; + return { votingGaugesWithStakingGaugeId, saveErrors }; } async saveVotingGauge(gauge: VotingGauge) { From 8e571a571f124f6e77c44b1f4bcb80083ca40f4e Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 14 Nov 2023 17:05:04 +0100 Subject: [PATCH 063/175] fix test --- modules/vebal/debug-voting-list.spec.ts | 2 +- modules/vebal/voting-gauges.repository.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/vebal/debug-voting-list.spec.ts b/modules/vebal/debug-voting-list.spec.ts index 4a9f0331a..21874ed6e 100644 --- a/modules/vebal/debug-voting-list.spec.ts +++ b/modules/vebal/debug-voting-list.spec.ts @@ -286,7 +286,7 @@ it('Uses streamer-v1-map to find gauges (that use streamer instead of recipient) const service = new VeBalVotingListService(); const rootGaugeAddresses = [oldRootV1GaugeAddress, anotherOldRootV1GaugeAddress]; - const fetchedVotingGauges = await service.fetchVotingGauges(rootGaugeAddresses); + const { votingGauges: fetchedVotingGauges } = await service.fetchVotingGauges(rootGaugeAddresses); const repository = new VotingGaugesRepository(); const savedGauges = await repository.saveVotingGauges(fetchedVotingGauges); diff --git a/modules/vebal/voting-gauges.repository.spec.ts b/modules/vebal/voting-gauges.repository.spec.ts index 46a8115f0..941705780 100644 --- a/modules/vebal/voting-gauges.repository.spec.ts +++ b/modules/vebal/voting-gauges.repository.spec.ts @@ -107,7 +107,7 @@ const repository = new VotingGaugesRepository(prismaMock); it('successfully saves onchain gauges', async () => { const votingGauge = aVotingGauge({ network: Chain.OPTIMISM }); - const votingGauges = await repository.saveVotingGauges([votingGauge]); + const { votingGaugesWithStakingGaugeId: votingGauges } = await repository.saveVotingGauges([votingGauge]); expect(votingGauges[0]).toMatchObject(votingGauge); expect(votingGauges[0].stakingGaugeId).toBe(defaultStakingGaugeId); From c6ce462d00d6767e7d271ed8dccaccf89f792cac Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 14 Nov 2023 21:11:04 +0100 Subject: [PATCH 064/175] generically parse apr group --- modules/pool/lib/pool-gql-loader.service.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 85e841c54..6fe369657 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -751,18 +751,7 @@ export class PoolGqlLoaderService { }), ); const apr = _.sumBy(items, 'apr'); - let title = ''; - - switch (group) { - case 'YEARN': - title = 'Yearn boosted APR'; - break; - case 'REAPER': - title = 'Reaper boosted APR'; - break; - case 'OVERNIGHT': - title = 'Overnight boosted APR'; - } + const title = `${group.charAt(0) + group.slice(1).toLowerCase()} boosted APR`; return { id: `${pool.id}-${group}`, From 7539083449a63c42d3f3a6e1ec81c2a1bfe1d527 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 15 Nov 2023 12:43:05 +0100 Subject: [PATCH 065/175] safe iteration --- modules/pool/lib/pool-swap.service.ts | 59 +++++++++++++-------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/modules/pool/lib/pool-swap.service.ts b/modules/pool/lib/pool-swap.service.ts index 58bf051ea..1c975a2f9 100644 --- a/modules/pool/lib/pool-swap.service.ts +++ b/modules/pool/lib/pool-swap.service.ts @@ -22,9 +22,7 @@ import * as Sentry from '@sentry/node'; import { AllNetworkConfigsKeyedOnChain } from '../../network/network-config'; export class PoolSwapService { - constructor( - private readonly tokenService: TokenService, - ) {} + constructor(private readonly tokenService: TokenService) {} private get balancerSubgraphService() { return networkContext.services.balancerSubgraphService; @@ -39,30 +37,31 @@ export class PoolSwapService { const allChainsJoinExits: GqlPoolJoinExit[] = []; - for (const chain of args.where!.chainIn!) { - const balancerSubgraphService = - AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; - - const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ - where: { pool_in: args.where?.poolIdIn }, - first, - skip: args.skip, - orderBy: JoinExit_OrderBy.Timestamp, - orderDirection: OrderDirection.Desc, - }); - - const mappedJoinExits: GqlPoolJoinExit[] = joinExits.map((joinExit) => ({ - ...joinExit, - __typename: 'GqlPoolJoinExit', - chain: chain, - poolId: joinExit.pool.id, - amounts: joinExit.amounts.map((amount, index) => ({ - address: joinExit.pool.tokensList[index], - amount, - })), - })); - - allChainsJoinExits.push(...mappedJoinExits); + if (args.where?.chainIn) { + for (const chain of args.where.chainIn) { + const balancerSubgraphService = AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; + + const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ + where: { pool_in: args.where?.poolIdIn }, + first, + skip: args.skip, + orderBy: JoinExit_OrderBy.Timestamp, + orderDirection: OrderDirection.Desc, + }); + + const mappedJoinExits: GqlPoolJoinExit[] = joinExits.map((joinExit) => ({ + ...joinExit, + __typename: 'GqlPoolJoinExit', + chain: chain, + poolId: joinExit.pool.id, + amounts: joinExit.amounts.map((amount, index) => ({ + address: joinExit.pool.tokensList[index], + amount, + })), + })); + + allChainsJoinExits.push(...mappedJoinExits); + } } return allChainsJoinExits; @@ -75,8 +74,7 @@ export class PoolSwapService { first = 10, skip = 0, ): Promise { - const balancerSubgraphService = - AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; + const balancerSubgraphService = AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; const { joinExits } = await balancerSubgraphService.getPoolJoinExits({ where: { pool: poolId, user: userAddress }, @@ -126,8 +124,7 @@ export class PoolSwapService { first = 10, skip = 0, ): Promise { - const balancerSubgraphService = - AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; + const balancerSubgraphService = AllNetworkConfigsKeyedOnChain[chain].services.balancerSubgraphService; const result = await balancerSubgraphService.getSwaps({ first, From 10cce4bf0bb541d6fe0c54212df2cb58b9f9f357 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 15 Nov 2023 13:32:13 +0100 Subject: [PATCH 066/175] more specific errors for chain param --- modules/pool/pool.resolvers.ts | 14 +++++++------- modules/user/user.resolvers.ts | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 3a88f526c..e9fe7431b 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -12,7 +12,7 @@ const balancerResolvers: Resolvers = { if (!chain && currentChain) { chain = currentChain; } else if (!chain) { - throw new Error('Chain is required'); + throw new Error('poolGetPool error: Provide "chain" param'); } return poolService.getGqlPool(id, chain); }, @@ -27,7 +27,7 @@ const balancerResolvers: Resolvers = { if (!args.where?.chainIn && currentChain) { args.where = { ...args.where, chainIn: [currentChain] }; } else if (!args.where?.chainIn) { - throw new Error('Chain is required'); + throw new Error('poolGetSwaps error: Provide "where.chainIn" param'); } return poolService.getPoolSwaps(args); }, @@ -36,7 +36,7 @@ const balancerResolvers: Resolvers = { if (!args.where?.chainIn && currentChain) { args.where = { ...args.where, chainIn: [currentChain] }; } else if (!args.where?.chainIn) { - throw new Error('Chain is required'); + throw new Error('poolGetBatchSwaps error: Provide "where.chainIn" param'); } return poolService.getPoolBatchSwaps(args); }, @@ -45,7 +45,7 @@ const balancerResolvers: Resolvers = { if (!args.where?.chainIn && currentChain) { args.where = { ...args.where, chainIn: [currentChain] }; } else if (!args.where?.chainIn) { - throw new Error('Chain is required'); + throw new Error('poolGetJoinExits error: Provide "where.chainIn" param'); } return poolService.getPoolJoinExits(args); }, @@ -57,7 +57,7 @@ const balancerResolvers: Resolvers = { if (!chain && currentChain) { chain = currentChain; } else if (!chain) { - throw new Error('Chain is required'); + throw new Error('poolGetSnapshots error: Provide "chain" param'); } const snapshots = await poolService.getSnapshotsForPool(id, chain, range); @@ -78,7 +78,7 @@ const balancerResolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + throw new Error('poolGetAllPoolsSnapshots error: Provide "chains" param'); } const snapshots = await poolService.getSnapshotsForAllPools(chains, range); @@ -99,7 +99,7 @@ const balancerResolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + throw new Error('poolGetLinearPools error: Provide "chains" param'); } return poolService.getGqlLinearPools(chains); }, diff --git a/modules/user/user.resolvers.ts b/modules/user/user.resolvers.ts index e94480d62..8912e1186 100644 --- a/modules/user/user.resolvers.ts +++ b/modules/user/user.resolvers.ts @@ -11,7 +11,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + throw new Error('userGetPoolBalances error: Provide "chains" param'); } const accountAddress = address || getRequiredAccountAddress(context); const tokenPrices = await tokenService.getTokenPricesForChains(chains); @@ -27,7 +27,7 @@ const resolvers: Resolvers = { if (!chain && currentChain) { chain = currentChain; } else if (!chain) { - throw new Error('Chain is required'); + throw new Error('userGetPoolJoinExits error: Provide "chain" param'); } const accountAddress = address || getRequiredAccountAddress(context); @@ -38,7 +38,7 @@ const resolvers: Resolvers = { if (!chain && currentChain) { chain = currentChain; } else if (!chain) { - throw new Error('Chain is required'); + throw new Error('userGetSwaps error: Provide "chain" param'); } const accountAddress = address || getRequiredAccountAddress(context); return userService.getUserSwaps(accountAddress, poolId, chain, first, skip); @@ -48,7 +48,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + throw new Error('userGetStaking error: Provide "chains" param'); } const accountAddress = address || getRequiredAccountAddress(context); From ef157748c11a3f2818417bd0c4303f4813251979 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 15 Nov 2023 13:44:38 +0100 Subject: [PATCH 067/175] better errors for token queries --- modules/token/token.resolvers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index b5c54cafe..8bc79db4c 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -11,7 +11,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + throw new Error('tokenGetTokens error: Provide "chains" param'); } return tokenService.getTokenDefinitions(chains); }, @@ -20,7 +20,7 @@ const resolvers: Resolvers = { if (!chains && currentChain) { chains = [currentChain]; } else if (!chains) { - throw new Error('Chain is required'); + throw new Error('tokenGetCurrentPrices error: Provide "chains" param'); } const prices = await tokenService.getWhiteListedTokenPrices(chains); From f8b30b7eb39057cd741dac834f2df4ede9ebcb75 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 15 Nov 2023 17:34:24 +0100 Subject: [PATCH 068/175] add userAddress to where --- modules/pool/lib/pool-gql-loader.service.ts | 2 +- modules/pool/pool.gql | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 6b71bb69f..4d965269e 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -132,7 +132,7 @@ export class PoolGqlLoaderService { private mapQueryArgsToPoolQuery(args: QueryPoolGetPoolsArgs): Prisma.PrismaPoolFindManyArgs { let orderBy: Prisma.PrismaPoolOrderByWithRelationInput = {}; const orderDirection = args.orderDirection || undefined; - const userAddress = args.userAddress; + const userAddress = args.where?.userAddress; switch (args.orderBy) { case 'totalLiquidity': diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 36fee06ac..219dea792 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -7,7 +7,6 @@ extend type Query { orderDirection: GqlPoolOrderDirection where: GqlPoolFilter textSearch: String - userAddress: String ): [GqlPoolMinimal!]! poolGetPoolsCount( first: Int @@ -16,7 +15,6 @@ extend type Query { orderDirection: GqlPoolOrderDirection where: GqlPoolFilter textSearch: String - userAddress: String ): Int! poolGetSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolSwap!]! poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! @@ -618,6 +616,7 @@ input GqlPoolFilter { chainIn: [GqlChain!] chainNotIn: [GqlChain!] createTime: GqlPoolTimePeriod + userAddress: String } enum GqlPoolFilterCategory { From 5c33a234089f91fb86b543b114ac5f4789974ea2 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:08:42 +0100 Subject: [PATCH 069/175] isolating networkContext.chain dependency --- modules/pool/lib/pool-creator.service.ts | 34 +++++++++++++---------- modules/pool/lib/pool-snapshot.service.ts | 6 ++-- modules/pool/lib/pool-usd-data.service.ts | 34 +++++++++++++---------- modules/user/lib/user-snapshot.service.ts | 11 ++++---- tsconfig.json | 4 ++- 5 files changed, 50 insertions(+), 39 deletions(-) diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index fb22dc4fb..2b632c652 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -15,8 +15,12 @@ export class PoolCreatorService { return networkContext.services.balancerSubgraphService; } + private get chain() { + return networkContext.chain + } + public async syncAllPoolsFromSubgraph(blockNumber: number): Promise { - const existingPools = await prisma.prismaPool.findMany({ where: { chain: networkContext.chain } }); + const existingPools = await prisma.prismaPool.findMany({ where: { chain: this.chain } }); const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); const sortedSubgraphPools = this.sortSubgraphPools(subgraphPools); @@ -39,11 +43,11 @@ export class PoolCreatorService { } public async syncNewPoolsFromSubgraph(blockNumber: number): Promise { - const existingPools = await prisma.prismaPool.findMany({ where: { chain: networkContext.chain } }); + const existingPools = await prisma.prismaPool.findMany({ where: { chain: this.chain } }); const latest = await prisma.prismaPool.findFirst({ orderBy: { createTime: 'desc' }, select: { createTime: true }, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); const subgraphPools = await this.balancerSubgraphService.getAllPools( @@ -93,7 +97,7 @@ export class PoolCreatorService { if (nestedPool) { await prisma.prismaPoolToken.update({ - where: { id_chain: { id: token.id, chain: networkContext.chain } }, + where: { id_chain: { id: token.id, chain: this.chain } }, data: { nestedPoolId: nestedPool.id }, }); } @@ -106,12 +110,12 @@ export class PoolCreatorService { let operations: any[] = []; const pools = await prisma.prismaPool.findMany({ ...prismaPoolWithExpandedNesting, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); //clear any existing await prisma.prismaPoolExpandedTokens.updateMany({ - where: { chain: networkContext.chain }, + where: { chain: this.chain }, data: { nestedPoolId: null }, }); @@ -141,7 +145,7 @@ export class PoolCreatorService { tokenAddress_poolId_chain: { tokenAddress: token.address, poolId: pool.id, - chain: networkContext.chain, + chain: this.chain, }, }, data: { nestedPoolId: token.nestedPoolId }, @@ -159,7 +163,7 @@ export class PoolCreatorService { const allNestedTypePools = await prisma.prismaPool.findMany({ where: { - chain: networkContext.chain, + chain: this.chain, type: { in: [PrismaPoolType.LINEAR, PrismaPoolType.PHANTOM_STABLE] }, }, select: { id: true, address: true }, @@ -173,14 +177,14 @@ export class PoolCreatorService { symbol: token.symbol, name: token.name, decimals: token.decimals, - chain: networkContext.chain, + chain: this.chain, })), { address: pool.address, symbol: pool.symbol || '', name: pool.name || '', decimals: 18, - chain: networkContext.chain, + chain: this.chain, }, ], }); @@ -188,7 +192,7 @@ export class PoolCreatorService { await prisma.prismaPool.create({ data: { id: pool.id, - chain: networkContext.chain, + chain: this.chain, createTime: pool.createTime, address: pool.address, symbol: pool.symbol || '', @@ -286,7 +290,7 @@ export class PoolCreatorService { await prisma.prismaPoolTokenDynamicData.createMany({ data: poolTokens.map((token) => ({ id: token.id, - chain: networkContext.chain, + chain: this.chain, poolTokenId: token.id, blockNumber, priceRate: token.priceRate || '1.0', @@ -303,7 +307,7 @@ export class PoolCreatorService { public async createAllTokensRelationshipForPool(poolId: string): Promise { const pool = await prisma.prismaPool.findUnique({ ...prismaPoolWithExpandedNesting, - where: { id_chain: { id: poolId, chain: networkContext.chain } }, + where: { id_chain: { id: poolId, chain: this.chain } }, }); if (!pool) { @@ -330,7 +334,7 @@ export class PoolCreatorService { skipDuplicates: true, data: allTokens.map((token) => ({ poolId, - chain: networkContext.chain, + chain: this.chain, tokenAddress: token.address, nestedPoolId: token.nestedPoolId || null, })), @@ -350,7 +354,7 @@ export class PoolCreatorService { const token = poolTokens[i]; await prisma.prismaPoolToken.update({ - where: { id_chain: { id: token.id, chain: networkContext.chain } }, + where: { id_chain: { id: token.id, chain: this.chain } }, data: { index: token.index || subgraphPool.tokensList.findIndex((address) => address === token.address), }, diff --git a/modules/pool/lib/pool-snapshot.service.ts b/modules/pool/lib/pool-snapshot.service.ts index ac54bacc0..8a7e0866d 100644 --- a/modules/pool/lib/pool-snapshot.service.ts +++ b/modules/pool/lib/pool-snapshot.service.ts @@ -33,14 +33,14 @@ export class PoolSnapshotService { const timestamp = this.getTimestampForRange(range); return prisma.prismaPoolSnapshot.findMany({ - where: { poolId, timestamp: { gte: timestamp }, chain: chain }, + where: { poolId, timestamp: { gte: timestamp }, chain }, orderBy: { timestamp: 'asc' }, }); } - public async getSnapshotForPool(poolId: string, timestamp: number) { + public async getSnapshotForPool(poolId: string, timestamp: number, chain: Chain) { return prisma.prismaPoolSnapshot.findUnique({ - where: { id_chain: { id: `${poolId}-${timestamp}`, chain: this.chain } }, + where: { id_chain: { id: `${poolId}-${timestamp}`, chain } }, }); } diff --git a/modules/pool/lib/pool-usd-data.service.ts b/modules/pool/lib/pool-usd-data.service.ts index aafffbf3c..9191a273c 100644 --- a/modules/pool/lib/pool-usd-data.service.ts +++ b/modules/pool/lib/pool-usd-data.service.ts @@ -18,6 +18,10 @@ export class PoolUsdDataService { return networkContext.services.balancerSubgraphService; } + private get chain() { + return networkContext.chain; + } + /** * Liquidity is dependent on token prices, so the values here are constantly in flux. * When updating, the easiest is to update all pools at once. @@ -26,7 +30,7 @@ export class PoolUsdDataService { minShares: number = 0.00000000001, maxShares: number = Number.MAX_SAFE_INTEGER, ) { - const tokenPrices = await this.tokenService.getTokenPrices(); + const tokenPrices = await this.tokenService.getTokenPrices(this.chain); const pools = await prisma.prismaPool.findMany({ include: { dynamicData: true, tokens: { include: { dynamicData: true } } }, where: { @@ -40,7 +44,7 @@ export class PoolUsdDataService { }, ], }, - chain: networkContext.chain, + chain: this.chain, }, }); @@ -66,7 +70,7 @@ export class PoolUsdDataService { tokenId: item.id, poolId: pool.id, poolName: pool.name, - chain: networkContext.chain, + chain: pool.chain, }, }, ); @@ -74,7 +78,7 @@ export class PoolUsdDataService { } updates.push( prisma.prismaPoolTokenDynamicData.update({ - where: { id_chain: { id: item.id, chain: networkContext.chain } }, + where: { id_chain: { id: item.id, chain: pool.chain } }, data: { balanceUSD: item.balanceUSD }, }), ); @@ -86,7 +90,7 @@ export class PoolUsdDataService { tags: { poolId: pool.id, poolName: pool.name, - chain: networkContext.chain, + chain: pool.chain, }, }, ); @@ -95,7 +99,7 @@ export class PoolUsdDataService { updates.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: pool.chain } }, data: { totalLiquidity }, }), ); @@ -136,7 +140,7 @@ export class PoolUsdDataService { updates.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: this.chain } }, data: { totalLiquidity24hAgo: totalLiquidity, totalShares24hAgo: pool.totalShares }, }), ); @@ -153,7 +157,7 @@ export class PoolUsdDataService { const yesterday = moment().subtract(1, 'day').unix(); const twoDaysAgo = moment().subtract(2, 'day').unix(); const pools = await prisma.prismaPool.findMany({ - where: poolIds ? { id: { in: poolIds }, chain: networkContext.chain } : { chain: networkContext.chain }, + where: poolIds ? { id: { in: poolIds }, chain: this.chain } : { chain: this.chain }, include: { swaps: { where: { timestamp: { gte: twoDaysAgo } } }, dynamicData: true, @@ -182,7 +186,7 @@ export class PoolUsdDataService { ) { operations.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: pool.chain } }, data: { volume24h, fees24h, volume48h, fees48h }, }), ); @@ -198,7 +202,7 @@ export class PoolUsdDataService { */ public async updateYieldCaptureForAllPools() { const pools = await prisma.prismaPool.findMany({ - where: { chain: networkContext.chain }, + where: { chain: this.chain }, include: { dynamicData: true, aprItems: true, @@ -252,7 +256,7 @@ export class PoolUsdDataService { operations.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: pool.chain } }, data: { yieldCapture24h, yieldCapture48h }, }), ); @@ -272,7 +276,7 @@ export class PoolUsdDataService { const stakedUsers = await prisma.prismaUserStakedBalance.groupBy({ by: ['poolId'], _count: { userAddress: true }, - where: { chain: networkContext.chain, balanceNum: { gt: 0 } }, + where: { chain: this.chain, balanceNum: { gt: 0 } }, }); for (const pool of subgraphPools) { @@ -280,7 +284,7 @@ export class PoolUsdDataService { updates.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: this.chain } }, data: { lifetimeVolume: parseFloat(pool.totalSwapVolume), lifetimeSwapFees: parseFloat(pool.totalSwapFee), @@ -291,7 +295,7 @@ export class PoolUsdDataService { ); const snapshots = await prisma.prismaPoolSnapshot.findMany({ - where: { poolId: pool.id, chain: networkContext.chain }, + where: { poolId: pool.id, chain: this.chain }, }); if (snapshots.length > 0) { @@ -306,7 +310,7 @@ export class PoolUsdDataService { updates.push( prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: networkContext.chain } }, + where: { id_chain: { id: pool.id, chain: this.chain } }, data: { sharePriceAth: sharePriceAth.sharePrice, sharePriceAthTimestamp: sharePriceAth.timestamp, diff --git a/modules/user/lib/user-snapshot.service.ts b/modules/user/lib/user-snapshot.service.ts index f5d262761..db62c8ea2 100644 --- a/modules/user/lib/user-snapshot.service.ts +++ b/modules/user/lib/user-snapshot.service.ts @@ -231,7 +231,7 @@ export class UserSnapshotService { where: { id_chain: { id: latestStoredUserPoolSnapshot.poolId, - chain: networkContext.chain, + chain: latestStoredUserPoolSnapshot.chain, }, }, include: { @@ -246,8 +246,9 @@ export class UserSnapshotService { if (totalBalance > 0) { //enrich with poolsnapshot data and save const poolSnapshot = await this.poolSnapshotService.getSnapshotForPool( - latestStoredUserPoolSnapshot.poolId, + pool.id, userSubgraphSnapshot.timestamp, + pool.chain, ); /* @@ -267,7 +268,7 @@ export class UserSnapshotService { operations.push( prisma.prismaUserPoolBalanceSnapshot.upsert({ where: { - id_chain: { id: userPoolBalanceSnapshotData.id, chain: networkContext.chain }, + id_chain: { id: userPoolBalanceSnapshotData.id, chain: userPoolBalanceSnapshotData.chain }, }, create: userPoolBalanceSnapshotData, update: userPoolBalanceSnapshotData, @@ -280,7 +281,7 @@ export class UserSnapshotService { id: `${pool.id}-${userSubgraphSnapshot.user.id.toLowerCase()}-${ userSubgraphSnapshot.timestamp }`, - chain: networkContext.chain, + chain: pool.chain, timestamp: userSubgraphSnapshot.timestamp, userAddress: userSubgraphSnapshot.user.id.toLowerCase(), poolId: pool.id, @@ -297,7 +298,7 @@ export class UserSnapshotService { operations.push( prisma.prismaUserPoolBalanceSnapshot.upsert({ where: { - id_chain: { id: userPoolBalanceSnapshotData.id, chain: networkContext.chain }, + id_chain: { id: userPoolBalanceSnapshotData.id, chain: userPoolBalanceSnapshotData.chain }, }, create: userPoolBalanceSnapshotData, update: userPoolBalanceSnapshotData, diff --git a/tsconfig.json b/tsconfig.json index c4157392a..817776afd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,8 @@ }, "exclude": [ "node_modules", - "debug" + "debug", + "**/*.spec.ts", + "**/*.test.ts" ] } From b54f8158af3796bb8c88439ccc5fae78d91a7825 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 16 Nov 2023 11:21:18 +0100 Subject: [PATCH 070/175] fix network.context chain --- modules/user/lib/user-snapshot.service.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/user/lib/user-snapshot.service.ts b/modules/user/lib/user-snapshot.service.ts index f5d262761..d886305d8 100644 --- a/modules/user/lib/user-snapshot.service.ts +++ b/modules/user/lib/user-snapshot.service.ts @@ -257,6 +257,7 @@ export class UserSnapshotService { const userPoolBalanceSnapshotData = this.createUserPoolSnapshotData( poolSnapshot, pool, + networkContext.chain, userSubgraphSnapshot, totalBalance, walletBalance, @@ -328,6 +329,7 @@ export class UserSnapshotService { userAddress, 0, poolId, + chain, ); let storedUserSnapshotsInRangeForPool = storedUserSnapshotsForPool.filter( @@ -347,7 +349,7 @@ export class UserSnapshotService { const pool = await prisma.prismaPool.findUniqueOrThrow({ where: { - id_chain: { id: poolId, chain: networkContext.chain }, + id_chain: { id: poolId, chain: chain }, }, include: { staking: true, @@ -422,6 +424,7 @@ export class UserSnapshotService { const userPoolBalanceSnapshotData = this.createUserPoolSnapshotData( poolSnapshotForTimestamp, pool, + chain, userSubgraphSnapshot, totalBalance, walletBalance, @@ -439,6 +442,7 @@ export class UserSnapshotService { userAddress, oldestRequestedSnapshotTimestamp, poolId, + chain, ); } @@ -475,7 +479,7 @@ export class UserSnapshotService { lt: oldestRequestedSnapshotTimestamp, }, poolId: poolId, - chain: networkContext.chain, + chain: chain, }, orderBy: { timestamp: 'desc' }, }); @@ -573,7 +577,7 @@ export class UserSnapshotService { (1 - networkContext.data.balancer.swapProtocolFeePercentage) }`; await prisma.prismaUserPoolBalanceSnapshot.update({ - where: { id_chain: { id: currentSnapshot.id, chain: networkContext.chain } }, + where: { id_chain: { id: currentSnapshot.id, chain: chain } }, data: currentSnapshot, }); } @@ -621,6 +625,7 @@ export class UserSnapshotService { private createUserPoolSnapshotData( poolSnapshot: PrismaPoolSnapshot | undefined | null, pool: PrismaPool & { staking: PrismaPoolStaking[] }, + chain: Chain, subgraphSnapshot: UserBalanceSnapshotFragment, totalBalance: number, walletBalance: string, @@ -631,7 +636,7 @@ export class UserSnapshotService { const userPoolBalanceSnapshotData = { id: `${pool.id}-${subgraphSnapshot.user.id.toLowerCase()}-${subgraphSnapshot.timestamp}`, - chain: networkContext.chain, + chain: chain, timestamp: subgraphSnapshot.timestamp, userAddress: subgraphSnapshot.user.id.toLowerCase(), poolId: pool.id, @@ -702,6 +707,7 @@ export class UserSnapshotService { userAddress: string, oldestRequestedSnapshotTimestamp: number, poolId: string, + chain: Chain, ) { return await prisma.prismaUserPoolBalanceSnapshot.findMany({ where: { @@ -710,7 +716,7 @@ export class UserSnapshotService { gte: oldestRequestedSnapshotTimestamp, }, poolId: poolId, - chain: networkContext.chain, + chain: chain, }, orderBy: { timestamp: 'asc' }, }); From 3cf1cfcde7c5c71044e28fa1468ceb10c3d14314 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:26:51 +0100 Subject: [PATCH 071/175] fix status updates for filtered pools --- modules/pool/lib/pool-on-chain-data.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 0f54decb7..44346b3e9 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -57,8 +57,11 @@ export class PoolOnChainDataService { const operations = []; for (const pool of filteredPools) { + if (!state[pool.id]) continue; // Some pool types are filtered out in the state fetching function + const { isPaused, isInRecoveryMode } = state[pool.id]; - if (pool.dynamicData) { + const data = pool.dynamicData; + if (data && data.isPaused !== isPaused && data.isInRecoveryMode !== isInRecoveryMode) { operations.push( prisma.prismaPoolDynamicData.update({ where: { id_chain: { id: pool.id, chain: this.options.chain } }, @@ -66,7 +69,7 @@ export class PoolOnChainDataService { isPaused, isInRecoveryMode, }, - }), + }) ); } } From e9b230a055fbcb7b982736b04edc099fbfdb54ae Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 17 Nov 2023 13:23:58 +0100 Subject: [PATCH 072/175] minor change plus prettier --- .../pool/lib/pool-on-chain-data.service.ts | 44 ++++---- modules/pool/lib/pool-onchain-data.ts | 105 ++++++++---------- modules/pool/lib/pool-onchain-gyro-fee.ts | 59 +++------- modules/pool/lib/pool-onchain-state.ts | 12 +- modules/pool/pool.service.ts | 8 +- 5 files changed, 89 insertions(+), 139 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 44346b3e9..72bef5972 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -2,7 +2,7 @@ import { formatFixed } from '@ethersproject/bignumber'; import { Chain, PrismaPoolType } from '@prisma/client'; import { isSameAddress } from '@balancer-labs/sdk'; import { prisma } from '../../../prisma/prisma-client'; -import { isStablePool } from './pool-utils'; +import { isComposableStablePool, isStablePool } from './pool-utils'; import { TokenService } from '../../token/token.service'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { fetchOnChainPoolState } from './pool-onchain-state'; @@ -25,9 +25,7 @@ const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ ]; export class PoolOnChainDataService { - constructor( - private readonly tokenService: TokenService, - ) {} + constructor(private readonly tokenService: TokenService) {} private get options() { return { @@ -36,7 +34,7 @@ export class PoolOnChainDataService { yieldProtocolFeePercentage: networkContext.data.balancer.yieldProtocolFeePercentage, gyroConfig: networkContext.data.gyro?.config, composableStableFactories: networkContext.data.balancer.composableStablePoolFactories, - } + }; } public async updateOnChainStatus(poolIds: string[]): Promise { @@ -61,7 +59,7 @@ export class PoolOnChainDataService { const { isPaused, isInRecoveryMode } = state[pool.id]; const data = pool.dynamicData; - if (data && data.isPaused !== isPaused && data.isInRecoveryMode !== isInRecoveryMode) { + if (data && (data.isPaused !== isPaused || data.isInRecoveryMode !== isInRecoveryMode)) { operations.push( prisma.prismaPoolDynamicData.update({ where: { id_chain: { id: pool.id, chain: this.options.chain } }, @@ -69,7 +67,7 @@ export class PoolOnChainDataService { isPaused, isInRecoveryMode, }, - }) + }), ); } } @@ -99,20 +97,20 @@ export class PoolOnChainDataService { const gyroPools = filteredPools.filter((pool) => pool.type.includes('GYRO')); const poolsWithComposableStableType = filteredPools.map((pool) => ({ ...pool, - type: ( - pool.factory && this.options.composableStableFactories.includes(pool.factory) - ? 'COMPOSABLE_STABLE' - : pool.type - ) as PrismaPoolType | 'COMPOSABLE_STABLE', + type: (isComposableStablePool(pool) ? 'COMPOSABLE_STABLE' : pool.type) as + | PrismaPoolType + | 'COMPOSABLE_STABLE', })); const tokenPrices = await this.tokenService.getTokenPrices(); - const onchainResults = await fetchOnChainPoolData(poolsWithComposableStableType, this.options.vaultAddress, 1024); - const gyroFees = await ( - this.options.gyroConfig - ? fetchOnChainGyroFees(gyroPools, this.options.gyroConfig, 1024) - : Promise.resolve({} as { [address: string]: string }) + const onchainResults = await fetchOnChainPoolData( + poolsWithComposableStableType, + this.options.vaultAddress, + 1024, ); + const gyroFees = await (this.options.gyroConfig + ? fetchOnChainGyroFees(gyroPools, this.options.gyroConfig, 1024) + : Promise.resolve({} as { [address: string]: string })); const operations = []; for (const pool of filteredPools) { @@ -133,7 +131,7 @@ export class PoolOnChainDataService { where: { id_chain: { id: pool.id, chain: this.options.chain } }, create: { id: pool.id, chain: this.options.chain, poolId: pool.id, amp, blockNumber }, update: { amp, blockNumber }, - }) + }), ); } } @@ -163,7 +161,7 @@ export class PoolOnChainDataService { blockNumber, }, update: { upperTarget, lowerTarget, blockNumber }, - }) + }), ); } } @@ -198,7 +196,7 @@ export class PoolOnChainDataService { protocolYieldFee: yieldProtocolFeePercentage, blockNumber, }, - }) + }), ); } @@ -253,7 +251,7 @@ export class PoolOnChainDataService { poolToken.address === pool.address ? 0 : this.tokenService.getPriceForToken(tokenPrices, poolToken.address) * - parseFloat(balance), + parseFloat(balance), }, update: { blockNumber, @@ -264,9 +262,9 @@ export class PoolOnChainDataService { poolToken.address === pool.address ? 0 : this.tokenService.getPriceForToken(tokenPrices, poolToken.address) * - parseFloat(balance), + parseFloat(balance), }, - }) + }), ); } } diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index 2ee091af4..189c5f62e 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -20,10 +20,10 @@ interface PoolInput { address: string; type: PrismaPoolType | 'COMPOSABLE_STABLE'; tokens: { - address: string, + address: string; token: { - decimals: number, - } + decimals: number; + }; }[]; version: number; } @@ -79,20 +79,21 @@ const getTotalSupplyFn = (type: PoolInput['type'], version: number) => { if (['LINEAR', 'PHANTOM_STABLE'].includes(type)) { return 'getVirtualSupply'; } else if ( - type === 'COMPOSABLE_STABLE' - || (type === 'WEIGHTED' && version > 1) - || (type === 'GYROE' && version > 1) - || (type === 'UNKNOWN' && version > 1)) { + type === 'COMPOSABLE_STABLE' || + (type === 'WEIGHTED' && version > 1) || + (type === 'GYROE' && version > 1) || + (type === 'UNKNOWN' && version > 1) + ) { return 'getActualSupply'; } else { return 'totalSupply'; } -} +}; -const defaultCalls = ( +const addDefaultCallsToMulticaller = ( { id, address, type, version }: PoolInput, vaultAddress: string, - multicaller: Multicaller3 + multicaller: Multicaller3, ) => { multicaller.call(`${id}.poolTokens`, vaultAddress, 'getPoolTokens', [id]); multicaller.call(`${id}.totalSupply`, address, getTotalSupplyFn(type, version)); @@ -101,33 +102,21 @@ const defaultCalls = ( multicaller.call(`${id}.protocolYieldFeePercentageCache`, address, 'getProtocolFeePercentageCache', [2]); }; -const weightedCalls = ( - { id, address }: PoolInput, - multicaller: Multicaller3 -) => { +const weightedCalls = ({ id, address }: PoolInput, multicaller: Multicaller3) => { multicaller.call(`${id}.weights`, address, 'getNormalizedWeights'); }; -const lbpAndInvestmentCalls =( - { id, address }: PoolInput, - multicaller: Multicaller3 -) => { +const lbpAndInvestmentCalls = ({ id, address }: PoolInput, multicaller: Multicaller3) => { multicaller.call(`${id}.weights`, address, 'getNormalizedWeights'); multicaller.call(`${id}.swapEnabled`, address, 'getSwapEnabled'); }; -const linearCalls = ( - { id, address }: PoolInput, - multicaller: Multicaller3 -) => { +const linearCalls = ({ id, address }: PoolInput, multicaller: Multicaller3) => { multicaller.call(`${id}.targets`, address, 'getTargets'); multicaller.call(`${id}.wrappedTokenRate`, address, 'getWrappedTokenRate'); }; -const stableCalls = ( - { id, address, tokens }: PoolInput, - multicaller: Multicaller3 -) => { +const stableCalls = ({ id, address, tokens }: PoolInput, multicaller: Multicaller3) => { multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); tokens.forEach(({ address: tokenAddress }, i) => { @@ -135,25 +124,19 @@ const stableCalls = ( }); }; -const metaStableCalls = ( - { id, address, tokens }: PoolInput, - multicaller: Multicaller3 -) => { +const metaStableCalls = ({ id, address, tokens }: PoolInput, multicaller: Multicaller3) => { multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); tokens.forEach(({ address: tokenAddress }, i) => { multicaller.call(`${id}.metaPriceRateCache[${i}]`, address, 'getPriceRateCache', [tokenAddress]); }); -} +}; -const gyroECalls = ( - { id, address }: PoolInput, - multicaller: Multicaller3 -) => { +const gyroECalls = ({ id, address }: PoolInput, multicaller: Multicaller3) => { multicaller.call(`${id}.tokenRates`, address, 'getTokenRates'); }; -const poolTypeCalls = (type: PoolInput['type'], version = 1) => { +const addPoolTypeSpecificCallsToMulticaller = (type: PoolInput['type'], version = 1) => { const do_nothing = () => ({}); switch (type) { case 'WEIGHTED': @@ -186,30 +169,32 @@ const parse = (result: OnchainData, decimalsLookup: { [address: string]: number totalShares: formatEther(result.totalSupply || '0'), weights: result.weights?.map(formatEther), targets: result.targets?.map(String), - poolTokens: result.poolTokens ? { - tokens: result.poolTokens[0].map((token) => token.toLowerCase()), - balances: result.poolTokens[1].map((balance, i) => formatUnits(balance, decimalsLookup[result.poolTokens[0][i].toLowerCase()])), - rates: result.poolTokens[0].map((_, i) => - result.tokenRate && result.tokenRate[i] - ? formatEther(result.tokenRate[i]) - : result.tokenRates && result.tokenRates[i] - ? formatEther(result.tokenRates[i]) - : result.metaPriceRateCache && result.metaPriceRateCache[i][0].gt(0) - ? formatEther(result.metaPriceRateCache[i][0]) - : undefined - ) - } : { tokens: [], balances: [], rates: [] }, + poolTokens: result.poolTokens + ? { + tokens: result.poolTokens[0].map((token) => token.toLowerCase()), + balances: result.poolTokens[1].map((balance, i) => + formatUnits(balance, decimalsLookup[result.poolTokens[0][i].toLowerCase()]), + ), + rates: result.poolTokens[0].map((_, i) => + result.tokenRate && result.tokenRate[i] + ? formatEther(result.tokenRate[i]) + : result.tokenRates && result.tokenRates[i] + ? formatEther(result.tokenRates[i]) + : result.metaPriceRateCache && result.metaPriceRateCache[i][0].gt(0) + ? formatEther(result.metaPriceRateCache[i][0]) + : undefined, + ), + } + : { tokens: [], balances: [], rates: [] }, wrappedTokenRate: result.wrappedTokenRate ? formatEther(result.wrappedTokenRate) : '1.0', rate: result.rate ? formatEther(result.rate) : '1.0', swapEnabled: result.swapEnabled, - protocolYieldFeePercentageCache: result.protocolYieldFeePercentageCache ? formatEther(result.protocolYieldFeePercentageCache) : undefined, + protocolYieldFeePercentageCache: result.protocolYieldFeePercentageCache + ? formatEther(result.protocolYieldFeePercentageCache) + : undefined, }); -export const fetchOnChainPoolData = async ( - pools: PoolInput[], - vaultAddress: string, - batchSize = 1024 -) => { +export const fetchOnChainPoolData = async (pools: PoolInput[], vaultAddress: string, batchSize = 1024) => { if (pools.length === 0) { return {}; } @@ -217,8 +202,8 @@ export const fetchOnChainPoolData = async ( const multicaller = new Multicaller3(abi, batchSize); pools.forEach((pool) => { - defaultCalls(pool, vaultAddress, multicaller); - poolTypeCalls(pool.type, pool.version)(pool, multicaller); + addDefaultCallsToMulticaller(pool, vaultAddress, multicaller); + addPoolTypeSpecificCallsToMulticaller(pool.type, pool.version)(pool, multicaller); }); const results = (await multicaller.execute()) as { @@ -226,13 +211,11 @@ export const fetchOnChainPoolData = async ( }; const decimalsLookup = Object.fromEntries( - pools.flatMap((pool) => - pool.tokens.map(({ address, token }) => [address, token.decimals]) - ) + pools.flatMap((pool) => pool.tokens.map(({ address, token }) => [address, token.decimals])), ); const parsed = Object.fromEntries( - Object.entries(results).map(([key, result]) => [key, parse(result, decimalsLookup)]) + Object.entries(results).map(([key, result]) => [key, parse(result, decimalsLookup)]), ); return parsed; diff --git a/modules/pool/lib/pool-onchain-gyro-fee.ts b/modules/pool/lib/pool-onchain-gyro-fee.ts index d59d51efe..aef4b3c3d 100644 --- a/modules/pool/lib/pool-onchain-gyro-fee.ts +++ b/modules/pool/lib/pool-onchain-gyro-fee.ts @@ -22,14 +22,10 @@ interface OnchainGyroFees { [id: string]: { poolFee?: string; }; - } + }; } -export const fetchOnChainGyroFees = async ( - pools: PoolInput[], - gyroConfigAddress?: string, - batchSize = 1024 -) => { +export const fetchOnChainGyroFees = async (pools: PoolInput[], gyroConfigAddress?: string, batchSize = 1024) => { if (pools.length === 0 || !gyroConfigAddress) { return {}; } @@ -40,32 +36,17 @@ export const fetchOnChainGyroFees = async ( const eclpKey = keccak256( ['bytes'], - [ - defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [feeKey, formatBytes32String('ECLP')] - ), - ] + [defaultAbiCoder.encode(['bytes32', 'bytes32'], [feeKey, formatBytes32String('ECLP')])], ); const twoClpKey = keccak256( ['bytes'], - [ - defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [feeKey, formatBytes32String('2CLP')] - ), - ] + [defaultAbiCoder.encode(['bytes32', 'bytes32'], [feeKey, formatBytes32String('2CLP')])], ); const threeClpKey = keccak256( ['bytes'], - [ - defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [feeKey, formatBytes32String('3CLP')] - ), - ] + [defaultAbiCoder.encode(['bytes32', 'bytes32'], [feeKey, formatBytes32String('3CLP')])], ); multicaller.call('defaultFee', gyroConfigAddress, 'getUint', [feeKey]); @@ -78,12 +59,7 @@ export const fetchOnChainGyroFees = async ( if (type.includes('GYRO')) { const poolFeeKey = keccak256( ['bytes'], - [ - defaultAbiCoder.encode( - ['bytes32', 'uint256'], - [feeKey, address] - ), - ] + [defaultAbiCoder.encode(['bytes32', 'uint256'], [feeKey, address])], ); multicaller.call(`pools.${id}.poolFee`, gyroConfigAddress, 'getUint', [poolFeeKey]); @@ -101,19 +77,20 @@ export const fetchOnChainGyroFees = async ( let parsed: { [address: string]: string } = {}; if (results.pools) { parsed = Object.fromEntries( - Object.entries(results.pools).map(([id, { poolFee }]) => [id, + Object.entries(results.pools).map(([id, { poolFee }]) => [ + id, formatEther( poolFee - ? poolFee - : poolTypeLookup[id] == 'GYROE' - ? eclpFee - : poolTypeLookup[id] == 'GYRO' - ? twoClpFee - : poolTypeLookup[id] == 'GYRO3' - ? threeClpFee - : defaultFee - ) - ]) + ? poolFee + : poolTypeLookup[id] == 'GYROE' + ? eclpFee + : poolTypeLookup[id] == 'GYRO' + ? twoClpFee + : poolTypeLookup[id] == 'GYRO3' + ? threeClpFee + : defaultFee, + ), + ]), ); } diff --git a/modules/pool/lib/pool-onchain-state.ts b/modules/pool/lib/pool-onchain-state.ts index 8301f8c23..a11928d01 100644 --- a/modules/pool/lib/pool-onchain-state.ts +++ b/modules/pool/lib/pool-onchain-state.ts @@ -21,10 +21,7 @@ const parse = (result: OnchainState) => ({ isInRecoveryMode: result.inRecoveryMode ? result.inRecoveryMode : false, }); -export const fetchOnChainPoolState = async ( - pools: PoolInput[], - batchSize = 1024 -) => { +export const fetchOnChainPoolState = async (pools: PoolInput[], batchSize = 1024) => { if (pools.length === 0) { return {}; } @@ -32,7 +29,8 @@ export const fetchOnChainPoolState = async ( const multicaller = new Multicaller3(abi, batchSize); pools.forEach(({ id, type, address }) => { - if (!(type === 'ELEMENT')) { + // filter certain pool types that don't have pausedState or recovery mode + if (type !== 'ELEMENT') { multicaller.call(`${id}.pausedState`, address, 'getPausedState'); } if ( @@ -48,9 +46,7 @@ export const fetchOnChainPoolState = async ( [id: string]: OnchainState; }; - const parsed = Object.fromEntries( - Object.entries(results).map(([key, result]) => [key, parse(result)]) - ); + const parsed = Object.fromEntries(Object.entries(results).map(([key, result]) => [key, parse(result)])); return parsed; }; diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 76f003030..ae54c3e2b 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -125,11 +125,7 @@ export class PoolService { const featuredPoolGroups = await this.poolGqlLoaderService.getFeaturedPoolGroups(); - this.cache.put( - `${FEATURED_POOL_GROUPS_CACHE_KEY}:${this.chainId}`, - featuredPoolGroups, - 60 * 5 * 1000, - ); + this.cache.put(`${FEATURED_POOL_GROUPS_CACHE_KEY}:${this.chainId}`, featuredPoolGroups, 60 * 5 * 1000); return featuredPoolGroups; } @@ -368,7 +364,7 @@ export class PoolService { version: subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1, }, }); - } catch(e: any) { + } catch (e: any) { // Some pools are filtered from the DB, like test pools, // so we just ignore them without breaking the loop const error = e.meta ? e.meta.cause : e; From 14d21da5535089b5e9c131f765d76b558443bf72 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 20 Nov 2023 05:37:01 +0100 Subject: [PATCH 073/175] fix: beetswars apr (#534) --- .../apr-data-sources/fantom/beetswars-gauge-voting-apr.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr.ts b/modules/pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr.ts index 676bbcc97..93af8076b 100644 --- a/modules/pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr.ts +++ b/modules/pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr.ts @@ -20,7 +20,10 @@ export class BeetswarsGaugeVotingAprService implements PoolAprService { const response = await axios.get('https://www.beetswars.live/api/trpc/chart.chartdata'); - const votingAprs: number[] = response.data.result.data.json.chartdata.votingApr; + const raw: number[] = response.data.result.data.json.chartdata.votingApr; + + // Filter out non-numbers and infinity values + const votingAprs = raw.filter((apr) => apr && isFinite(apr)); const minApr = 0; const maxApr = votingAprs[votingAprs.length - 1] / 100; From 1757da088ef58a4fe1802569dbd91220e2ef7679 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 20 Nov 2023 18:31:47 +0100 Subject: [PATCH 074/175] add composable --- modules/pool/pool.gql | 1 + modules/pool/pool.prisma | 1 + .../20231120163312_add_composablestable_pooltype/migration.sql | 2 ++ prisma/schema.prisma | 1 + 4 files changed, 5 insertions(+) create mode 100644 prisma/migrations/20231120163312_add_composablestable_pooltype/migration.sql diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 219dea792..fc6aab4ca 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -82,6 +82,7 @@ enum GqlPoolMinimalType { STABLE META_STABLE PHANTOM_STABLE + COMPOSABLE_STABLE ELEMENT LINEAR UNKNOWN diff --git a/modules/pool/pool.prisma b/modules/pool/pool.prisma index 16767b4df..fa2c8bb8c 100644 --- a/modules/pool/pool.prisma +++ b/modules/pool/pool.prisma @@ -47,6 +47,7 @@ enum PrismaPoolType { STABLE META_STABLE PHANTOM_STABLE + COMPOSABLE_STABLE ELEMENT LINEAR UNKNOWN diff --git a/prisma/migrations/20231120163312_add_composablestable_pooltype/migration.sql b/prisma/migrations/20231120163312_add_composablestable_pooltype/migration.sql new file mode 100644 index 000000000..78f3ee0b4 --- /dev/null +++ b/prisma/migrations/20231120163312_add_composablestable_pooltype/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "PrismaPoolType" ADD VALUE 'COMPOSABLE_STABLE'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9ec3e38d1..2f56c2e23 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -93,6 +93,7 @@ enum PrismaPoolType { STABLE META_STABLE PHANTOM_STABLE + COMPOSABLE_STABLE ELEMENT LINEAR UNKNOWN From 1e2f7f5664ccad612223b877a145f86197ec5315 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 20 Nov 2023 18:41:21 +0100 Subject: [PATCH 075/175] excluding trumatic --- modules/network/mainnet.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 8e77f13ff..986c83a63 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -57,7 +57,7 @@ const data: NetworkData = { coingecko: { nativeAssetId: 'ethereum', platformId: 'ethereum', - excludedTokenAddresses: [], + excludedTokenAddresses: ['0xa43a7c62d56df036c187e1966c03e2799d8987ed'], // truMatic, has coingecko entry but no price }, tokenPrices: { maxHourlyPriceHistoryNumDays: 100, @@ -378,10 +378,7 @@ export const mainnetNetworkConfig: NetworkConfig = { data.balancer.yieldProtocolFeePercentage, data.balancer.swapProtocolFeePercentage, ), - new PhantomStableAprService( - data.chain.prismaId, - data.balancer.yieldProtocolFeePercentage, - ), + new PhantomStableAprService(data.chain.prismaId, data.balancer.yieldProtocolFeePercentage), new BoostedPoolAprService(), new SwapFeeAprService(data.balancer.swapProtocolFeePercentage), new GaugeAprService(tokenService, [data.bal!.address]), From 6813fbc8a8629f1e34f01b4904c2346b5e22d62f Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 10:13:33 +0100 Subject: [PATCH 076/175] remove filter to remove bpt from tokens --- modules/pool/lib/pool-gql-loader.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 4d965269e..71dc8cc40 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -357,9 +357,7 @@ export class PoolGqlLoaderService { investConfig: this.getPoolInvestConfig(pool), withdrawConfig: this.getPoolWithdrawConfig(pool), nestingType: this.getPoolNestingType(pool), - tokens: pool.tokens - .filter((token) => token.address !== pool.address) - .map((token) => this.mapPoolTokenToGqlUnion(token)), + tokens: pool.tokens.map((token) => this.mapPoolTokenToGqlUnion(token)), allTokens: this.mapAllTokens(pool), displayTokens: this.mapDisplayTokens(pool), }; From 4943f8736827a3147d7ab848d1cbbb49a748c52e Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 10:35:34 +0100 Subject: [PATCH 077/175] only exposing composable stable at query --- modules/pool/lib/pool-gql-loader.service.ts | 35 +++++++++++---------- modules/pool/pool.gql | 25 +++++++-------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 4d965269e..ec211a6d5 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -20,7 +20,7 @@ import { GqlPoolLinearNested, GqlPoolMinimal, GqlPoolNestingType, - GqlPoolPhantomStableNested, + GqlPoolComposableStableNested, GqlPoolStaking, GqlPoolToken, GqlPoolTokenDisplay, @@ -34,7 +34,7 @@ import { import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; -import { Chain, Prisma, PrismaPoolAprType } from '@prisma/client'; +import { Chain, Prisma, PrismaPoolAprType, PrismaPoolType } from '@prisma/client'; import { isWeightedPoolV2 } from './pool-utils'; import { oldBnum } from '../../big-number/old-big-number'; import { networkContext } from '../../network/network-context.service'; @@ -80,6 +80,7 @@ export class PoolGqlLoaderService { public mapToMinimalGqlPool(pool: PrismaPoolMinimal): GqlPoolMinimal { return { ...pool, + type: pool.type === 'PHANTOM_STABLE' ? PrismaPoolType.COMPOSABLE_STABLE : pool.type, decimals: 18, dynamicData: this.getPoolDynamicData(pool), allTokens: this.mapAllTokens(pool), @@ -380,9 +381,9 @@ export class PoolGqlLoaderService { amp: pool.stableDynamicData?.amp || '0', tokens: mappedData.tokens as GqlPoolToken[], }; - case 'PHANTOM_STABLE': + case 'COMPOSABLE_STABLE': return { - __typename: 'GqlPoolPhantomStable', + __typename: 'GqlPoolComposableStable', ...mappedData, amp: pool.stableDynamicData?.amp || '0', bptPriceRate: bpt?.dynamicData?.priceRate || '1.0', @@ -488,7 +489,7 @@ export class PoolGqlLoaderService { weight: poolToken?.dynamicData?.weight, }; } - } else if (allToken.nestedPool?.type === 'PHANTOM_STABLE') { + } else if (allToken.nestedPool?.type === 'COMPOSABLE_STABLE') { const mainTokens = allToken.nestedPool.allTokens.filter( (nestedToken) => @@ -806,7 +807,7 @@ export class PoolGqlLoaderService { private getPoolInvestConfig(pool: PrismaPoolWithExpandedNesting): GqlPoolInvestConfig { const poolTokens = pool.tokens.filter((token) => token.address !== pool.address); - const supportsNativeAssetDeposit = pool.type !== 'PHANTOM_STABLE'; + const supportsNativeAssetDeposit = pool.type !== 'COMPOSABLE_STABLE'; let options: GqlPoolInvestOption[] = []; for (const poolToken of poolTokens) { @@ -815,7 +816,7 @@ export class PoolGqlLoaderService { return { //TODO could flag these as disabled in sanity - proportionalEnabled: pool.type !== 'PHANTOM_STABLE' && pool.type !== 'META_STABLE', + proportionalEnabled: pool.type !== 'COMPOSABLE_STABLE' && pool.type !== 'META_STABLE', singleAssetEnabled: true, options, }; @@ -871,11 +872,11 @@ export class PoolGqlLoaderService { ] : [this.mapPoolTokenToGql(mainToken)], }); - } else if (nestedPool && nestedPool.type === 'PHANTOM_STABLE') { + } else if (nestedPool && nestedPool.type === 'COMPOSABLE_STABLE') { const nestedTokens = nestedPool.tokens.filter((token) => token.address !== nestedPool.address); - if (pool.type === 'PHANTOM_STABLE' || isWeightedPoolV2(pool)) { - //when nesting a phantom stable inside a phantom stable, all of the underlying tokens can be used when investing + if (pool.type === 'COMPOSABLE_STABLE' || isWeightedPoolV2(pool)) { + //when nesting a composable stable inside a composable stable, all of the underlying tokens can be used when investing //when withdrawing from a v2 weighted pool, we withdraw into all underlying assets. // ie: USDC/DAI/USDT for nested bbaUSD for (const nestedToken of nestedTokens) { @@ -895,7 +896,7 @@ export class PoolGqlLoaderService { }); } } else { - //if the parent pool does not have phantom bpt (ie: weighted), the user can only invest with 1 of the phantom stable tokens + //if the parent pool does not have phantom bpt (ie: weighted), the user can only invest with 1 of the composable stable tokens options.push({ poolTokenIndex: poolToken.index, poolTokenAddress: poolToken.address, @@ -956,7 +957,7 @@ export class PoolGqlLoaderService { ...this.getLinearPoolTokenData(token, nestedPool), pool: this.mapNestedPoolToGqlPoolLinearNested(nestedPool, percentOfSupplyNested), }; - } else if (nestedPool && nestedPool.type === 'PHANTOM_STABLE') { + } else if (nestedPool && nestedPool.type === 'COMPOSABLE_STABLE') { const totalShares = parseFloat(nestedPool.dynamicData?.totalShares || '0'); const percentOfSupplyNested = totalShares > 0 ? parseFloat(token.dynamicData?.balance || '0') / totalShares : 0; @@ -964,8 +965,8 @@ export class PoolGqlLoaderService { //50_000_000_000_000 return { ...this.mapPoolTokenToGql(token), - __typename: 'GqlPoolTokenPhantomStable', - pool: this.mapNestedPoolToGqlPoolPhantomStableNested(nestedPool, percentOfSupplyNested), + __typename: 'GqlPoolTokenComposableStable', + pool: this.mapNestedPoolToGqlPoolComposableStableNested(nestedPool, percentOfSupplyNested), }; } @@ -1019,14 +1020,14 @@ export class PoolGqlLoaderService { }; } - private mapNestedPoolToGqlPoolPhantomStableNested( + private mapNestedPoolToGqlPoolComposableStableNested( pool: PrismaNestedPoolWithSingleLayerNesting, percentOfSupplyNested: number, - ): GqlPoolPhantomStableNested { + ): GqlPoolComposableStableNested { const bpt = pool.tokens.find((token) => token.address === pool.address); return { - __typename: 'GqlPoolPhantomStableNested', + __typename: 'GqlPoolComposableStableNested', ...pool, nestingType: this.getPoolNestingType(pool), tokens: pool.tokens.map((token) => { diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index fc6aab4ca..87332397f 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -81,7 +81,6 @@ enum GqlPoolMinimalType { WEIGHTED STABLE META_STABLE - PHANTOM_STABLE COMPOSABLE_STABLE ELEMENT LINEAR @@ -177,7 +176,7 @@ type GqlPoolInvestOption { # we use an array here for invest options where there are more than one option, but only one can be selected # - FTM/wFTM or ETH/wETH - # - weighted boosted with nested phantom stable (bb-yv-USD) where you can only invest with DAI or USDC, not both at the same time + # - weighted boosted with nested composable stable (bb-yv-USD) where you can only invest with DAI or USDC, not both at the same time tokenOptions: [GqlPoolToken!]! } @@ -318,7 +317,7 @@ type GqlPoolMetaStable implements GqlPoolBase { version: Int! } -type GqlPoolPhantomStable implements GqlPoolBase { +type GqlPoolComposableStable implements GqlPoolBase { id: ID! chain: GqlChain! name: String! @@ -420,7 +419,7 @@ type GqlPoolLinearNested { version: Int! } -type GqlPoolPhantomStableNested { +type GqlPoolComposableStableNested { id: ID! name: String! symbol: String! @@ -428,7 +427,7 @@ type GqlPoolPhantomStableNested { owner: Bytes! factory: Bytes createTime: Int! - tokens: [GqlPoolTokenPhantomStableNestedUnion!]! + tokens: [GqlPoolTokenComposableStableNestedUnion!]! nestingType: GqlPoolNestingType! totalShares: BigDecimal! @@ -446,14 +445,14 @@ union GqlPoolUnion = | GqlPoolStable | GqlPoolMetaStable | GqlPoolLinear - | GqlPoolPhantomStable + | GqlPoolComposableStable | GqlPoolElement | GqlPoolLiquidityBootstrapping | GqlPoolGyro -union GqlPoolNestedUnion = GqlPoolLinearNested | GqlPoolPhantomStableNested +union GqlPoolNestedUnion = GqlPoolLinearNested | GqlPoolComposableStableNested -union GqlPoolTokenUnion = GqlPoolToken | GqlPoolTokenPhantomStable | GqlPoolTokenLinear -union GqlPoolTokenPhantomStableNestedUnion = GqlPoolToken | GqlPoolTokenLinear +union GqlPoolTokenUnion = GqlPoolToken | GqlPoolTokenComposableStable | GqlPoolTokenLinear +union GqlPoolTokenComposableStableNestedUnion = GqlPoolToken | GqlPoolTokenLinear interface GqlPoolTokenBase { id: ID! @@ -505,7 +504,7 @@ type GqlPoolTokenLinear implements GqlPoolTokenBase { totalBalance: BigDecimal! } -type GqlPoolTokenPhantomStable implements GqlPoolTokenBase { +type GqlPoolTokenComposableStable implements GqlPoolTokenBase { id: ID! address: String! balance: BigDecimal! @@ -518,7 +517,7 @@ type GqlPoolTokenPhantomStable implements GqlPoolTokenBase { #the total balance in the pool, regardless of nesting totalBalance: BigDecimal! - pool: GqlPoolPhantomStableNested! + pool: GqlPoolComposableStableNested! } type GqlPoolLinearPoolData { @@ -535,7 +534,7 @@ type GqlPoolLinearPoolData { mainTokenTotalBalance: String! } -type GqlPoolStablePhantomPoolData { +type GqlPoolStableComposablePoolData { id: ID! address: String! symbol: String! @@ -629,7 +628,7 @@ enum GqlPoolFilterType { WEIGHTED STABLE META_STABLE - PHANTOM_STABLE + COMPOSABLE_STABLE ELEMENT LINEAR UNKNOWN From 1b2d0e7db2a336d566bd18cd80467644e1e765e6 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 10:50:24 +0100 Subject: [PATCH 078/175] pool creation phantom stable as composable stable v0 --- modules/pool/lib/pool-creator.service.ts | 31 +++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index fb22dc4fb..46c16d490 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -88,7 +88,9 @@ export class PoolCreatorService { const nestedPool = subgraphPools.find((nestedPool) => { const poolType = this.mapSubgraphPoolTypeToPoolType(nestedPool.poolType || ''); - return nestedPool.address === token.address && (poolType === 'LINEAR' || poolType === 'PHANTOM_STABLE'); + return ( + nestedPool.address === token.address && (poolType === 'LINEAR' || poolType === 'COMPOSABLE_STABLE') + ); }); if (nestedPool) { @@ -160,7 +162,7 @@ export class PoolCreatorService { const allNestedTypePools = await prisma.prismaPool.findMany({ where: { chain: networkContext.chain, - type: { in: [PrismaPoolType.LINEAR, PrismaPoolType.PHANTOM_STABLE] }, + type: { in: [PrismaPoolType.LINEAR, PrismaPoolType.COMPOSABLE_STABLE] }, }, select: { id: true, address: true }, }); @@ -185,6 +187,12 @@ export class PoolCreatorService { ], }); + // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 + let poolTypeVersion = pool.poolTypeVersion ? pool.poolTypeVersion : 1; + if (pool.poolType === 'PHANTOM_STABLE') { + poolTypeVersion = 0; + } + await prisma.prismaPool.create({ data: { id: pool.id, @@ -195,7 +203,7 @@ export class PoolCreatorService { name: pool.name || '', decimals: 18, type: poolType, - version: pool.poolTypeVersion ? pool.poolTypeVersion : 1, + version: poolTypeVersion, owner: pool.owner || ZERO_ADDRESS, factory: pool.factory, tokens: { @@ -256,7 +264,7 @@ export class PoolCreatorService { } : undefined, stableDynamicData: - poolType === 'STABLE' || poolType === 'PHANTOM_STABLE' || poolType === 'META_STABLE' + poolType === 'STABLE' || poolType === 'COMPOSABLE_STABLE' || poolType === 'META_STABLE' ? { create: { id: pool.id, @@ -364,9 +372,9 @@ export class PoolCreatorService { if (poolType === 'LINEAR') { return 0; - } else if (poolType === 'PHANTOM_STABLE') { - //if the phantom stable has a nested phantom stable, it needs to appear later in the list - const nestedPhantomStableToken = (pool.tokens || []).find((token) => { + } else if (poolType === 'COMPOSABLE_STABLE') { + //if the composable stable has a nested composable stable, it needs to appear later in the list + const nestedComposableStableToken = (pool.tokens || []).find((token) => { if (token.address === pool.address) { return false; } @@ -374,10 +382,10 @@ export class PoolCreatorService { const nestedPool = subgraphPools.find((nestedPool) => nestedPool.address === token.address); const nestedPoolType = this.mapSubgraphPoolTypeToPoolType(nestedPool?.poolType || ''); - return nestedPoolType === 'PHANTOM_STABLE'; + return nestedPoolType === 'COMPOSABLE_STABLE'; }); - return nestedPhantomStableToken ? 2 : 1; + return nestedComposableStableToken ? 2 : 1; } return 3; @@ -394,10 +402,11 @@ export class PoolCreatorService { return 'STABLE'; case 'MetaStable': return 'META_STABLE'; + // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 case 'StablePhantom': - return 'PHANTOM_STABLE'; + return 'COMPOSABLE_STABLE'; case 'ComposableStable': - return 'PHANTOM_STABLE'; + return 'COMPOSABLE_STABLE'; case 'Linear': return 'LINEAR'; case 'Element': From 991e36a2b9a5f5a17526bfb1b8df6e4f637c9e0f Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 10:51:50 +0100 Subject: [PATCH 079/175] add comment --- modules/pool/lib/pool-gql-loader.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index ec211a6d5..3440b4168 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -80,6 +80,7 @@ export class PoolGqlLoaderService { public mapToMinimalGqlPool(pool: PrismaPoolMinimal): GqlPoolMinimal { return { ...pool, + // we need this mapping because we migrated from PHANTOMSTABLE type to COMPOSABLESTABLE in the DB, but now only return COMPOSABLESTABLE type: pool.type === 'PHANTOM_STABLE' ? PrismaPoolType.COMPOSABLE_STABLE : pool.type, decimals: 18, dynamicData: this.getPoolDynamicData(pool), From 84c236e9dba81eb82eecb39dc4bf9c8159b7333b Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 11:03:12 +0100 Subject: [PATCH 080/175] add update mutation --- modules/pool/lib/pool-creator.service.ts | 36 ++++++++++++++++++++++++ modules/pool/pool.gql | 2 +- modules/pool/pool.resolvers.ts | 4 +-- modules/pool/pool.service.ts | 20 ++----------- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index 46c16d490..e9c68111e 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -366,6 +366,42 @@ export class PoolCreatorService { } } + public async updatePoolTypesAndVersionForAllPools() { + const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); + + for (const subgraphPool of subgraphPools) { + // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 + let poolTypeVersion = subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1; + if (subgraphPool.poolType === 'PHANTOM_STABLE') { + poolTypeVersion = 0; + } + + const poolType = this.mapSubgraphPoolTypeToPoolType(subgraphPool.poolType || ''); + + try { + await prisma.prismaPool.update({ + where: { id_chain: { chain: networkContext.chain, id: subgraphPool.id } }, + data: { + version: poolTypeVersion, + type: poolType, + }, + }); + } catch (e: any) { + // Some pools are filtered from the DB, like test pools, + // so we just ignore them without breaking the loop + const error = e.meta ? e.meta.cause : e; + console.error( + 'Error in updating pool versions: ', + error, + 'Network', + networkContext.chain, + 'Pool ID: ', + subgraphPool.id, + ); + } + } + } + private sortSubgraphPools(subgraphPools: BalancerPoolFragment[]) { return _.sortBy(subgraphPools, (pool) => { const poolType = this.mapSubgraphPoolTypeToPoolType(pool.poolType || ''); diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 87332397f..cc3ca9675 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -54,7 +54,7 @@ extend type Mutation { poolBlackListAddPool(poolId: String!): String! poolBlackListRemovePool(poolId: String!): String! poolDeletePool(poolId: String!): String! - poolSyncAllPoolVersions: String! + poolSyncAllPoolTypesVersions: String! poolInitOnChainDataForAllPools: String! } diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index e9fe7431b..9edbe1cfd 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -313,10 +313,10 @@ const balancerResolvers: Resolvers = { return 'success'; }, - poolSyncAllPoolVersions: async (parent, {}, context) => { + poolSyncAllPoolTypesVersions: async (parent, {}, context) => { isAdminRoute(context); - await poolService.syncPoolVersionForAllPools(); + await poolService.syncPoolTypeAndVersionForAllPools(); return 'success'; }, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index ae54c3e2b..5555bdbc4 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -353,24 +353,8 @@ export class PoolService { await this.poolSyncService.setPoolsWithPreferredGaugesAsIncentivized(); } - public async syncPoolVersionForAllPools() { - const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); - - for (const subgraphPool of subgraphPools) { - try { - await prisma.prismaPool.update({ - where: { id_chain: { chain: this.chain, id: subgraphPool.id } }, - data: { - version: subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1, - }, - }); - } catch (e: any) { - // Some pools are filtered from the DB, like test pools, - // so we just ignore them without breaking the loop - const error = e.meta ? e.meta.cause : e; - console.error(error, 'Network', networkContext.chain, 'Pool ID: ', subgraphPool.id); - } - } + public async syncPoolTypeAndVersionForAllPools() { + await this.poolCreatorService.updatePoolTypesAndVersionForAllPools(); } public async addToBlackList(poolId: string) { From 32f5898da7787a7d61125b6f32484b6d0879c82e Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 11:12:12 +0100 Subject: [PATCH 081/175] get rid of factory tracking --- modules/network/arbitrum.ts | 17 ++++--------- modules/network/avalanche.ts | 13 ++++------ modules/network/base.ts | 7 +++--- modules/network/fantom.ts | 24 ++++--------------- modules/network/gnosis.ts | 16 ++++--------- modules/network/mainnet.ts | 19 +-------------- modules/network/network-config-types.ts | 4 ---- modules/network/optimism.ts | 16 ++++--------- modules/network/polygon.ts | 17 ++++--------- modules/network/zkevm.ts | 11 ++++----- .../pool/lib/pool-on-chain-data.service.ts | 10 ++------ modules/pool/lib/pool-utils.ts | 17 ++----------- 12 files changed, 37 insertions(+), 134 deletions(-) diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 20bb523f5..e4fedf51b 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -72,18 +72,6 @@ const arbitrumNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: [ - '0xaeb406b0e430bf5ea2dc0b9fe62e4e53f74b3a33', - '0x85a80afee867adf27b50bdb7b76da70f1e853062', - '0x1c99324edc771c82a0dccb780cc7dda0045e50e7', - '0x2498a2b0d6462d2260eac50ae1c3e03f4829ba95', - '0xa8920455934da4d853faac1f94fe7bef72943ef1', - ], - weightedPoolV2Factories: [ - '0x8df6efec5547e31b0eb7d1291b511ff8a2bf987c', - '0xf1665e19bc105be4edd3739f88315cc699cc5b65', - '0xc7e5ed1054a24ef31d827e6f86caa58b3bc168d7', - ], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -251,7 +239,10 @@ export const arbitrumNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(arbitrumNetworkData.subgraphs.balancer, arbitrumNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + arbitrumNetworkData.subgraphs.balancer, + arbitrumNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index f696e5005..b93ee348b 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -70,14 +70,6 @@ const avalancheNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: [ - '0x3b1eb8eb7b43882b385ab30533d9a2bef9052a98', - '0xe42ffa682a26ef8f25891db4882932711d42e467', - ], - weightedPoolV2Factories: [ - '0x94f68b54191f62f781fe8298a8a5fa3ed772d227', - '0x230a59f4d9adc147480f03b0d3fffecd56c3289a', - ], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -241,7 +233,10 @@ export const avalancheNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(avalancheNetworkData.subgraphs.balancer, avalancheNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + avalancheNetworkData.subgraphs.balancer, + avalancheNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/base.ts b/modules/network/base.ts index c25d9b91e..be69466e9 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -70,8 +70,6 @@ const baseNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: ['0x8df317a729fcaa260306d7de28888932cb579b88'], - weightedPoolV2Factories: ['0x4c32a8a8fda4e24139b51b456b42290f51d6a1c4'], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -139,7 +137,10 @@ export const baseNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(baseNetworkData.subgraphs.balancer, baseNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + baseNetworkData.subgraphs.balancer, + baseNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index a266d331c..0bd2f7bd5 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -116,27 +116,8 @@ const fantomNetworkData: NetworkData = { }, balancer: { vault: '0x20dd72ed959b6147912c2e529f0a0c651c33c9ce', - composableStablePoolFactories: [ - '0x5adaf6509bcec3219455348ac45d6d3261b1a990', - '0xb384a86f2fd7788720db42f9daa60fc07ecbea06', - '0x44814e3a603bb7f1198617995c5696c232f6e8ed', - '0x911566c808bf00acb200b418564440a2af177548', - '0x5c3094982cf3c97a06b7d62a6f7669f14a199b19', - '0x23f03a4fb344d8b98833d2ace093cc305e03474f', - ], - weightedPoolV2Factories: [ - '0xb2ed595afc445b47db7043bec25e772bf0fa1fbb', - '0x8ea1c497c16726e097f62c8c9fbd944143f27090', - '0xea87f3dffc679035653c0fba70e7bfe46e3fb733', - '0xd678b6acd834cc969bb19ce82727f2a541fb7941', - '0xb841df73861e65e6d61a80f503f095a91ce75e15', - ], swapProtocolFeePercentage: 0.25, yieldProtocolFeePercentage: 0.25, - factoriesWithpoolSpecificProtocolFeePercentagesProvider: [ - '0xb841df73861e65e6d61a80f503f095a91ce75e15', - '0x5c3094982cf3c97a06b7d62a6f7669f14a199b19', - ], }, multicall: '0x66335d7ad8011f6aa3f48aadcb523b62b38ed961', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', @@ -347,7 +328,10 @@ export const fantomNetworkConfig: NetworkConfig = { new UserSyncReliquaryFarmBalanceService(fantomNetworkData.reliquary!.address), ], services: { - balancerSubgraphService: new BalancerSubgraphService(fantomNetworkData.subgraphs.balancer, fantomNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + fantomNetworkData.subgraphs.balancer, + fantomNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index d00aa09f0..57b91773d 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -68,17 +68,6 @@ const gnosisNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: [ - '0x76578ecf9a141296ec657847fb45b0585bcda3a6', - '0xc128468b7ce63ea702c1f104d55a2566b13d3abd', - '0xd87f44df0159dc78029ab9ca7d7e57e7249f5acd', - '0x4bdcc2fb18aeb9e2d281b0278d946445070eada7', - ], - weightedPoolV2Factories: [ - '0x6cad2ea22bfa7f4c14aae92e47f510cd5c509bc7', - '0xf302f9f50958c5593770fdf4d4812309ff77414f', - '0xc128a9954e6c874ea3d62ce62b468ba073093f25', - ], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -168,7 +157,10 @@ export const gnosisNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(gnosisNetworkData.subgraphs.balancer, gnosisNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + gnosisNetworkData.subgraphs.balancer, + gnosisNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 8e77f13ff..2b1bbdfa7 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -83,22 +83,8 @@ const data: NetworkData = { balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', tokenAdmin: '0xf302f9f50958c5593770fdf4d4812309ff77414f', - composableStablePoolFactories: [ - '0xf9ac7b9df2b3454e841110cce5550bd5ac6f875f', - '0x85a80afee867adf27b50bdb7b76da70f1e853062', - '0xdba127fbc23fb20f5929c546af220a991b5c6e01', - '0xfada0f4547ab2de89d1304a668c39b3e09aa7c76', - '0xdb8d758bcb971e482b2c45f7f8a7740283a1bd3a', - '0xba1b4a90bad57470a2cba762a32955dc491f76e0', - ], - weightedPoolV2Factories: [ - '0xcc508a455f5b0073973107db6a878ddbdab957bc', - '0x5dd94da3644ddd055fcf6b3e1aa310bb7801eb8b', - '0x897888115ada5773e02aa29f775430bfb5f34c51', - ], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, - excludedPoolDataQueryPoolIds: ['0xf71d0774b214c4cf51e33eb3d30ef98132e4dbaa00000000000000000000046e'], }, multicall: '0x5ba1e12693dc8f9c48aad8770482f4739beed696', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', @@ -378,10 +364,7 @@ export const mainnetNetworkConfig: NetworkConfig = { data.balancer.yieldProtocolFeePercentage, data.balancer.swapProtocolFeePercentage, ), - new PhantomStableAprService( - data.chain.prismaId, - data.balancer.yieldProtocolFeePercentage, - ), + new PhantomStableAprService(data.chain.prismaId, data.balancer.yieldProtocolFeePercentage), new BoostedPoolAprService(), new SwapFeeAprService(data.balancer.swapProtocolFeePercentage), new GaugeAprService(tokenService, [data.bal!.address]), diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index 10c8e33dd..e921c65b3 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -104,12 +104,8 @@ export interface NetworkData { balancer: { vault: string; tokenAdmin?: string; - weightedPoolV2Factories: string[]; - composableStablePoolFactories: string[]; yieldProtocolFeePercentage: number; swapProtocolFeePercentage: number; - excludedPoolDataQueryPoolIds?: string[]; - factoriesWithpoolSpecificProtocolFeePercentagesProvider?: string[]; }; multicall: string; multicall3: string; diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index af9a1a00d..c16361a65 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -81,17 +81,6 @@ const optimismNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: [ - '0xf145cafb67081895ee80eb7c04a30cf87f07b745', - '0xe2e901ab09f37884ba31622df3ca7fc19aa443be', - '0x1802953277fd955f9a254b80aa0582f193cf1d77', - '0x043a2dad730d585c44fb79d2614f295d2d625412', - ], - weightedPoolV2Factories: [ - '0xad901309d9e9dbc5df19c84f729f429f0189a633', - '0xa0dabebaad1b243bbb243f933013d560819eb66f', - '0x230a59f4d9adc147480f03b0d3fffecd56c3289a', - ], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -296,7 +285,10 @@ export const optimismNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(optimismNetworkData.subgraphs.balancer, optimismNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + optimismNetworkData.subgraphs.balancer, + optimismNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index 8fd44b1fd..9407a51fa 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -72,18 +72,6 @@ const polygonNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: [ - '0x136fd06fa01ecf624c7f2b3cb15742c1339dc2c4', - '0x85a80afee867adf27b50bdb7b76da70f1e853062', - '0x7bc6c0e73edaa66ef3f6e2f27b0ee8661834c6c9', - '0x6ab5549bbd766a43afb687776ad8466f8b42f777', - '0xe2fa4e1d17725e72dcdafe943ecf45df4b9e285b', - ], - weightedPoolV2Factories: [ - '0x0e39c3d9b2ec765efd9c5c70bb290b1fcd8536e3', - '0x82e4cfaef85b1b6299935340c964c942280327f4', - '0xfc8a407bba312ac761d8bfe04ce1201904842b76', - ], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -286,7 +274,10 @@ export const polygonNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(polygonNetworkData.subgraphs.balancer, polygonNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + polygonNetworkData.subgraphs.balancer, + polygonNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 77ed05875..0a2f93c23 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -70,12 +70,6 @@ const zkevmNetworkData: NetworkData = { }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - composableStablePoolFactories: [ - '0x8ea89804145c007e7d226001a96955ad53836087', - '0x956ccab09898c0af2aca5e6c229c3ad4e93d9288', - '0x577e5993b9cc480f07f98b5ebd055604bd9071c4' - ], - weightedPoolV2Factories: ['0x03f3fb107e74f2eac9358862e91ad3c692712054'], swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, @@ -187,7 +181,10 @@ export const zkevmNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(zkevmNetworkData.subgraphs.balancer, zkevmNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + zkevmNetworkData.subgraphs.balancer, + zkevmNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 72bef5972..c60c15d85 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -1,5 +1,5 @@ import { formatFixed } from '@ethersproject/bignumber'; -import { Chain, PrismaPoolType } from '@prisma/client'; +import { PrismaPoolType } from '@prisma/client'; import { isSameAddress } from '@balancer-labs/sdk'; import { prisma } from '../../../prisma/prisma-client'; import { isComposableStablePool, isStablePool } from './pool-utils'; @@ -33,7 +33,6 @@ export class PoolOnChainDataService { vaultAddress: networkContext.data.balancer.vault, yieldProtocolFeePercentage: networkContext.data.balancer.yieldProtocolFeePercentage, gyroConfig: networkContext.data.gyro?.config, - composableStableFactories: networkContext.data.balancer.composableStablePoolFactories, }; } @@ -95,12 +94,7 @@ export class PoolOnChainDataService { }); const gyroPools = filteredPools.filter((pool) => pool.type.includes('GYRO')); - const poolsWithComposableStableType = filteredPools.map((pool) => ({ - ...pool, - type: (isComposableStablePool(pool) ? 'COMPOSABLE_STABLE' : pool.type) as - | PrismaPoolType - | 'COMPOSABLE_STABLE', - })); + const poolsWithComposableStableType = filteredPools.filter((pool) => isComposableStablePool(pool)); const tokenPrices = await this.tokenService.getTokenPrices(); const onchainResults = await fetchOnChainPoolData( diff --git a/modules/pool/lib/pool-utils.ts b/modules/pool/lib/pool-utils.ts index d1cc25d06..321575fd9 100644 --- a/modules/pool/lib/pool-utils.ts +++ b/modules/pool/lib/pool-utils.ts @@ -1,7 +1,4 @@ import { PrismaPoolDynamicData, PrismaPoolType } from '@prisma/client'; -import { isSameAddress } from '@balancer-labs/sdk'; -import { networkContext } from '../../network/network-context.service'; -import { prisma } from '../../../prisma/prisma-client'; type PoolWithTypeAndFactory = { address: string; @@ -16,21 +13,11 @@ export function isStablePool(poolType: PrismaPoolType) { } export function isWeightedPoolV2(pool: PoolWithTypeAndFactory) { - return ( - pool.type === 'WEIGHTED' && - networkContext.data.balancer.weightedPoolV2Factories.find((factory) => - isSameAddress(pool.factory || '', factory), - ) !== undefined - ); + return pool.type === 'WEIGHTED' && pool.version >= 2; } export function isComposableStablePool(pool: PoolWithTypeAndFactory) { - return ( - pool.type === 'PHANTOM_STABLE' && - networkContext.data.balancer.composableStablePoolFactories.find((factory) => - isSameAddress(pool.factory || '', factory), - ) !== undefined - ); + return pool.type === 'COMPOSABLE_STABLE' && pool.version > 0; } export function collectsYieldFee(pool: PoolWithTypeAndFactory) { From c7e05bbbd25d2db28d29d316b47105e8acfc37a6 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 11:24:31 +0100 Subject: [PATCH 082/175] add phantomstable to gql schema again --- modules/pool/lib/pool-creator.service.ts | 4 ++-- modules/pool/lib/pool-gql-loader.service.ts | 2 -- modules/pool/pool.gql | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index e9c68111e..0108cba5a 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -189,7 +189,7 @@ export class PoolCreatorService { // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 let poolTypeVersion = pool.poolTypeVersion ? pool.poolTypeVersion : 1; - if (pool.poolType === 'PHANTOM_STABLE') { + if (pool.poolType === 'StablePhantom') { poolTypeVersion = 0; } @@ -372,7 +372,7 @@ export class PoolCreatorService { for (const subgraphPool of subgraphPools) { // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 let poolTypeVersion = subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1; - if (subgraphPool.poolType === 'PHANTOM_STABLE') { + if (subgraphPool.poolType === 'StablePhantom') { poolTypeVersion = 0; } diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 3440b4168..0e5686456 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -80,8 +80,6 @@ export class PoolGqlLoaderService { public mapToMinimalGqlPool(pool: PrismaPoolMinimal): GqlPoolMinimal { return { ...pool, - // we need this mapping because we migrated from PHANTOMSTABLE type to COMPOSABLESTABLE in the DB, but now only return COMPOSABLESTABLE - type: pool.type === 'PHANTOM_STABLE' ? PrismaPoolType.COMPOSABLE_STABLE : pool.type, decimals: 18, dynamicData: this.getPoolDynamicData(pool), allTokens: this.mapAllTokens(pool), diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index cc3ca9675..17599275c 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -81,6 +81,7 @@ enum GqlPoolMinimalType { WEIGHTED STABLE META_STABLE + PHANTOM_STABLE COMPOSABLE_STABLE ELEMENT LINEAR From a6342c9852ec9ccd557fb42f4c3b7cab0cce7b0f Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 12:01:55 +0100 Subject: [PATCH 083/175] only have one set of resolvers and schema, keep folders for dev --- app.ts | 10 ++++------ app/gql/resolvers.ts | 8 ++------ codegen.yml | 24 +++--------------------- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/app.ts b/app.ts index 225f53f74..6923dbdce 100644 --- a/app.ts +++ b/app.ts @@ -12,10 +12,8 @@ import { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginUsageReporting, } from 'apollo-server-core'; -import { ApolloServerPlugin } from 'apollo-server-plugin-base'; -import { beethovenSchema } from './graphql_schema_generated_beethoven'; -import { balancerSchema } from './graphql_schema_generated_balancer'; -import { balancerResolvers, beethovenResolvers } from './app/gql/resolvers'; +import { schema } from './graphql_schema_generated'; +import { resolvers } from './app/gql/resolvers'; import helmet from 'helmet'; import GraphQLJSON from 'graphql-type-json'; import * as Sentry from '@sentry/node'; @@ -103,9 +101,9 @@ async function startServer() { const server = new ApolloServer({ resolvers: { JSON: GraphQLJSON, - ...(env.PROTOCOL === 'beethoven' ? beethovenResolvers : balancerResolvers), + ...resolvers, }, - typeDefs: env.PROTOCOL === 'beethoven' ? beethovenSchema : balancerSchema, + typeDefs: schema, introspection: true, plugins, context: ({ req }) => req.context, diff --git a/app/gql/resolvers.ts b/app/gql/resolvers.ts index 29e80c1a4..b6794c924 100644 --- a/app/gql/resolvers.ts +++ b/app/gql/resolvers.ts @@ -2,9 +2,5 @@ import { loadFilesSync } from '@graphql-tools/load-files'; import path from 'path'; import { mergeResolvers } from '@graphql-tools/merge'; -const balancerResolversArray = loadFilesSync(path.join(__dirname, '../../modules/!(beethoven)/**/*.resolvers.*')); - -const beethovenResolversArray = loadFilesSync(path.join(__dirname, '../../modules/!(balancer)/**/*.resolvers.*')); - -export const balancerResolvers = mergeResolvers(balancerResolversArray); -export const beethovenResolvers = mergeResolvers(beethovenResolversArray); +const resolversArray = loadFilesSync(path.join(__dirname, '../../modules/**/*.resolvers.*')); +export const resolvers = mergeResolvers(resolversArray); diff --git a/codegen.yml b/codegen.yml index 958b0e043..57c4ff13b 100644 --- a/codegen.yml +++ b/codegen.yml @@ -137,31 +137,13 @@ generates: schema: ${USER_SNAPSHOT_SUBGRAPH} plugins: - schema-ast - graphql_schema_generated_balancer.ts: - schema: - - ./modules/**/*.gql - - '!./modules/beethoven/beets.gql' - - '!./modules/**/*.beets.gql' - plugins: - - add: - content: | - import { gql } from 'apollo-server-express'; - export const balancerSchema = gql` - #\n# THIS FILE IS AUTOGENERATED — DO NOT EDIT IT\n# - - schema-ast - - add: - placement: 'append' - content: '`;' - graphql_schema_generated_beethoven.ts: - schema: - - ./modules/**/*.gql - - '!./modules/balancer/balancer.gql' - - '!./modules/**/*.balancer.gql' + graphql_schema_generated.ts: + schema: ./modules/**/*.gql plugins: - add: content: | import { gql } from 'apollo-server-express'; - export const beethovenSchema = gql` + export const schema = gql` #\n# THIS FILE IS AUTOGENERATED — DO NOT EDIT IT\n# - schema-ast - add: From b7e9ab0f2bbb628df4e53196d73f36404c2ccc93 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 12:12:35 +0100 Subject: [PATCH 084/175] protocol stats chain param --- modules/protocol/protocol.gql | 4 ++-- modules/protocol/protocol.resolvers.ts | 23 ++++++++++++++++++----- modules/protocol/protocol.service.ts | 24 ++++++++++++------------ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/modules/protocol/protocol.gql b/modules/protocol/protocol.gql index 0a63a8532..cb27c1c5c 100644 --- a/modules/protocol/protocol.gql +++ b/modules/protocol/protocol.gql @@ -1,6 +1,6 @@ extend type Query { - protocolMetricsChain: GqlProtocolMetricsChain! - protocolMetricsAggregated(chainIds: [String!]!): GqlProtocolMetricsAggregated! + protocolMetricsChain(chain: GqlChain!): GqlProtocolMetricsChain! + protocolMetricsAggregated(chains: [GqlChain!]): GqlProtocolMetricsAggregated! latestSyncedBlocks: GqlLatestSyncedBlocks! } diff --git a/modules/protocol/protocol.resolvers.ts b/modules/protocol/protocol.resolvers.ts index 829681c29..09fa59460 100644 --- a/modules/protocol/protocol.resolvers.ts +++ b/modules/protocol/protocol.resolvers.ts @@ -1,14 +1,27 @@ import { GqlLatestSyncedBlocks, Resolvers } from '../../schema'; import { protocolService } from './protocol.service'; import { networkContext } from '../network/network-context.service'; +import { headerChain } from '../context/header-chain'; const protocolResolvers: Resolvers = { Query: { - protocolMetricsChain: async (parent, args, context) => { - return protocolService.getMetrics(networkContext.chainId); + protocolMetricsChain: async (parent, { chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('poolGetPool error: Provide "chain" param'); + } + return protocolService.getMetrics(chain); }, - protocolMetricsAggregated: async (parent, { chainIds }, context) => { - return protocolService.getAggregatedMetrics(chainIds); + protocolMetricsAggregated: async (parent, { chains }, context) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('tokenGetTokens error: Provide "chains" param'); + } + return protocolService.getAggregatedMetrics(chains); }, latestSyncedBlocks: async (): Promise => { return protocolService.getLatestSyncedBlocks(); @@ -16,7 +29,7 @@ const protocolResolvers: Resolvers = { }, Mutation: { protocolCacheMetrics: async (): Promise => { - await protocolService.cacheProtocolMetrics(networkContext.chainId, networkContext.chain); + await protocolService.cacheProtocolMetrics(networkContext.chain); return 'success'; }, }, diff --git a/modules/protocol/protocol.service.ts b/modules/protocol/protocol.service.ts index d64085b84..68dccc425 100644 --- a/modules/protocol/protocol.service.ts +++ b/modules/protocol/protocol.service.ts @@ -5,7 +5,7 @@ import { Cache } from 'memory-cache'; import { Chain, PrismaLastBlockSyncedCategory, PrismaUserBalanceType } from '@prisma/client'; import _ from 'lodash'; import { networkContext } from '../network/network-context.service'; -import { AllNetworkConfigs } from '../network/network-config'; +import { AllNetworkConfigs, AllNetworkConfigsKeyedOnChain } from '../network/network-config'; import { GqlProtocolMetricsAggregated, GqlProtocolMetricsChain } from '../../schema'; import { GraphQLClient } from 'graphql-request'; import { getSdk } from '../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; @@ -24,12 +24,12 @@ export class ProtocolService { constructor() {} - public async getAggregatedMetrics(chainIds: string[]): Promise { + public async getAggregatedMetrics(chains: Chain[]): Promise { const chainMetrics: GqlProtocolMetricsChain[] = []; - for (const chainId of chainIds) { + for (const chain of chains) { // this should resolve quickly if all chains are cached, possible to get slammed by an unlucky query though - const metrics = await this.getMetrics(chainId); + const metrics = await this.getMetrics(chain); chainMetrics.push(metrics); } @@ -60,22 +60,22 @@ export class ProtocolService { }; } - public async getMetrics(chainId: string): Promise { - const cached = this.cache.get(`${PROTOCOL_METRICS_CACHE_KEY}:${chainId}`); + public async getMetrics(chain: Chain): Promise { + const cached = this.cache.get(`${PROTOCOL_METRICS_CACHE_KEY}:${chain}`); if (cached) { return cached; } - return this.cacheProtocolMetrics(chainId, AllNetworkConfigs[chainId].data.chain.prismaId); + return this.cacheProtocolMetrics(chain); } - public async cacheProtocolMetrics(chainId: string, chain: Chain): Promise { + public async cacheProtocolMetrics(chain: Chain): Promise { const oneDayAgo = moment().subtract(24, 'hours').unix(); const startOfDay = moment().startOf('day').unix(); const sevenDayRange = moment().startOf('day').subtract(7, 'days').unix(); - const client = new GraphQLClient(AllNetworkConfigs[chainId].data.subgraphs.balancer); + const client = new GraphQLClient(AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer); const subgraphClient = getSdk(client); const { balancers } = await subgraphClient.BalancerProtocolData({}); @@ -125,10 +125,10 @@ export class ProtocolService { }, }); - const balancerV1Tvl = await this.getBalancerV1Tvl(chainId); + const balancerV1Tvl = await this.getBalancerV1Tvl(`${AllNetworkConfigsKeyedOnChain[chain].data.chain.id}`); const protocolData = { - chainId, + chainId: `${AllNetworkConfigsKeyedOnChain[chain].data.chain.id}`, totalLiquidity: `${totalLiquidity + balancerV1Tvl}`, totalSwapFee, totalSwapVolume, @@ -141,7 +141,7 @@ export class ProtocolService { numLiquidityProviders: `${holdersQueryResponse._sum.holdersCount || '0'}`, }; - this.cache.put(`${PROTOCOL_METRICS_CACHE_KEY}:${chainId}`, protocolData, 60 * 30 * 1000); + this.cache.put(`${PROTOCOL_METRICS_CACHE_KEY}:${chain}`, protocolData, 60 * 30 * 1000); return protocolData; } From 6651086319c4ad09019f390b3f5fc30fc4bbb737 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 12:14:04 +0100 Subject: [PATCH 085/175] cleanup --- modules/protocol/protocol.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/protocol/protocol.service.ts b/modules/protocol/protocol.service.ts index 68dccc425..45af70a3f 100644 --- a/modules/protocol/protocol.service.ts +++ b/modules/protocol/protocol.service.ts @@ -1,11 +1,10 @@ import moment from 'moment-timezone'; import { prisma } from '../../prisma/prisma-client'; -import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { Cache } from 'memory-cache'; import { Chain, PrismaLastBlockSyncedCategory, PrismaUserBalanceType } from '@prisma/client'; import _ from 'lodash'; import { networkContext } from '../network/network-context.service'; -import { AllNetworkConfigs, AllNetworkConfigsKeyedOnChain } from '../network/network-config'; +import { AllNetworkConfigsKeyedOnChain } from '../network/network-config'; import { GqlProtocolMetricsAggregated, GqlProtocolMetricsChain } from '../../schema'; import { GraphQLClient } from 'graphql-request'; import { getSdk } from '../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; From 0c57ba7528e1b5c116844feaea86b68f56eb5d79 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 12:15:48 +0100 Subject: [PATCH 086/175] optional param --- modules/protocol/protocol.gql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/protocol/protocol.gql b/modules/protocol/protocol.gql index cb27c1c5c..c9af32d44 100644 --- a/modules/protocol/protocol.gql +++ b/modules/protocol/protocol.gql @@ -1,5 +1,5 @@ extend type Query { - protocolMetricsChain(chain: GqlChain!): GqlProtocolMetricsChain! + protocolMetricsChain(chain: GqlChain): GqlProtocolMetricsChain! protocolMetricsAggregated(chains: [GqlChain!]): GqlProtocolMetricsAggregated! latestSyncedBlocks: GqlLatestSyncedBlocks! } From e3fec90897015b0c36a9564e58fc4a80ac6356ab Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 21 Nov 2023 12:51:34 +0100 Subject: [PATCH 087/175] fix worker --- worker/job-handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 8225f9ee7..4da33e683 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -173,7 +173,7 @@ export function configureWorkerRoutes(app: Express) { await runIfNotAlreadyRunning( job.name, chainId, - () => protocolService.cacheProtocolMetrics(networkContext.chainId, networkContext.chain), + () => protocolService.cacheProtocolMetrics(networkContext.chain), res, next, ); From 0717c07bd75652d9913c578d66aa299d767ad271 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:36:53 +0100 Subject: [PATCH 088/175] Update pool-onchain-data.ts --- modules/pool/lib/pool-onchain-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index 189c5f62e..eee9af4ed 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -76,7 +76,7 @@ const getSwapFeeFn = (type: string) => { }; const getTotalSupplyFn = (type: PoolInput['type'], version: number) => { - if (['LINEAR', 'PHANTOM_STABLE'].includes(type)) { + if (['LINEAR'].includes(type) || (type === 'COMPOSABLE_STABLE' && version === 0)) { return 'getVirtualSupply'; } else if ( type === 'COMPOSABLE_STABLE' || From 034549f3c03c460be333e3ecdb500edb94b04c48 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:37:51 +0100 Subject: [PATCH 089/175] Update pool-on-chain-data.service.ts --- modules/pool/lib/pool-on-chain-data.service.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index c60c15d85..2a0e70d9d 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -94,14 +94,9 @@ export class PoolOnChainDataService { }); const gyroPools = filteredPools.filter((pool) => pool.type.includes('GYRO')); - const poolsWithComposableStableType = filteredPools.filter((pool) => isComposableStablePool(pool)); const tokenPrices = await this.tokenService.getTokenPrices(); - const onchainResults = await fetchOnChainPoolData( - poolsWithComposableStableType, - this.options.vaultAddress, - 1024, - ); + const onchainResults = await fetchOnChainPoolData(filteredPools, this.options.vaultAddress, 1024); const gyroFees = await (this.options.gyroConfig ? fetchOnChainGyroFees(gyroPools, this.options.gyroConfig, 1024) : Promise.resolve({} as { [address: string]: string })); From 06c2a0ff993e4af92d79fcb1f9ef7bd0f54c43b6 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 27 Nov 2023 15:53:37 +0100 Subject: [PATCH 090/175] start user balances for pools --- modules/pool/lib/pool-gql-loader.service.ts | 36 +++++++++++++++++++-- modules/pool/pool.gql | 7 ++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 71dc8cc40..3d46b8439 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -11,7 +11,6 @@ import { import { GqlBalancePoolAprItem, GqlBalancePoolAprSubItem, - GqlChain, GqlPoolDynamicData, GqlPoolFeaturedPoolGroup, GqlPoolInvestConfig, @@ -27,6 +26,7 @@ import { GqlPoolTokenExpanded, GqlPoolTokenUnion, GqlPoolUnion, + GqlPoolUserBalance, GqlPoolWithdrawConfig, GqlPoolWithdrawOption, QueryPoolGetPoolsArgs, @@ -34,7 +34,7 @@ import { import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; -import { Chain, Prisma, PrismaPoolAprType } from '@prisma/client'; +import { Chain, Prisma, PrismaPoolAprType, PrismaUserStakedBalance, PrismaUserWalletBalance } from '@prisma/client'; import { isWeightedPoolV2 } from './pool-utils'; import { oldBnum } from '../../big-number/old-big-number'; import { networkContext } from '../../network/network-context.service'; @@ -59,6 +59,24 @@ export class PoolGqlLoaderService { } public async getPools(args: QueryPoolGetPoolsArgs): Promise { + // only include wallet and staked balances if the query requests it + // this makes sure that we don't load ALL user balances when we don't filter on userAddress + + if (args.where?.userAddress) { + const pools = await prisma.prismaPool.findMany({ + ...this.mapQueryArgsToPoolQuery(args), + include: { + ...prismaPoolMinimal.include, + userWalletBalances: true, + userStakedBalances: true, + }, + }); + + return pools.map((pool) => + this.mapToMinimalGqlPool(pool, pool.userWalletBalances, pool.userStakedBalances), + ); + } + const pools = await prisma.prismaPool.findMany({ ...this.mapQueryArgsToPoolQuery(args), include: prismaPoolMinimal.include, @@ -77,7 +95,11 @@ export class PoolGqlLoaderService { return pools.map((pool) => this.mapPoolToGqlPool(pool)) as GqlPoolLinear[]; } - public mapToMinimalGqlPool(pool: PrismaPoolMinimal): GqlPoolMinimal { + public mapToMinimalGqlPool( + pool: PrismaPoolMinimal, + userWalletbalances: PrismaUserWalletBalance[] = [], + userStakedBalances: PrismaUserStakedBalance[] = [], + ): GqlPoolMinimal { return { ...pool, decimals: 18, @@ -85,6 +107,7 @@ export class PoolGqlLoaderService { allTokens: this.mapAllTokens(pool), displayTokens: this.mapDisplayTokens(pool), staking: this.getStakingData(pool), + userBalance: this.getUserBalance(userWalletbalances, userStakedBalances), }; } @@ -570,6 +593,13 @@ export class PoolGqlLoaderService { }; } + private getUserBalance( + userWalletBalances: PrismaUserWalletBalance[], + userStakedBalances: PrismaUserStakedBalance[], + ): GqlPoolUserBalance | null { + throw new Error('Method not implemented.'); + } + private getPoolDynamicData(pool: PrismaPoolMinimal): GqlPoolDynamicData { const { fees24h, diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 219dea792..f6d231cf3 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -75,6 +75,13 @@ type GqlPoolMinimal { staking: GqlPoolStaking type: GqlPoolMinimalType! version: Int! + userBalance: GqlPoolUserBalance +} + +type GqlPoolUserBalance { + totalBalance: AmountHumanReadable! + walletBalance: AmountHumanReadable! + stakedBalance: AmountHumanReadable! } enum GqlPoolMinimalType { From eebf7c5ea230895495a7cf220c131c6e859f8153 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 27 Nov 2023 15:56:48 +0100 Subject: [PATCH 091/175] unify rest routes --- app.ts | 9 ++------- modules/balancer/loadRestRoutes.ts | 5 ----- modules/{beethoven => common}/loadRestRoutes.ts | 14 +++++++------- 3 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 modules/balancer/loadRestRoutes.ts rename modules/{beethoven => common}/loadRestRoutes.ts (52%) diff --git a/app.ts b/app.ts index 6923dbdce..0d70d9ef3 100644 --- a/app.ts +++ b/app.ts @@ -1,5 +1,4 @@ -import { loadRestRoutesBeethoven } from './modules/beethoven/loadRestRoutes'; -import { loadRestRoutesBalancer } from './modules/balancer/loadRestRoutes'; +import { loadRestRoutes } from './modules/common/loadRestRoutes'; import { env } from './app/env'; import createExpressApp from 'express'; import { corsMiddleware } from './app/middleware/corsMiddleware'; @@ -74,11 +73,7 @@ async function startServer() { app.use(contextMiddleware); app.use(sessionMiddleware); - if (env.PROTOCOL === 'beethoven') { - loadRestRoutesBeethoven(app); - } else if (env.PROTOCOL === 'balancer') { - loadRestRoutesBalancer(app); - } + loadRestRoutes(app); const httpServer = http.createServer(app); diff --git a/modules/balancer/loadRestRoutes.ts b/modules/balancer/loadRestRoutes.ts deleted file mode 100644 index aad0efca7..000000000 --- a/modules/balancer/loadRestRoutes.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Express } from 'express'; - -export function loadRestRoutesBalancer(app: Express) { - app.use('/health', (req, res) => res.sendStatus(200)); -} diff --git a/modules/beethoven/loadRestRoutes.ts b/modules/common/loadRestRoutes.ts similarity index 52% rename from modules/beethoven/loadRestRoutes.ts rename to modules/common/loadRestRoutes.ts index 999c068ba..fb0f89769 100644 --- a/modules/beethoven/loadRestRoutes.ts +++ b/modules/common/loadRestRoutes.ts @@ -2,7 +2,7 @@ import { Express } from 'express'; import { beetsGetCirculatingSupply } from '../beets/lib/beets'; import { tokenService } from '../token/token.service'; -export function loadRestRoutesBeethoven(app: Express) { +export function loadRestRoutes(app: Express) { app.use('/health', (req, res) => res.sendStatus(200)); app.use('/circulating_supply', (req, res) => { beetsGetCirculatingSupply().then((result) => { @@ -10,11 +10,11 @@ export function loadRestRoutesBeethoven(app: Express) { }); }); - app.use('/late-quartet', async (req, res) => { - const tokenPrices = await tokenService.getTokenPrices(); + // app.use('/late-quartet', async (req, res) => { + // const tokenPrices = await tokenService.getTokenPrices(); - res.send({ - bptPrice: tokenService.getPriceForToken(tokenPrices, '0xf3a602d30dcb723a74a0198313a7551feaca7dac'), - }); - }); + // res.send({ + // bptPrice: tokenService.getPriceForToken(tokenPrices, '0xf3a602d30dcb723a74a0198313a7551feaca7dac'), + // }); + // }); } From 6ecaa5b742d3facaa1b83c359c8284f753aa4659 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 27 Nov 2023 16:37:29 +0100 Subject: [PATCH 092/175] updating and testing stellate config --- package.json | 2 +- stellate-beets-canary.ts | 137 +++++++++++++++++++++ stellate-fantom.ts | 51 -------- stellate-optimism.ts | 51 -------- yarn.lock | 248 +++++++++++++++++++++++++++++++-------- 5 files changed, 335 insertions(+), 154 deletions(-) create mode 100644 stellate-beets-canary.ts delete mode 100644 stellate-fantom.ts delete mode 100644 stellate-optimism.ts diff --git a/package.json b/package.json index d44899b0e..5c7bd2869 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "node-cron": "^3.0.0", "prisma": "^4.1.1", "safe-compare": "^1.1.4", - "stellate": "^1.15.2", + "stellate": "2.7.1", "ts-dotenv": "^0.8.1", "uuid": "^8.3.2", "yarn": "^1.22.19" diff --git a/stellate-beets-canary.ts b/stellate-beets-canary.ts new file mode 100644 index 000000000..cade05a4f --- /dev/null +++ b/stellate-beets-canary.ts @@ -0,0 +1,137 @@ +import { Config, EdgeRequest } from 'stellate'; + +const config: Config = { + config: { + originUrl: 'https://backend-v3-canary-origin.beets-ftm-node.com/graphql', + schema: 'https://backend-v3-canary.beets-ftm-node.com/graphql', + name: 'backend-v3-canary', + devPortal: { + enabled: true, + auth: false, + description: 'IMPORTANT: To stay up to date with changes of our API, please join https://t.me/beetsapi', + readme: 'Learn how to use the API ### Query pools for a certain or multiple chains and apply sorting ```graphiql { poolGetPools(where: {chainIn: [FANTOM, OPTIMISM]}, orderBy: totalLiquidity, orderDirection: desc) { id name } } ``` ### Query user balances for one or more chains ```graphiql { userGetPoolBalances( address: "0x4fbe899d37fb7514adf2f41b0630e018ec275a0c" chains: [FANTOM] ) { poolId stakedBalance walletBalance } } ``` Check out the Graphql Schema for endless possibilities.', + urls: { + logo: 'https://beethoven-assets.s3.eu-central-1.amazonaws.com/logo-full%402x.png', + favicon: 'https://assets.coingecko.com/coins/images/19158/large/beets-icon-large.png?1634545465', + support: 'https://discord.gg/kbPnYJjvwZ', + website: 'https://beets.fi', + }, + }, + queryDepthLimit: 10, + scopes: { + AUTHENTICATED: 'header:accountaddress', + CHAIN: 'header:chainid', + AUTHENTICATED_CHAIN: 'header:accountaddress|header:chainid', + }, + rootTypeNames: { + query: 'Query', + mutation: 'Mutation', + }, + rules: [ + { + types: ['Query'], + maxAge: 15, + swr: 30, + description: 'Cache everything (default)', + scope: 'CHAIN', + }, + { + types: { + Query: [ + 'userGetSwaps', + 'userGetStaking', + 'userGetPoolBalances', + 'userGetFbeetsBalance', + 'userGetPoolJoinExits', + ], + }, + maxAge: 10, + swr: 15, + scope: 'AUTHENTICATED_CHAIN', + description: 'Time critical user queries', + }, + { + types: { + Query: ['latestSyncedBlocks'], + }, + maxAge: 2, + swr: 10, + description: 'Time critical block data', + scope: 'CHAIN', + }, + { + types: { + Query: [ + 'protocolMetricsChain', + 'protocolMetricsAggregated', + 'tokenGetProtocolTokenPrice', + 'beetsGetFbeetsRatio', + 'blocksGetBlocksPerSecond', + 'blocksGetBlocksPerDay', + 'blocksGetAverageBlockTime', + 'tokenGetTokens', + 'poolGetFeaturedPoolGroups', + 'contentGetNewsItems', + ], + }, + maxAge: 60, + swr: 120, + description: 'Mostly static, cache for a long time', + scope: 'CHAIN', + }, + ], + rateLimits: (req) => { + if ( + req.headers['stellate-api-token'] && + req.headers['stellate-api-token'] === + 'stl8_bcebb2b60910a55e58a82c8e83825034dc763e294582447118fab0a6a1225ebb' + ) { + return [ + { + name: 'Specific API Token based limits', + state: 'dryRun', + group: req.headers['stellate-api-token'], + limit: { + type: 'RequestCount', + budget: 20, + window: '1m', + }, + }, + ]; + } + + if (req.headers['stellate-api-token']) { + return [ + { + name: 'General API Token based limits', + state: 'dryRun', + group: req.headers['stellate-api-token'], + limit: { + type: 'RequestCount', + budget: 10, + window: '1m', + }, + }, + ]; + } + + const xForwardedFor = Array.isArray(req.headers['x-forwarded-for']) + ? req.headers['x-forwarded-for'][0] + : req.headers['x-forwarded-for']; + return [ + { + name: 'IP based limits', + state: 'dryRun', + group: xForwardedFor ? xForwardedFor.split(',')[0] : req.ip, + limit: { + type: 'RequestCount', + budget: 5, + window: '1m', + }, + }, + ]; + }, + }, +}; + +export default config; diff --git a/stellate-fantom.ts b/stellate-fantom.ts deleted file mode 100644 index b747d333f..000000000 --- a/stellate-fantom.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Config } from 'stellate'; - -const config: Config = { - config: { - schema: 'https://backend-v2.beets-ftm-node.com/graphql', - queryDepthLimit: 10, - scopes: { - AUTHENTICATED: 'header:accountaddress', - }, - rootTypeNames: { - query: 'Query', - mutation: 'Mutation', - }, - rules: [ - { - types: ['Query'], - maxAge: 15, - swr: 30, - description: 'Cache everything (default)', - }, - { - types: { - Query: [ - 'userGetSwaps', - 'userGetStaking', - 'userGetPoolBalances', - 'userGetFbeetsBalance', - 'userGetPoolJoinExits', - 'poolGetUserSwapVolume', - ], - }, - maxAge: 10, - swr: 15, - scope: 'AUTHENTICATED', - description: 'Time critical user queries', - }, - { - types: { - Query: ['latestSyncedBlocks', 'blocksGetAverageBlockTime'], - }, - maxAge: 2, - swr: 5, - description: 'Time critical block data', - }, - ], - name: 'beetx-backend-v2', - originUrl: 'https://backend-v2-cdn.beets-ftm-node.com/graphql', - }, -}; - -export default config; diff --git a/stellate-optimism.ts b/stellate-optimism.ts deleted file mode 100644 index 63e035f98..000000000 --- a/stellate-optimism.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Config } from 'stellate'; - -const config: Config = { - config: { - schema: 'https://backend-optimism-v2.beets-ftm-node.com/graphql', - queryDepthLimit: 10, - scopes: { - AUTHENTICATED: 'header:accountaddress', - }, - rootTypeNames: { - query: 'Query', - mutation: 'Mutation', - }, - rules: [ - { - types: ['Query'], - maxAge: 15, - swr: 30, - description: 'Cache everything (default)', - }, - { - types: { - Query: [ - 'userGetSwaps', - 'userGetStaking', - 'userGetPoolBalances', - 'userGetFbeetsBalance', - 'userGetPoolJoinExits', - 'poolGetUserSwapVolume', - ], - }, - maxAge: 10, - swr: 15, - scope: 'AUTHENTICATED', - description: 'Time critical user queries', - }, - { - types: { - Query: ['latestSyncedBlocks', 'blocksGetAverageBlockTime'], - }, - maxAge: 2, - swr: 5, - description: 'Time critical block data', - }, - ], - name: 'beetx-backend-v2-optimism', - originUrl: 'https://backend-optimism-v2-cdn.beets-ftm-node.com/graphql', - }, -}; - -export default config; diff --git a/yarn.lock b/yarn.lock index ccf0f16fa..1c3313ada 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2478,111 +2478,221 @@ ts-node "^9" tslib "^2" +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + "@esbuild/android-arm64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz#fa6f0cc7105367cb79cc0a8bf32bf50cb1673e45" integrity sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw== +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + "@esbuild/android-arm@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.11.tgz#ae84a410696c9f549a15be94eaececb860bacacb" integrity sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q== +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + "@esbuild/android-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.11.tgz#0e58360bbc789ad0d68174d32ba20e678c2a16b6" integrity sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw== +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + "@esbuild/darwin-arm64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz#fcdcd2ef76ca656540208afdd84f284072f0d1f9" integrity sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w== +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + "@esbuild/darwin-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz#c5ac602ec0504a8ff81e876bc8a9811e94d69d37" integrity sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw== +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + "@esbuild/freebsd-arm64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz#7012fb06ee3e6e0d5560664a65f3fefbcc46db2e" integrity sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A== +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + "@esbuild/freebsd-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz#c5de1199f70e1f97d5c8fca51afa9bf9a2af5969" integrity sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q== +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + "@esbuild/linux-arm64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz#2a6d3a74e0b8b5f294e22b4515b29f76ebd42660" integrity sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog== +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + "@esbuild/linux-arm@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz#5175bd61b793b436e4aece6328aa0d9be07751e1" integrity sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg== +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + "@esbuild/linux-ia32@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz#20ee6cfd65a398875f321a485e7b2278e5f6f67b" integrity sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw== +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + "@esbuild/linux-loong64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz#8e7b251dede75083bf44508dab5edce3f49d052b" integrity sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw== +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + "@esbuild/linux-mips64el@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz#a3125eb48538ac4932a9d05089b157f94e443165" integrity sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg== +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + "@esbuild/linux-ppc64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz#842abadb7a0995bd539adee2be4d681b68279499" integrity sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ== +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + "@esbuild/linux-riscv64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz#7ce6e6cee1c72d5b4d2f4f8b6fcccf4a9bea0e28" integrity sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w== +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + "@esbuild/linux-s390x@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz#98fbc794363d02ded07d300df2e535650b297b96" integrity sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg== +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + "@esbuild/linux-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz#f8458ec8cf74c8274e4cacd00744d8446cac52eb" integrity sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA== +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + "@esbuild/netbsd-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz#a7b2f991b8293748a7be42eac1c4325faf0c7cca" integrity sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q== +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + "@esbuild/openbsd-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz#3e50923de84c54008f834221130fd23646072b2f" integrity sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ== +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + "@esbuild/sunos-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz#ae47a550b0cd395de03606ecfba03cc96c7c19e2" integrity sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng== +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + "@esbuild/win32-arm64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz#05d364582b7862d7fbf4698ef43644f7346dcfcc" integrity sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg== +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + "@esbuild/win32-ia32@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz#a3372095a4a1939da672156a3c104f8ce85ee616" integrity sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg== +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + "@esbuild/win32-x64@0.18.11": version "0.18.11" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz#6526c7e1b40d5b9f0a222c6b767c22f6fb97aa57" @@ -4527,9 +4637,9 @@ graphql "^14.5.3" "@types/http-proxy@^1.17.8": - version "1.17.9" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" - integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + version "1.17.14" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" + integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== dependencies: "@types/node" "*" @@ -4566,9 +4676,9 @@ integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g== "@types/js-yaml@^3.12.1": - version "3.12.7" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.7.tgz#330c5d97a3500e9c903210d6e49f02964af04a0e" - integrity sha512-S6+8JAYTE1qdsc9HMVsfY7+SgSuUU/Tp6TYTmITW0PZxiyIMvol3Gy//y69Wkhs0ti4py5qgR3uZH6uz/DNzJQ== + version "3.12.10" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.10.tgz#4d80d0c7dfc570eb4f0be280cb2d67789f977ba5" + integrity sha512-/Mtaq/wf+HxXpvhzFYzrzCqNRcA958sW++7JOFC8nPrZcvfi/TrzOaaGbvt27ltJB2NQbHVAg5a1wUCsyMH7NA== "@types/js-yaml@^4.0.0": version "4.0.4" @@ -4622,16 +4732,23 @@ resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.0.tgz#f946cefb5c05c64f460090f6be97bd50460c8898" integrity sha512-RNBIyVwa/1v2r8/SqK8tadH2sJlFRAo5Ghac/cOcCv4Kp94m0I03UmAh9WVhCqS9ZdB84dF3x47p9aTw8E4c4A== -"@types/node@*", "@types/node@^16.11.7": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== +"@types/node@*": + version "20.9.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.2.tgz#002815c8e87fe0c9369121c78b52e800fadc0ac6" + integrity sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg== + dependencies: + undici-types "~5.26.4" "@types/node@^10.1.0": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/node@^16.11.7": + version "16.11.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" + integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -5747,9 +5864,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" - integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -6468,10 +6585,33 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -esbuild@0.8.57: - version "0.8.57" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.57.tgz#a42d02bc2b57c70bcd0ef897fe244766bb6dd926" - integrity sha512-j02SFrUwFTRUqiY0Kjplwjm1psuzO1d6AjaXKuOR9hrY0HuPsT6sV42B6myW34h1q4CRy+Y3g4RU/cGJeI/nNA== +esbuild@^0.17.7: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== + optionalDependencies: + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" esbuild@^0.18.10: version "0.18.11" @@ -6659,7 +6799,7 @@ execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^5.0.0, execa@^5.1.1: +execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -6892,9 +7032,9 @@ find-up@^4.0.0, find-up@^4.1.0: path-exists "^4.0.0" follow-redirects@^1.0.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== follow-redirects@^1.14.4: version "1.14.5" @@ -7736,7 +7876,7 @@ is-date-object@^1.0.1: is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^1.0.0: version "1.0.0" @@ -7976,7 +8116,7 @@ isarray@~1.0.0: isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" @@ -8899,6 +9039,11 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== +lz-string@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + magic-string@^0.30.0: version "0.30.1" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.1.tgz#ce5cd4b0a81a5d032bd69aab4522299b2166284d" @@ -9761,9 +9906,11 @@ queue-microtask@^1.2.2: integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== random-words@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/random-words/-/random-words-1.2.0.tgz#94d0cc8061965efe955d60b80ad93392a7edf2f5" - integrity sha512-YP2bXrT19pxtBh22DK9CLcWsmBjUBAGzw3JWJycTNbXm1+0aS6PrKuAJ9aLT0GGaPlPp9LExfJIMVkzhrDZE6g== + version "1.3.0" + resolved "https://registry.yarnpkg.com/random-words/-/random-words-1.3.0.tgz#00715efb8dd787d244f963c994367707c1e95676" + integrity sha512-brwCGe+DN9DqZrAQVNj1Tct1Lody6GrYL/7uei5wfjeQdacFyFd2h/51LNlOoBMzIKMS9xohuL4+wlF/z1g/xg== + dependencies: + seedrandom "^3.0.5" randombytes@^2.1.0: version "2.1.0" @@ -9936,7 +10083,7 @@ require-main-filename@^2.0.0: requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== resolve-cwd@^3.0.0: version "3.0.0" @@ -10111,6 +10258,11 @@ secp256k1@^4.0.1: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -10232,12 +10384,7 @@ signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== -signal-exit@^3.0.3: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== - -signal-exit@^3.0.7: +signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -10272,11 +10419,6 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slugify@^1.4.7: - version "1.6.5" - resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.5.tgz#c8f5c072bf2135b80703589b39a3d41451fbe8c8" - integrity sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ== - snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -10348,7 +10490,7 @@ sponge-case@^1.0.1: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssh-remote-port-forward@^1.0.4: version "1.0.4" @@ -10396,21 +10538,20 @@ std-env@^3.3.3: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.3.tgz#a54f06eb245fdcfef53d56f3c0251f1d5c3d01fe" integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== -stellate@^1.15.2: - version "1.15.2" - resolved "https://registry.yarnpkg.com/stellate/-/stellate-1.15.2.tgz#0fce013cbf02fb217faa50e4df424ae6a4ab78d2" - integrity sha512-0i5lnzmNnAoEgCqzbt93hYVfBBOC/DwFhFFxcvjHJM3rTA7zfFcoBfJ2kicXy/Z2JDHXv7wzWe0E6uLa0SL0CA== +stellate@2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/stellate/-/stellate-2.7.1.tgz#c83a689dbb096c9df7ea77685986272c149a1883" + integrity sha512-vtRL1w3oDtLpMX1vENKAkrlf3U/mLEzGo5DaujfBvgOV5hm3qar1J3XdKPNFhauK7UWd9czOCTQVucDgmNJSbA== dependencies: "@atomist/yaml-updater" "^1.0.2" "@sindresorhus/slugify" "^1.1.0" - esbuild "0.8.57" - execa "^5.1.1" + esbuild "^0.17.7" express "^4.17.1" http-proxy-middleware "^2.0.1" is-ci "^3.0.1" + lz-string "1.5.0" random-words "^1.1.1" - slugify "^1.4.7" - zod "^3.15.1" + zod "^3.21.4" strict-event-emitter@^0.2.0, strict-event-emitter@^0.2.4: version "0.2.5" @@ -10932,6 +11073,11 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + undici@^4.9.3: version "4.10.0" resolved "https://registry.yarnpkg.com/undici/-/undici-4.10.0.tgz#f2684de9cbe2ab0a85a477ce0ec59b739be4438d" @@ -11435,7 +11581,7 @@ zip-stream@^4.1.0: compress-commons "^4.1.0" readable-stream "^3.6.0" -zod@^3.15.1: - version "3.18.0" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.18.0.tgz#2eed58b3cafb8d9a67aa2fee69279702f584f3bc" - integrity sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA== +zod@^3.21.4: + version "3.22.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" + integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== From 4e4f049a22cbb27821841944baf69585a9a5216c Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 29 Nov 2023 10:57:31 +0100 Subject: [PATCH 093/175] filter on query --- modules/vebal/debug-voting-list.spec.ts | 7 +++++-- modules/vebal/vebal-voting-list.service.ts | 8 +++++++- modules/vebal/voting-gauges.repository.spec.ts | 11 +++-------- modules/vebal/voting-gauges.repository.ts | 1 - 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/vebal/debug-voting-list.spec.ts b/modules/vebal/debug-voting-list.spec.ts index 21874ed6e..b131011dc 100644 --- a/modules/vebal/debug-voting-list.spec.ts +++ b/modules/vebal/debug-voting-list.spec.ts @@ -292,7 +292,9 @@ it('Uses streamer-v1-map to find gauges (that use streamer instead of recipient) const savedGauges = await repository.saveVotingGauges(fetchedVotingGauges); expect(savedGauges).toMatchInlineSnapshot(` - [ + { + "saveErrors": [], + "votingGaugesWithStakingGaugeId": [ { "addedTimestamp": 1657479716, "gaugeAddress": "0xcf5938ca6d9f19c73010c7493e19c02acfa8d24d", @@ -315,6 +317,7 @@ it('Uses streamer-v1-map to find gauges (that use streamer instead of recipient) "relativeWeightCap": undefined, "stakingGaugeId": "0xfaad21203a7856889cb6eb644ab6864e7253107a", }, - ] + ], + } `); }, 1000_000); diff --git a/modules/vebal/vebal-voting-list.service.ts b/modules/vebal/vebal-voting-list.service.ts index 3f78e2061..1275fcbde 100644 --- a/modules/vebal/vebal-voting-list.service.ts +++ b/modules/vebal/vebal-voting-list.service.ts @@ -100,9 +100,13 @@ export class VeBalVotingListService { } public async getValidVotingGauges() { + // A gauge should be included in the voting list when: + // - it is alive (not killed) + // - it is killed and has valid votes (the users should be able to reallocate votes) const gaugesWithStaking = await prisma.prismaVotingGauge.findMany({ where: { stakingGaugeId: { not: null }, + OR: [{ status: 'ACTIVE' }, { relativeWeight: { not: '0' } }], }, select: { id: true, @@ -140,6 +144,9 @@ export class VeBalVotingListService { const { votingGauges, errors } = await this.fetchVotingGauges(addressChunk); syncErrors.push(...errors); + if (addressChunk.includes('0xd639e7fae7a8d0233d416bfd5da2ae4f917d2e77')) { + console.log(`found`); + } /* We avoid saving gauges in specialVotingGaugeAddresses because they require special handling */ @@ -177,7 +184,6 @@ export class VeBalVotingListService { const gaugesWithMissingData = votingGauges .filter((gauge) => !veGauges.includes(gauge.gaugeAddress)) .filter((gauge) => !gauge.isInSubgraph) - .filter(this.votingGauges.isValidForVotingList) // Ignore old Vebal gauge address .filter((gauge) => gauge.gaugeAddress !== oldVeBalAddress); diff --git a/modules/vebal/voting-gauges.repository.spec.ts b/modules/vebal/voting-gauges.repository.spec.ts index 941705780..7f35f11d5 100644 --- a/modules/vebal/voting-gauges.repository.spec.ts +++ b/modules/vebal/voting-gauges.repository.spec.ts @@ -116,19 +116,14 @@ it('successfully saves onchain gauges', async () => { describe('When staking gauge is not found ', () => { beforeEach(() => prismaMock.prismaPoolStakingGauge.findFirst.mockResolvedValue(null)); - it('throws when gauge is valid for voting (not killed)', async () => { + it('has errors when gauge is valid for voting (not killed)', async () => { const repository = new VotingGaugesRepository(prismaMock); const votingGauge = aVotingGauge({ network: Chain.MAINNET, isKilled: false }); - let error: Error = EmptyError; - try { - await repository.saveVotingGauges([votingGauge]); - } catch (e) { - error = e as Error; - } + const { votingGaugesWithStakingGaugeId, saveErrors } = await repository.saveVotingGauges([votingGauge]); - expect(error.message).toContain('VotingGauge not found in PrismaPoolStakingGauge:'); + expect(saveErrors.length).toBe(1); }); it('does not throw when gauge is valid for voting (killed with no votes)', async () => { diff --git a/modules/vebal/voting-gauges.repository.ts b/modules/vebal/voting-gauges.repository.ts index 78d16a8c7..cd252b8f9 100644 --- a/modules/vebal/voting-gauges.repository.ts +++ b/modules/vebal/voting-gauges.repository.ts @@ -138,7 +138,6 @@ export class VotingGaugesRepository { } async saveVotingGauge(gauge: VotingGauge) { - if (!this.isValidForVotingList(gauge)) return; try { const upsertFields = { id: gauge.gaugeAddress, From e9c0cd0b19faf90fc45eaa37d39b3cdf0d4d11ca Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 29 Nov 2023 11:12:40 +0100 Subject: [PATCH 094/175] not in use stellate config --- stellate-beets-canary.ts | 266 +++++++++++++++++++-------------------- 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/stellate-beets-canary.ts b/stellate-beets-canary.ts index cade05a4f..1a21f625a 100644 --- a/stellate-beets-canary.ts +++ b/stellate-beets-canary.ts @@ -1,137 +1,137 @@ -import { Config, EdgeRequest } from 'stellate'; +// import { Config } from 'stellate'; -const config: Config = { - config: { - originUrl: 'https://backend-v3-canary-origin.beets-ftm-node.com/graphql', - schema: 'https://backend-v3-canary.beets-ftm-node.com/graphql', - name: 'backend-v3-canary', - devPortal: { - enabled: true, - auth: false, - description: 'IMPORTANT: To stay up to date with changes of our API, please join https://t.me/beetsapi', - readme: 'Learn how to use the API ### Query pools for a certain or multiple chains and apply sorting ```graphiql { poolGetPools(where: {chainIn: [FANTOM, OPTIMISM]}, orderBy: totalLiquidity, orderDirection: desc) { id name } } ``` ### Query user balances for one or more chains ```graphiql { userGetPoolBalances( address: "0x4fbe899d37fb7514adf2f41b0630e018ec275a0c" chains: [FANTOM] ) { poolId stakedBalance walletBalance } } ``` Check out the Graphql Schema for endless possibilities.', - urls: { - logo: 'https://beethoven-assets.s3.eu-central-1.amazonaws.com/logo-full%402x.png', - favicon: 'https://assets.coingecko.com/coins/images/19158/large/beets-icon-large.png?1634545465', - support: 'https://discord.gg/kbPnYJjvwZ', - website: 'https://beets.fi', - }, - }, - queryDepthLimit: 10, - scopes: { - AUTHENTICATED: 'header:accountaddress', - CHAIN: 'header:chainid', - AUTHENTICATED_CHAIN: 'header:accountaddress|header:chainid', - }, - rootTypeNames: { - query: 'Query', - mutation: 'Mutation', - }, - rules: [ - { - types: ['Query'], - maxAge: 15, - swr: 30, - description: 'Cache everything (default)', - scope: 'CHAIN', - }, - { - types: { - Query: [ - 'userGetSwaps', - 'userGetStaking', - 'userGetPoolBalances', - 'userGetFbeetsBalance', - 'userGetPoolJoinExits', - ], - }, - maxAge: 10, - swr: 15, - scope: 'AUTHENTICATED_CHAIN', - description: 'Time critical user queries', - }, - { - types: { - Query: ['latestSyncedBlocks'], - }, - maxAge: 2, - swr: 10, - description: 'Time critical block data', - scope: 'CHAIN', - }, - { - types: { - Query: [ - 'protocolMetricsChain', - 'protocolMetricsAggregated', - 'tokenGetProtocolTokenPrice', - 'beetsGetFbeetsRatio', - 'blocksGetBlocksPerSecond', - 'blocksGetBlocksPerDay', - 'blocksGetAverageBlockTime', - 'tokenGetTokens', - 'poolGetFeaturedPoolGroups', - 'contentGetNewsItems', - ], - }, - maxAge: 60, - swr: 120, - description: 'Mostly static, cache for a long time', - scope: 'CHAIN', - }, - ], - rateLimits: (req) => { - if ( - req.headers['stellate-api-token'] && - req.headers['stellate-api-token'] === - 'stl8_bcebb2b60910a55e58a82c8e83825034dc763e294582447118fab0a6a1225ebb' - ) { - return [ - { - name: 'Specific API Token based limits', - state: 'dryRun', - group: req.headers['stellate-api-token'], - limit: { - type: 'RequestCount', - budget: 20, - window: '1m', - }, - }, - ]; - } +// const config: Config = { +// config: { +// originUrl: 'https://backend-v3-canary-origin.beets-ftm-node.com/graphql', +// schema: 'https://backend-v3-canary.beets-ftm-node.com/graphql', +// name: 'backend-v3-canary', +// devPortal: { +// enabled: true, +// auth: false, +// description: 'IMPORTANT: To stay up to date with changes of our API, please join https://t.me/beetsapi', +// readme: 'Learn how to use the API ### Query pools for a certain or multiple chains and apply sorting ```graphiql { poolGetPools(where: {chainIn: [FANTOM, OPTIMISM]}, orderBy: totalLiquidity, orderDirection: desc) { id name } } ``` ### Query user balances for one or more chains ```graphiql { userGetPoolBalances( address: "0x4fbe899d37fb7514adf2f41b0630e018ec275a0c" chains: [FANTOM] ) { poolId stakedBalance walletBalance } } ``` Check out the Graphql Schema for endless possibilities.', +// urls: { +// logo: 'https://beethoven-assets.s3.eu-central-1.amazonaws.com/logo-full%402x.png', +// favicon: 'https://assets.coingecko.com/coins/images/19158/large/beets-icon-large.png?1634545465', +// support: 'https://discord.gg/kbPnYJjvwZ', +// website: 'https://beets.fi', +// }, +// }, +// queryDepthLimit: 10, +// scopes: { +// AUTHENTICATED: 'header:accountaddress', +// CHAIN: 'header:chainid', +// AUTHENTICATED_CHAIN: 'header:accountaddress|header:chainid', +// }, +// rootTypeNames: { +// query: 'Query', +// mutation: 'Mutation', +// }, +// rules: [ +// { +// types: ['Query'], +// maxAge: 15, +// swr: 30, +// description: 'Cache everything (default)', +// scope: 'CHAIN', +// }, +// { +// types: { +// Query: [ +// 'userGetSwaps', +// 'userGetStaking', +// 'userGetPoolBalances', +// 'userGetFbeetsBalance', +// 'userGetPoolJoinExits', +// ], +// }, +// maxAge: 10, +// swr: 15, +// scope: 'AUTHENTICATED_CHAIN', +// description: 'Time critical user queries', +// }, +// { +// types: { +// Query: ['latestSyncedBlocks'], +// }, +// maxAge: 2, +// swr: 10, +// description: 'Time critical block data', +// scope: 'CHAIN', +// }, +// { +// types: { +// Query: [ +// 'protocolMetricsChain', +// 'protocolMetricsAggregated', +// 'tokenGetProtocolTokenPrice', +// 'beetsGetFbeetsRatio', +// 'blocksGetBlocksPerSecond', +// 'blocksGetBlocksPerDay', +// 'blocksGetAverageBlockTime', +// 'tokenGetTokens', +// 'poolGetFeaturedPoolGroups', +// 'contentGetNewsItems', +// ], +// }, +// maxAge: 60, +// swr: 120, +// description: 'Mostly static, cache for a long time', +// scope: 'CHAIN', +// }, +// ], +// rateLimits: (req) => { +// if ( +// req.headers['stellate-api-token'] && +// req.headers['stellate-api-token'] === +// 'stl8_bcebb2b60910a55e58a82c8e83825034dc763e294582447118fab0a6a1225ebb' +// ) { +// return [ +// { +// name: 'Specific API Token based limits', +// state: 'dryRun', +// group: req.headers['stellate-api-token'], +// limit: { +// type: 'RequestCount', +// budget: 20, +// window: '1m', +// }, +// }, +// ]; +// } - if (req.headers['stellate-api-token']) { - return [ - { - name: 'General API Token based limits', - state: 'dryRun', - group: req.headers['stellate-api-token'], - limit: { - type: 'RequestCount', - budget: 10, - window: '1m', - }, - }, - ]; - } +// if (req.headers['stellate-api-token']) { +// return [ +// { +// name: 'General API Token based limits', +// state: 'dryRun', +// group: req.headers['stellate-api-token'], +// limit: { +// type: 'RequestCount', +// budget: 10, +// window: '1m', +// }, +// }, +// ]; +// } - const xForwardedFor = Array.isArray(req.headers['x-forwarded-for']) - ? req.headers['x-forwarded-for'][0] - : req.headers['x-forwarded-for']; - return [ - { - name: 'IP based limits', - state: 'dryRun', - group: xForwardedFor ? xForwardedFor.split(',')[0] : req.ip, - limit: { - type: 'RequestCount', - budget: 5, - window: '1m', - }, - }, - ]; - }, - }, -}; +// const xForwardedFor = Array.isArray(req.headers['x-forwarded-for']) +// ? req.headers['x-forwarded-for'][0] +// : req.headers['x-forwarded-for']; +// return [ +// { +// name: 'IP based limits', +// state: 'dryRun', +// group: xForwardedFor ? xForwardedFor.split(',')[0] : req.ip, +// limit: { +// type: 'RequestCount', +// budget: 5, +// window: '1m', +// }, +// }, +// ]; +// }, +// }, +// }; -export default config; +// export default config; From a74456f6dd9a818d210a5a0927e3f7489342fd53 Mon Sep 17 00:00:00 2001 From: mendesfabio Date: Wed, 29 Nov 2023 08:14:10 -0300 Subject: [PATCH 095/175] fix vebal rank sorting --- modules/vebal/vebal.service.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/vebal/vebal.service.ts b/modules/vebal/vebal.service.ts index 841cd510b..0df5742f7 100644 --- a/modules/vebal/vebal.service.ts +++ b/modules/vebal/vebal.service.ts @@ -31,12 +31,18 @@ export class VeBalService { if (networkContext.data.veBal) { const veBalUsers = await prisma.prismaVeBalUserBalance.findMany({ where: { chain: networkContext.chain }, - orderBy: { balance: 'desc' }, }); - for (const user of veBalUsers) { + const veBalUsersNum = veBalUsers.map(user => ({ + ...user, + balance: parseFloat(user.balance), + })); + + veBalUsersNum.sort((a, b) => b.balance - a.balance); + + for (const user of veBalUsersNum) { if (user.userAddress === userAddress) { - balance = user.balance; + balance = user.balance.toString(); break; } rank++; From f9b26cda6b8b844554a9c5e269c8bc38415c72b0 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 30 Nov 2023 15:21:10 +0100 Subject: [PATCH 096/175] remove logging --- modules/vebal/vebal-voting-list.service.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/vebal/vebal-voting-list.service.ts b/modules/vebal/vebal-voting-list.service.ts index 1275fcbde..29f81a405 100644 --- a/modules/vebal/vebal-voting-list.service.ts +++ b/modules/vebal/vebal-voting-list.service.ts @@ -143,10 +143,6 @@ export class VeBalVotingListService { for (const addressChunk of chunks) { const { votingGauges, errors } = await this.fetchVotingGauges(addressChunk); syncErrors.push(...errors); - - if (addressChunk.includes('0xd639e7fae7a8d0233d416bfd5da2ae4f917d2e77')) { - console.log(`found`); - } /* We avoid saving gauges in specialVotingGaugeAddresses because they require special handling */ From 6cb10a2efb74cbc310e78cfa15b20114b4c38f75 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 30 Nov 2023 17:52:45 +0100 Subject: [PATCH 097/175] reduce coingecko address chunk size --- modules/coingecko/coingecko.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/coingecko/coingecko.service.ts b/modules/coingecko/coingecko.service.ts index d638b9ff7..14cab46c6 100644 --- a/modules/coingecko/coingecko.service.ts +++ b/modules/coingecko/coingecko.service.ts @@ -74,6 +74,7 @@ interface CoinId { */ const tokensPerInterval = env.COINGECKO_API_KEY ? ((env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' ? 10 : 5) : 3; const requestRateLimiter = new RateLimiter({ tokensPerInterval, interval: 'minute' }); +const addressChunkSize = env.COINGECKO_API_KEY ? ((env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' ? 180 : 10) : 10; export class CoingeckoService { private readonly baseUrl: string; @@ -104,8 +105,8 @@ export class CoingeckoService { * Rate limit for the CoinGecko API is 10 calls each second per IP address. */ public async getTokenPrices(addresses: string[]): Promise { - //max 180 addresses per request because of URI size limit - const addressesPerRequest = 180; + //max 10 addresses per request because of URI size limit + const addressesPerRequest = addressChunkSize; try { if (addresses.length / addressesPerRequest > 10) throw new Error('Too many requests for rate limit.'); From 1e0aed21cc775dd20aef7dbe4f3e4d64d4ed7f9b Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 30 Nov 2023 17:55:40 +0100 Subject: [PATCH 098/175] filter gauges that don't have votes --- modules/vebal/vebal-voting-list.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/vebal/vebal-voting-list.service.ts b/modules/vebal/vebal-voting-list.service.ts index 29f81a405..7bc22f13b 100644 --- a/modules/vebal/vebal-voting-list.service.ts +++ b/modules/vebal/vebal-voting-list.service.ts @@ -180,6 +180,7 @@ export class VeBalVotingListService { const gaugesWithMissingData = votingGauges .filter((gauge) => !veGauges.includes(gauge.gaugeAddress)) .filter((gauge) => !gauge.isInSubgraph) + .filter((gauge) => gauge.relativeWeight > 0) // Ignore old Vebal gauge address .filter((gauge) => gauge.gaugeAddress !== oldVeBalAddress); From 9ec1d360fdd8ff6aafc3f1b7b2f9325688b99476 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 30 Nov 2023 18:21:46 +0100 Subject: [PATCH 099/175] filter missing info gauges --- modules/vebal/vebal-voting-list.service.ts | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/modules/vebal/vebal-voting-list.service.ts b/modules/vebal/vebal-voting-list.service.ts index 7bc22f13b..db30f16b9 100644 --- a/modules/vebal/vebal-voting-list.service.ts +++ b/modules/vebal/vebal-voting-list.service.ts @@ -141,12 +141,12 @@ export class VeBalVotingListService { const syncErrors: Error[] = []; for (const addressChunk of chunks) { - const { votingGauges, errors } = await this.fetchVotingGauges(addressChunk); + const { filteredGauges, errors } = await this.fetchVotingGauges(addressChunk); syncErrors.push(...errors); /* We avoid saving gauges in specialVotingGaugeAddresses because they require special handling */ - const cleanVotingGauges = votingGauges.filter( + const cleanVotingGauges = filteredGauges.filter( (gauge) => !specialVotingGaugeAddresses.includes(gauge.gaugeAddress), ); @@ -167,16 +167,23 @@ export class VeBalVotingListService { const votingGauges = this.votingGauges.updateOnchainGaugesWithSubgraphData(onchainGauges, subgraphGauges); - try { - this.throwIfMissingVotingGaugeData(votingGauges); - } catch (error) { - errors.push(error as Error); - } + const gaugesWithMissingData = this.returnGaugesWithMissingData(votingGauges); + + const filteredGauges = votingGauges.filter( + (gauge) => !gaugesWithMissingData.map((gauge) => gauge.gaugeAddress).includes(gauge.gaugeAddress), + ); - return { votingGauges, errors }; + if (gaugesWithMissingData.length > 0) { + const errorMessage = + 'Detected active voting gauge/s with votes (relative weight > 0) that are not in subgraph: ' + + JSON.stringify(gaugesWithMissingData); + console.error(errorMessage); + errors.push(new Error(errorMessage)); + } + return { filteredGauges, errors }; } - throwIfMissingVotingGaugeData(votingGauges: VotingGauge[]) { + returnGaugesWithMissingData(votingGauges: VotingGauge[]) { const gaugesWithMissingData = votingGauges .filter((gauge) => !veGauges.includes(gauge.gaugeAddress)) .filter((gauge) => !gauge.isInSubgraph) @@ -184,13 +191,7 @@ export class VeBalVotingListService { // Ignore old Vebal gauge address .filter((gauge) => gauge.gaugeAddress !== oldVeBalAddress); - if (gaugesWithMissingData.length > 0) { - const errorMessage = - 'Detected active voting gauge/s with votes (relative weight > 0) that are not in subgraph: ' + - JSON.stringify(gaugesWithMissingData); - console.error(errorMessage); - throw new Error(errorMessage); - } + return gaugesWithMissingData; } } From fbc44d0e30f3858a71a90ff1c06c7f7ea6a3d045 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:19:18 +0100 Subject: [PATCH 100/175] node 18 and secrets in the manager --- .platform/confighooks/prebuild/env-secret.sh | 11 ++++++++++ .platform/confighooks/predeploy/env-secret.sh | 20 +++++++++++++++++++ .platform/hooks/prebuild/env-secret.sh | 11 ++++++++++ .platform/hooks/predeploy/env-secret.sh | 20 +++++++++++++++++++ buildspec.yml | 4 ++++ 5 files changed, 66 insertions(+) create mode 100644 .platform/confighooks/prebuild/env-secret.sh create mode 100644 .platform/confighooks/predeploy/env-secret.sh create mode 100644 .platform/hooks/prebuild/env-secret.sh create mode 100644 .platform/hooks/predeploy/env-secret.sh diff --git a/.platform/confighooks/prebuild/env-secret.sh b/.platform/confighooks/prebuild/env-secret.sh new file mode 100644 index 000000000..2f7890b2c --- /dev/null +++ b/.platform/confighooks/prebuild/env-secret.sh @@ -0,0 +1,11 @@ +#!/bin/sh +secrets=$(aws secretsmanager get-secret-value --secret-id api-secrets --region $AWS_REGION | jq -r '.SecretString') + +IFS=$'\n' read -r -d '' -a keys <<< "$(echo $secrets | jq -r 'keys[]')" + +touch .env +chown webapp:webapp .env +for key in "${keys[@]}"; do + value=$(echo $secrets | jq -r ".\"$key\"") + printf "%s=%s\n" "$key" "$value" +done > .env diff --git a/.platform/confighooks/predeploy/env-secret.sh b/.platform/confighooks/predeploy/env-secret.sh new file mode 100644 index 000000000..e3997700d --- /dev/null +++ b/.platform/confighooks/predeploy/env-secret.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# In case we merge two env secrets into a flattened one in the same region +# secrets=$(aws secretsmanager get-secret-value --secret-id api-secrets --region $AWS_REGION | jq -r '.SecretString') + +# IFS=$'\n' read -r -d '' -a key_value_pairs <<< "$(echo $secrets | jq -r --arg prefix "$PREFIX" 'to_entries[] | select((.key | ascii_upcase) | startswith(($prefix+"_") | ascii_upcase)) | ((.key | sub(($prefix+"_"); ""; "i")) + "=" + .value)')" + +# for pair in "${key_value_pairs[@]}"; do +# echo "$pair" +# done + +secrets=$(aws secretsmanager get-secret-value --secret-id api-secrets --region $AWS_REGION | jq -r '.SecretString') + +IFS=$'\n' read -r -d '' -a keys <<< "$(echo $secrets | jq -r 'keys[]')" + +touch /var/app/staging/.env +chown webapp:webapp /var/app/staging/.env +for key in "${keys[@]}"; do + value=$(echo $secrets | jq -r ".\"$key\"") + printf "%s=%s\n" "$key" "$value" +done >> /var/app/staging/.env diff --git a/.platform/hooks/prebuild/env-secret.sh b/.platform/hooks/prebuild/env-secret.sh new file mode 100644 index 000000000..2f7890b2c --- /dev/null +++ b/.platform/hooks/prebuild/env-secret.sh @@ -0,0 +1,11 @@ +#!/bin/sh +secrets=$(aws secretsmanager get-secret-value --secret-id api-secrets --region $AWS_REGION | jq -r '.SecretString') + +IFS=$'\n' read -r -d '' -a keys <<< "$(echo $secrets | jq -r 'keys[]')" + +touch .env +chown webapp:webapp .env +for key in "${keys[@]}"; do + value=$(echo $secrets | jq -r ".\"$key\"") + printf "%s=%s\n" "$key" "$value" +done > .env diff --git a/.platform/hooks/predeploy/env-secret.sh b/.platform/hooks/predeploy/env-secret.sh new file mode 100644 index 000000000..e3997700d --- /dev/null +++ b/.platform/hooks/predeploy/env-secret.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# In case we merge two env secrets into a flattened one in the same region +# secrets=$(aws secretsmanager get-secret-value --secret-id api-secrets --region $AWS_REGION | jq -r '.SecretString') + +# IFS=$'\n' read -r -d '' -a key_value_pairs <<< "$(echo $secrets | jq -r --arg prefix "$PREFIX" 'to_entries[] | select((.key | ascii_upcase) | startswith(($prefix+"_") | ascii_upcase)) | ((.key | sub(($prefix+"_"); ""; "i")) + "=" + .value)')" + +# for pair in "${key_value_pairs[@]}"; do +# echo "$pair" +# done + +secrets=$(aws secretsmanager get-secret-value --secret-id api-secrets --region $AWS_REGION | jq -r '.SecretString') + +IFS=$'\n' read -r -d '' -a keys <<< "$(echo $secrets | jq -r 'keys[]')" + +touch /var/app/staging/.env +chown webapp:webapp /var/app/staging/.env +for key in "${keys[@]}"; do + value=$(echo $secrets | jq -r ".\"$key\"") + printf "%s=%s\n" "$key" "$value" +done >> /var/app/staging/.env diff --git a/buildspec.yml b/buildspec.yml index ca5de7c63..047371c61 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -13,6 +13,8 @@ env: VEBALLOCKS_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges' phases: install: + runtime-versions: + nodejs: 18 commands: - yarn install build: @@ -23,6 +25,8 @@ phases: - yarn build post_build: commands: + - rm -fr node_modules + - yarn install --prod - echo Build completed on `date` # - echo Pushing to graph cdn # - npx graphcdn push From 33c28d3187fcb87083b55ffffc37c886da9a746c Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:38:30 +0100 Subject: [PATCH 101/175] new EBS platform in Prisma --- prisma/base.prisma | 2 +- prisma/schema.prisma | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prisma/base.prisma b/prisma/base.prisma index 4318651fd..8a55320cb 100644 --- a/prisma/base.prisma +++ b/prisma/base.prisma @@ -3,7 +3,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "rhel-openssl-1.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-3.0.x"] } datasource db { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9ec3e38d1..dd7174384 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -5,7 +5,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "rhel-openssl-1.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-3.0.x"] } datasource db { From fd9f241a71a0605d10a6cdc628ef0826cb3a8fce Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 28 Nov 2023 23:18:04 +0100 Subject: [PATCH 102/175] database_url in .env --- app/env.ts | 5 +++-- prisma/prisma-client.ts | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/env.ts b/app/env.ts index bda6dec53..7ab6f5689 100644 --- a/app/env.ts +++ b/app/env.ts @@ -31,9 +31,10 @@ export const schema = { type: String, }, WORKER_QUEUE_URL: String, + DATABASE_URL: String, }; export const env: Env = load(schema, { - path: resolve(__dirname, '../.env'), - overrideProcessEnv: process.env.NODE_ENV !== 'production', + path: resolve(__dirname, `../../.env`), + overrideProcessEnv: true, }); diff --git a/prisma/prisma-client.ts b/prisma/prisma-client.ts index 13652ad9f..c31743dd0 100644 --- a/prisma/prisma-client.ts +++ b/prisma/prisma-client.ts @@ -1,6 +1,13 @@ import { PrismaClient } from '@prisma/client'; +import { env } from '../app/env'; -export let prisma = new PrismaClient(); +export let prisma = new PrismaClient({ + datasources: { + db: { + url: env.DATABASE_URL, + }, + }, +}); // Debugging query times // export let prisma = new PrismaClient({ From 97589a1ef45c7fccefd2351a42ed1a8050540e6c Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 1 Dec 2023 09:13:01 +0100 Subject: [PATCH 103/175] fix coingecko --- modules/coingecko/coingecko.service.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/coingecko/coingecko.service.ts b/modules/coingecko/coingecko.service.ts index 14cab46c6..c3da74188 100644 --- a/modules/coingecko/coingecko.service.ts +++ b/modules/coingecko/coingecko.service.ts @@ -72,9 +72,10 @@ interface CoinId { that happen. */ -const tokensPerInterval = env.COINGECKO_API_KEY ? ((env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' ? 10 : 5) : 3; -const requestRateLimiter = new RateLimiter({ tokensPerInterval, interval: 'minute' }); -const addressChunkSize = env.COINGECKO_API_KEY ? ((env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' ? 180 : 10) : 10; +const tokensPerMinute = env.COINGECKO_API_KEY ? 10 : 3; +const requestRateLimiter = new RateLimiter({ tokensPerInterval: tokensPerMinute, interval: 'minute' }); +//max 10 addresses per request because of URI size limit, pro is max 180 because of URI limit +const addressChunkSize = env.COINGECKO_API_KEY ? 180 : 20; export class CoingeckoService { private readonly baseUrl: string; @@ -105,10 +106,10 @@ export class CoingeckoService { * Rate limit for the CoinGecko API is 10 calls each second per IP address. */ public async getTokenPrices(addresses: string[]): Promise { - //max 10 addresses per request because of URI size limit const addressesPerRequest = addressChunkSize; try { - if (addresses.length / addressesPerRequest > 10) throw new Error('Too many requests for rate limit.'); + // if (addresses.length / addressesPerRequest > tokensPerMinute) + // throw new Error('Too many requests for rate limit.'); const tokenDefinitions = await tokenService.getTokenDefinitions([networkContext.chain]); const mapped = addresses.map((address) => this.getMappedTokenDetails(address, tokenDefinitions)); From fbb73de74d490cd0abccbdb8a4b8d560238c9a60 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 1 Dec 2023 10:14:11 +0100 Subject: [PATCH 104/175] return balances --- modules/pool/lib/pool-gql-loader.service.ts | 34 ++++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 3d46b8439..1e90acefc 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -39,6 +39,8 @@ import { isWeightedPoolV2 } from './pool-utils'; import { oldBnum } from '../../big-number/old-big-number'; import { networkContext } from '../../network/network-context.service'; import { fixedNumber } from '../../view-helpers/fixed-number'; +import { parseUnits } from 'ethers/lib/utils'; +import { formatFixed } from '@ethersproject/bignumber'; export class PoolGqlLoaderService { public async getPool(id: string, chain: Chain): Promise { @@ -61,14 +63,29 @@ export class PoolGqlLoaderService { public async getPools(args: QueryPoolGetPoolsArgs): Promise { // only include wallet and staked balances if the query requests it // this makes sure that we don't load ALL user balances when we don't filter on userAddress - if (args.where?.userAddress) { const pools = await prisma.prismaPool.findMany({ ...this.mapQueryArgsToPoolQuery(args), include: { ...prismaPoolMinimal.include, - userWalletBalances: true, - userStakedBalances: true, + userWalletBalances: { + where: { + userAddress: { + equals: args.where?.userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, + }, + userStakedBalances: { + where: { + userAddress: { + equals: args.where?.userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, + }, }, }); @@ -596,8 +613,15 @@ export class PoolGqlLoaderService { private getUserBalance( userWalletBalances: PrismaUserWalletBalance[], userStakedBalances: PrismaUserStakedBalance[], - ): GqlPoolUserBalance | null { - throw new Error('Method not implemented.'); + ): GqlPoolUserBalance { + const stakedNum = parseUnits(userWalletBalances.at(0)?.balance || '0', 18); + const walletNum = parseUnits(userStakedBalances.at(0)?.balance || '0', 18); + + return { + walletBalance: userWalletBalances.at(0)?.balance || '0', + stakedBalance: userStakedBalances.at(0)?.balance || '0', + totalBalance: formatFixed(stakedNum.add(walletNum), 18), + }; } private getPoolDynamicData(pool: PrismaPoolMinimal): GqlPoolDynamicData { From 69120bc4e60fdb4d90f1bab0bed226a63230ddc0 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 1 Dec 2023 11:37:45 +0100 Subject: [PATCH 105/175] unity poolType enum --- modules/pool/pool.gql | 59 ++++++++++++++++++++--------------------- modules/vebal/vebal.gql | 2 +- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index f6d231cf3..83ee81202 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -73,7 +73,7 @@ type GqlPoolMinimal { allTokens: [GqlPoolTokenExpanded!]! dynamicData: GqlPoolDynamicData! staking: GqlPoolStaking - type: GqlPoolMinimalType! + type: GqlPoolType! version: Int! userBalance: GqlPoolUserBalance } @@ -84,7 +84,7 @@ type GqlPoolUserBalance { stakedBalance: AmountHumanReadable! } -enum GqlPoolMinimalType { +enum GqlPoolType { WEIGHTED STABLE META_STABLE @@ -104,7 +104,7 @@ interface GqlPoolBase { #fields that never change after creation id: ID! chain: GqlChain! - type: String! + type: GqlPoolType! version: Int! name: String! symbol: String! @@ -224,7 +224,7 @@ type GqlPoolWeighted implements GqlPoolBase { tokens: [GqlPoolTokenUnion!]! nestingType: GqlPoolNestingType! staking: GqlPoolStaking - type: String! + type: GqlPoolType! version: Int! } @@ -232,7 +232,7 @@ type GqlPoolGyro implements GqlPoolBase { id: ID! chain: GqlChain! name: String! - type: String! + type: GqlPoolType! symbol: String! address: Bytes! decimals: Int! @@ -251,7 +251,6 @@ type GqlPoolGyro implements GqlPoolBase { tokens: [GqlPoolTokenUnion!]! nestingType: GqlPoolNestingType! staking: GqlPoolStaking - type: String! version: Int! } @@ -274,7 +273,7 @@ type GqlPoolLiquidityBootstrapping implements GqlPoolBase { tokens: [GqlPoolTokenUnion!]! nestingType: GqlPoolNestingType! staking: GqlPoolStaking - type: String! + type: GqlPoolType! version: Int! } @@ -297,7 +296,7 @@ type GqlPoolStable implements GqlPoolBase { tokens: [GqlPoolToken!]! amp: BigInt! staking: GqlPoolStaking - type: String! + type: GqlPoolType! version: Int! } @@ -320,7 +319,7 @@ type GqlPoolMetaStable implements GqlPoolBase { tokens: [GqlPoolToken!]! amp: BigInt! staking: GqlPoolStaking - type: String! + type: GqlPoolType! version: Int! } @@ -344,7 +343,7 @@ type GqlPoolPhantomStable implements GqlPoolBase { amp: BigInt! staking: GqlPoolStaking bptPriceRate: BigDecimal! - type: String! + type: GqlPoolType! version: Int! } @@ -370,7 +369,7 @@ type GqlPoolElement implements GqlPoolBase { principalToken: Bytes! baseToken: Bytes! staking: GqlPoolStaking - type: String! + type: GqlPoolType! version: Int! } @@ -398,7 +397,7 @@ type GqlPoolLinear implements GqlPoolBase { lowerTarget: BigInt! staking: GqlPoolStaking bptPriceRate: BigDecimal! - type: String! + type: GqlPoolType! version: Int! } @@ -422,7 +421,7 @@ type GqlPoolLinearNested { lowerTarget: BigInt! bptPriceRate: BigDecimal! - type: String! + type: GqlPoolType! version: Int! } @@ -443,7 +442,7 @@ type GqlPoolPhantomStableNested { swapFee: BigDecimal! bptPriceRate: BigDecimal! - type: String! + type: GqlPoolType! version: Int! } @@ -614,8 +613,8 @@ input GqlPoolFilter { categoryNotIn: [GqlPoolFilterCategory!] tokensIn: [String!] tokensNotIn: [String!] - poolTypeIn: [GqlPoolFilterType!] - poolTypeNotIn: [GqlPoolFilterType!] + poolTypeIn: [GqlPoolType!] + poolTypeNotIn: [GqlPoolType!] idIn: [String!] idNotIn: [String!] filterIn: [String!] @@ -631,20 +630,20 @@ enum GqlPoolFilterCategory { BLACK_LISTED } -enum GqlPoolFilterType { - WEIGHTED - STABLE - META_STABLE - PHANTOM_STABLE - ELEMENT - LINEAR - UNKNOWN - LIQUIDITY_BOOTSTRAPPING - INVESTMENT - GYRO - GYRO3 - GYROE -} +# enum GqlPoolFilterType { +# WEIGHTED +# STABLE +# META_STABLE +# PHANTOM_STABLE +# ELEMENT +# LINEAR +# UNKNOWN +# LIQUIDITY_BOOTSTRAPPING +# INVESTMENT +# GYRO +# GYRO3 +# GYROE +# } type GqlPoolTokenExpanded { id: ID! diff --git a/modules/vebal/vebal.gql b/modules/vebal/vebal.gql index 3c2d4c68b..a16cd12ce 100644 --- a/modules/vebal/vebal.gql +++ b/modules/vebal/vebal.gql @@ -20,7 +20,7 @@ type GqlVotingPool { chain: GqlChain! symbol: String! address: Bytes! - type: GqlPoolMinimalType! + type: GqlPoolType! tokens: [GqlVotingGaugeToken!]! From bb6c2310c928f1f79dd691bf18137b01ed4caa9d Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 29 Nov 2023 01:17:35 +0100 Subject: [PATCH 106/175] don't work unless JOBS_ENABLED --- worker/job-handlers.ts | 9 ++++++++- worker/job-queue.ts | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 4da33e683..86ca90ba8 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -63,7 +63,14 @@ async function runIfNotAlreadyRunning( export function configureWorkerRoutes(app: Express) { app.post('/', async (req, res, next) => { - const job = req.body as { name: string; chain: string }; + const job = req.body as { name: string; chain: string; run?: boolean }; + + // Worker only works when enabled + if (process.env.JOBS_ENABLED !== 'true' && Boolean(job.run) !== true) { + res.sendStatus(200); + return; + } + console.log(`Current jobqueue length: ${runningJobs.size}`); const chainId = job.chain; initRequestScopedContext(); diff --git a/worker/job-queue.ts b/worker/job-queue.ts index 72a88b281..c41dc8863 100644 --- a/worker/job-queue.ts +++ b/worker/job-queue.ts @@ -10,7 +10,7 @@ class WokerQueue { public async sendWithInterval(json: string, intervalMs: number, deDuplicationId?: string): Promise { try { if (env.WORKER_QUEUE_URL.match(/localhost/)) { - await this.sendLocalMessage(json ); + await this.sendLocalMessage(json); } else { await this.sendMessage(json, deDuplicationId); } @@ -28,8 +28,8 @@ class WokerQueue { public async sendLocalMessage(json: string): Promise { await axios.post(env.WORKER_QUEUE_URL, json, { headers: { - 'Content-Type': 'application/json' - } + 'Content-Type': 'application/json', + }, }); } From 466fbe7262d1614ce2c6764be7216aaa3c15f0c7 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 29 Nov 2023 03:34:21 +0100 Subject: [PATCH 107/175] use WORKER_QUEUE_URL only in scheduler --- app/env.ts | 5 ++++- worker/job-queue.ts | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/env.ts b/app/env.ts index 7ab6f5689..b27d1df07 100644 --- a/app/env.ts +++ b/app/env.ts @@ -30,7 +30,10 @@ export const schema = { optional: true, type: String, }, - WORKER_QUEUE_URL: String, + WORKER_QUEUE_URL: { + optional: true, + type: String, + }, DATABASE_URL: String, }; diff --git a/worker/job-queue.ts b/worker/job-queue.ts index c41dc8863..435db784f 100644 --- a/worker/job-queue.ts +++ b/worker/job-queue.ts @@ -5,16 +5,20 @@ import { SendMessageCommand, SendMessageCommandInput, SQSClient } from '@aws-sdk import { env } from '../app/env'; class WokerQueue { - constructor(private readonly client: SQSClient, private readonly queueUrl: string) {} + constructor(private readonly client: SQSClient, private readonly queueUrl?: string) {} public async sendWithInterval(json: string, intervalMs: number, deDuplicationId?: string): Promise { try { - if (env.WORKER_QUEUE_URL.match(/localhost/)) { + if (this.queueUrl === undefined) { + return; + } + + if (this.queueUrl.match(/localhost/)) { await this.sendLocalMessage(json); } else { await this.sendMessage(json, deDuplicationId); } - console.log(`Sent message to schedule job on queue ${env.WORKER_QUEUE_URL}: ${json}`); + console.log(`Sent message to schedule job on queue ${this.queueUrl}: ${json}`); } catch (error) { console.log(error); Sentry.captureException(error); @@ -26,7 +30,11 @@ class WokerQueue { } public async sendLocalMessage(json: string): Promise { - await axios.post(env.WORKER_QUEUE_URL, json, { + if (this.queueUrl === undefined) { + throw new Error('WORKER_QUEUE_URL is undefined'); + } + + await axios.post(this.queueUrl, json, { headers: { 'Content-Type': 'application/json', }, From 7eed2d5e737c93fbc34ebd96b39aa6617a215d1f Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:14:03 +0100 Subject: [PATCH 108/175] use PROTOCOL only in web tier not needed in workers --- app/env.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/env.ts b/app/env.ts index b27d1df07..a782dd24c 100644 --- a/app/env.ts +++ b/app/env.ts @@ -13,7 +13,10 @@ export const schema = { SENTRY_DSN: String, SENTRY_AUTH_TOKEN: String, AWS_REGION: String, - PROTOCOL: String, + PROTOCOL: { + optional: true, + type: String, + }, INFURA_API_KEY: { optional: true, type: String, From 41f45cbbfe64b8fa1b981d2eea7dc72a76239a3e Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:58:09 +0100 Subject: [PATCH 109/175] adding missing fx#protocolPercentFee fn --- modules/pool/abi/FxPool.json | 553 ++++++++++++++++++++++++++ modules/pool/lib/pool-onchain-data.ts | 2 + 2 files changed, 555 insertions(+) create mode 100644 modules/pool/abi/FxPool.json diff --git a/modules/pool/abi/FxPool.json b/modules/pool/abi/FxPool.json new file mode 100644 index 000000000..75eb2ed77 --- /dev/null +++ b/modules/pool/abi/FxPool.json @@ -0,0 +1,553 @@ +[ + { + "inputs": [ + { "internalType": "address[]", "name": "_assetsToRegister", "type": "address[]" }, + { "internalType": "contract IVault", "name": "vault", "type": "address" }, + { "internalType": "uint256", "name": "_protocolPercentFee", "type": "uint256" }, + { "internalType": "string", "name": "_name", "type": "string" }, + { "internalType": "string", "name": "_symbol", "type": "string" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "numeraire", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "reserve", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "weight", "type": "uint256" } + ], + "name": "AssetIncluded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "derivative", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "numeraire", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "reserve", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "assimilator", "type": "address" } + ], + "name": "AssimilatorIncluded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "newCollector", "type": "address" }], + "name": "ChangeCollectorAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "bool", "name": "isEmergency", "type": "bool" }], + "name": "EmergencyAlarm", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "poolId", "type": "bytes32" }, + { "indexed": false, "internalType": "uint256", "name": "lptAmountBurned", "type": "uint256" }, + { "indexed": false, "internalType": "uint256[]", "name": "amountsWithdrawn", "type": "uint256[]" } + ], + "name": "EmergencyWithdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "feesCollected", "type": "uint256" }], + "name": "FeesAccrued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "recipient", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "feesCollected", "type": "uint256" } + ], + "name": "FeesCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "poolId", "type": "bytes32" }, + { "indexed": false, "internalType": "uint256", "name": "lptAmountBurned", "type": "uint256" }, + { "indexed": false, "internalType": "uint256[]", "name": "amountsWithdrawn", "type": "uint256[]" } + ], + "name": "OnExitPool", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "poolId", "type": "bytes32" }, + { "indexed": false, "internalType": "uint256", "name": "lptAmountMinted", "type": "uint256" }, + { "indexed": false, "internalType": "uint256[]", "name": "amountsDeposited", "type": "uint256[]" } + ], + "name": "OnJoinPool", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "alpha", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "beta", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "delta", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "epsilon", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "lambda", "type": "uint256" } + ], + "name": "ParametersSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "updater", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newProtocolPercentage", "type": "uint256" } + ], + "name": "ProtocolFeeShareUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "trader", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "origin", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "target", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "originAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "targetAmount", "type": "uint256" } + ], + "name": "Trade", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_derivative", "type": "address" }], + "name": "assimilator", + "outputs": [{ "internalType": "address", "name": "assimilator_", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectorAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "curve", + "outputs": [ + { "internalType": "int128", "name": "alpha", "type": "int128" }, + { "internalType": "int128", "name": "beta", "type": "int128" }, + { "internalType": "int128", "name": "delta", "type": "int128" }, + { "internalType": "int128", "name": "epsilon", "type": "int128" }, + { "internalType": "int128", "name": "lambda", "type": "int128" }, + { "internalType": "uint256", "name": "cap", "type": "uint256" }, + { "internalType": "contract IVault", "name": "vault", "type": "address" }, + { "internalType": "address", "name": "fxPoolAddress", "type": "address" }, + { "internalType": "bytes32", "name": "poolId", "type": "bytes32" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "decreaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "derivatives", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "emergency", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPoolId", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [{ "internalType": "contract IVault", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "_assets", "type": "address[]" }, + { "internalType": "uint256[]", "name": "_assetWeights", "type": "uint256[]" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidity", + "outputs": [ + { "internalType": "uint256", "name": "total_", "type": "uint256" }, + { "internalType": "uint256[]", "name": "individual_", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "nonces", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "numeraires", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "poolId", "type": "bytes32" }, + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "bytes", "name": "userData", "type": "bytes" } + ], + "name": "onExitPool", + "outputs": [ + { "internalType": "uint256[]", "name": "amountsOut", "type": "uint256[]" }, + { "internalType": "uint256[]", "name": "dueProtocolFeeAmounts", "type": "uint256[]" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "poolId", "type": "bytes32" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "bytes", "name": "userData", "type": "bytes" } + ], + "name": "onJoinPool", + "outputs": [ + { "internalType": "uint256[]", "name": "amountsIn", "type": "uint256[]" }, + { "internalType": "uint256[]", "name": "dueProtocolFeeAmounts", "type": "uint256[]" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "enum IVault.SwapKind", "name": "kind", "type": "uint8" }, + { "internalType": "contract IERC20", "name": "tokenIn", "type": "address" }, + { "internalType": "contract IERC20", "name": "tokenOut", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "bytes32", "name": "poolId", "type": "bytes32" }, + { "internalType": "uint256", "name": "lastChangeBlock", "type": "uint256" }, + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "bytes", "name": "userData", "type": "bytes" } + ], + "internalType": "struct IPoolSwapStructs.SwapRequest", + "name": "swapRequest", + "type": "tuple" + }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "name": "onSwap", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "protocolPercentFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "reserves", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "_cap", "type": "uint256" }], + "name": "setCap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_collectorAddress", "type": "address" }], + "name": "setCollectorAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "_emergency", "type": "bool" }], + "name": "setEmergency", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_alpha", "type": "uint256" }, + { "internalType": "uint256", "name": "_beta", "type": "uint256" }, + { "internalType": "uint256", "name": "_feeAtHalt", "type": "uint256" }, + { "internalType": "uint256", "name": "_epsilon", "type": "uint256" }, + { "internalType": "uint256", "name": "_lambda", "type": "uint256" } + ], + "name": "setParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "setPaused", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "uint256", "name": "_protocolPercentFee", "type": "uint256" }], + "name": "setProtocolPercentFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalUnclaimedFeesInNumeraire", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "totalDepositNumeraire", "type": "uint256" }], + "name": "viewDeposit", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "viewParameters", + "outputs": [ + { "internalType": "uint256", "name": "alpha_", "type": "uint256" }, + { "internalType": "uint256", "name": "beta_", "type": "uint256" }, + { "internalType": "uint256", "name": "delta_", "type": "uint256" }, + { "internalType": "uint256", "name": "epsilon_", "type": "uint256" }, + { "internalType": "uint256", "name": "lambda_", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "_curvesToBurn", "type": "uint256" }], + "name": "viewWithdraw", + "outputs": [{ "internalType": "uint256[]", "name": "", "type": "uint256[]" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index 189c5f62e..ca333d132 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -13,6 +13,7 @@ import WeightedPoolAbi from '../abi/WeightedPool.json'; import StablePoolAbi from '../abi/StablePool.json'; import MetaStablePoolAbi from '../abi/MetaStablePool.json'; import StablePhantomPoolAbi from '../abi/StablePhantomPool.json'; +import FxPoolAbi from '../abi/FxPool.json'; import { JsonFragment } from '@ethersproject/abi'; interface PoolInput { @@ -60,6 +61,7 @@ const abi: JsonFragment[] = Object.values( ...StablePhantomPoolAbi, ...MetaStablePoolAbi, ...ComposableStablePoolAbi, + ...FxPoolAbi, //...WeightedPoolV2Abi, ].map((row) => [row.name, row]), ), From ace6b65ecceb0a856d03be370214b0ae3e58bb9c Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 1 Dec 2023 16:05:23 +0100 Subject: [PATCH 110/175] worker switch --- worker/job-handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 86ca90ba8..9a0f61675 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -66,7 +66,7 @@ export function configureWorkerRoutes(app: Express) { const job = req.body as { name: string; chain: string; run?: boolean }; // Worker only works when enabled - if (process.env.JOBS_ENABLED !== 'true' && Boolean(job.run) !== true) { + if (process.env.JOBS_ENABLED !== 'true') { res.sendStatus(200); return; } From ba203f1ecd5cf340c192731e65bcd45d277a9991 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 1 Dec 2023 16:20:23 +0100 Subject: [PATCH 111/175] remove switch --- worker/job-handlers.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 9a0f61675..802364dae 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -65,12 +65,6 @@ export function configureWorkerRoutes(app: Express) { app.post('/', async (req, res, next) => { const job = req.body as { name: string; chain: string; run?: boolean }; - // Worker only works when enabled - if (process.env.JOBS_ENABLED !== 'true') { - res.sendStatus(200); - return; - } - console.log(`Current jobqueue length: ${runningJobs.size}`); const chainId = job.chain; initRequestScopedContext(); From 2a93aa9905897490581609142cfd1bd3eff32edd Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 4 Dec 2023 13:03:52 +0100 Subject: [PATCH 112/175] more logs for voting gauges --- modules/vebal/voting-gauges.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vebal/voting-gauges.repository.ts b/modules/vebal/voting-gauges.repository.ts index cd252b8f9..347932c17 100644 --- a/modules/vebal/voting-gauges.repository.ts +++ b/modules/vebal/voting-gauges.repository.ts @@ -128,7 +128,7 @@ export class VotingGaugesRepository { await this.saveVotingGauge(gauge); return gauge; } catch (error) { - saveErrors.push(error as Error); + saveErrors.push(new Error(`Failed to save voting gauge ${gauge.gaugeAddress} with error ${error}`)); return gauge; } }), From 9269002852e80d12031a9f2a6c49761fa4370867 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 4 Dec 2023 13:22:10 +0100 Subject: [PATCH 113/175] we can have duplicate stakingGauges in the list, will be filtered --- modules/vebal/vebal.prisma | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/vebal/vebal.prisma b/modules/vebal/vebal.prisma index c4b1b3a0f..df28431b5 100644 --- a/modules/vebal/vebal.prisma +++ b/modules/vebal/vebal.prisma @@ -27,7 +27,6 @@ enum PrismaVotingGaugeStatus { model PrismaVotingGauge { @@id([id, chain]) - @@unique([stakingGaugeId, chain]) id String chain Chain From 1305660403a8e5db4e654a7bacb7d63926de0ff5 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 4 Dec 2023 13:55:14 +0100 Subject: [PATCH 114/175] change to one-to-many --- modules/pool/pool.prisma | 2 +- modules/pool/pool.service.ts | 2 +- modules/vebal/vebal.prisma | 2 +- prisma/schema.prisma | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/pool/pool.prisma b/modules/pool/pool.prisma index 16767b4df..9244c46b5 100644 --- a/modules/pool/pool.prisma +++ b/modules/pool/pool.prisma @@ -423,7 +423,7 @@ model PrismaPoolStakingGauge { chain Chain gaugeAddress String - votingGauge PrismaVotingGauge? + votingGauge PrismaVotingGauge[] rewards PrismaPoolStakingGaugeReward[] status PrismaPoolStakingGaugeStatus @default(ACTIVE) version Int @default(1) diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index ae54c3e2b..468a8f201 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -494,7 +494,7 @@ export class PoolService { if (gauge && gauge.votingGauge) await prisma.prismaVotingGauge.deleteMany({ - where: { chain: this.chain, id: gauge.votingGauge.id }, + where: { chain: this.chain, id: { in: gauge.votingGauge.map((gauge) => gauge.id) } }, }); await prisma.prismaPoolStakingGauge.deleteMany({ diff --git a/modules/vebal/vebal.prisma b/modules/vebal/vebal.prisma index df28431b5..119f4778d 100644 --- a/modules/vebal/vebal.prisma +++ b/modules/vebal/vebal.prisma @@ -31,7 +31,7 @@ model PrismaVotingGauge { id String chain Chain - stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain]) + stakingGauge PrismaPoolStakingGauge? @relation(fields: [id, chain], references: [id, chain]) status PrismaVotingGaugeStatus @default(ACTIVE) gaugeAddress String stakingGaugeId String? diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9ec3e38d1..b4ee18b87 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -469,7 +469,7 @@ model PrismaPoolStakingGauge { chain Chain gaugeAddress String - votingGauge PrismaVotingGauge? + votingGauge PrismaVotingGauge[] rewards PrismaPoolStakingGaugeReward[] status PrismaPoolStakingGaugeStatus @default(ACTIVE) version Int @default(1) @@ -846,12 +846,11 @@ enum PrismaVotingGaugeStatus { model PrismaVotingGauge { @@id([id, chain]) - @@unique([stakingGaugeId, chain]) id String chain Chain - stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain]) + stakingGauge PrismaPoolStakingGauge? @relation(fields: [id, chain], references: [id, chain]) status PrismaVotingGaugeStatus @default(ACTIVE) gaugeAddress String stakingGaugeId String? From 7311e8c5933ad4c70e97667c1eef57f460293d6c Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 4 Dec 2023 15:23:03 +0100 Subject: [PATCH 115/175] add migration --- modules/vebal/vebal.prisma | 2 +- .../20231204142011_remove_constraint_votinggauge/migration.sql | 2 ++ prisma/schema.prisma | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20231204142011_remove_constraint_votinggauge/migration.sql diff --git a/modules/vebal/vebal.prisma b/modules/vebal/vebal.prisma index 119f4778d..df28431b5 100644 --- a/modules/vebal/vebal.prisma +++ b/modules/vebal/vebal.prisma @@ -31,7 +31,7 @@ model PrismaVotingGauge { id String chain Chain - stakingGauge PrismaPoolStakingGauge? @relation(fields: [id, chain], references: [id, chain]) + stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain]) status PrismaVotingGaugeStatus @default(ACTIVE) gaugeAddress String stakingGaugeId String? diff --git a/prisma/migrations/20231204142011_remove_constraint_votinggauge/migration.sql b/prisma/migrations/20231204142011_remove_constraint_votinggauge/migration.sql new file mode 100644 index 000000000..4b0b35734 --- /dev/null +++ b/prisma/migrations/20231204142011_remove_constraint_votinggauge/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "PrismaVotingGauge_stakingGaugeId_chain_key"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b4ee18b87..79f305c65 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -850,7 +850,7 @@ model PrismaVotingGauge { id String chain Chain - stakingGauge PrismaPoolStakingGauge? @relation(fields: [id, chain], references: [id, chain]) + stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain]) status PrismaVotingGaugeStatus @default(ACTIVE) gaugeAddress String stakingGaugeId String? From fc6a9e6617a4b012a73b699c8c8da4a4cd63e280 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:40:14 +0100 Subject: [PATCH 116/175] fix onchain call for gyro supply (#551) --- modules/pool/lib/pool-onchain-data.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/pool/lib/pool-onchain-data.ts b/modules/pool/lib/pool-onchain-data.ts index ca333d132..7a799a481 100644 --- a/modules/pool/lib/pool-onchain-data.ts +++ b/modules/pool/lib/pool-onchain-data.ts @@ -83,7 +83,6 @@ const getTotalSupplyFn = (type: PoolInput['type'], version: number) => { } else if ( type === 'COMPOSABLE_STABLE' || (type === 'WEIGHTED' && version > 1) || - (type === 'GYROE' && version > 1) || (type === 'UNKNOWN' && version > 1) ) { return 'getActualSupply'; From ad6e4d5d32a9ca571af90801c3c6b53e76a85cde Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 4 Dec 2023 21:44:04 +0100 Subject: [PATCH 117/175] removing run --- worker/job-handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 802364dae..a8dffcf31 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -63,7 +63,7 @@ async function runIfNotAlreadyRunning( export function configureWorkerRoutes(app: Express) { app.post('/', async (req, res, next) => { - const job = req.body as { name: string; chain: string; run?: boolean }; + const job = req.body as { name: string; chain: string }; console.log(`Current jobqueue length: ${runningJobs.size}`); const chainId = job.chain; From 532024a2ce5fc1a45df0a0d72aee41f06694d187 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:05:06 +0100 Subject: [PATCH 118/175] try to ignore chain errors for sentry --- app.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app.ts b/app.ts index 0d70d9ef3..49b240ff8 100644 --- a/app.ts +++ b/app.ts @@ -28,6 +28,7 @@ async function startServer() { // tracesSampleRate: 0.005, environment: `multichain-${env.DEPLOYMENT_ENV}`, enabled: env.NODE_ENV === 'production', + ignoreErrors: [/.*error: Provide.*chain.*param/], integrations: [ // new Tracing.Integrations.Apollo(), // new Tracing.Integrations.GraphQL(), From 6effea879154dc116028a69a46eea29beb020aa2 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:37:53 +0100 Subject: [PATCH 119/175] sentry job tracing --- worker/job-handlers.ts | 30 +++++++++++++++++++++++++----- worker/worker.ts | 1 + 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index a8dffcf31..cc3a2caa6 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -31,14 +31,29 @@ async function runIfNotAlreadyRunning( res.sendStatus(200); return; } + + const sentryTags = { + job: id, + chainId, + }; + + const sentryTransaction = Sentry.startTransaction({ + op: 'http', + name: `POST /${jobId}`, + tags: sentryTags, + }); + + Sentry.configureScope((scope) => scope.setSpan(sentryTransaction)); + + const sentryChildSpan = sentryTransaction.startChild({ + op: 'background-job', + description: `Running job ${jobId}`, + tags: sentryTags, + }); + try { runningJobs.add(jobId); - // TODO, this does not seem to work properly as it is a "global" scope - Sentry.configureScope((scope) => { - scope.setTransactionName(`POST /${jobId}`); - }); - console.time(jobId); console.log(`Start job ${jobId}-start`); @@ -49,12 +64,17 @@ async function runIfNotAlreadyRunning( } console.log(`Successful job ${jobId}-done`); } catch (error) { + Sentry.captureException(error); + if (process.env.AWS_ALERTS === 'true') { await cronsMetricPublisher.publish(`${jobId}-error`); } console.log(`Error job ${jobId}-error`, error); next(error); } finally { + sentryChildSpan.finish(); + sentryTransaction.finish(); + runningJobs.delete(jobId); console.timeEnd(jobId); res.sendStatus(200); diff --git a/worker/worker.ts b/worker/worker.ts index 325aefb2d..5ba9d73be 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -14,6 +14,7 @@ export async function startWorker() { // new Tracing.Integrations.Express({ app }), new Sentry.Integrations.Http({ tracing: true }), ], + tracesSampleRate: env.DEPLOYMENT_ENV === 'main' ? 0.2 : 1.0, }); app.use(Sentry.Handlers.requestHandler()); From aca5617b9cc1ee340bffe4ec0680a17ac2e41bae Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:51:54 +0100 Subject: [PATCH 120/175] sentry profiling --- package.json | 3 +- worker/worker.ts | 2 + yarn.lock | 533 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 505 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 5c7bd2869..f02ca2c23 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "@graphql-tools/merge": "^8.2.1", "@prisma/client": "^4.1.1", "@sanity/client": "^2.23.1", - "@sentry/node": "^7.56.0", + "@sentry/node": "^7.0.0", + "@sentry/profiling-node": "^1.2.6", "@sentry/tracing": "^7.56.0", "apollo-server-core": "^3.5.0", "apollo-server-express": "^3.5.0", diff --git a/worker/worker.ts b/worker/worker.ts index 5ba9d73be..857349c42 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -1,5 +1,6 @@ import express from 'express'; import * as Sentry from '@sentry/node'; +import { ProfilingIntegration } from '@sentry/profiling-node'; import { env } from '../app/env'; import { configureWorkerRoutes } from './job-handlers'; @@ -13,6 +14,7 @@ export async function startWorker() { integrations: [ // new Tracing.Integrations.Express({ app }), new Sentry.Integrations.Http({ tracing: true }), + new ProfilingIntegration(), ], tracesSampleRate: env.DEPLOYMENT_ENV === 'main' ? 0.2 : 1.0, }); diff --git a/yarn.lock b/yarn.lock index 1c3313ada..4c8f3ad55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3093,6 +3093,11 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@gar/promisify@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + "@gnosis.pm/safe-core-sdk-types@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-core-sdk-types/-/safe-core-sdk-types-0.1.1.tgz#908c394cb4660493b4e9c8e01b5a7aa36efafd30" @@ -3867,6 +3872,22 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@npmcli/fs@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== + dependencies: + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + +"@npmcli/move-file@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" + integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" @@ -4003,6 +4024,15 @@ "@sentry/utils" "7.56.0" tslib "^1.9.3" +"@sentry-internal/tracing@7.85.0": + version "7.85.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.85.0.tgz#1b4781a61e1e43badeff826cf40abe33dd760f1d" + integrity sha512-p3YMUwkPCy2su9cm/3+7QYR4RiMI0+07DU1BZtht9NLTzY2O87/yvUbn1v2yHR3vJQTy/+7N0ud9/mPBFznRQQ== + dependencies: + "@sentry/core" "7.85.0" + "@sentry/types" "7.85.0" + "@sentry/utils" "7.85.0" + "@sentry/core@7.56.0": version "7.56.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.56.0.tgz#f4253e0d61f55444180a63e5278b62e57303f7cf" @@ -4012,19 +4042,47 @@ "@sentry/utils" "7.56.0" tslib "^1.9.3" -"@sentry/node@^7.56.0": - version "7.56.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.56.0.tgz#ddeb34a848c8a544d0dbb5f2c3031615be040d2b" - integrity sha512-QXbWy/ypRxfFd8iP6zLvHInYZyjGKPrkVNYt43mhKAZHm764NxX/29vDfj1FztgG9Z6lVLIG2eyqTvLruYmsWw== +"@sentry/core@7.85.0", "@sentry/core@^7.76.0": + version "7.85.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.85.0.tgz#dd90d772a5f75ff674f931f59b22a3fc286d0983" + integrity sha512-DFDAc4tWmHN5IWhr7XbHCiyF1Xgb95jz8Uj/JTX9atlgodId1UIbER77qpEmH3eQGid/QBdqrlR98zCixgSbwg== dependencies: - "@sentry-internal/tracing" "7.56.0" - "@sentry/core" "7.56.0" - "@sentry/types" "7.56.0" - "@sentry/utils" "7.56.0" - cookie "^0.4.1" + "@sentry/types" "7.85.0" + "@sentry/utils" "7.85.0" + +"@sentry/hub@^7.76.0": + version "7.85.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.85.0.tgz#503d1e0acc17a55f79ec27a7722168c0f6b12fb1" + integrity sha512-08Vq+N7P538D68HpIq4GpvGkqc2zj3yk0Xh5z0xYBf4QoTDTzeELt9Ryi5lCW9mgXVzHbKbgzcxIO1EE4FugoA== + dependencies: + "@sentry/core" "7.85.0" + "@sentry/types" "7.85.0" + "@sentry/utils" "7.85.0" + +"@sentry/node@^7.0.0", "@sentry/node@^7.76.0": + version "7.85.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.85.0.tgz#cf4e6022b5cd1f3fb007186c5e04427b108ebe1d" + integrity sha512-uiBtRW9G017NHoCXBlK3ttkTwHXLFyI8ndHpaObtyajKTv3ptGIThVEn7DuK7Pwor//RjwjSEEOa7WDK+FdMVQ== + dependencies: + "@sentry-internal/tracing" "7.85.0" + "@sentry/core" "7.85.0" + "@sentry/types" "7.85.0" + "@sentry/utils" "7.85.0" https-proxy-agent "^5.0.0" - lru_map "^0.3.3" - tslib "^1.9.3" + +"@sentry/profiling-node@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@sentry/profiling-node/-/profiling-node-1.2.6.tgz#e43494896657bcfd11e75480ed6def4172e86c21" + integrity sha512-WsXO7VmLze5wPWHpvoRZFTtN+wHw9lYWKZs4T2FwPmvfNVaScGJey/+Wp51aM47Yy12Gj9n/BpqFYDsUXRLMvw== + dependencies: + "@sentry/core" "^7.76.0" + "@sentry/hub" "^7.76.0" + "@sentry/node" "^7.76.0" + "@sentry/types" "^7.76.0" + "@sentry/utils" "^7.76.0" + detect-libc "^2.0.2" + node-abi "^3.47.0" + node-gyp "^9.4.0" "@sentry/tracing@^7.56.0": version "7.56.0" @@ -4038,6 +4096,11 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.56.0.tgz#9042a099cf9e8816d081886d24b88082a5d9f87a" integrity sha512-5WjhVOQm75ItOytOx2jTx+5yw8/qJ316+g1Di8dS9+kgIi1zniqdMcX00C2yYe3FMUgFB49PegCUYulm9Evapw== +"@sentry/types@7.85.0", "@sentry/types@^7.76.0": + version "7.85.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.85.0.tgz#648488b90f958ca6a86922cc5d26004853410ba6" + integrity sha512-R5jR4XkK5tBU2jDiPdSVqzkmjYRr666bcGaFGUHB/xDQCjPsjk+pEmCCL+vpuWoaZmQJUE1hVU7rgnVX81w8zg== + "@sentry/utils@7.56.0": version "7.56.0" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.56.0.tgz#e60e4935d17b2197584abf6ce61b522ad055352c" @@ -4046,6 +4109,13 @@ "@sentry/types" "7.56.0" tslib "^1.9.3" +"@sentry/utils@7.85.0", "@sentry/utils@^7.76.0": + version "7.85.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.85.0.tgz#b84467fd07bc2ef09fdf382ddcdcdc3f5b0d78b0" + integrity sha512-JZ7seNOLvhjAQ8GeB3GYknPQJkuhF88xAYOaESZP3xPOWBMFUN+IO4RqjMqMLFDniOwsVQS7GB/MfP+hxufieg== + dependencies: + "@sentry/types" "7.85.0" + "@sinclair/typebox@^0.24.1": version "0.24.43" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.43.tgz#2e2bce0e5e493aaf639beed0cd6c88cfde7dd3d7" @@ -4952,7 +5022,7 @@ resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== -abbrev@1: +abbrev@1, abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -4992,13 +5062,28 @@ aes-js@3.0.0: resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= -agent-base@6: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" +agentkeepalive@^4.2.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ansi-align@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -5178,6 +5263,11 @@ apollo-server-types@^3.4.0: apollo-server-caching "^3.3.0" apollo-server-env "^4.2.0" +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + archiver-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" @@ -5207,6 +5297,14 @@ archiver@^5.3.0: tar-stream "^2.2.0" zip-stream "^4.1.0" +are-we-there-yet@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" + integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -5516,6 +5614,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -5652,6 +5757,30 @@ cac@^6.7.14: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== +cacache@^16.1.0: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^2.0.0" + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -5858,6 +5987,11 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -5881,6 +6015,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" @@ -5987,6 +6126,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -6050,6 +6194,11 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" +console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -6088,7 +6237,7 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -cookie@^0.4.1, cookie@^0.4.2: +cookie@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== @@ -6363,6 +6512,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -6383,6 +6537,11 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-libc@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -6520,7 +6679,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.11: +encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -6539,6 +6698,16 @@ entities@2.2.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -6835,6 +7004,11 @@ expect@^28.0.0, expect@^28.1.3: jest-message-util "^28.1.3" jest-util "^28.1.3" +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + express-promise-router@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/express-promise-router/-/express-promise-router-4.1.0.tgz#79160e145c27610ba411bceb0552a36f11dbab4f" @@ -7099,6 +7273,13 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-minipass@^2.0.0, fs-minipass@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -7129,6 +7310,20 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + gaxios@^5.0.0, gaxios@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.0.2.tgz#ca3a40e851c728d31d7001c2357062d46bf966d1" @@ -7260,6 +7455,17 @@ glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + global-dirs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" @@ -7348,6 +7554,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.0, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -7495,6 +7706,11 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -7561,6 +7777,11 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-cache-semantics@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -7630,6 +7851,13 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7700,6 +7928,16 @@ indent-string@^3.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -7792,6 +8030,11 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -7927,6 +8170,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + is-lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" @@ -9034,10 +9282,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru_map@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" - integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== +lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== lz-string@1.5.0: version "1.5.0" @@ -9063,6 +9311,28 @@ make-error@1.x, make-error@^1, make-error@^1.1.1, make-error@^1.3.0: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +make-fetch-happen@^10.0.3: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^16.1.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-fetch "^2.0.3" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -9196,17 +9466,83 @@ minimatch@3.0.4, minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-fetch@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" + integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== + dependencies: + minipass "^3.1.6" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.1.1, minizlib@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^1.0.4: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -9248,7 +9584,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -9309,6 +9645,11 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -9317,6 +9658,13 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-abi@^3.47.0: + version "3.51.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.51.0.tgz#970bf595ef5a26a271307f8a4befa02823d4e87d" + integrity sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA== + dependencies: + semver "^7.3.5" + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -9366,6 +9714,23 @@ node-gyp-build@^4.2.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== +node-gyp@^9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" + integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^10.0.3" + nopt "^6.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -9397,6 +9762,13 @@ nodemon@^2.0.15: undefsafe "^2.0.5" update-notifier "^5.1.0" +nopt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" + integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== + dependencies: + abbrev "^1.0.0" + nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -9428,6 +9800,16 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npmlog@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -9581,6 +9963,13 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -9833,6 +10222,19 @@ progress-stream@^2.0.0: speedometer "~1.0.0" through2 "~2.0.3" +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -10143,12 +10545,17 @@ retry@0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -10419,6 +10826,11 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -10427,6 +10839,23 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.6.2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + sonic-boom@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.3.1.tgz#e6572184fb3adf145dbfeccff48141bbd1009e4c" @@ -10511,6 +10940,13 @@ ssh2@^1.4.0: cpu-features "0.0.2" nan "^2.15.0" +ssri@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== + dependencies: + minipass "^3.1.1" + stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -10590,15 +11026,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10607,6 +11035,14 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string.prototype.trimend@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" @@ -10795,6 +11231,18 @@ tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar@^6.1.11, tar@^6.1.2: + version "6.2.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" + integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -11083,6 +11531,20 @@ undici@^4.9.3: resolved "https://registry.yarnpkg.com/undici/-/undici-4.10.0.tgz#f2684de9cbe2ab0a85a477ce0ec59b739be4438d" integrity sha512-W7NwyQzfiSYIZ+maOvz/u9tPFyAYhstF9XhtohK7txvD1jg41oatcHVq6Zls81LhzGvh4BN5WpQGuMiT4IipoA== +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== + dependencies: + unique-slug "^3.0.0" + +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + dependencies: + imurmurhash "^0.1.4" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -11349,7 +11811,7 @@ which-typed-array@^1.1.2: has-tostringtag "^1.0.0" is-typed-array "^1.1.9" -which@^2.0.1: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -11364,6 +11826,13 @@ why-is-node-running@^2.2.2: siginfo "^2.0.0" stackback "0.0.2" +wide-align@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" From 0338af5af3c9eb6529476c3b93e564468af8b9f1 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:07:10 +0100 Subject: [PATCH 121/175] prisma binary target for linux2 --- prisma/base.prisma | 2 +- prisma/schema.prisma | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prisma/base.prisma b/prisma/base.prisma index 8a55320cb..ed3ed1715 100644 --- a/prisma/base.prisma +++ b/prisma/base.prisma @@ -3,7 +3,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] } datasource db { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e3de046e7..3872f129d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -5,7 +5,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] } datasource db { From 06ab601d78d08d655289e750fb4bfdb8c82907d8 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:39:49 +0100 Subject: [PATCH 122/175] isolating prisma binary target --- prisma/base.prisma | 2 +- prisma/schema.prisma | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prisma/base.prisma b/prisma/base.prisma index ed3ed1715..5793c1606 100644 --- a/prisma/base.prisma +++ b/prisma/base.prisma @@ -3,7 +3,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] //, "linux-arm64-openssl-3.0.x"] } datasource db { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3872f129d..988be5d51 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -5,7 +5,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] //, "linux-arm64-openssl-3.0.x"] } datasource db { From 009ce5035b9b3df78ddcd03064971fd01952d681 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 5 Dec 2023 14:42:27 +0100 Subject: [PATCH 123/175] add cron duration metric --- .../metrics/cron-duration-metrics.client.ts | 63 +++++++++++++++++++ worker/job-handlers.ts | 7 +++ 2 files changed, 70 insertions(+) create mode 100644 modules/metrics/cron-duration-metrics.client.ts diff --git a/modules/metrics/cron-duration-metrics.client.ts b/modules/metrics/cron-duration-metrics.client.ts new file mode 100644 index 000000000..9c15793c9 --- /dev/null +++ b/modules/metrics/cron-duration-metrics.client.ts @@ -0,0 +1,63 @@ +import { + CloudWatchClient, + CloudWatchClientConfig, + MetricDatum, + PutMetricDataCommand, +} from '@aws-sdk/client-cloudwatch'; +import { env } from '../../app/env'; + +export interface NotificationsCloudwatchMetric { + merticData: MetricDatum[]; + nameSpace: string; +} + +export class CloudwatchCronDurationMetricsPublisher { + protected environment: string; + protected configuration?: CloudWatchClientConfig; + private client: CloudWatchClient; + public namespace: string; + + constructor( + namespace: string = 'default', + environment: string = env.DEPLOYMENT_ENV, + configuration?: CloudWatchClientConfig, + ) { + this.namespace = namespace; + this.environment = environment; + this.client = new CloudWatchClient({ region: env.AWS_REGION, ...configuration }); + } + + public async publish(metricName: string, count?: number): Promise { + const [major, minor, patch] = process.versions.node.split('.').map(Number); + try { + const command = new PutMetricDataCommand({ + MetricData: [ + { + MetricName: metricName, + Dimensions: [ + { + Name: 'Environment', + Value: this.environment, + }, + { + Name: 'NodeVersion', + Value: major.toString(), + }, + ], + Unit: 'None', + Timestamp: new Date(), + Value: count ? count : 1, + }, + ], + Namespace: this.namespace, + }); + + await this.client.send(command); + } catch (err) { + // Sentry.captureException(`Failed to publish "${metricName}" in "${this.environment}: ${err}`); + console.log(`Failed to publish "${metricName}" in "${this.environment}: ${err}`); + } + } +} + +export const cronsDurationMetricPublisher = new CloudwatchCronDurationMetricsPublisher('backendCronsDuration'); diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 4da33e683..49b9e6090 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -12,6 +12,8 @@ import { networkContext } from '../modules/network/network-context.service'; import { veBalService } from '../modules/vebal/vebal.service'; import { veBalVotingListService } from '../modules/vebal/vebal-voting-list.service'; import { cronsMetricPublisher } from '../modules/metrics/metrics.client'; +import moment from 'moment'; +import { cronsDurationMetricPublisher } from '../modules/metrics/cron-duration-metrics.client'; const runningJobs: Set = new Set(); @@ -31,6 +33,7 @@ async function runIfNotAlreadyRunning( res.sendStatus(200); return; } + const startJobTime = moment(); try { runningJobs.add(jobId); @@ -44,13 +47,17 @@ async function runIfNotAlreadyRunning( await fn(); + const durationSuccess = moment.duration(moment().diff(startJobTime)).asSeconds(); if (process.env.AWS_ALERTS === 'true') { await cronsMetricPublisher.publish(`${jobId}-done`); + await cronsDurationMetricPublisher.publish(`${jobId}-done`, durationSuccess); } console.log(`Successful job ${jobId}-done`); } catch (error) { + const durationError = moment.duration(moment().diff(startJobTime)).asSeconds(); if (process.env.AWS_ALERTS === 'true') { await cronsMetricPublisher.publish(`${jobId}-error`); + await cronsDurationMetricPublisher.publish(`${jobId}-error`, durationError); } console.log(`Error job ${jobId}-error`, error); next(error); From 91049dc4ae1a0ada653a6e6a09fdaaccd5734e8e Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:46:05 +0100 Subject: [PATCH 124/175] readding prisma new bin --- prisma/base.prisma | 2 +- prisma/schema.prisma | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prisma/base.prisma b/prisma/base.prisma index 5793c1606..ed3ed1715 100644 --- a/prisma/base.prisma +++ b/prisma/base.prisma @@ -3,7 +3,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] //, "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] } datasource db { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 988be5d51..3872f129d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -5,7 +5,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] //, "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] } datasource db { From d5469a4bb7f75bf8a1bee15ed5507cb96cffef22 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:39:00 +0100 Subject: [PATCH 125/175] prisma bin rollback --- prisma/base.prisma | 2 +- prisma/schema.prisma | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prisma/base.prisma b/prisma/base.prisma index ed3ed1715..5793c1606 100644 --- a/prisma/base.prisma +++ b/prisma/base.prisma @@ -3,7 +3,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] //, "linux-arm64-openssl-3.0.x"] } datasource db { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3872f129d..988be5d51 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -5,7 +5,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-arm64-openssl-1.0.x", "linux-arm64-openssl-3.0.x"] + binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] //, "linux-arm64-openssl-3.0.x"] } datasource db { From 38b98190cd655a58972c427eddd360e75ad3bffa Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:15:58 +0100 Subject: [PATCH 126/175] fix: pool volumes beyond reasonable range --- modules/pool/lib/pool-usd-data.service.ts | 98 +++++++++++++---------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/modules/pool/lib/pool-usd-data.service.ts b/modules/pool/lib/pool-usd-data.service.ts index 9191a273c..9734446d6 100644 --- a/modules/pool/lib/pool-usd-data.service.ts +++ b/modules/pool/lib/pool-usd-data.service.ts @@ -282,61 +282,71 @@ export class PoolUsdDataService { for (const pool of subgraphPools) { const staked = stakedUsers.find((stakedUser) => stakedUser.poolId === pool.id); - updates.push( - prisma.prismaPoolDynamicData.update({ + // Limit numbers, as we have seen some pools with skewd values + const max = 1e18; + const lifetimeVolume = Math.min(parseFloat(pool.totalSwapVolume), max); + const lifetimeSwapFees = Math.min(parseFloat(pool.totalSwapFee), max); + + // updates.push( + try { + await prisma.prismaPoolDynamicData.update({ where: { id_chain: { id: pool.id, chain: this.chain } }, data: { - lifetimeVolume: parseFloat(pool.totalSwapVolume), - lifetimeSwapFees: parseFloat(pool.totalSwapFee), + lifetimeVolume: lifetimeVolume, + lifetimeSwapFees: lifetimeSwapFees, holdersCount: parseInt(pool.holdersCount) + (staked?._count.userAddress || 0), swapsCount: parseInt(pool.swapsCount), }, - }), - ); + }); + } catch (e) { + console.error(`Error updating lifetime values for pool ${pool.id} (${pool.name})`); + throw e; + } + // ); const snapshots = await prisma.prismaPoolSnapshot.findMany({ where: { poolId: pool.id, chain: this.chain }, }); - if (snapshots.length > 0) { - const sharePriceAth = _.orderBy(snapshots, 'sharePrice', 'desc')[0]; - const sharePriceAtl = _.orderBy(snapshots, 'sharePrice', 'asc')[0]; - const totalLiquidityAth = _.orderBy(snapshots, 'totalLiquidity', 'desc')[0]; - const totalLiquidityAtl = _.orderBy(snapshots, 'totalLiquidity', 'asc')[0]; - const volume24hAth = _.orderBy(snapshots, 'volume24h', 'desc')[0]; - const volume24hAtl = _.orderBy(snapshots, 'volume24h', 'asc')[0]; - const fees24hAth = _.orderBy(snapshots, 'fees24h', 'desc')[0]; - const fees24hAtl = _.orderBy(snapshots, 'fees24h', 'asc')[0]; - - updates.push( - prisma.prismaPoolDynamicData.update({ - where: { id_chain: { id: pool.id, chain: this.chain } }, - data: { - sharePriceAth: sharePriceAth.sharePrice, - sharePriceAthTimestamp: sharePriceAth.timestamp, - sharePriceAtl: sharePriceAtl.sharePrice, - sharePriceAtlTimestamp: sharePriceAtl.timestamp, - - totalLiquidityAth: totalLiquidityAth.totalLiquidity, - totalLiquidityAthTimestamp: totalLiquidityAth.timestamp, - totalLiquidityAtl: totalLiquidityAtl.totalLiquidity, - totalLiquidityAtlTimestamp: totalLiquidityAtl.timestamp, - - volume24hAth: volume24hAth.volume24h, - volume24hAthTimestamp: volume24hAth.timestamp, - volume24hAtl: volume24hAtl.volume24h, - volume24hAtlTimestamp: volume24hAtl.timestamp, - - fees24hAth: fees24hAth.fees24h, - fees24hAthTimestamp: fees24hAth.timestamp, - fees24hAtl: fees24hAtl.fees24h, - fees24hAtlTimestamp: fees24hAtl.timestamp, - }, - }), - ); - } + // if (snapshots.length > 0) { + // const sharePriceAth = _.orderBy(snapshots, 'sharePrice', 'desc')[0]; + // const sharePriceAtl = _.orderBy(snapshots, 'sharePrice', 'asc')[0]; + // const totalLiquidityAth = _.orderBy(snapshots, 'totalLiquidity', 'desc')[0]; + // const totalLiquidityAtl = _.orderBy(snapshots, 'totalLiquidity', 'asc')[0]; + // const volume24hAth = _.orderBy(snapshots, 'volume24h', 'desc')[0]; + // const volume24hAtl = _.orderBy(snapshots, 'volume24h', 'asc')[0]; + // const fees24hAth = _.orderBy(snapshots, 'fees24h', 'desc')[0]; + // const fees24hAtl = _.orderBy(snapshots, 'fees24h', 'asc')[0]; + + // updates.push( + // prisma.prismaPoolDynamicData.update({ + // where: { id_chain: { id: pool.id, chain: this.chain } }, + // data: { + // sharePriceAth: sharePriceAth.sharePrice, + // sharePriceAthTimestamp: sharePriceAth.timestamp, + // sharePriceAtl: sharePriceAtl.sharePrice, + // sharePriceAtlTimestamp: sharePriceAtl.timestamp, + + // totalLiquidityAth: totalLiquidityAth.totalLiquidity, + // totalLiquidityAthTimestamp: totalLiquidityAth.timestamp, + // totalLiquidityAtl: totalLiquidityAtl.totalLiquidity, + // totalLiquidityAtlTimestamp: totalLiquidityAtl.timestamp, + + // volume24hAth: volume24hAth.volume24h, + // volume24hAthTimestamp: volume24hAth.timestamp, + // volume24hAtl: volume24hAtl.volume24h, + // volume24hAtlTimestamp: volume24hAtl.timestamp, + + // fees24hAth: fees24hAth.fees24h, + // fees24hAthTimestamp: fees24hAth.timestamp, + // fees24hAtl: fees24hAtl.fees24h, + // fees24hAtlTimestamp: fees24hAtl.timestamp, + // }, + // }), + // ); + // } } - await prismaBulkExecuteOperations(updates); + // await prismaBulkExecuteOperations(updates); } } From 852fdc27974199ddf4af8e827b9b0a1666dc8637 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:16:59 +0100 Subject: [PATCH 127/175] fix: uncommenting updates --- modules/pool/lib/pool-usd-data.service.ts | 89 +++++++++++------------ 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/modules/pool/lib/pool-usd-data.service.ts b/modules/pool/lib/pool-usd-data.service.ts index 9734446d6..7125c5d6b 100644 --- a/modules/pool/lib/pool-usd-data.service.ts +++ b/modules/pool/lib/pool-usd-data.service.ts @@ -287,9 +287,8 @@ export class PoolUsdDataService { const lifetimeVolume = Math.min(parseFloat(pool.totalSwapVolume), max); const lifetimeSwapFees = Math.min(parseFloat(pool.totalSwapFee), max); - // updates.push( - try { - await prisma.prismaPoolDynamicData.update({ + updates.push( + prisma.prismaPoolDynamicData.update({ where: { id_chain: { id: pool.id, chain: this.chain } }, data: { lifetimeVolume: lifetimeVolume, @@ -297,56 +296,52 @@ export class PoolUsdDataService { holdersCount: parseInt(pool.holdersCount) + (staked?._count.userAddress || 0), swapsCount: parseInt(pool.swapsCount), }, - }); - } catch (e) { - console.error(`Error updating lifetime values for pool ${pool.id} (${pool.name})`); - throw e; - } - // ); + }), + ); const snapshots = await prisma.prismaPoolSnapshot.findMany({ where: { poolId: pool.id, chain: this.chain }, }); - // if (snapshots.length > 0) { - // const sharePriceAth = _.orderBy(snapshots, 'sharePrice', 'desc')[0]; - // const sharePriceAtl = _.orderBy(snapshots, 'sharePrice', 'asc')[0]; - // const totalLiquidityAth = _.orderBy(snapshots, 'totalLiquidity', 'desc')[0]; - // const totalLiquidityAtl = _.orderBy(snapshots, 'totalLiquidity', 'asc')[0]; - // const volume24hAth = _.orderBy(snapshots, 'volume24h', 'desc')[0]; - // const volume24hAtl = _.orderBy(snapshots, 'volume24h', 'asc')[0]; - // const fees24hAth = _.orderBy(snapshots, 'fees24h', 'desc')[0]; - // const fees24hAtl = _.orderBy(snapshots, 'fees24h', 'asc')[0]; - - // updates.push( - // prisma.prismaPoolDynamicData.update({ - // where: { id_chain: { id: pool.id, chain: this.chain } }, - // data: { - // sharePriceAth: sharePriceAth.sharePrice, - // sharePriceAthTimestamp: sharePriceAth.timestamp, - // sharePriceAtl: sharePriceAtl.sharePrice, - // sharePriceAtlTimestamp: sharePriceAtl.timestamp, - - // totalLiquidityAth: totalLiquidityAth.totalLiquidity, - // totalLiquidityAthTimestamp: totalLiquidityAth.timestamp, - // totalLiquidityAtl: totalLiquidityAtl.totalLiquidity, - // totalLiquidityAtlTimestamp: totalLiquidityAtl.timestamp, - - // volume24hAth: volume24hAth.volume24h, - // volume24hAthTimestamp: volume24hAth.timestamp, - // volume24hAtl: volume24hAtl.volume24h, - // volume24hAtlTimestamp: volume24hAtl.timestamp, - - // fees24hAth: fees24hAth.fees24h, - // fees24hAthTimestamp: fees24hAth.timestamp, - // fees24hAtl: fees24hAtl.fees24h, - // fees24hAtlTimestamp: fees24hAtl.timestamp, - // }, - // }), - // ); - // } + if (snapshots.length > 0) { + const sharePriceAth = _.orderBy(snapshots, 'sharePrice', 'desc')[0]; + const sharePriceAtl = _.orderBy(snapshots, 'sharePrice', 'asc')[0]; + const totalLiquidityAth = _.orderBy(snapshots, 'totalLiquidity', 'desc')[0]; + const totalLiquidityAtl = _.orderBy(snapshots, 'totalLiquidity', 'asc')[0]; + const volume24hAth = _.orderBy(snapshots, 'volume24h', 'desc')[0]; + const volume24hAtl = _.orderBy(snapshots, 'volume24h', 'asc')[0]; + const fees24hAth = _.orderBy(snapshots, 'fees24h', 'desc')[0]; + const fees24hAtl = _.orderBy(snapshots, 'fees24h', 'asc')[0]; + + updates.push( + prisma.prismaPoolDynamicData.update({ + where: { id_chain: { id: pool.id, chain: this.chain } }, + data: { + sharePriceAth: sharePriceAth.sharePrice, + sharePriceAthTimestamp: sharePriceAth.timestamp, + sharePriceAtl: sharePriceAtl.sharePrice, + sharePriceAtlTimestamp: sharePriceAtl.timestamp, + + totalLiquidityAth: totalLiquidityAth.totalLiquidity, + totalLiquidityAthTimestamp: totalLiquidityAth.timestamp, + totalLiquidityAtl: totalLiquidityAtl.totalLiquidity, + totalLiquidityAtlTimestamp: totalLiquidityAtl.timestamp, + + volume24hAth: volume24hAth.volume24h, + volume24hAthTimestamp: volume24hAth.timestamp, + volume24hAtl: volume24hAtl.volume24h, + volume24hAtlTimestamp: volume24hAtl.timestamp, + + fees24hAth: fees24hAth.fees24h, + fees24hAthTimestamp: fees24hAth.timestamp, + fees24hAtl: fees24hAtl.fees24h, + fees24hAtlTimestamp: fees24hAtl.timestamp, + }, + }), + ); + } } - // await prismaBulkExecuteOperations(updates); + await prismaBulkExecuteOperations(updates); } } From a6d548961498c9c366089dcd35454aaa3d9855d6 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:47:39 +0100 Subject: [PATCH 128/175] add Sentry.profilesSampleRate --- worker/worker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/worker/worker.ts b/worker/worker.ts index 857349c42..c896cd63b 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -17,6 +17,7 @@ export async function startWorker() { new ProfilingIntegration(), ], tracesSampleRate: env.DEPLOYMENT_ENV === 'main' ? 0.2 : 1.0, + profilesSampleRate: 1.0, }); app.use(Sentry.Handlers.requestHandler()); From c197f95c28dc6b8328cfa3da54974e86d99129bd Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:27:29 +0100 Subject: [PATCH 129/175] decrease Sentry trace transactions --- worker/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/worker.ts b/worker/worker.ts index c896cd63b..5051ffb4e 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -16,7 +16,7 @@ export async function startWorker() { new Sentry.Integrations.Http({ tracing: true }), new ProfilingIntegration(), ], - tracesSampleRate: env.DEPLOYMENT_ENV === 'main' ? 0.2 : 1.0, + tracesSampleRate: env.DEPLOYMENT_ENV === 'main' ? 0.1 : 1.0, profilesSampleRate: 1.0, }); From cc15318f79a994ae5a80edf98baa555de2595d93 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:28:38 +0100 Subject: [PATCH 130/175] tuning Sentry tracing --- worker/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/worker.ts b/worker/worker.ts index 5051ffb4e..45cd2e0ed 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -16,7 +16,7 @@ export async function startWorker() { new Sentry.Integrations.Http({ tracing: true }), new ProfilingIntegration(), ], - tracesSampleRate: env.DEPLOYMENT_ENV === 'main' ? 0.1 : 1.0, + tracesSampleRate: 0.2, profilesSampleRate: 1.0, }); From be0630cf4d496117036f0688f456e5f6ff756c37 Mon Sep 17 00:00:00 2001 From: John Grant Date: Fri, 8 Dec 2023 11:35:31 +0000 Subject: [PATCH 131/175] [WIP] Add b-sdk for trade queries (#333) * Basic addition of b-sdk. * Uses pools from Prisma db. * Add TradeResults Prisma for recording results. * Update SOR service to have placeholders for compare, etc. * Add placeholder for SORV1 service call to make more obvious. * Move SOR_QUERIES in to temp file as only used there and can potentially remove. * Update Phantom/Composable pool comments. * Add pool ids to ignore in to network config. * WIP SORv1 API query. * Use cache for pools. * Query API (Currently Balancers) for SORV1. * Add basic compare, WIP. * Remove Prisma trade result in favour of cloudwatch implementation. * Find best and log result via cloudwatch. * Change response to be in API format. Use generated types. * Remove temp. * Implement mapResultToCowSwap. * Compare correctly for ExactOut. * Use cloneDeep for b-sdk as it mutates pools. * Remove TODO on swapOptions as I believe we dont need it. * Remove old gql. * Refactor to common Swap result interface. * Update SDK package. * Implement onchain query method for V2 service. * Implement onchain query method for V1 service. * Handle empty responses. * Return correct empty response. * Use prisma pool version. * Handle invalid swap case correctly. * Filtering out Linear pools with 0 price rate which causes issues on b-sdk. * Update logs with chainId. Fix prettier. * Initial restructure for Balancer/Beets separation. * Split resolvers. * Add getBeetsSwapResponse type. * Add reusable formatResponse function. * Add mapResultToBeetsSwap - still need to add routes. * Add sorV1Beets service and hook up to resolver. * Best swap for beets endpoint. Zero response. * Map Routes for SingleSwap. * Route mapping. * Route mapping for batchSwap with paths > 1. * Wider catch in V2 service. (Catches b-sdk/Fantom issue so response is still returned). * Remove balancerQueryTest query. * Remove union type in sorGetSwaps query. * fix schema exclusion * Return marketSp of 0 for beets. * Revert commented out section in codegen.yml. * exclude all in protocol specific folder * Revert "exclude all in protocol specific folder" This reverts commit ae35e8306e6c6afc77683ce25f370c1bab27f821. * Change to use 2 different query types for Beets/Cow swaps. Add missing config to new networks. * fix: Filter out Linear pools. Filter out pools from vulnerability/mitigation. * chore: Remove unneccesary Migration folders. * chore: Update sdk to 0.1.1 and add missing RawPool fields. * feat: Add poolGetGyroPools query. * chore: Add new Gyro fields for SOR calcs. * chore: More Gyro fields for SOR. * feat: Add Gyro pools for sorV2 service. * bump typescript version * formatting and output * adding cloudwatch metrics * logging * chore: Bump b-sdk. * chore: Add Gyro3 to mapRawPoolType. * fix: Handle amount scaling correctly across various services by using TokenAmounts. * chore: Remove debug statements. * fix: For Balancer resolver use raw scale for swapAmount. * chain aware SOR queries * gyro pool data updates * excluding unsupported pools * sor metrics * sor bench logging * last review and styling * updating b-sdk * prettier * V2 options * node18 in github action * sor args cleaup * less profiling --------- Co-authored-by: franz Co-authored-by: gmbronco <83549293+gmbronco@users.noreply.github.com> --- .github/workflows/checks.yml | 4 +- modules/balancer/balancer.gql | 38 +- modules/balancer/balancer.resolvers.ts | 12 +- modules/beethoven/balancer-sdk.gql | 31 ++ modules/beethoven/balancer-sdk.resolvers.ts | 21 +- modules/beethoven/balancer-sor.service.ts | 169 +++--- modules/beethoven/balancer-sor.test.ts | 299 ++++++++++ modules/metrics/sor.metric.ts | 12 + modules/network/arbitrum.ts | 2 + modules/network/avalanche.ts | 2 + modules/network/base.ts | 2 + modules/network/fantom.ts | 2 + modules/network/gnosis.ts | 2 + modules/network/mainnet.ts | 8 + modules/network/network-config-types.ts | 1 + modules/network/network-config.ts | 6 +- modules/network/optimism.ts | 2 + modules/network/polygon.ts | 2 + modules/network/zkevm.ts | 2 + modules/pool/lib/pool-creator.service.ts | 50 +- modules/pool/lib/pool-gql-loader.service.ts | 56 ++ modules/pool/pool.gql | 16 + modules/pool/pool.prisma | 15 + modules/pool/pool.resolvers.ts | 3 + modules/pool/pool.service.ts | 5 + modules/sor/constants.ts | 355 ++++++++++++ modules/sor/sor.service.ts | 180 ++++++ .../sorV1Balancer/sorV1Balancer.service.ts | 136 +++++ modules/sor/sorV1Beets/sorV1Beets.service.ts | 70 +++ modules/sor/sorV2/beetsHelpers.test.ts | 432 +++++++++++++++ modules/sor/sorV2/beetsHelpers.ts | 116 ++++ modules/sor/sorV2/sorV2.service.ts | 518 ++++++++++++++++++ modules/sor/types.ts | 31 ++ modules/sor/utils.ts | 20 + .../balancer-subgraph-queries.graphql | 15 + modules/token/token.service.ts | 12 +- package.json | 3 +- .../migration.sql | 13 + .../migration.sql | 4 + prisma/prisma-types.ts | 19 + prisma/schema.prisma | 15 + tsconfig.json | 32 +- worker/worker.ts | 2 +- yarn.lock | 200 ++++++- 44 files changed, 2828 insertions(+), 107 deletions(-) create mode 100644 modules/beethoven/balancer-sor.test.ts create mode 100644 modules/metrics/sor.metric.ts create mode 100644 modules/sor/constants.ts create mode 100644 modules/sor/sor.service.ts create mode 100644 modules/sor/sorV1Balancer/sorV1Balancer.service.ts create mode 100644 modules/sor/sorV1Beets/sorV1Beets.service.ts create mode 100644 modules/sor/sorV2/beetsHelpers.test.ts create mode 100644 modules/sor/sorV2/beetsHelpers.ts create mode 100644 modules/sor/sorV2/sorV2.service.ts create mode 100644 modules/sor/types.ts create mode 100644 modules/sor/utils.ts create mode 100644 prisma/migrations/20230905102723_gyro_sor_fields/migration.sql create mode 100644 prisma/migrations/20230907135550_gyro_missing_fields/migration.sql diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 5d111eefa..321219553 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -20,9 +20,9 @@ jobs: - name: Use Node.js uses: actions/setup-node@v3 with: - node-version: '16.x' + node-version: '18.x' - name: Install deps - run: yarn + run: yarn - name: Generate Schema run: yarn generate - name: Prisma Generate diff --git a/modules/balancer/balancer.gql b/modules/balancer/balancer.gql index 284b6d89d..228429a2f 100644 --- a/modules/balancer/balancer.gql +++ b/modules/balancer/balancer.gql @@ -1,7 +1,39 @@ +extend type Mutation { + balancerMutationTest: String! +} + extend type Query { - balancerQueryTest: String! + sorGetCowSwaps( + chain: GqlChain! + tokenIn: String! + tokenOut: String! + swapType: GqlSorSwapType! + swapAmount: BigDecimal! #expected in raw amount + ): GqlCowSwapApiResponse! } -extend type Mutation { - balancerMutationTest: String! +enum GqlSorSwapType { + EXACT_IN + EXACT_OUT +} + +type GqlCowSwapApiResponse { + tokenAddresses: [String!]! + swaps: [GqlSwap!]! + swapAmount: String! + swapAmountForSwaps: String! + returnAmount: String! + returnAmountFromSwaps: String! + returnAmountConsideringFees: String! + tokenIn: String! + tokenOut: String! + marketSp: String! +} + +type GqlSwap { + poolId: String! + assetInIndex: Int! + assetOutIndex: Int! + amount: String! + userData: String! } diff --git a/modules/balancer/balancer.resolvers.ts b/modules/balancer/balancer.resolvers.ts index 00723d41e..a7f0b1843 100644 --- a/modules/balancer/balancer.resolvers.ts +++ b/modules/balancer/balancer.resolvers.ts @@ -1,10 +1,16 @@ -import moment from 'moment'; import { Resolvers } from '../../schema'; +import { sorService } from '../sor/sor.service'; +import { getTokenAmountRaw } from '../sor/utils'; const balancerResolvers: Resolvers = { Query: { - balancerQueryTest: async (parent, {}, context) => { - return `${moment().utc().valueOf()}`; + sorGetCowSwaps: async (parent, args, context) => { + const amountToken = args.swapType === "EXACT_IN" ? args.tokenIn : args.tokenOut; + // Use TokenAmount to help follow scaling requirements in later logic + // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 + const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain); + const swaps = await sorService.getCowSwaps({ ...args, swapAmount: amount, swapOptions: {} }); + return { ...swaps, __typename: 'GqlCowSwapApiResponse' }; }, }, Mutation: { diff --git a/modules/beethoven/balancer-sdk.gql b/modules/beethoven/balancer-sdk.gql index e4ab912fc..a2d9e8d26 100644 --- a/modules/beethoven/balancer-sdk.gql +++ b/modules/beethoven/balancer-sdk.gql @@ -1,10 +1,12 @@ extend type Query { sorGetSwaps( + chain: GqlChain! tokenIn: String! tokenOut: String! swapType: GqlSorSwapType! swapAmount: BigDecimal! #expected in human readable form swapOptions: GqlSorSwapOptionsInput! + graphTraversalConfig: GqlGraphTraversalConfigInput ): GqlSorGetSwapsResponse! sorGetBatchSwapForTokensIn( tokensIn: [GqlTokenAmountHumanReadable!]! @@ -24,6 +26,14 @@ input GqlSorSwapOptionsInput { forceRefresh: Boolean #don't use any cached responses } +input GqlGraphTraversalConfigInput { + maxDepth: Int # default 6 + maxNonBoostedPathDepth: Int # default 3 + maxNonBoostedHopTokensInBoostedPath: Int # default 2 + approxPathsToReturn: Int # default 5 + poolIdsToInclude: [String] +} + type GqlSorGetSwapsResponse { tokenIn: String! tokenOut: String! @@ -77,3 +87,24 @@ type GqlSorGetBatchSwapForTokensInResponse { swaps: [GqlSorSwap!]! assets: [String!]! } + +type GqlCowSwapApiResponse { + tokenAddresses: [String!]! + swaps: [GqlSwap!]! + swapAmount: String! + swapAmountForSwaps: String! + returnAmount: String! + returnAmountFromSwaps: String! + returnAmountConsideringFees: String! + tokenIn: String! + tokenOut: String! + marketSp: String! +} + +type GqlSwap { + poolId: String! + assetInIndex: Int! + assetOutIndex: Int! + amount: String! + userData: String! +} diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index 3f889b5af..ccedce89b 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -1,13 +1,30 @@ import { Resolvers } from '../../schema'; import { balancerSorService } from './balancer-sor.service'; import { tokenService } from '../token/token.service'; +import { sorService } from '../sor/sor.service'; +import { getTokenAmountHuman } from '../sor/utils'; +import { GraphTraversalConfig } from '../sor/types'; const balancerSdkResolvers: Resolvers = { Query: { sorGetSwaps: async (parent, args, context) => { - const tokens = await tokenService.getTokens(); + const tokenIn = args.tokenIn.toLowerCase(); + const tokenOut = args.tokenOut.toLowerCase(); + const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; + // Use TokenAmount to help follow scaling requirements in later logic + // args.swapAmount is HumanScale + const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain); + const graphTraversalConfig = args.graphTraversalConfig as GraphTraversalConfig; + + const swaps = await sorService.getBeetsSwaps({ + ...args, + tokenIn, + tokenOut, + graphTraversalConfig, + swapAmount: amount, + }); - return balancerSorService.getSwaps({ ...args, tokens }); + return { ...swaps, __typename: 'GqlSorGetSwapsResponse' }; }, sorGetBatchSwapForTokensIn: async (parent, args, context) => { const tokens = await tokenService.getTokens(); diff --git a/modules/beethoven/balancer-sor.service.ts b/modules/beethoven/balancer-sor.service.ts index 8f1f5888e..37106b79a 100644 --- a/modules/beethoven/balancer-sor.service.ts +++ b/modules/beethoven/balancer-sor.service.ts @@ -1,4 +1,4 @@ -import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType } from '../../schema'; +import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../schema'; import { formatFixed, parseFixed } from '@ethersproject/bignumber'; import { PrismaToken } from '@prisma/client'; import { poolService } from '../pool/pool.service'; @@ -17,6 +17,7 @@ import { DeploymentEnv } from '../network/network-config-types'; import * as Sentry from '@sentry/node'; import _ from 'lodash'; import { Logger } from '@ethersproject/logger'; +import { SwapInfoRoute } from '@balancer-labs/sor'; interface GetSwapsInput { tokenIn: string; @@ -55,35 +56,7 @@ export class BalancerSorService { let swapInfo = await this.querySor(swapType, tokenIn, tokenOut, swapAmountScaled, swapOptions); // no swaps found, return 0 if (swapInfo.swaps.length === 0) { - return { - ...swapInfo, - tokenIn: replaceZeroAddressWithEth(swapInfo.tokenIn), - tokenOut: replaceZeroAddressWithEth(swapInfo.tokenOut), - swapType, - tokenInAmount: swapType === 'EXACT_IN' ? swapAmount : BigNumber.from('0').toString(), - tokenOutAmount: swapType === 'EXACT_IN' ? BigNumber.from('0').toString() : swapAmount, - swapAmount: swapType === 'EXACT_IN' ? BigNumber.from('0').toString() : swapAmount, - swapAmountScaled: BigNumber.from('0').toString(), - swapAmountForSwaps: swapInfo.swapAmountForSwaps - ? BigNumber.from(swapInfo.swapAmountForSwaps).toString() - : undefined, - returnAmount: BigNumber.from('0').toString(), - returnAmountScaled: BigNumber.from('0').toString(), - returnAmountConsideringFees: BigNumber.from(swapInfo.returnAmountConsideringFees).toString(), - returnAmountFromSwaps: swapInfo.returnAmountFromSwaps - ? BigNumber.from(swapInfo.returnAmountFromSwaps).toString() - : undefined, - routes: swapInfo.routes.map((route) => ({ - ...route, - hops: route.hops.map((hop) => ({ - ...hop, - pool: pools.find((pool) => pool.id === hop.poolId)!, - })), - })), - effectivePrice: BigNumber.from('0').toString(), - effectivePriceReversed: BigNumber.from('0').toString(), - priceImpact: BigNumber.from('0').toString(), - }; + return this.zeroResponse(swapType, tokenIn, tokenOut, swapAmount); } let deltas: string[] = []; @@ -142,27 +115,70 @@ export class BalancerSorService { const tokenInAmount = BigNumber.from(deltas[swapInfo.tokenAddresses.indexOf(tokenIn)]); const tokenOutAmount = BigNumber.from(deltas[swapInfo.tokenAddresses.indexOf(tokenOut)]).abs(); - const swapAmountQuery = swapType === 'EXACT_OUT' ? tokenOutAmount : tokenInAmount; - const returnAmount = swapType === 'EXACT_IN' ? tokenOutAmount : tokenInAmount; - - const returnAmountFixed = formatFixed( - returnAmount, - this.getTokenDecimals(swapType === 'EXACT_IN' ? tokenOut : tokenIn, tokens), - ); - - const swapAmountQueryFixed = formatFixed( - swapAmountQuery, - this.getTokenDecimals(swapType === 'EXACT_OUT' ? tokenOut : tokenIn, tokens), - ); + return this.formatResponse({ + tokenIn: swapInfo.tokenIn, + tokenOut: swapInfo.tokenOut, + tokens, + tokenInAmtEvm: tokenInAmount.toString(), + tokenOutAmtEvm: tokenOutAmount.toString(), + swapAmountForSwaps: BigNumber.from(swapInfo.swapAmountForSwaps).toString(), + returnAmountConsideringFees: BigNumber.from(swapInfo.returnAmountConsideringFees).toString(), + returnAmountFromSwaps: BigNumber.from(swapInfo.returnAmountFromSwaps).toString(), + routes: swapInfo.routes, + pools, + marketSp: swapInfo.marketSp, + swaps: swapInfo.swaps, + tokenAddresses: swapInfo.tokenAddresses, + swapType, + }); + } - const tokenInAmountFixed = formatFixed(tokenInAmount, this.getTokenDecimals(tokenIn, tokens)); - const tokenOutAmountFixed = formatFixed(tokenOutAmount, this.getTokenDecimals(tokenOut, tokens)); + formatResponse(swapData: { + tokenIn: string; + tokenOut: string; + swapType: GqlSorSwapType; + tokens: PrismaToken[]; + tokenInAmtEvm: string; + tokenOutAmtEvm: string; + swapAmountForSwaps: string; + returnAmountConsideringFees: string; + returnAmountFromSwaps: string; + routes: SwapInfoRoute[]; + pools: GqlPoolMinimal[]; + marketSp: string; + swaps: SwapV2[]; + tokenAddresses: string[]; + }): GqlSorGetSwapsResponse { + const { + tokenIn, + tokenOut, + swapType, + tokens, + tokenInAmtEvm, + tokenOutAmtEvm, + swapAmountForSwaps, + returnAmountConsideringFees, + returnAmountFromSwaps, + routes, + pools, + marketSp, + swaps, + tokenAddresses, + } = swapData; + + const tokenInAmountFixed = formatFixed(tokenInAmtEvm, this.getTokenDecimals(tokenIn, tokens)); + const tokenOutAmountFixed = formatFixed(tokenOutAmtEvm, this.getTokenDecimals(tokenOut, tokens)); + + const swapAmountQuery = swapType === 'EXACT_OUT' ? tokenOutAmtEvm : tokenInAmtEvm; + const returnAmount = swapType === 'EXACT_IN' ? tokenOutAmtEvm : tokenInAmtEvm; + const swapAmountQueryFixed = swapType === 'EXACT_OUT' ? tokenOutAmountFixed : tokenInAmountFixed; + const returnAmountFixed = swapType === 'EXACT_IN' ? tokenOutAmountFixed : tokenInAmountFixed; const effectivePrice = oldBnum(tokenInAmountFixed).div(tokenOutAmountFixed); const effectivePriceReversed = oldBnum(tokenOutAmountFixed).div(tokenInAmountFixed); - const priceImpact = effectivePrice.div(swapInfo.marketSp).minus(1); + const priceImpact = effectivePrice.div(marketSp).minus(1); - for (const route of swapInfo.routes) { + for (const route of routes) { route.tokenInAmount = oldBnum(tokenInAmountFixed) .multipliedBy(route.share) .dp(this.getTokenDecimals(tokenIn, tokens)) @@ -174,24 +190,22 @@ export class BalancerSorService { } return { - ...swapInfo, - tokenIn: replaceZeroAddressWithEth(swapInfo.tokenIn), - tokenOut: replaceZeroAddressWithEth(swapInfo.tokenOut), + swaps, + marketSp, + tokenAddresses, + tokenIn: replaceZeroAddressWithEth(tokenIn), + tokenOut: replaceZeroAddressWithEth(tokenOut), swapType, tokenInAmount: tokenInAmountFixed, tokenOutAmount: tokenOutAmountFixed, swapAmount: swapAmountQueryFixed, - swapAmountScaled: swapAmountQuery.toString(), - swapAmountForSwaps: swapInfo.swapAmountForSwaps - ? BigNumber.from(swapInfo.swapAmountForSwaps).toString() - : undefined, + swapAmountScaled: swapAmountQuery, + swapAmountForSwaps: swapAmountForSwaps ? BigNumber.from(swapAmountForSwaps).toString() : undefined, returnAmount: returnAmountFixed, - returnAmountScaled: returnAmount.toString(), - returnAmountConsideringFees: BigNumber.from(swapInfo.returnAmountConsideringFees).toString(), - returnAmountFromSwaps: swapInfo.returnAmountFromSwaps - ? BigNumber.from(swapInfo.returnAmountFromSwaps).toString() - : undefined, - routes: swapInfo.routes.map((route) => ({ + returnAmountScaled: returnAmount, + returnAmountConsideringFees: BigNumber.from(returnAmountConsideringFees).toString(), + returnAmountFromSwaps: returnAmountFromSwaps ? BigNumber.from(returnAmountFromSwaps).toString() : undefined, + routes: routes.map((route) => ({ ...route, hops: route.hops.map((hop) => ({ ...hop, @@ -204,6 +218,35 @@ export class BalancerSorService { }; } + zeroResponse( + swapType: GqlSorSwapType, + tokenIn: string, + tokenOut: string, + swapAmount: string, + ): GqlSorGetSwapsResponse { + return { + marketSp: '0', + tokenAddresses: [], + swaps: [], + tokenIn: replaceZeroAddressWithEth(tokenIn), + tokenOut: replaceZeroAddressWithEth(tokenOut), + swapType, + tokenInAmount: swapType === 'EXACT_IN' ? swapAmount : '0', + tokenOutAmount: swapType === 'EXACT_IN' ? '0' : swapAmount, + swapAmount: swapType === 'EXACT_IN' ? '0' : swapAmount, + swapAmountScaled: '0', + swapAmountForSwaps: '0', + returnAmount: '0', + returnAmountScaled: '0', + returnAmountConsideringFees: '0', + returnAmountFromSwaps: '0', + routes: [], + effectivePrice: '0', + effectivePriceReversed: '0', + priceImpact: '0', + }; + } + private async querySor( swapType: string, tokenIn: string, @@ -293,11 +336,13 @@ export class BalancerSorService { tokenAddress = tokenAddress.toLowerCase(); const match = tokens.find((token) => token.address === tokenAddress); - if (!match) { - throw new Error('Unknown token: ' + tokenAddress); + let decimals = match?.decimals; + if (!decimals) { + console.error(`Unknown token: ${tokenAddress}`); + decimals = 18; } - return match.decimals; + return decimals; } private batchSwaps(assetArray: string[][], swaps: SwapV2[][]): { swaps: SwapV2[]; assets: string[] } { diff --git a/modules/beethoven/balancer-sor.test.ts b/modules/beethoven/balancer-sor.test.ts new file mode 100644 index 000000000..0a7261f9e --- /dev/null +++ b/modules/beethoven/balancer-sor.test.ts @@ -0,0 +1,299 @@ +import { BalancerSorService } from './balancer-sor.service'; +import { tokenService } from '../token/token.service'; +import { poolService } from '../pool/pool.service'; + +import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../schema'; + +// npx jest --testPathPattern=modules/beethoven/balancer-sor.test.ts +describe('SmartOrderRouter', () => { + test('swap with mixed decimals', async () => { + const tokens = await tokenService.getTokens(); + const pools = await poolService.getGqlPools({ + where: { idIn: ['0x03c6b3f09d2504606936b1a4decefad204687890000200000000000000000015'] }, + }); + + const sor = new BalancerSorService(); + const out = { + tokenIn: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + tokenOut: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + swapType: 'EXACT_IN' as GqlSorSwapType, + tokens, + tokenInAmtEvm: '1000000000000000000000', + tokenOutAmtEvm: '21524991', + swapAmountForSwaps: '1000000000000000000000', + returnAmountConsideringFees: '21524991', + returnAmountFromSwaps: '21524991', + routes: [ + { + tokenIn: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + tokenOut: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenInAmount: '1000', + tokenOutAmount: '21.524991694650096005', + share: 1, + hops: [ + { + tokenIn: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + tokenOut: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenInAmount: '1000', + tokenOutAmount: '21.524991694650096005', + poolId: '0x03c6b3f09d2504606936b1a4decefad204687890000200000000000000000015', + }, + ], + }, + ], + pools, + marketSp: '46.44498985968269', + swaps: [ + { + poolId: '0x03c6b3f09d2504606936b1a4decefad204687890000200000000000000000015', + assetInIndex: 0, + assetOutIndex: 1, + amount: '1000000000000000000000', + userData: '0x', + returnAmount: '21524991', + }, + ], + tokenAddresses: [ + '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + ], + }; + const result = sor.formatResponse(out); + const actual = { + swapAmount: '1000.0', + swapAmountForSwaps: '1000000000000000000000', + returnAmount: '21.524991', + returnAmountFromSwaps: '21524991', + returnAmountConsideringFees: '21524991', + swaps: [ + { + poolId: '0x03c6b3f09d2504606936b1a4decefad204687890000200000000000000000015', + assetInIndex: 0, + assetOutIndex: 1, + amount: '1000000000000000000000', + userData: '0x', + returnAmount: '21524991', + }, + ], + tokenAddresses: [ + '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + ], + tokenIn: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + tokenOut: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + marketSp: '46.44498985968269', + routes: [ + { + tokenIn: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + tokenOut: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenInAmount: '1000', + tokenOutAmount: '21.524991', + share: 1, + hops: [ + { + tokenIn: '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e', + tokenOut: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenInAmount: '1000', + tokenOutAmount: '21.524991694650096005', + poolId: '0x03c6b3f09d2504606936b1a4decefad204687890000200000000000000000015', + pool: pools[0], + }, + ], + }, + ], + swapType: 'EXACT_IN', + tokenInAmount: '1000.0', + tokenOutAmount: '21.524991', + swapAmountScaled: '1000000000000000000000', + returnAmountScaled: '21524991', + effectivePrice: '46.45762685800890694914', + effectivePriceReversed: '0.021524991', + priceImpact: '0.00027208528550431865', + }; + expect(result).toEqual(actual); + }); + test('swap with native asset', async () => { + const tokens = await tokenService.getTokens(); + const pools = await poolService.getGqlPools({ + where: { + idIn: [ + '0xc385e76e575b2d71eb877c27dcc1608f77fada99000000000000000000000719', + '0x7449f09c8f0ed490472d7c14b4eef235620d027000010000000000000000072d', + '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f000000000000000000000718', + ], + }, + }); + + const sor = new BalancerSorService(); + const out = { + tokenIn: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenOut: '0x0000000000000000000000000000000000000000', + swapType: 'EXACT_IN' as GqlSorSwapType, + tokens, + tokenInAmtEvm: '10000000', + tokenOutAmtEvm: '34434611675857195780', + swapAmountForSwaps: '10000000', + returnAmountConsideringFees: '34434650939050394265', + returnAmountFromSwaps: '34434650939080394265', + routes: [ + { + tokenIn: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenOut: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', + tokenInAmount: '10', + tokenOutAmount: '34.434650939080394265', + share: 1, + hops: [ + { + tokenIn: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenOut: '0xc385e76e575b2d71eb877c27dcc1608f77fada99', + tokenInAmount: '10', + tokenOutAmount: '9.953043613661588446', + poolId: '0xc385e76e575b2d71eb877c27dcc1608f77fada99000000000000000000000719', + }, + { + tokenIn: '0xc385e76e575b2d71eb877c27dcc1608f77fada99', + tokenOut: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f', + tokenInAmount: '9.953043613661588446', + tokenOutAmount: '34.355661787346070666', + poolId: '0x7449f09c8f0ed490472d7c14b4eef235620d027000010000000000000000072d', + }, + { + tokenIn: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f', + tokenOut: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', + tokenInAmount: '34.355661787346070666', + tokenOutAmount: '34.434650939080394265', + poolId: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f000000000000000000000718', + }, + ], + }, + ], + pools, + marketSp: '0.29021486756308521004963850902453996332730905029153527', + swaps: [ + { + poolId: '0xc385e76e575b2d71eb877c27dcc1608f77fada99000000000000000000000719', + assetInIndex: 0, + assetOutIndex: 1, + amount: '10000000', + userData: '0x', + returnAmount: '9953043613661588446', + }, + { + poolId: '0x7449f09c8f0ed490472d7c14b4eef235620d027000010000000000000000072d', + assetInIndex: 1, + assetOutIndex: 2, + amount: '0', + userData: '0x', + returnAmount: '34355661787346070666', + }, + { + poolId: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f000000000000000000000718', + assetInIndex: 2, + assetOutIndex: 3, + amount: '0', + userData: '0x', + returnAmount: '34434650939080394265', + }, + ], + tokenAddresses: [ + '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + '0xc385e76e575b2d71eb877c27dcc1608f77fada99', + '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f', + '0x0000000000000000000000000000000000000000', + ], + }; + const result = sor.formatResponse(out); + const actual = { + swapAmount: '10.0', + swapAmountForSwaps: '10000000', + returnAmount: '34.43461167585719578', + returnAmountFromSwaps: '34434650939080394265', + returnAmountConsideringFees: '34434650939050394265', + swaps: [ + { + poolId: '0xc385e76e575b2d71eb877c27dcc1608f77fada99000000000000000000000719', + assetInIndex: 0, + assetOutIndex: 1, + amount: '10000000', + userData: '0x', + returnAmount: '9953043613661588446', + }, + { + poolId: '0x7449f09c8f0ed490472d7c14b4eef235620d027000010000000000000000072d', + assetInIndex: 1, + assetOutIndex: 2, + amount: '0', + userData: '0x', + returnAmount: '34355661787346070666', + }, + { + poolId: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f000000000000000000000718', + assetInIndex: 2, + assetOutIndex: 3, + amount: '0', + userData: '0x', + returnAmount: '34434650939080394265', + }, + ], + tokenAddresses: [ + '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + '0xc385e76e575b2d71eb877c27dcc1608f77fada99', + '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f', + '0x0000000000000000000000000000000000000000', + ], + tokenIn: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenOut: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + marketSp: '0.29021486756308521004963850902453996332730905029153527', + routes: [ + { + tokenIn: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenOut: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', + tokenInAmount: '10', + tokenOutAmount: '34.43461167585719578', + share: 1, + hops: [ + { + tokenIn: '0x04068da6c83afcfa0e13ba15a6696662335d5b75', + tokenOut: '0xc385e76e575b2d71eb877c27dcc1608f77fada99', + tokenInAmount: '10', + tokenOutAmount: '9.953043613661588446', + poolId: '0xc385e76e575b2d71eb877c27dcc1608f77fada99000000000000000000000719', + pool: pools.find( + (p) => p.id === '0xc385e76e575b2d71eb877c27dcc1608f77fada99000000000000000000000719', + ), + }, + { + tokenIn: '0xc385e76e575b2d71eb877c27dcc1608f77fada99', + tokenOut: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f', + tokenInAmount: '9.953043613661588446', + tokenOutAmount: '34.355661787346070666', + poolId: '0x7449f09c8f0ed490472d7c14b4eef235620d027000010000000000000000072d', + pool: pools.find( + (p) => p.id === '0x7449f09c8f0ed490472d7c14b4eef235620d027000010000000000000000072d', + ), + }, + { + tokenIn: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f', + tokenOut: '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', + tokenInAmount: '34.355661787346070666', + tokenOutAmount: '34.434650939080394265', + poolId: '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f000000000000000000000718', + pool: pools.find( + (p) => p.id === '0x92502cd8e00f5b8e737b2ba203fdd7cd27b23c8f000000000000000000000718', + ), + }, + ], + }, + ], + swapType: 'EXACT_IN', + tokenInAmount: '10.0', + tokenOutAmount: '34.43461167585719578', + swapAmountScaled: '10000000', + returnAmountScaled: '34434611675857195780', + effectivePrice: '0.29040548196485696673', + effectivePriceReversed: '3.443461167585719578', + priceImpact: '0.00065680439934843116', + }; + expect(result).toEqual(actual); + }); +}); diff --git a/modules/metrics/sor.metric.ts b/modules/metrics/sor.metric.ts new file mode 100644 index 000000000..bac38295e --- /dev/null +++ b/modules/metrics/sor.metric.ts @@ -0,0 +1,12 @@ +import { Chain } from '@prisma/client'; +import { CloudwatchMetricsPublisher } from './metrics.client'; + +const publishers: Record = {}; + +export function getSorMetricsPublisher(chain: Chain): CloudwatchMetricsPublisher { + if (!publishers[chain]) { + console.log(`Creating new SOR publisher for ${chain}`); + publishers[chain] = new CloudwatchMetricsPublisher(`Backend-${chain}/Sor`); + } + return publishers[chain]; +} diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 20bb523f5..5db4ebdcc 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -97,6 +97,7 @@ const arbitrumNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', @@ -104,6 +105,7 @@ const arbitrumNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index f696e5005..504f3b373 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -91,6 +91,7 @@ const avalancheNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', @@ -98,6 +99,7 @@ const avalancheNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/network/base.ts b/modules/network/base.ts index c25d9b91e..b2ddf1291 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -96,6 +96,7 @@ const baseNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', @@ -103,6 +104,7 @@ const baseNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, monitoring: { diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index a266d331c..ca6112e98 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -164,6 +164,7 @@ const fantomNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://mep53ds2noe6rhicd67q7raqhq0dkupc.lambda-url.eu-central-1.on.aws/', @@ -171,6 +172,7 @@ const fantomNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index d00aa09f0..d5312029c 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -92,6 +92,7 @@ const gnosisNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: '', @@ -99,6 +100,7 @@ const gnosisNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 986c83a63..2210eedd2 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -110,6 +110,10 @@ const data: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [ + "0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3", + "0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4", // Linear pools that cause issues with new b-sdk + ], }, canary: { url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', @@ -117,6 +121,10 @@ const data: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [ + "0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3", + "0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4", // Linear pools that cause issues with new b-sdk + ], }, }, ibAprConfig: { diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index 10c8e33dd..71a566434 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -156,6 +156,7 @@ export interface NetworkData { forceRefresh: boolean; gasPrice: BigNumber; swapGas: BigNumber; + poolIdsToExclude: string[]; }; }; datastudio?: { diff --git a/modules/network/network-config.ts b/modules/network/network-config.ts index 7cc0786da..74ec636e3 100644 --- a/modules/network/network-config.ts +++ b/modules/network/network-config.ts @@ -9,7 +9,6 @@ import { zkevmNetworkConfig } from './zkevm'; import { avalancheNetworkConfig } from './avalanche'; import { baseNetworkConfig } from './base'; import { Chain } from '@prisma/client'; -import { keyBy, pickBy } from 'lodash'; export const AllNetworkConfigs: { [chainId: string]: NetworkConfig } = { '250': fantomNetworkConfig, @@ -37,3 +36,8 @@ export const AllNetworkConfigsKeyedOnChain: { [chain in Chain]: NetworkConfig } export const BalancerChainIds = ['1', '137', '42161', '100', '1101', '43114', '8453']; export const BeethovenChainIds = ['250', '10']; + +export const chainToIdMap = Object.values(AllNetworkConfigs).reduce((acc, config) => { + acc[config.data.chain.gqlId] = String(config.data.chain.id); + return acc; +}, {} as { [chain in Chain]: string }); diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index af9a1a00d..c0f3beccb 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -109,6 +109,7 @@ const optimismNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://svlitjilcr5qtp7iolimlrlg7e0ipupj.lambda-url.eu-central-1.on.aws/', @@ -116,6 +117,7 @@ const optimismNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index 8fd44b1fd..e641bd715 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -97,6 +97,7 @@ const polygonNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', @@ -104,6 +105,7 @@ const polygonNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 77ed05875..32c89798e 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -93,6 +93,7 @@ const zkevmNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, canary: { url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', @@ -100,6 +101,7 @@ const zkevmNetworkData: NetworkData = { forceRefresh: false, gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], }, }, ibAprConfig: { diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index 2b632c652..9235162e1 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -16,7 +16,7 @@ export class PoolCreatorService { } private get chain() { - return networkContext.chain + return networkContext.chain; } public async syncAllPoolsFromSubgraph(blockNumber: number): Promise { @@ -36,6 +36,39 @@ export class PoolCreatorService { await this.createPoolRecord(subgraphPool, blockNumber); poolIds.push(subgraphPool.id); + } else if (subgraphPool.poolType?.includes('Gyro')) { + await prisma.prismaPool.update({ + data: { + gyroData: { + update: { + id: subgraphPool.id, + alpha: subgraphPool.alpha || '', + beta: subgraphPool.beta || '', + sqrtAlpha: subgraphPool.sqrtAlpha || '', + sqrtBeta: subgraphPool.sqrtBeta || '', + root3Alpha: subgraphPool.root3Alpha || '', + c: subgraphPool.c || '', + s: subgraphPool.s || '', + lambda: subgraphPool.lambda || '', + tauAlphaX: subgraphPool.tauAlphaX || '', + tauAlphaY: subgraphPool.tauAlphaY || '', + tauBetaX: subgraphPool.tauBetaX || '', + tauBetaY: subgraphPool.tauBetaY || '', + u: subgraphPool.u || '', + v: subgraphPool.v || '', + w: subgraphPool.w || '', + z: subgraphPool.z || '', + dSq: subgraphPool.dSq || '', + }, + }, + }, + where: { + id_chain: { + id: subgraphPool.id, + chain: this.chain, + }, + }, + }); } } @@ -256,6 +289,21 @@ export class PoolCreatorService { id: pool.id, alpha: pool.alpha || '', beta: pool.beta || '', + sqrtAlpha: pool.sqrtAlpha || '', + sqrtBeta: pool.sqrtBeta || '', + root3Alpha: pool.root3Alpha || '', + c: pool.c || '', + s: pool.s || '', + lambda: pool.lambda || '', + tauAlphaX: pool.tauAlphaX || '', + tauAlphaY: pool.tauAlphaY || '', + tauBetaX: pool.tauBetaX || '', + tauBetaY: pool.tauBetaY || '', + u: pool.u || '', + v: pool.v || '', + w: pool.w || '', + z: pool.z || '', + dSq: pool.dSq || '', }, } : undefined, diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 1e90acefc..3c5ac2126 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -13,6 +13,7 @@ import { GqlBalancePoolAprSubItem, GqlPoolDynamicData, GqlPoolFeaturedPoolGroup, + GqlPoolGyro, GqlPoolInvestConfig, GqlPoolInvestOption, GqlPoolLinear, @@ -112,6 +113,16 @@ export class PoolGqlLoaderService { return pools.map((pool) => this.mapPoolToGqlPool(pool)) as GqlPoolLinear[]; } + public async getGyroPools(): Promise { + const pools = await prisma.prismaPool.findMany({ + where: { type: { in: ['GYRO', 'GYRO3', 'GYROE'] }, chain: networkContext.chain }, + orderBy: { dynamicData: { totalLiquidity: 'desc' } }, + include: prismaPoolWithExpandedNesting.include, + }); + + return pools.map((pool) => this.mapPoolToGqlPool(pool)) as GqlPoolGyro[]; + } + public mapToMinimalGqlPool( pool: PrismaPoolMinimal, userWalletbalances: PrismaUserWalletBalance[] = [], @@ -457,6 +468,21 @@ export class PoolGqlLoaderService { type: mappedData.type, alpha: pool.gyroData?.alpha || '', beta: pool.gyroData?.beta || '', + sqrtAlpha: pool.gyroData?.sqrtAlpha || '', + sqrtBeta: pool.gyroData?.sqrtBeta || '', + root3Alpha: pool.gyroData?.root3Alpha || '', + c: pool.gyroData?.c || '', + s: pool.gyroData?.s || '', + lambda: pool.gyroData?.lambda || '', + tauAlphaX: pool.gyroData?.tauAlphaX || '', + tauAlphaY: pool.gyroData?.tauAlphaY || '', + tauBetaX: pool.gyroData?.tauBetaX || '', + tauBetaY: pool.gyroData?.tauBetaY || '', + u: pool.gyroData?.u || '', + v: pool.gyroData?.v || '', + w: pool.gyroData?.w || '', + z: pool.gyroData?.z || '', + dSq: pool.gyroData?.dSq || '', }; case 'GYRO3': return { @@ -465,6 +491,21 @@ export class PoolGqlLoaderService { type: mappedData.type, alpha: pool.gyroData?.alpha || '', beta: pool.gyroData?.beta || '', + sqrtAlpha: pool.gyroData?.sqrtAlpha || '', + sqrtBeta: pool.gyroData?.sqrtBeta || '', + root3Alpha: pool.gyroData?.root3Alpha || '', + c: pool.gyroData?.c || '', + s: pool.gyroData?.s || '', + lambda: pool.gyroData?.lambda || '', + tauAlphaX: pool.gyroData?.tauAlphaX || '', + tauAlphaY: pool.gyroData?.tauAlphaY || '', + tauBetaX: pool.gyroData?.tauBetaX || '', + tauBetaY: pool.gyroData?.tauBetaY || '', + u: pool.gyroData?.u || '', + v: pool.gyroData?.v || '', + w: pool.gyroData?.w || '', + z: pool.gyroData?.z || '', + dSq: pool.gyroData?.dSq || '', }; case 'GYROE': return { @@ -473,6 +514,21 @@ export class PoolGqlLoaderService { type: mappedData.type, alpha: pool.gyroData?.alpha || '', beta: pool.gyroData?.beta || '', + sqrtAlpha: pool.gyroData?.sqrtAlpha || '', + sqrtBeta: pool.gyroData?.sqrtBeta || '', + root3Alpha: pool.gyroData?.root3Alpha || '', + c: pool.gyroData?.c || '', + s: pool.gyroData?.s || '', + lambda: pool.gyroData?.lambda || '', + tauAlphaX: pool.gyroData?.tauAlphaX || '', + tauAlphaY: pool.gyroData?.tauAlphaY || '', + tauBetaX: pool.gyroData?.tauBetaX || '', + tauBetaY: pool.gyroData?.tauBetaY || '', + u: pool.gyroData?.u || '', + v: pool.gyroData?.v || '', + w: pool.gyroData?.w || '', + z: pool.gyroData?.z || '', + dSq: pool.gyroData?.dSq || '', }; } diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index f6d231cf3..2d1b84e59 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -23,6 +23,7 @@ extend type Query { poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetAllPoolsSnapshots(chains: [GqlChain!], range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetLinearPools(chains: [GqlChain!]): [GqlPoolLinear!]! + poolGetGyroPools(chains: [GqlChain!]): [GqlPoolGyro!]! } extend type Mutation { @@ -247,6 +248,21 @@ type GqlPoolGyro implements GqlPoolBase { alpha: String! beta: String! + sqrtAlpha: String! + sqrtBeta: String! + root3Alpha: String! + c: String! + s: String! + lambda: String! + tauAlphaX: String! + tauAlphaY: String! + tauBetaX: String! + tauBetaY: String! + u: String! + v: String! + w: String! + z: String! + dSq: String! tokens: [GqlPoolTokenUnion!]! nestingType: GqlPoolNestingType! diff --git a/modules/pool/pool.prisma b/modules/pool/pool.prisma index 9244c46b5..6781f3727 100644 --- a/modules/pool/pool.prisma +++ b/modules/pool/pool.prisma @@ -96,6 +96,21 @@ model PrismaPoolGyroData{ alpha String beta String + sqrtAlpha String? + sqrtBeta String? + root3Alpha String? + c String? + s String? + lambda String? + tauAlphaX String? + tauAlphaY String? + tauBetaX String? + tauBetaY String? + u String? + v String? + w String? + z String? + dSq String? } model PrismaPoolDynamicData { diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index e9fe7431b..bb7387b93 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -103,6 +103,9 @@ const balancerResolvers: Resolvers = { } return poolService.getGqlLinearPools(chains); }, + poolGetGyroPools: async () => { + return poolService.getGqlGyroPools(); + } }, Mutation: { poolSyncAllPoolsFromSubgraph: async (parent, {}, context) => { diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 468a8f201..704d56364 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -7,6 +7,7 @@ import { GqlChain, GqlPoolBatchSwap, GqlPoolFeaturedPoolGroup, + GqlPoolGyro, GqlPoolJoinExit, GqlPoolLinear, GqlPoolMinimal, @@ -84,6 +85,10 @@ export class PoolService { return this.poolGqlLoaderService.getLinearPools(chains); } + public async getGqlGyroPools(): Promise { + return this.poolGqlLoaderService.getGyroPools(); + } + public async getPoolsCount(args: QueryPoolGetPoolsArgs): Promise { return this.poolGqlLoaderService.getPoolsCount(args); } diff --git a/modules/sor/constants.ts b/modules/sor/constants.ts new file mode 100644 index 000000000..0fa69e5be --- /dev/null +++ b/modules/sor/constants.ts @@ -0,0 +1,355 @@ +import { TokenAmount } from '@balancer/sdk'; +import { GqlCowSwapApiResponse, GqlSorSwapType } from '../../schema'; + +export const EMPTY_COWSWAP_RESPONSE = (assetIn: string, assetOut: string, amount: TokenAmount): GqlCowSwapApiResponse => { + return { + marketSp: '0', + returnAmount: '0', + returnAmountConsideringFees: '0', + returnAmountFromSwaps: '0', + swapAmount: amount.amount.toString(), + swapAmountForSwaps: '0', + swaps: [], + tokenAddresses: [], + tokenIn: assetIn, + tokenOut: assetOut, + }; +}; + +// These are pools related to Aug `23 vulnerability/mitigation and should be ignored for swaps +export const poolsToIgnore = [ + '0x00c2a4be503869fa751c2dbcb7156cc970b5a8da000000000000000000000477', + '0x02d928e68d8f10c0358566152677db51e1e2dc8c00000000000000000000051e', + '0x04248aabca09e9a1a3d5129a7ba05b7f17de768400000000000000000000050e', + '0x05513ca725b6ce035ca2641075474eb469f05f4c00020000000000000000041f', + '0x0a0fb4ff697de5ac5b6770cd8ee1b72af80b57cf000000000000000000000496', + '0x0afbd58beca09545e4fb67772faf3858e610bcd00000000000000000000004b9', + '0x0d05aac44ac7dd3c7ba5d50be93eb884a057d23400000000000000000000051c', + '0x11839d635e2f0270da37e8ef4324d4d5d54329570002000000000000000004d8', + '0x126e7643235ec0ab9c103c507642dc3f4ca23c66000000000000000000000468', + '0x133d241f225750d2c92948e464a5a80111920331000000000000000000000476', + '0x159cb00338fb63f263fd6f621df619cef71da9540000000000000000000004d5', + '0x173063a30e095313eee39411f07e95a8a806014e0002000000000000000003ab', + '0x1bd2f176a812e312077bca87e37c08432bb09f3e0000000000000000000005a1', + '0x20b156776114e8a801e9767d90c6ccccc8adf398000000000000000000000499', + '0x246ffb4d928e394a02e45761fecdba6c2e79b8eb000000000000000000000541', + '0x25accb7943fd73dda5e23ba6329085a3c24bfb6a000200000000000000000387', + '0x26c2b83fc8535deead276f5cc3ad9c1a2192e02700020000000000000000056b', + '0x2b218683178d029bab6c9789b1073aa6c96e517600000000000000000000058c', + '0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b00000000000000000000003a3', + '0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c0000000000000000000000fd', + '0x2e52c64fd319e380cdbcfc4577ea1fda558a32e40002000000000000000005ba', + '0x2f4eb100552ef93840d5adc30560e5513dfffacb000000000000000000000334', + '0x2ff1a9dbdacd55297452cfd8a4d94724bc22a5f7000000000000000000000484', + '0x3035917be42af437cbdd774be26b9ec90a2bd677000200000000000000000543', + '0x331d50e0b00fc1c32742f151e56b9b616227e23e00000000000000000000047c', + '0x334c96d792e4b26b841d28f53235281cec1be1f200020000000000000000038a', + '0x335d1709d4da9aca59d16328db5cd4ea66bfe06b0000000000000000000004d6', + '0x395d8a1d9ad82b5abe558f8abbfe183b27138af40000000000000000000004e5', + '0x3bb22fc9033b802f2ac47c18885f63476f158afc000000000000000000000483', + '0x3c640f0d3036ad85afa2d5a9e32be651657b874f00000000000000000000046b', + '0x3cdae4f12a67ba563499e102f309c73213cb241c000000000000000000000335', + '0x3dbb8d974b82e82ce79c20c0f5995f4f1f533ede000000000000000000000470', + '0x3f7a7fd7f214be45ec26820fd01ac3be4fc75aa70002000000000000000004c5', + '0x3fcb7085b8f2f473f80bf6d879cae99ea4de934400000000000000000000056d', + '0x41503c9d499ddbd1dcdf818a1b05e9774203bf46000000000000000000000594', + '0x4228290ee9cab692938ff0b4ba303fbcdb68e9f200020000000000000000057d', + '0x454ed96955d04d2f5cdd05e0fd1c77975bfe5307000000000000000000000410', + '0x481c5fc05d63a58aa2f0f2aa417c021b5d419cb200000000000000000000056a', + '0x483006684f422a9448023b2382615c57c5ecf18f000000000000000000000488', + '0x4a82b580365cff9b146281ab72500957a849abdc000000000000000000000494', + '0x4c81255cc9ed7062180ea99962fe05ac0d57350b0000000000000000000005a3', + '0x4c8d2e60863e8d7e1033eda2b3d84e92a641802000000000000000000000040f', + '0x4cbde5c4b4b53ebe4af4adb85404725985406163000000000000000000000595', + '0x4ce0bd7debf13434d3ae127430e9bd4291bfb61f00020000000000000000038b', + '0x4ce277df0feb5b4d07a0ca2adcf5326e4005239d000000000000000000000518', + '0x4fd4687ec38220f805b6363c3c1e52d0df3b5023000200000000000000000473', + '0x4fd63966879300cafafbb35d157dc5229278ed230000000000000000000000e9', + '0x50cf90b954958480b8df7958a9e965752f62712400000000000000000000046f', + '0x53bc3cba3832ebecbfa002c12023f8ab1aa3a3a0000000000000000000000411', + '0x5a6a8cffb4347ff7fc484bf5f0f8a2e234d34255000200000000000000000275', + '0x5b3240b6be3e7487d61cd1afdfc7fe4fa1d81e6400000000000000000000037b', + '0x60683b05e9a39e3509d8fdb9c959f23170f8a0fa000000000000000000000489', + '0x60d604890feaa0b5460b28a424407c24fe89374a0000000000000000000004fc', + '0x639883476960a23b38579acfd7d71561a0f408cf000200000000000000000505', + '0x652d486b80c461c397b0d95612a404da936f3db30000000000000000000000e7', + '0x6667c6fa9f2b3fc1cc8d85320b62703d938e43850000000000000000000004fb', + '0x6a1eb2e9b45e772f55bd9a34659a04b6f75da68700000000000000000000040d', + '0x6c56e72c551b5ac4bf54a620a76077ca768c8fe40002000000000000000004da', + '0x70b7d3b3209a59fb0400e17f67f3ee8c37363f4900020000000000000000018f', + '0x7337224d59cb16c2dc6938cd45a7b2c60c865d6a0000000000000000000004d4', + '0x74cbfaf94a3577c539a9dcee9870a6349a33b34f000000000000000000000534', + '0x779d01f939d78a918a3de18cc236ee89221dfd4e0000000000000000000004c7', + '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe', + '0x804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000fb', + '0x813e3fe1761f714c502d1d2d3a7cceb33f37f59d00000000000000000000040c', + '0x82698aecc9e28e9bb27608bd52cf57f704bd1b83000000000000000000000336', + '0x8a6b25e33b12d1bb6929a8793961076bd1f9d3eb0002000000000000000003e8', + '0x8e6ec57a822c2f527f2df7c7d7d361df3e7530a1000000000000000000000498', + '0x8f4063446f5011bc1c9f79a819efe87776f23704000000000000000000000197', + '0x9001cbbd96f54a658ff4e6e65ab564ded76a543100000000000000000000050a', + '0x9210f1204b5a24742eba12f710636d76240df3d00000000000000000000000fc', + '0x9516a2d25958edb8da246a320f2c7d94a0dbe25d000000000000000000000519', + '0x959216bb492b2efa72b15b7aacea5b5c984c3cca000200000000000000000472', + '0x968024662b9566b42d78af23a0f441bc8723fa83000200000000000000000418', + '0x99c88ad7dc566616548adde8ed3effa730eb6c3400000000000000000000049a', + '0x9b1c8407a360443a9e5eca004713e4088fab8ac0000000000000000000000497', + '0x9b692f571b256140a39a34676bffa30634c586e100000000000000000000059d', + '0x9d7f992c900fbea0ec314bdd71b7cc1becf76a33000200000000000000000573', + '0x9fb771d530b0ceba5160f7bfe2dd1e8b8aa1340300000000000000000000040e', + '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', + '0xa1697f9af0875b63ddc472d6eebada8c1fab85680000000000000000000004f9', + '0xa3823e50f20982656557a4a6a9c06ba5467ae9080000000000000000000000e6', + '0xa718042e5622099e5f0ace4e7122058ab39e1bbe000200000000000000000475', + '0xa8b103a10a94f4f2d7ed2fdcd5545e807557330700000000000000000000048e', + '0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f00000000000000000000051d', + '0xac976bb42cb0c85635644e8c7c74d0e0286aa61c0000000000000000000003cb', + '0xae37d54ae477268b9997d4161b96b8200755935c000000000000000000000337', + '0xae8535c23afedda9304b03c68a3563b75fc8f92b0000000000000000000005a0', + '0xb0f75e97a114a4eb4a425edc48990e6760726709000000000000000000000198', + '0xb5e3de837f869b0248825e0175da73d4e8c3db6b000200000000000000000474', + '0xb841b062ea8ccf5c4cb78032e91de4ae875560420002000000000000000005b7', + '0xb9bd68a77ccf8314c0dfe51bc291c77590c4e9e6000200000000000000000385', + '0xbb6881874825e60e1160416d6c426eae65f2459e000000000000000000000592', + '0xbc0f2372008005471874e426e86ccfae7b4de79d000000000000000000000485', + '0xbf2ef8bdc2fc0f3203b3a01778e3ec5009aeef3300000000000000000000058d', + '0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3', + '0xc2b021133d1b0cf07dba696fd5dd89338428225b000000000000000000000598', + '0xc443c15033fcb6cf72cc24f1bda0db070ddd9786000000000000000000000593', + '0xc50d4347209f285247bda8a09fc1c12ce42031c3000000000000000000000590', + '0xc5dc1316ab670a2eed5716d7f19ced321191f38200000000000000000000056e', + '0xc8c79fcd0e859e7ec81118e91ce8e4379a481ee6000000000000000000000196', + '0xcaa052584b462198a5a9356c28bce0634d65f65c0000000000000000000004db', + '0xcbfa4532d8b2ade2c261d3dd5ef2a2284f7926920000000000000000000004fa', + '0xcfae6e251369467f465f13836ac8135bd42f8a56000000000000000000000591', + '0xd4e7c1f3da1144c9e2cfd1b015eda7652b4a439900000000000000000000046a', + '0xd6e355036f41dc261b3f1ed3bbc6003e87aadb4f000000000000000000000495', + '0xd7edb56f63b2a0191742aea32df1f98ca81ed9c600000000000000000000058e', + '0xd997f35c9b1281b82c8928039d14cddab5e13c2000000000000000000000019c', + '0xdba274b4d04097b90a72b62467d828cefd708037000000000000000000000486', + '0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4', + '0xdec02e6642e2c999af429f5ce944653cad15e093000000000000000000000469', + '0xe03af00fabe8401560c1ff7d242d622a5b601573000000000000000000000493', + '0xe0fcbf4d98f0ad982db260f86cf28b49845403c5000000000000000000000504', + '0xe2d16b0a39f3fbb4389a0e8f1efcbecfb3d1e6e10000000000000000000005a7', + '0xe4dc3c1998ac693d68f4c77476d7c815694c3e94000200000000000000000416', + '0xe6bcc79f328eec93d4ec8f7ed35534d9ab549faa0000000000000000000000e8', + '0xe8c56405bc405840154d9b572927f4197d110de10000000000000000000005a4', + '0xeb486af868aeb3b6e53066abc9623b1041b42bc000000000000000000000046c', + '0xeb567dde03f3da7fe185bdacd5ab495ab220769d000000000000000000000548', + '0xec3626fee40ef95e7c0cbb1d495c8b67b34d398300000000000000000000053d', + '0xf22ff21e17157340575158ad7394e068048dd98b0000000000000000000004b8', + '0xf57c794f42da72b38c8f610ff3b5e8502e48cbde00000000000000000000055c', + '0xf71d0774b214c4cf51e33eb3d30ef98132e4dbaa00000000000000000000046e', + '0xfa24a90a3f2bbe5feea92b95cd0d14ce709649f900000000000000000000058f', + '0xfd11ccdbdb7ab91cb9427a6d6bf570c95876d1950000000000000000000004c2', + '0xfebb0bbf162e64fb9d0dfe186e517d84c395f016000000000000000000000502', + '0xfef969638c52899f91781f1be594af6f40b99bad00000000000000000000047b', + '0x02e139d53ebf4033bf78ab66c6a1e7f1f204487f0002000000000000000009f9', + '0x03090a9811181a2afe830a3a0b467698ccf3a8b1000000000000000000000bf5', + '0x0320c1c5b6df19a194d48882aaec1c72940081d9000000000000000000000a7d', + '0x04b54ea92d73de2d62d651db7d9778f0c49157d8000200000000000000000ba2', + '0x0503dd6b2d3dd463c9bef67fb5156870af63393e00000000000000000000042e', + '0x0889b240a5876aae745ac19f1771853671dc5d36000000000000000000000b3f', + '0x0bc54e914f53f98d16035f4f0d948f3e09c2fac0000200000000000000000bac', + '0x0c06e87c7b88d998f645b91c1f53b51294b12bca000100000000000000000bb9', + '0x10b040038f87219d9b42e025e3bd9b8095c87dd9000000000000000000000b11', + '0x117a3d474976274b37b7b94af5dcade5c90c6e85000000000000000000000aca', + '0x11884da90fb4221b3aa288a7741c51ec4fc43b2f000000000000000000000a5f', + '0x1379b816b9be611431d693290289c204720ca56d000100000000000000000b6f', + '0x150e7b885bdfce974f2abe88a72fdbd692175c6f0002000000000000000009fd', + '0x178e029173417b1f9c8bc16dcec6f697bc323746000000000000000000000758', + '0x1aafc31091d93c3ff003cff5d2d8f7ba2e7284250000000000000000000003b3', + '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + '0x216d6db0c28204014618482c369d7fbf0a8f3232000100000000000000000b60', + '0x230ecdb2a7cee56d6889965a023aa0473d6da507000000000000000000000bf3', + '0x252ff6a3a6fd7b5e8e999de8e3f5c3b306ed1401000200000000000000000bec', + '0x25e57f4612912614e6c99616bd2abb9b5ae71e99000000000000000000000bf0', + '0x2645b13fd2c5295296e94a76280b968bdcbbdfed000000000000000000000c11', + '0x284eb68520c8fa83361c1a3a5910aec7f873c18b000000000000000000000ac9', + '0x2c8dbe8eb86135d9f2f26d196748c088d47f73e7000200000000000000000a29', + '0x31bccf9e28b94e5dacebaa67fe8bc1603cecd904000000000000000000000a01', + '0x341068a547c3cde3c09e338714010dd01b32f93f000200000000000000000a34', + '0x3db543faf7a92052de7860c5c9debabee59ed5bd000000000000000000000a62', + '0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd00000000000000000000070d', + '0x3efb91c4f9b103ee45885695c67794591916f34e000200000000000000000b43', + '0x402cfdb7781fa85d52f425352661128250b79e12000000000000000000000be3', + '0x43894de14462b421372bcfe445fa51b1b4a0ff3d000000000000000000000b36', + '0x4739e50b59b552d490d3fdc60d200977a38510c0000000000000000000000b10', + '0x48e6b98ef6329f8f0a30ebb8c7c960330d64808500000000000000000000075b', + '0x4a0b73f0d13ff6d43e304a174697e3d5cfd310a400020000000000000000091c', + '0x4a77ef015ddcd972fd9ba2c7d5d658689d090f1a000000000000000000000b38', + '0x4ae3661afa119892f0cc8c43edaf6a94989ac171000000000000000000000c06', + '0x4ccb966d8246240afb7a1a24628efb930870b1c40002000000000000000009fc', + '0x52cc8389c6b93d740325729cc7c958066cee4262000000000000000000000b0f', + '0x5b77107fcdf2b41903bab2bc555d4fc14cf7667d000000000000000000000b32', + '0x5bae72b75caab1f260d21bc028c630140607d6e8000000000000000000000ac6', + '0x600bd01b6526611079e12e1ff93aba7a3e34226f0000000000000000000009e4', + '0x63ce19ccd39930725b8a3d2733627804718ab83d000000000000000000000bf2', + '0x64efad69f099813021b41f4cac6e749fd55e188f000000000000000000000b39', + '0x6933ec1ca55c06a894107860c92acdfd2dd8512f000000000000000000000428', + '0x6abe4e7a497b8293c258389c1b00d177e4f257ed00010000000000000000080d', + '0x6c8c7fc50247a47038015eb1fd5dc105d05dafba000200000000000000000ba0', + '0x7079a25dec33be61bbd81b2fb69b468e80d3e72c0000000000000000000009ff', + '0x71bd10c2a590b5858f5576550c163976a48af906000000000000000000000b27', + '0x7c82a23b4c48d796dee36a9ca215b641c6a8709d000000000000000000000acd', + '0x7f4f4942f2a14b6ab7b08b10ada1aacede4ee8d4000200000000000000000b44', + '0x86aef31951e0a3a54333bd9e72f9a95587d058c5000200000000000000000912', + '0x882c7a84231484b3e9f3fd45ac04b1eb5d35b076000200000000000000000a91', + '0x894c82800526e0391e709c0983a5aea3718b7f6d000000000000000000000ac5', + '0x89b28a9494589b09dbccb69911c189f74fdadc5a000000000000000000000b33', + '0x89bb15076c9f2d86aa98ec6cffc1a71e31c38953000000000000000000000bf1', + '0x89f1146fee52b5d9166e9c83cc388b6d8f69f1380001000000000000000009e7', + '0x8a819a4cabd6efcb4e5504fe8679a1abd831dd8f00000000000000000000042d', + '0x8b58a1e7fff52001c22386c2918d45938a6a9be30001000000000000000008d9', + '0x8b8225bfedebaf1708c55743acb4ad43fd4d0f21000200000000000000000918', + '0x8fbd0f8e490735cfc3abf4f29cbddd5c3289b9a7000000000000000000000b5b', + '0x8fd39252d683fdb60bddd4df4b53c9380b496d59000200000000000000000b45', + '0x9321e2250767d79bab5aa06daa8606a2b3b7b4c5000000000000000000000bf4', + '0x949a12b95ec5b80c375b98963a5d6b33b0d0efff0002000000000000000009fe', + '0x9a020bdc2faff5bd24c6acc2020d01ff9f2c627a000000000000000000000ae2', + '0x9cf9358300e34bf9373d30129a1e718d8d058b54000200000000000000000913', + '0x9e34631547adcf2f8cefa0f5f223955c7b137571000000000000000000000ad5', + '0xa5a935833f6a5312715f182733eab088452335d7000100000000000000000bee', + '0xa5fe91dde37d8bf2dacacc0168b115d28ed03f84000000000000000000000b35', + '0xa8bf1c584519be0184311c48adbdc4c15cb2e8c1000000000000000000000bf6', + '0xab269164a10fab22bc87c39946da06c870b172d6000000000000000000000bfc', + '0xac2cae8d2f78a4a8f92f20dbe74042cd0a8d5af3000000000000000000000be2', + '0xae646817e458c0be890b81e8d880206710e3c44e000000000000000000000acb', + '0xaef2c171dbe64b0c18977e16e70bfd29d4ee0256000000000000000000000ac8', + '0xb0c830dceb4ef55a60192472c20c8bf19df03488000000000000000000000be1', + '0xb266ac3b7c98d7bcb28731dac0ef42dba1b276be000000000000000000000be4', + '0xb371aa09f5a110ab69b39a84b5469d29f9b22b76000000000000000000000b37', + '0xb3d658d5b95bf04e2932370dd1ff976fe18dd66a000000000000000000000ace', + '0xb54b2125b711cd183edd3dd09433439d5396165200000000000000000000075e', + '0xb59be8f3c85a9dd6e2899103b6fbf6ea405b99a4000000000000000000000b34', + '0xb878ecce26838fbba4f78cb5b791a0e09152c067000000000000000000000427', + '0xb973ca96a3f0d61045f53255e319aedb6ed4924000000000000000000000042f', + '0xbd4e35784c832d0f9049b54cb3609e5907c5b495000100000000000000000b14', + '0xc55ec796a4debe625d95436a3531f4950b11bdcf000000000000000000000b3e', + '0xc7e6389e364f4275eb442ef215ed21877028e2af000000000000000000000ac7', + '0xc83b55bbd005f1f84906545fcdb145dee53523e0000200000000000000000b30', + '0xcb21a9e647c95127ed784626485b3104cb28d0e7000000000000000000000425', + '0xd00f9ca46ce0e4a63067c4657986f0167b0de1e5000000000000000000000b42', + '0xd2f3b9e67c69762dd1c88f1d3dadd1649a190761000200000000000000000bf7', + '0xd4accb350f9cf59fe3cf7a5ee6ed9ace6a568ea9000200000000000000000b75', + '0xda1cd1711743e57dd57102e9e61b75f3587703da000000000000000000000acc', + '0xdae301690004946424e41051ace1791083be42a1000000000000000000000b40', + '0xde0a77ab6689b980c30306b10f9131a007e1af81000200000000000000000ba1', + '0xe051605a83deae38d26a7346b100ef1ac2ef8a0b0000000000000000000003ce', + '0xe1fb90d0d3b47e551d494d7ebe8f209753526b01000000000000000000000ac4', + '0xe2272cddb2cc408e79e02a73d1db9acc24a843d5000200000000000000000ba7', + '0xe2dc0e0f2c358d6e31836dee69a558ab8d1390e70000000000000000000009fa', + '0xe4885ed2818cc9e840a25f94f9b2a28169d1aea7000000000000000000000b29', + '0xe6909c2f18a29d97217a6146f045e1780606991f000100000000000000000bfe', + '0xe78b25c06db117fdf8f98583cdaaa6c92b79e917000000000000000000000b2b', + '0xea11645ac7d8f2def94c9d8d86bd766296c9b6b6000000000000000000000b3a', + '0xeb480dbbdd921cd6c359e4cc4c65ddea6395e2a1000200000000000000000946', + '0xed35f28f837e96f81240ebb82e0e3f518c7e8a2f000100000000000000000bb5', + '0xf0211cceebe6fcc45052b4e57ee95d233f5669d2000100000000000000000c01', + '0xf22a66046b5307842f21b311ecb4c462c24c0635000000000000000000000b15', + '0xf28f17be00f8ca3c9b7f66a4aad5513757fb3341000200000000000000000b5a', + '0xf42ed61450458ee4620f5ef4f29adb25a6ef0fb6000000000000000000000bf8', + '0xf48f01dcb2cbb3ee1f6aab0e742c2d3941039d56000000000000000000000445', + '0xf93579002dbe8046c43fefe86ec78b1112247bb8000000000000000000000759', + '0xf984eb2b8a7ef780245a797a2fccd82f346409ca000000000000000000000a59', + '0xfa2c0bd8327c99db5bde4c9e9e5cbf30946351bb000000000000000000000948', + '0xff4ce5aaab5a627bf82f4a571ab1ce94aa365ea600000000000000000000075a', + '0x1ac55c31dac78ca943cb8ebfca5945ce09e036e2000000000000000000000024', + '0x225e0047671939a8d78e08ebd692788abe63f15c000000000000000000000009', + '0x41211bba6d37f5a74b22e667533f080c7c7f3f1300000000000000000000000b', + '0x4de21b365d6543661d0e105e579a34b963862497000200000000000000000045', + '0x581ec1f5e7ced12b186deae32256adb53bdd5b08000000000000000000000001', + '0x66f33ae36dd80327744207a48122f874634b3ada000100000000000000000013', + '0xa3ed6f78edc29f69df8a0d16b1d1ccf9871f918c000000000000000000000032', + '0xa611a551b95b205ccd9490657acf7899daee5db700000000000000000000002e', + '0xb95829adbacd8af89e291dee78bc09e24de51d6b000000000000000000000043', + '0xb973ca96a3f0d61045f53255e319aedb6ed49240000200000000000000000011', + '0xba1a5b19d09a79dada039b1f974015c5a989d5fd000100000000000000000046', + '0xbb9cd48d33033f5effbedec9dd700c7d7e1dcf5000000000000000000000000e', + '0xd16f72b02da5f51231fde542a8b9e2777a478c8800000000000000000000000f', + '0xd4015683b8153666190e0b2bec352580ebc4caca00000000000000000000000d', + '0xe15cac1df3621e001f76210ab12a7f1a1691481f000000000000000000000044', + '0xe7f88d7d4ef2eb18fcf9dd7216ba7da1c46f3dd600000000000000000000000a', + '0xf48f01dcb2cbb3ee1f6aab0e742c2d3941039d56000200000000000000000012', + '0xfedb19ec000d38d92af4b21436870f115db22725000000000000000000000010', + '0xffff76a3280e95dc855696111c2562da09db2ac000000000000000000000000c', + '0x00fcd3d55085e998e291a0005cedecf58ac14c4000020000000000000000047f', + '0x077794c30afeccdf5ad2abc0588e8cee7197b71a000000000000000000000352', + '0x117a3d474976274b37b7b94af5dcade5c90c6e85000000000000000000000381', + '0x11884da90fb4221b3aa288a7741c51ec4fc43b2f000000000000000000000353', + '0x19b1c92631405a0a9495ccba0becf4f2e8e908bd000000000000000000000410', + '0x1e550b7764da9638fdd32c8a701364de31f45ee800000000000000000000047c', + '0x1fa7f727934226aedab636d62a084931b97d366b000000000000000000000411', + '0x23ca0306b21ea71552b148cf3c4db4fc85ae19290000000000000000000000c9', + '0x284eb68520c8fa83361c1a3a5910aec7f873c18b000000000000000000000380', + '0x2a96254ca32020b20ed3506f8f75318da24709f9000200000000000000000456', + '0x36942963e3b6f37ecc45a4e72349558514233f0000000000000000000000048a', + '0x3f53a862919ccfa023cb6ace91378a79fb0f6bf500000000000000000000040f', + '0x40af308e3d07ec769d85eb80afb116525ff4ac99000000000000000000000485', + '0x418de00ae109e6f874d872658767866d680eaa1900000000000000000000047d', + '0x45c4d1376943ab28802b995acffc04903eb5223f000000000000000000000470', + '0x4689122d360c4725d244c5cfea22861333d862e6000100000000000000000468', + '0x4739e50b59b552d490d3fdc60d200977a38510c0000000000000000000000409', + '0x49a0e3334496442a9706e481617724e7e37eaa080000000000000000000003ff', + '0x519cce718fcd11ac09194cff4517f12d263be067000000000000000000000382', + '0x52cc8389c6b93d740325729cc7c958066cee4262000000000000000000000408', + '0x567ecfcb22205d279bb8eed3e066989902bf03d5000000000000000000000452', + '0x585d95df0231fa08aeee35ff0c16b92fd0ecdc3300020000000000000000045f', + '0x5a7f39435fd9c381e4932fa2047c9a5136a5e3e7000000000000000000000400', + '0x5bae72b75caab1f260d21bc028c630140607d6e8000000000000000000000350', + '0x6cb787a419c3e6ee2e9ff365856c29cd10659113000000000000000000000474', + '0x7c82a23b4c48d796dee36a9ca215b641c6a8709d000000000000000000000406', + '0x81fc12c60ee5b753cf5fd0adc342dfb5f3817e3200000000000000000000035d', + '0x894c82800526e0391e709c0983a5aea3718b7f6d00000000000000000000034f', + '0x970712708a08e8fb152be4d81b2dc586923f5369000200000000000000000479', + '0x9bf7c3b63c77b4b4f2717776f15a4bec1b532a280000000000000000000000c8', + '0x9cebf13bb702f253abf1579294694a1edad00eaa000000000000000000000486', + '0x9e34631547adcf2f8cefa0f5f223955c7b137571000000000000000000000407', + '0x9fb7d6dcac7b6aa20108bad226c35b85a9e31b63000200000000000000000412', + '0xa1ea76c42b2938cfa9abea12357881006c52851300000000000000000000048f', + '0xa50f89e9f439fde2a6fe05883721a00475da3c4500000000000000000000048b', + '0xa612b6aed2e7ca1a3a4f23fbca9128461bbb7718000000000000000000000274', + '0xa8af146d79ac0bb981e4e0d8b788ec5711b1d5d000000000000000000000047b', + '0xad28940024117b442a9efb6d0f25c8b59e1c950b00000000000000000000046f', + '0xae646817e458c0be890b81e8d880206710e3c44e00000000000000000000039d', + '0xaef2c171dbe64b0c18977e16e70bfd29d4ee0256000000000000000000000351', + '0xbbf9d705b75f408cfcaee91da32966124d2c6f7d00000000000000000000047e', + '0xbd724eb087d4cc0f61a5fed1fffaf937937e14de000000000000000000000473', + '0xbe0f30217be1e981add883848d0773a86d2d2cd4000000000000000000000471', + '0xc46be4b8bb6b5a3d3120660efae9c5416318ed40000000000000000000000472', + '0xc69771058481551261709d8db44977e9afde645000010000000000000000042a', + '0xc6eee8cb7643ec2f05f46d569e9ec8ef8b41b389000000000000000000000475', + '0xcba9ff45cfb9ce238afde32b0148eb82cbe635620000000000000000000003fd', + '0xcf8b555b7754556cf2ac2165e77ee23ed8517d7900020000000000000000045e', + '0xd0dc20e6342db2de82692b8dc842301ff9121805000200000000000000000454', + '0xd3d5d45f4edf82ba0dfaf061d230766032a10e07000200000000000000000413', + '0xd6d20527c7b0669989ee082b9d3a1c63af742290000000000000000000000483', + '0xda1cd1711743e57dd57102e9e61b75f3587703da0000000000000000000003fc', + '0xe1fb90d0d3b47e551d494d7ebe8f209753526b0100000000000000000000034e', + '0xee02583596aee94cccb7e8ccd3921d955f17982a00000000000000000000040a', + '0xf984eb2b8a7ef780245a797a2fccd82f346409ca00000000000000000000034d', + '0xff8f84e8c87532af96aef5582ee451572233678b000200000000000000000478', + '0x054e7b0c73e1ee5aed6864fa511658fc2b54bcaa000000000000000000000015', + '0x3f1a2c4a3a751f6626bd90ef16e104f0772d4d6b00020000000000000000001b', + '0x7275c131b1f67e8b53b4691f92b0e35a4c1c6e22000000000000000000000010', + '0xa154009870e9b6431305f19b09f9cfd7284d4e7a000000000000000000000013', + '0xa1d14d922a575232066520eda11e27760946c991000000000000000000000012', + '0xa826a114b0c7db4d1ff4a4be845a78998c64564c000000000000000000000008', + '0xea67626e1f0b59e0d172a04f5702ef90bcdf440c00000000000000000000000f', + '0xeb496161099d45b3ea4892408ef745c6182eb56e00000000000000000000000e', + '0xece571847897fd61e764d455dc15cf1cd9de8d6f000000000000000000000014', + '0xed3e2f496cbcd8e212192fb8d1499842f04a0d19000000000000000000000009', + '0x02c9dcb975262719a61f9b40bdf0987ead9add3a000000000000000000000006', + '0x16c9a4d841e88e52b51936106010f27085a529ec00000000000000000000000c', + '0x32be2d0ddeaf3333501b24a28668ce373ba8e763000200000000000000000014', + '0x32f03464fdf909fdf3798f87ff3712b10c59bd86000000000000000000000005', + '0x4b718e0e2fea1da68b763cd50c446fba03ceb2ea00000000000000000000000b', + '0x68a69c596b3839023c0e08d09682314f582314e5000200000000000000000011', + '0x6f34a44fce1506352a171232163e7716dd073ade000200000000000000000015', + '0x9e2d87f904862671eb49cb358e74284762cc9f42000200000000000000000013', + '0xac4b72c01072a52b73ca71105504f1372efcce0d000000000000000000000003', + '0xbfd65c6160cfd638a85c645e6e6d8acac5dac935000000000000000000000004', + '0xe274c9deb6ed34cfe4130f8d0a8a948dea5bb28600000000000000000000000d', +]; + diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts new file mode 100644 index 000000000..1602e1d34 --- /dev/null +++ b/modules/sor/sor.service.ts @@ -0,0 +1,180 @@ +import { GqlCowSwapApiResponse, GqlSorSwapType, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../schema'; +import { sorV1BalancerService } from './sorV1Balancer/sorV1Balancer.service'; +import { sorV1BeetsService } from './sorV1Beets/sorV1Beets.service'; +import { sorV2Service } from './sorV2/sorV2.service'; +import { GetSwapsInput, SwapResult, SwapService } from './types'; +import { EMPTY_COWSWAP_RESPONSE } from './constants'; +import { getSorMetricsPublisher } from '../metrics/sor.metric'; +import { Chain } from '@prisma/client'; +import { parseUnits, formatUnits } from '@ethersproject/units'; +import { tokenService } from '../token/token.service'; + +export class SorService { + async getCowSwaps(input: GetSwapsInput): Promise { + const swap = await this.getSwap({ ...input, swapOptions: {} }); + const emptyResponse = EMPTY_COWSWAP_RESPONSE(input.tokenIn, input.tokenOut, input.swapAmount); + + if (!swap) return emptyResponse; + + try { + // Updates with latest onchain data before returning + return await swap.getCowSwapResponse(input.chain, true); + } catch (err) { + console.log(`Error Retrieving QuerySwap`, err); + return emptyResponse; + } + } + + async getBeetsSwaps(input: GetSwapsInput): Promise { + const swap = await this.getSwap(input, sorV1BeetsService); + const emptyResponse = sorV1BeetsService.zeroResponse( + input.swapType, + input.tokenIn, + input.tokenOut, + input.swapAmount, + ); + + if (!swap) return emptyResponse; + + try { + // Updates with latest onchain data before returning + return swap.getBeetsSwapResponse(true); + } catch (err) { + console.log(`Error Retrieving QuerySwap`, err); + return emptyResponse; + } + } + + private async getSwap(input: GetSwapsInput, v1Service: SwapService = sorV1BalancerService) { + const v1Start = +new Date(); + const swapV1 = await v1Service.getSwapResult(input); + const v1Time = +new Date() - v1Start; + + const v2Start = +new Date(); + const swapV2 = await sorV2Service.getSwapResult(input); + const v2Time = +new Date() - v2Start; + + const version = this.getBestSwap(swapV1, swapV2, input.swapType); + + await this.logResult( + version, + swapV1, + swapV2, + input.swapType, + input.tokenIn, + input.tokenOut, + input.chain, + v1Time, + v2Time, + ); + + if (!version) return null; + + return version === 'V1' ? swapV1 : swapV2; + } + + /** + * Find best swap result for V1 vs V2 and return in CowSwap API format. Log if V1 wins. + * @param v1 + * @param v2 + * @param swapType + * @returns + */ + private getBestSwap(v1: SwapResult, v2: SwapResult, swapType: GqlSorSwapType, debugOut = false) { + // Useful for comparing + if (debugOut) { + console.log(`------ DEBUG`); + console.log(v1); + console.log(v2); + } + + if (!v1.isValid && !v2.isValid) return null; + + let isV1 = false; + if (!v1.isValid || !v2.isValid) { + isV1 = v1.isValid ? true : false; + } else if (swapType === 'EXACT_IN') { + if (v2.outputAmount < v1.outputAmount) { + isV1 = true; + } + } else { + if (v2.inputAmount > v1.inputAmount) { + isV1 = true; + } + } + + if (isV1 === true) { + return 'V1'; + } + + return 'V2'; + } + + private async logResult( + version: string | null, + v1: SwapResult, + v2: SwapResult, + swapType: GqlSorSwapType, + assetIn: string, + assetOut: string, + chain: Chain, + v1Time: number, + v2Time: number, + ) { + const sorMetricsPublisher = getSorMetricsPublisher(chain); + await sorMetricsPublisher.publish(`SOR_VALID_V1`, v1.isValid ? 1 : 0); + await sorMetricsPublisher.publish(`SOR_VALID_V2`, v2.isValid ? 1 : 0); + + if (!version) return; + + let v1ResultAmount = v1.inputAmount; + let v2ResultAmount = v2.inputAmount < 0 ? -v2.inputAmount : v2.inputAmount; + let tradeAmount = v1.outputAmount; + let userToken = assetOut; + let resultToken = assetIn; + if (swapType === 'EXACT_IN') { + v1ResultAmount = v1.outputAmount; + v2ResultAmount = v2.outputAmount < 0 ? -v2.outputAmount : v2.outputAmount; + tradeAmount = v1.inputAmount; + userToken = assetIn; + resultToken = assetOut; + } + + const fp = (a: bigint, d: number) => Number(formatUnits(String(a), d)); + const bn = (a: string, d: number) => BigInt(String(parseUnits(a, d))); + const prismaToken = await tokenService.getToken(resultToken, chain); + const decimals = prismaToken!.decimals; + let v2Perf = + version === 'V1' + ? 1 - fp(v1ResultAmount, decimals) / fp(v2ResultAmount, decimals) // negative perf means V1 is better + : fp(v2ResultAmount, decimals) / fp(v1ResultAmount, decimals) - 1; // positive perf means V2 is better + + v2Perf = Math.max(-1, Math.min(1, v2Perf)); + let diffN = fp(v2ResultAmount, decimals) - fp(v1ResultAmount, decimals); + let diff = bn(diffN.toFixed(decimals), decimals); + let bestResultAmount = version === 'V1' ? v1ResultAmount : v2ResultAmount; + + await sorMetricsPublisher.publish(`SOR_TIME_V1`, v1Time); + await sorMetricsPublisher.publish(`SOR_TIME_V2`, v2Time); + await sorMetricsPublisher.publish(`SOR_V2_PERFORMACE`, v2Perf); + + console.log( + [ + 'SOR_RESULT', + v1Time, + v2Time, + chain, + version, + swapType, + userToken, + resultToken, + String(tradeAmount), + String(bestResultAmount), + String(diff), + v2Perf.toFixed(8), + ].join(','), + ); + } +} + +export const sorService = new SorService(); diff --git a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts b/modules/sor/sorV1Balancer/sorV1Balancer.service.ts new file mode 100644 index 000000000..73ad3b5ab --- /dev/null +++ b/modules/sor/sorV1Balancer/sorV1Balancer.service.ts @@ -0,0 +1,136 @@ +import axios from 'axios'; +import * as Sentry from '@sentry/node'; +import { AddressZero } from '@ethersproject/constants'; +import { Contract } from '@ethersproject/contracts'; +import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse } from '../../../schema'; +import { GetSwapsInput, SwapService, SwapResult } from '../types'; +import { FundManagement, SwapTypes, SwapV2 } from '@balancer-labs/sdk'; +import { env } from '../../../app/env'; +import { networkContext } from '../../network/network-context.service'; +import { AllNetworkConfigs, AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; +import { DeploymentEnv } from '../../network/network-config-types'; + +import VaultAbi from '../../pool/abi/Vault.json'; +import { BigNumber } from 'ethers'; +import { TokenAmount } from '@balancer/sdk'; +import { Chain } from '@prisma/client'; + +type CowSwapSwapType = 'buy' | 'sell'; + +class SwapResultV1 implements SwapResult { + public inputAmount: bigint = BigInt(0); + public outputAmount: bigint = BigInt(0); + public isValid: boolean; + + constructor(private swap: GqlCowSwapApiResponse | null, private swapType: GqlSorSwapType) { + if (swap === null) { + this.isValid = false; + this.swap = null; + } else { + this.inputAmount = swapType === 'EXACT_IN' ? BigInt(swap.swapAmount) : BigInt(swap.returnAmount); + this.outputAmount = swapType === 'EXACT_IN' ? BigInt(swap.returnAmount) : BigInt(swap.swapAmount); + this.isValid = swap.swaps.length === 0 ? false : true; + } + } + + async getCowSwapResponse(chain = networkContext.chain, queryFirst = false): Promise { + if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); + + if (queryFirst) { + const swapType = this.mapSwapType(this.swapType); + const deltas = await this.queryBatchSwap(swapType, this.swap.swaps, this.swap.tokenAddresses, chain); + const tokenInAmount = deltas[this.swap.tokenAddresses.indexOf(this.swap.tokenIn)].toString(); + const tokenOutAmount = deltas[this.swap.tokenAddresses.indexOf(this.swap.tokenOut)].abs().toString(); + // console.log(`UPDATE:`, this.inputAmount, this.outputAmount, tokenInAmount, tokenOutAmount, deltas.toString()); + return { + ...this.swap, + returnAmount: swapType === SwapTypes.SwapExactIn ? tokenOutAmount : tokenInAmount, + swapAmount: swapType === SwapTypes.SwapExactIn ? tokenInAmount : tokenOutAmount, + }; + } + return this.swap; + } + + async getBeetsSwapResponse(queryFirst: boolean): Promise { + throw new Error('Use Beets service.'); + } + + private queryBatchSwap(swapType: SwapTypes, swaps: SwapV2[], assets: string[], chain: Chain): Promise { + const vault = AllNetworkConfigsKeyedOnChain[chain].data.balancer.vault; + const provider = AllNetworkConfigsKeyedOnChain[chain].provider; + + const vaultContract = new Contract(vault, VaultAbi, provider); + const funds: FundManagement = { + sender: AddressZero, + recipient: AddressZero, + fromInternalBalance: false, + toInternalBalance: false, + }; + + return vaultContract.queryBatchSwap(swapType, swaps, assets, funds); + } + + private mapSwapType(swapType: GqlSorSwapType): SwapTypes { + return swapType === 'EXACT_IN' ? SwapTypes.SwapExactIn : SwapTypes.SwapExactOut; + } +} +export class SorV1BalancerService implements SwapService { + public async getSwapResult({ chain, tokenIn, tokenOut, swapType, swapAmount }: GetSwapsInput): Promise { + try { + const swap = await this.querySorBalancer(chain, swapType, tokenIn, tokenOut, swapAmount); + return new SwapResultV1(swap, swapType); + } catch (err: any) { + console.error( + `SOR_V1_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, + ); + Sentry.captureException(err.message, { + tags: { + service: 'sorV1', + tokenIn, + tokenOut, + swapAmount: swapAmount.amount, + swapType, + chain, + }, + }); + return new SwapResultV1(null, swapType); + } + } + + /** + * Query Balancer API CowSwap/SOR endpoint. + * @param swapType + * @param tokenIn + * @param tokenOut + * @param swapAmountScaled + * @param swapOptions + * @returns + */ + private async querySorBalancer( + chain: Chain, + swapType: GqlSorSwapType, + tokenIn: string, + tokenOut: string, + swapAmount: TokenAmount, + ): Promise { + const chainId = chainToIdMap[chain]; + const endPoint = `https://api.balancer.fi/sor/${chainId}`; + const gasPrice = AllNetworkConfigs[chainId].data.sor[env.DEPLOYMENT_ENV as DeploymentEnv].gasPrice.toString(); + const swapData = { + orderKind: this.mapSwapType(swapType), + sellToken: tokenIn, + buyToken: tokenOut, + amount: swapAmount.amount.toString(), + gasPrice, + }; + + const { data } = await axios.post(endPoint, swapData); + return data; + } + + private mapSwapType(swapType: GqlSorSwapType): CowSwapSwapType { + return swapType === 'EXACT_IN' ? 'sell' : 'buy'; + } +} + +export const sorV1BalancerService = new SorV1BalancerService(); diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts new file mode 100644 index 000000000..067e5f87c --- /dev/null +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -0,0 +1,70 @@ +import { formatEther } from 'viem'; +import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../../schema'; +import { GetSwapsInput, SwapService, SwapResult } from '../types'; +import { BalancerSorService } from '../../beethoven/balancer-sor.service'; +import { tokenService } from '../../token/token.service'; +import { TokenAmount } from '@balancer/sdk'; + +class SwapResultV1 implements SwapResult { + public inputAmount: bigint = BigInt(0); + public outputAmount: bigint = BigInt(0); + public isValid: boolean; + + constructor(private swap: GqlSorGetSwapsResponse | null, private swapType: GqlSorSwapType) { + if (swap === null) { + this.isValid = false; + this.swap = null; + } else { + this.inputAmount = + swapType === 'EXACT_IN' ? BigInt(swap.swapAmountScaled) : BigInt(swap.returnAmountScaled); + this.outputAmount = + swapType === 'EXACT_IN' ? BigInt(swap.returnAmountScaled) : BigInt(swap.swapAmountScaled); + this.isValid = swap.swaps.length === 0 ? false : true; + } + } + + async getCowSwapResponse(chain = 'MAINNET', queryFirst = false): Promise { + throw new Error('Use Balancer Service'); + } + + async getBeetsSwapResponse(queryFirst: boolean): Promise { + if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); + // Beets service is already querying onchain + return this.swap; + } +} +export class SorV1BeetsService implements SwapService { + sorService: BalancerSorService; + + constructor() { + this.sorService = new BalancerSorService(); + } + + public async getSwapResult(input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }): Promise { + try { + const swap = await this.querySorBeets(input); + return new SwapResultV1(swap, input.swapType); + } catch (err) { + console.log(`sorV1 Service Error`, err); + return new SwapResultV1(null, input.swapType); + } + } + + public zeroResponse( + swapType: GqlSorSwapType, + tokenIn: string, + tokenOut: string, + swapAmount: TokenAmount, + ): GqlSorGetSwapsResponse { + return this.sorService.zeroResponse(swapType, tokenIn, tokenOut, formatEther(swapAmount.scale18)); + } + + private async querySorBeets( + input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }, + ): Promise { + const tokens = await tokenService.getTokens(); + return await this.sorService.getSwaps({ ...input, tokens, swapAmount: formatEther(input.swapAmount.scale18) }); + } +} + +export const sorV1BeetsService = new SorV1BeetsService(); diff --git a/modules/sor/sorV2/beetsHelpers.test.ts b/modules/sor/sorV2/beetsHelpers.test.ts new file mode 100644 index 000000000..cdd0621f9 --- /dev/null +++ b/modules/sor/sorV2/beetsHelpers.test.ts @@ -0,0 +1,432 @@ +import { SingleSwap, SwapKind, BatchSwapStep } from '@balancer/sdk'; +import { GqlPoolMinimal, GqlSorSwapRoute } from '../../../schema'; +import { mapBatchSwap, mapRoutes, splitPaths } from './beetsHelpers'; +import { poolService } from '../../pool/pool.service'; + +// npx jest --testPathPattern=modules/sor/sorV2/beetsHelpers.test.ts +describe('sorV2 Service - Routes', () => { + describe('SingleSwap', () => { + let singleSwap: SingleSwap; + let pools: GqlPoolMinimal[]; + let expectedRoute: GqlSorSwapRoute[]; + const assetIn = '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4'; + const assetOut = '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7'; + const assets = ['0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7']; + + beforeAll(async () => { + pools = await poolService.getGqlPools({ + where: { idIn: ['0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c'] }, + }); + singleSwap = { + poolId: '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + kind: SwapKind.GivenIn, + assetIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + assetOut: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + amount: BigInt(0), + userData: '0x', + }; + expectedRoute = [ + { + hops: [ + { + pool: pools[0], + poolId: '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: '', + tokenOut: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + tokenOutAmount: '', + }, + ], + share: 1, + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: '', + tokenOut: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + tokenOutAmount: '', + }, + ]; + }); + test('GivenIn', () => { + const amountIn = '123456789112345678'; + const amountOut = '876543210987654321'; + singleSwap.kind = SwapKind.GivenIn; + singleSwap.amount = BigInt(amountIn); + expectedRoute[0].tokenInAmount = amountIn; + expectedRoute[0].tokenOutAmount = amountOut; + expectedRoute[0].hops[0].tokenInAmount = amountIn; + expectedRoute[0].hops[0].tokenOutAmount = amountOut; + const mappedRoute = mapRoutes( + singleSwap, + amountIn, + amountOut, + pools, + assetIn, + assetOut, + assets, + SwapKind.GivenIn, + ); + expect(mappedRoute).toEqual(expectedRoute); + }); + test('GivenOut', () => { + const amountIn = '876543210987654321'; + const amountOut = '123456789112345678'; + singleSwap.kind = SwapKind.GivenOut; + singleSwap.amount = BigInt(amountOut); + expectedRoute[0].tokenInAmount = amountIn; + expectedRoute[0].tokenOutAmount = amountOut; + expectedRoute[0].hops[0].tokenInAmount = amountIn; + expectedRoute[0].hops[0].tokenOutAmount = amountOut; + const mappedRoute = mapRoutes( + singleSwap, + amountIn, + amountOut, + pools, + assetIn, + assetOut, + assets, + SwapKind.GivenOut, + ); + expect(mappedRoute).toEqual(expectedRoute); + }); + }); + describe('BatchSwap', () => { + describe('ExactIn', () => { + let pools: GqlPoolMinimal[]; + let paths: BatchSwapStep[][]; + const assetIn = '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4'; + const assetOut = '0x8159462d255C1D24915CB51ec361F700174cD994'; + const amountIn = '111111111111111111'; + const amountOut = '222222222222222222'; + const batchSwap: BatchSwapStep[] = [ + { + poolId: '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + assetInIndex: BigInt(0), + assetOutIndex: BigInt(1), + amount: BigInt(amountIn), + userData: '0x', + }, + { + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + assetInIndex: BigInt(1), + assetOutIndex: BigInt(2), + amount: BigInt(0), + userData: '0x', + }, + ]; + const assets = [ + '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + '0x8159462d255C1D24915CB51ec361F700174cD994', + ]; + const expectedRoute: GqlSorSwapRoute[] = [ + { + hops: [ + { + pool: {} as GqlPoolMinimal, + poolId: '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + tokenOutAmount: '0', // TODO - Are we expecting to get the values for intermmediate steps? May need b-sdk to return that? + }, + { + pool: {} as GqlPoolMinimal, + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + tokenIn: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + tokenInAmount: '0', + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }, + ], + share: 1, + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }, + ]; + beforeAll(async () => { + pools = await poolService.getGqlPools({ + where: { + idIn: [ + '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + ], + }, + }); + expectedRoute[0].hops[0].pool = pools.find( + (p) => p.id === '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + ) as GqlPoolMinimal; + expectedRoute[0].hops[1].pool = pools.find( + (p) => p.id === '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + ) as GqlPoolMinimal; + }); + describe('Single Path', () => { + test('splitPaths', () => { + paths = splitPaths(batchSwap, assetIn, assetOut, assets, SwapKind.GivenIn); + expect(paths.length).toEqual(1); + expect(paths[0].length).toEqual(2); + }); + test('mapBatchSwap', () => { + const route = mapBatchSwap(paths[0], amountIn, amountOut, SwapKind.GivenIn, assets, pools); + expect(route).toEqual(expectedRoute[0]); + }); + test('GivenIn', () => { + const mappedRoute = mapRoutes( + batchSwap, + amountIn, + amountOut, + pools, + assetIn, + assetOut, + assets, + SwapKind.GivenIn, + ); + expect(mappedRoute).toEqual(expectedRoute); + }); + }); + describe('Multiple Paths', () => { + beforeAll(() => { + batchSwap.push({ + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + assetInIndex: BigInt(0), + assetOutIndex: BigInt(2), + amount: BigInt(amountIn), + userData: '0x', + }); + expectedRoute[0].share = 0.5; + expectedRoute.push({ + hops: [ + { + pool: pools.find( + (p) => + p.id === '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + ) as GqlPoolMinimal, + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }, + ], + share: 0.5, + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }); + }); + test('splitPaths', () => { + paths = splitPaths(batchSwap, assetIn, assetOut, assets, SwapKind.GivenIn); + expect(paths.length).toEqual(2); + expect(paths[0].length).toEqual(2); + expect(paths[1].length).toEqual(1); + }); + test('mapBatchSwap', () => { + const route = mapBatchSwap( + paths[0], + (BigInt(amountIn) * BigInt(2)).toString(), + (BigInt(amountOut) * BigInt(2)).toString(), + SwapKind.GivenIn, + assets, + pools, + ); + expect(route).toEqual(expectedRoute[0]); + }); + test('mapBatchSwap', () => { + const route = mapBatchSwap( + paths[1], + (BigInt(amountIn) * BigInt(2)).toString(), + (BigInt(amountOut) * BigInt(2)).toString(), + SwapKind.GivenIn, + assets, + pools, + ); + expect(route).toEqual(expectedRoute[1]); + }); + test('GivenIn', () => { + const mappedRoute = mapRoutes( + batchSwap, + (BigInt(amountIn) * BigInt(2)).toString(), + (BigInt(amountOut) * BigInt(2)).toString(), + pools, + assetIn, + assetOut, + assets, + SwapKind.GivenIn, + ); + expect(mappedRoute).toEqual(expectedRoute); + }); + }); + }); + describe('ExactOut', () => { + let pools: GqlPoolMinimal[]; + let paths: BatchSwapStep[][]; + const assetIn = '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4'; + const assetOut = '0x8159462d255C1D24915CB51ec361F700174cD994'; + const amountIn = '111111111111111111'; + const amountOut = '222222222222222222'; + const batchSwap: BatchSwapStep[] = [ + { + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + assetInIndex: BigInt(1), + assetOutIndex: BigInt(2), + amount: BigInt(amountOut), + userData: '0x', + }, + { + poolId: '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + assetInIndex: BigInt(0), + assetOutIndex: BigInt(1), + amount: BigInt(0), + userData: '0x', + }, + ]; + const assets = [ + '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + '0x8159462d255C1D24915CB51ec361F700174cD994', + ]; + const expectedRoute: GqlSorSwapRoute[] = [ + { + hops: [ + { + pool: {} as GqlPoolMinimal, + poolId: '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + tokenOutAmount: '0', // TODO - Are we expecting to get the values for intermmediate steps? May need b-sdk to return that? + }, + { + pool: {} as GqlPoolMinimal, + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + tokenIn: '0xE4885Ed2818Cc9E840A25f94F9b2A28169D1AEA7', + tokenInAmount: '0', + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }, + ], + share: 1, + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }, + ]; + + beforeAll(async () => { + pools = await poolService.getGqlPools({ + where: { + idIn: [ + '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + ], + }, + }); + expectedRoute[0].hops[0].pool = pools.find( + (p) => p.id === '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + ) as GqlPoolMinimal; + expectedRoute[0].hops[1].pool = pools.find( + (p) => p.id === '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + ) as GqlPoolMinimal; + }); + describe('Single Path', () => { + test('split paths', () => { + paths = splitPaths(batchSwap, assetIn, assetOut, assets, SwapKind.GivenOut); + expect(paths.length).toEqual(1); + expect(paths[0].length).toEqual(2); + }); + test('mapBatchSwap', () => { + const route = mapBatchSwap(paths[0], amountIn, amountOut, SwapKind.GivenOut, assets, pools); + expect(route).toEqual(expectedRoute[0]); + }); + test('GivenOut', () => { + const mappedRoute = mapRoutes( + batchSwap, + amountIn, + amountOut, + pools, + assetIn, + assetOut, + assets, + SwapKind.GivenOut, + ); + expect(mappedRoute).toEqual(expectedRoute); + }); + }); + describe('Multi Paths', () => { + beforeAll(() => { + batchSwap.push({ + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + assetInIndex: BigInt(0), + assetOutIndex: BigInt(2), + amount: BigInt(amountOut), + userData: '0x', + }); + expectedRoute[0].share = 0.5; + expectedRoute.unshift({ + hops: [ + { + pool: pools.find( + (p) => + p.id === '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + ) as GqlPoolMinimal, + poolId: '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d', + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }, + ], + share: 0.5, + tokenIn: '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', + tokenInAmount: amountIn, + tokenOut: '0x8159462d255C1D24915CB51ec361F700174cD994', + tokenOutAmount: amountOut, + }); + }); + test('splitPaths', () => { + paths = splitPaths(batchSwap, assetIn, assetOut, assets, SwapKind.GivenOut); + expect(paths.length).toEqual(2); + expect(paths[0].length).toEqual(1); + expect(paths[1].length).toEqual(2); + }); + test('mapBatchSwap', () => { + const route = mapBatchSwap( + paths[0], + (BigInt(amountIn) * BigInt(2)).toString(), + (BigInt(amountOut) * BigInt(2)).toString(), + SwapKind.GivenOut, + assets, + pools, + ); + expect(route).toEqual(expectedRoute[0]); + }); + test('mapBatchSwap', () => { + const route = mapBatchSwap( + paths[1], + (BigInt(amountIn) * BigInt(2)).toString(), + (BigInt(amountOut) * BigInt(2)).toString(), + SwapKind.GivenOut, + assets, + pools, + ); + expect(route).toEqual(expectedRoute[1]); + }); + test('GivenOut', () => { + const mappedRoute = mapRoutes( + batchSwap, + (BigInt(amountIn) * BigInt(2)).toString(), + (BigInt(amountOut) * BigInt(2)).toString(), + pools, + assetIn, + assetOut, + assets, + SwapKind.GivenOut, + ); + expect(mappedRoute).toEqual(expectedRoute); + }); + }); + }); + }); +}); diff --git a/modules/sor/sorV2/beetsHelpers.ts b/modules/sor/sorV2/beetsHelpers.ts new file mode 100644 index 000000000..135276c23 --- /dev/null +++ b/modules/sor/sorV2/beetsHelpers.ts @@ -0,0 +1,116 @@ +import { BatchSwapStep, SingleSwap, SwapKind } from '@balancer/sdk'; +import { GqlPoolMinimal, GqlSorSwapRoute, GqlSorSwapRouteHop } from '../../../schema'; +import { formatFixed } from '@ethersproject/bignumber'; + +export function mapRoutes( + swaps: BatchSwapStep[] | SingleSwap, + amountIn: string, + amountOut: string, + pools: GqlPoolMinimal[], + assetIn: string, + assetOut: string, + assets: string[], + kind: SwapKind, +): GqlSorSwapRoute[] { + const isBatchSwap = Array.isArray(swaps); + if (!isBatchSwap) { + const pool = pools.find((p) => p.id === swaps.poolId); + if (!pool) throw new Error('Pool not found while mapping route'); + return [mapSingleSwap(swaps, amountIn, amountOut, pool)]; + } + const paths = splitPaths(swaps, assetIn, assetOut, assets, kind); + return paths.map((p) => mapBatchSwap(p, amountIn, amountOut, kind, assets, pools)); +} + +export function mapBatchSwap( + swaps: BatchSwapStep[], + amountIn: string, + amountOut: string, + kind: SwapKind, + assets: string[], + pools: GqlPoolMinimal[], +): GqlSorSwapRoute { + const exactIn = kind === SwapKind.GivenIn; + const first = swaps[0]; + const last = swaps[swaps.length - 1]; + let tokenInAmount: string; + let tokenOutAmount: string; + let share: bigint; + const one = BigInt(1e18); + if (exactIn) { + tokenInAmount = first.amount.toString(); + share = (BigInt(tokenInAmount) * one) / BigInt(amountIn); + tokenOutAmount = ((BigInt(amountOut) * share) / one).toString(); + } else { + tokenOutAmount = last.amount.toString(); + share = (BigInt(tokenOutAmount) * one) / BigInt(amountOut); + tokenInAmount = ((BigInt(amountIn) * share) / one).toString(); + } + + return { + tokenIn: assets[Number(first.assetInIndex)], + tokenOut: assets[Number(last.assetOutIndex)], + tokenInAmount, + tokenOutAmount, + share: Number(formatFixed(share.toString(), 18)), + hops: swaps.map((swap, i) => { + return { + tokenIn: assets[Number(swap.assetInIndex)], + tokenOut: assets[Number(swap.assetOutIndex)], + tokenInAmount: i === 0 ? tokenInAmount : '0', + tokenOutAmount: i === swaps.length - 1 ? tokenOutAmount : '0', + poolId: swap.poolId, + pool: pools.find((p) => p.id === swap.poolId) as GqlPoolMinimal, + }; + }), + }; +} + +export function splitPaths( + swaps: BatchSwapStep[], + assetIn: string, + assetOut: string, + assets: string[], + kind: SwapKind, +): BatchSwapStep[][] { + const swapsCopy = [...swaps]; + if (kind === SwapKind.GivenOut) { + swapsCopy.reverse(); + } + const assetInIndex = BigInt(assets.indexOf(assetIn)); + const assetOutIndex = BigInt(assets.indexOf(assetOut)); + let path: BatchSwapStep[]; + let paths: BatchSwapStep[][] = []; + swapsCopy.forEach((swap) => { + if (swap.assetInIndex === assetInIndex && swap.assetOutIndex === assetOutIndex) { + paths.push([swap]); + } else if (swap.assetInIndex === assetInIndex) { + path = [swap]; + } else if (swap.assetOutIndex === assetOutIndex) { + path.push(swap); + paths.push(path); + } else { + path.push(swap); + } + }); + return paths; +} + +function mapSingleSwap(swap: SingleSwap, amountIn: string, amountOut: string, pool: GqlPoolMinimal): GqlSorSwapRoute { + const hop: GqlSorSwapRouteHop = { + pool, + poolId: swap.poolId, + tokenIn: swap.assetIn, + tokenInAmount: amountIn, + tokenOut: swap.assetOut, + tokenOutAmount: amountOut, + }; + return { + share: 1, + tokenIn: swap.assetIn, + tokenOut: swap.assetOut, + tokenInAmount: amountIn, + tokenOutAmount: amountOut, + hops: [hop], + } as GqlSorSwapRoute; +} diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts new file mode 100644 index 000000000..04b7892b0 --- /dev/null +++ b/modules/sor/sorV2/sorV2.service.ts @@ -0,0 +1,518 @@ +import { + BasePool, + sorGetSwapsWithPools, + Token, + Address, + SwapKind, + sorParseRawPools, + RawStablePool, + RawWeightedPool, + RawMetaStablePool, + Swap as SwapSdk, + RawPool, + TokenAmount, + RawGyro2Pool, + RawGyro3Pool, + RawGyroEPool, +} from '@balancer/sdk'; +import { GqlSorSwapType, GqlSwap, GqlSorGetSwapsResponse, GqlPoolMinimal, GqlSorSwapRoute } from '../../../schema'; +import { Chain, PrismaPoolType } from '@prisma/client'; +import { GetSwapsInput, SwapResult, SwapService } from '../types'; +import { tokenService } from '../../token/token.service'; +import { networkContext } from '../../network/network-context.service'; +import { prisma } from '../../../prisma/prisma-client'; +import { PrismaPoolWithDynamic, prismaPoolWithDynamic } from '../../../prisma/prisma-types'; +import { HumanAmount, SupportedRawPoolTypes } from '@balancer/sdk'; +import { env } from '../../../app/env'; +import { DeploymentEnv } from '../../network/network-config-types'; +import { Cache, CacheClass } from 'memory-cache'; +import { GqlCowSwapApiResponse } from '../../../schema'; +import { BalancerSorService } from '../../beethoven/balancer-sor.service'; +import { poolService } from '../../pool/pool.service'; +import { BatchSwapStep } from '@balancer/sdk'; +import { SingleSwap } from '@balancer/sdk'; +import { SwapInfoRoute, SwapTypes, Swap, bnum, SwapInfoRouteHop } from '@balancer-labs/sor'; +import { BigNumber } from 'ethers'; +import { oldBnumScale } from '../../big-number/old-big-number'; +import { mapRoutes } from './beetsHelpers'; +import { poolsToIgnore } from '../constants'; +import { AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; +import * as Sentry from '@sentry/node'; + +const ALL_BASEPOOLS_CACHE_KEY = `basePools:all`; + +class SwapResultV2 implements SwapResult { + private swap: SwapSdk | null; + public inputAmount: bigint = BigInt(0); + public outputAmount: bigint = BigInt(0); + public isValid: boolean; + + constructor(swap: SwapSdk | null) { + if (swap === null) { + this.isValid = false; + this.swap = null; + } else { + this.isValid = true; + this.swap = swap; + this.inputAmount = swap.inputAmount.amount; + this.outputAmount = swap.outputAmount.amount; + } + } + + async getCowSwapResponse(chain = networkContext.chain, queryFirst = false): Promise { + if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); + + if (!queryFirst) return this.mapResultToCowSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); + else { + const rpcUrl = AllNetworkConfigsKeyedOnChain[chain].data.rpcUrl; + // Needs node >= 18 (https://github.com/wagmi-dev/viem/discussions/147) + const updatedResult = await this.swap.query(rpcUrl); + // console.log(`UPDATE:`, this.swap.quote.amount.toString(), updatedResult.amount.toString()); + + const ip = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; + const op = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; + + return this.mapResultToCowSwap(this.swap, ip, op); + } + } + + async getBeetsSwapResponse(queryFirst: boolean): Promise { + if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); + + return await this.mapResultToBeetsSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); + } + + private async mapResultToBeetsSwap( + swap: SwapSdk, + inputAmount: TokenAmount, + outputAmount: TokenAmount, + ): Promise { + const sor = new BalancerSorService(); + const tokens = await tokenService.getTokens(); + let poolIds: string[]; + if (swap.isBatchSwap) { + const swaps = swap.swaps as BatchSwapStep[]; + poolIds = swaps.map((swap) => swap.poolId); + } else { + const singleSwap = swap.swaps as SingleSwap; + poolIds = [singleSwap.poolId]; + } + const pools = await poolService.getGqlPools({ + where: { idIn: poolIds }, + }); + + const swapAmountForSwaps = + swap.swapKind === SwapKind.GivenIn ? inputAmount.amount.toString() : outputAmount.amount.toString(); + const returnAmountFromSwaps = + swap.swapKind === SwapKind.GivenIn ? outputAmount.amount.toString() : inputAmount.amount.toString(); + + const swapData = { + tokenIn: inputAmount.token.address.toString(), + tokenOut: outputAmount.token.address.toString(), + tokens, + swapType: this.mapSwapKind(swap.swapKind), + tokenInAmtEvm: inputAmount.amount.toString(), + tokenOutAmtEvm: outputAmount.amount.toString(), + swapAmountForSwaps, + returnAmountFromSwaps, + returnAmountConsideringFees: returnAmountFromSwaps, + routes: this.mapRoutes( + swap.swaps, + inputAmount.amount.toString(), + outputAmount.amount.toString(), + pools, + swap.inputAmount.token.address, + swap.outputAmount.token.address, + swap.assets, + swap.swapKind, + ), + pools, + marketSp: '0', // Daniel confirmed returning 0 should be fine here + swaps: this.mapSwaps(swap.swaps, swap.assets), + tokenAddresses: swap.assets, + }; + return sor.formatResponse(swapData); + } + + private mapSwaps(swaps: BatchSwapStep[] | SingleSwap, assets: string[]): GqlSwap[] { + if (Array.isArray(swaps)) { + return swaps.map((swap) => { + return { + ...swap, + assetInIndex: Number(swap.assetInIndex.toString()), + assetOutIndex: Number(swap.assetOutIndex.toString()), + amount: swap.amount.toString(), + }; + }); + } else { + const assetInIndex = assets.indexOf(swaps.assetIn); + const assetOutIndex = assets.indexOf(swaps.assetOut); + return [ + { + ...swaps, + assetInIndex, + assetOutIndex, + amount: swaps.amount.toString(), + }, + ]; + } + } + + private mapSwapKind(kind: SwapKind): GqlSorSwapType { + return kind === SwapKind.GivenIn ? 'EXACT_IN' : 'EXACT_OUT'; + } + + private mapRoutes( + swaps: BatchSwapStep[] | SingleSwap, + inputAmount: string, + outputAmount: string, + pools: GqlPoolMinimal[], + assetIn: string, + assetOut: string, + assets: string[], + kind: SwapKind, + ): GqlSorSwapRoute[] { + return mapRoutes(swaps, inputAmount, outputAmount, pools, assetIn, assetOut, assets, kind); + } + + /** + * Formats a sequence of swaps to a format that is useful for displaying the routes in user interfaces. + * Taken directly from Beets SOR: https://github.com/beethovenxfi/balancer-sor/blob/beethovenx-master/src/formatSwaps.ts#L167 + * @dev The swaps are converted to an array of routes, where each route has an array of hops + * @param swapType - exact in or exact out + * @param routes - The original Swaps + * @param swapAmount - The total amount being swapped + * @returns SwapInfoRoute[] - The swaps formatted as routes with hops + */ + private formatRoutesSOR(swapType: SwapTypes, routes: Swap[][], swapAmount: BigNumber): SwapInfoRoute[] { + const exactIn = swapType === SwapTypes.SwapExactIn; + + return routes.map((swaps) => { + const first = swaps[0]; + const last = swaps[swaps.length - 1]; + const tokenInAmount = (exactIn ? first.swapAmount : last.swapAmountOut) || '0'; + const tokenOutAmount = (exactIn ? last.swapAmountOut : first.swapAmount) || '0'; + const tokenInAmountScaled = oldBnumScale(bnum(tokenInAmount), first.tokenInDecimals); + + return { + tokenIn: first.tokenIn, + tokenOut: last.tokenOut, + tokenInAmount, + tokenOutAmount, + share: tokenInAmountScaled.div(bnum(swapAmount.toString())).toNumber(), + hops: swaps.map((swap): SwapInfoRouteHop => { + return { + tokenIn: swap.tokenIn, + tokenOut: swap.tokenOut, + tokenInAmount: (exactIn ? swap.swapAmount : swap.swapAmountOut) || '0', + tokenOutAmount: (exactIn ? swap.swapAmountOut : swap.swapAmount) || '0', + poolId: swap.pool, + }; + }), + }; + }); + } + + /** + * Maps Swap to GqlCowSwapApiResponse which is what current CowSwap Solver uses. + * @param swap + * @returns + */ + private mapResultToCowSwap( + swap: SwapSdk, + inputAmount: TokenAmount, + outputAmount: TokenAmount, + ): GqlCowSwapApiResponse { + let swaps: GqlSwap[]; + if (swap.swaps instanceof Array) { + swaps = swap.swaps.map((swap) => { + return { + ...swap, + amount: swap.amount.toString(), + assetInIndex: Number(swap.assetInIndex), + assetOutIndex: Number(swap.assetOutIndex), + }; + }); + } else { + swaps = [ + { + amount: inputAmount.amount.toString(), + assetInIndex: swap.assets.indexOf(swap.swaps.assetIn), + assetOutIndex: swap.assets.indexOf(swap.swaps.assetOut), + poolId: swap.swaps.poolId, + userData: swap.swaps.userData, + }, + ]; + } + const returnAmount = + swap.swapKind === SwapKind.GivenIn ? outputAmount.amount.toString() : inputAmount.amount.toString(); + const swapAmount = + swap.swapKind === SwapKind.GivenIn ? inputAmount.amount.toString() : outputAmount.amount.toString(); + return { + marketSp: '', // TODO - Check if CowSwap actually use this? Could this be calculate using out/in? + returnAmount, + returnAmountConsideringFees: returnAmount, // TODO - Check if CowSwap actually use this? + returnAmountFromSwaps: returnAmount, // TODO - Check if CowSwap actually use this? + swapAmount, + swapAmountForSwaps: swapAmount, // TODO - Check if CowSwap actually use this? + swaps, + tokenAddresses: swap.assets, + tokenIn: swap.inputAmount.token.address, + tokenOut: swap.outputAmount.token.address, + }; + } +} + +export class SorV2Service implements SwapService { + cache: CacheClass; + + constructor() { + this.cache = new Cache(); + } + + public async getSwapResult({ + chain, + tokenIn, + tokenOut, + swapType, + swapAmount, + graphTraversalConfig, + }: GetSwapsInput): Promise { + try { + const poolsFromDb = await this.getBasePools(chain); + const tIn = await this.getToken(tokenIn as Address, chain); + const tOut = await this.getToken(tokenOut as Address, chain); + const swapKind = this.mapSwapType(swapType); + const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, { + graphTraversalConfig, + }); + return new SwapResultV2(swap); + } catch (err: any) { + console.error( + `SOR_V2_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, + ); + Sentry.captureException(err.message, { + tags: { + service: 'sorV2', + tokenIn, + tokenOut, + swapAmount: swapAmount.amount, + swapType, + chain, + }, + }); + return new SwapResultV2(null); + } + } + + /** + * Gets a b-sdk Token based off tokenAddr. + * @param address + * @param chain + * @returns + */ + private async getToken(address: Address, chain: Chain): Promise { + const token = await tokenService.getToken(address, chain); + if (!token) { + throw new Error('Unknown token: ' + address); + } + const chainId = Number(chainToIdMap[chain]); + return new Token(chainId, address, token.decimals, token.symbol); + } + + private mapSwapType(swapType: GqlSorSwapType): SwapKind { + return swapType === 'EXACT_IN' ? SwapKind.GivenIn : SwapKind.GivenOut; + } + + private async getBasePools(chain: Chain): Promise { + let basePools: BasePool[] | null = this.cache.get(`${ALL_BASEPOOLS_CACHE_KEY}:${chain}`); + if (!basePools) { + basePools = await this.getBasePoolsFromDb(chain); + this.cache.put(`${ALL_BASEPOOLS_CACHE_KEY}:${chain}`, basePools, 5 * 60 * 1000); + } + return basePools; + } + + /** + * Fetch pools from Prisma and map to b-sdk BasePool. + * @returns + */ + private async getBasePoolsFromDb(chain: Chain): Promise { + const { poolIdsToExclude } = AllNetworkConfigsKeyedOnChain[chain].data.sor[env.DEPLOYMENT_ENV as DeploymentEnv]; + const pools = await prisma.prismaPool.findMany({ + where: { + chain, + dynamicData: { + totalSharesNum: { + gt: 0.000000000001, + }, + swapEnabled: true, + }, + id: { + notIn: [...poolIdsToExclude, ...poolsToIgnore], + }, + type: { + notIn: [ + 'LINEAR', // Linear pools are sunset so ignore to avoid issues related to lack of support + 'LIQUIDITY_BOOTSTRAPPING', // not supported by b-sdk + 'ELEMENT', // not supported by b-sdk + 'UNKNOWN', // not supported by b-sdk + 'INVESTMENT', // not supported by b-sdk + ], + }, + AND: { + NOT: { + // not supported by b-sdk + type: 'STABLE', + version: { + in: [1, 2], + }, + }, + }, + }, + include: prismaPoolWithDynamic.include, + }); + const rawPools = this.mapToRawPools(pools); + return this.mapToBasePools(rawPools, chain); + } + + /** + * Map Prisma pools to b-sdk RawPool. + * @param pools + * @returns + */ + private mapToRawPools(pools: PrismaPoolWithDynamic[]): RawPool[] { + return pools.map((prismaPool) => { + // b-sdk: src/data/types.ts + let rawPool: RawPool = { + id: prismaPool.id as Address, + address: prismaPool.address as Address, + poolType: this.mapRawPoolType(prismaPool.type), + poolTypeVersion: prismaPool.version, + tokensList: prismaPool.tokens.map((t) => t.address as Address), + swapEnabled: prismaPool.dynamicData!.swapEnabled, + swapFee: prismaPool.dynamicData!.swapFee as unknown as HumanAmount, + totalShares: prismaPool.dynamicData!.totalShares as unknown as HumanAmount, + liquidity: prismaPool.dynamicData!.totalLiquidity as unknown as HumanAmount, + tokens: prismaPool.tokens.map((t) => { + return { + address: t.token.address as Address, + index: t.index, + symbol: t.token.symbol, + name: t.token.name, + decimals: t.token.decimals, + balance: t.dynamicData?.balance as unknown as HumanAmount, + }; + }), + isPaused: !!prismaPool.dynamicData?.isPaused, + inRecoveryMode: !!prismaPool.dynamicData?.isInRecoveryMode, + name: 'n/a', + }; + if (['Weighted', 'Investment', 'LiquidityBootstrapping'].includes(rawPool.poolType)) { + rawPool = { + ...rawPool, + tokens: rawPool.tokens.map((t, i) => { + return { ...t, weight: prismaPool.tokens[i].dynamicData?.weight }; + }), + } as RawWeightedPool; + } + if (rawPool.poolType === 'Stable') { + rawPool = { + ...rawPool, + amp: prismaPool.stableDynamicData?.amp, + } as RawStablePool; + } + if (['MetaStable', 'ComposableStable'].includes(rawPool.poolType)) { + rawPool = { + ...rawPool, + amp: prismaPool.stableDynamicData?.amp.split('.')[0], // Taken b-sdk onChainPoolDataEnricher.ts + tokens: rawPool.tokens.map((t, i) => { + return { ...t, priceRate: prismaPool.tokens[i].dynamicData?.priceRate }; + }), + } as RawMetaStablePool; + } + if (rawPool.poolType === 'Gyro2') { + rawPool = { + ...rawPool, + alpha: prismaPool.gyroData?.alpha, + beta: prismaPool.gyroData?.beta, + sqrtAlpha: prismaPool.gyroData?.sqrtAlpha, + sqrtBeta: prismaPool.gyroData?.sqrtBeta, + } as RawGyro2Pool; + } + if (rawPool.poolType === 'Gyro3') { + rawPool = { + ...rawPool, + root3Alpha: prismaPool.gyroData?.root3Alpha, + } as RawGyro3Pool; + } + if (rawPool.poolType === 'GyroE') { + rawPool = { + ...rawPool, + alpha: prismaPool.gyroData?.alpha, + beta: prismaPool.gyroData?.beta, + c: prismaPool.gyroData?.c, + s: prismaPool.gyroData?.s, + lambda: prismaPool.gyroData?.lambda, + tauAlphaX: prismaPool.gyroData?.tauAlphaX, + tauAlphaY: prismaPool.gyroData?.tauAlphaY, + tauBetaX: prismaPool.gyroData?.tauBetaX, + tauBetaY: prismaPool.gyroData?.tauBetaY, + u: prismaPool.gyroData?.u, + v: prismaPool.gyroData?.v, + w: prismaPool.gyroData?.w, + z: prismaPool.gyroData?.z, + dSq: prismaPool.gyroData?.dSq, + tokenRates: prismaPool.tokens.map((t) => t.dynamicData?.priceRate), + } as RawGyroEPool; + } + return rawPool; + }); + } + + /** + * Map b-sdk RawPools to BasePools. + * @param pools + * @returns + */ + private mapToBasePools(pools: RawPool[], chain: Chain): BasePool[] { + const chainId = Number(chainToIdMap[chain]); + return sorParseRawPools(chainId, pools); + } + + /** + * Map Prisma pool type to b-sdk Raw pool type. + * @param type + * @returns + */ + private mapRawPoolType(type: PrismaPoolType): SupportedRawPoolTypes | string { + // From b-sdk: + // - type LinearPoolType = `${string}Linear`; + // - LinearPoolType | 'Weighted' | 'Investment' | 'LiquidityBootstrapping' | 'Stable' | 'MetaStable' | 'ComposableStable' | 'StablePhantom' | 'Element'; + switch (type) { + case PrismaPoolType.WEIGHTED: + return 'Weighted'; + case PrismaPoolType.INVESTMENT: + return 'Investment'; + case PrismaPoolType.LIQUIDITY_BOOTSTRAPPING: + return 'LiquidityBootstrapping'; + case PrismaPoolType.STABLE: + return 'Stable'; + case PrismaPoolType.META_STABLE: + return 'MetaStable'; + case PrismaPoolType.PHANTOM_STABLE: + // Composablestables are PHANTOM_STABLE in Prisma. b-sdk treats Phantoms as ComposableStable. + return 'ComposableStable'; + case PrismaPoolType.GYRO: + return 'Gyro2'; + case PrismaPoolType.GYRO3: + return 'Gyro3'; + case PrismaPoolType.GYROE: + return 'GyroE'; + default: + return type; + } + } +} + +export const sorV2Service = new SorV2Service(); diff --git a/modules/sor/types.ts b/modules/sor/types.ts new file mode 100644 index 000000000..564647c84 --- /dev/null +++ b/modules/sor/types.ts @@ -0,0 +1,31 @@ +import { Chain } from '@prisma/client'; +import { GqlCowSwapApiResponse, GqlSorSwapType, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../schema'; +import { TokenAmount } from '@balancer/sdk'; +export interface GetSwapsInput { + chain: Chain; + tokenIn: string; + tokenOut: string; + swapType: GqlSorSwapType; + swapAmount: TokenAmount; + swapOptions: GqlSorSwapOptionsInput; + graphTraversalConfig?: GraphTraversalConfig; +} + +export interface GraphTraversalConfig { + approxPathsToReturn?: number; + maxDepth?: number; + maxNonBoostedHopTokensInBoostedPath?: number; + maxNonBoostedPathDepth?: number; +} + +export interface SwapResult { + getCowSwapResponse(chain: Chain, queryFirst: boolean): Promise; + getBeetsSwapResponse(queryFirst: boolean): Promise; + isValid: boolean; + outputAmount: bigint; + inputAmount: bigint; +} + +export interface SwapService { + getSwapResult(inputs: GetSwapsInput): Promise; +} diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts new file mode 100644 index 000000000..1be0ad8ba --- /dev/null +++ b/modules/sor/utils.ts @@ -0,0 +1,20 @@ +import { TokenAmount, Token, Address } from '@balancer/sdk'; +import { tokenService } from '../token/token.service'; +import { Chain } from '@prisma/client'; +import { chainToIdMap } from '../network/network-config'; + +export async function getTokenAmountHuman(tokenAddr: string, humanAmount: string, chain: Chain): Promise { + const chainId = Number(chainToIdMap[chain]); + const prismaToken = await tokenService.getToken(tokenAddr, chain); + if (!prismaToken) throw Error(`Missing token from tokenService ${tokenAddr}`); + const token = new Token(chainId, prismaToken.address as Address, prismaToken.decimals); + return TokenAmount.fromHumanAmount(token, humanAmount as `${number}`); +} + +export async function getTokenAmountRaw(tokenAddr: string, rawAmount: string, chain: Chain): Promise { + const chainId = Number(chainToIdMap[chain]); + const prismaToken = await tokenService.getToken(tokenAddr, chain); + if (!prismaToken) throw Error(`Missing token from tokenService ${tokenAddr}`); + const token = new Token(chainId, prismaToken.address as Address, prismaToken.decimals); + return TokenAmount.fromRawAmount(token, rawAmount); +} diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql index 8c37e682f..f154888d1 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql @@ -182,6 +182,21 @@ fragment BalancerPool on Pool { amp alpha beta + sqrtAlpha + sqrtBeta + root3Alpha + c + s + lambda + tauAlphaX + tauAlphaY + tauBetaX + tauBetaY + u + v + w + z + dSq tokens { ...BalancerPoolToken diff --git a/modules/token/token.service.ts b/modules/token/token.service.ts index 4e1eb374c..21aff4d98 100644 --- a/modules/token/token.service.ts +++ b/modules/token/token.service.ts @@ -27,22 +27,22 @@ export class TokenService { await networkContext.config.contentService.syncTokenContentData(); } - public async getToken(address: string): Promise { + public async getToken(address: string, chain = networkContext.chain): Promise { return prisma.prismaToken.findUnique({ where: { address_chain: { address: address.toLowerCase(), - chain: networkContext.chain, + chain, }, }, }); } - public async getTokens(addresses?: string[]): Promise { - let tokens: PrismaToken[] | null = this.cache.get(`${ALL_TOKENS_CACHE_KEY}:${networkContext.chain}`); + public async getTokens(addresses?: string[], chain = networkContext.chain): Promise { + let tokens: PrismaToken[] | null = this.cache.get(`${ALL_TOKENS_CACHE_KEY}:${chain}`); if (!tokens) { - tokens = await prisma.prismaToken.findMany({ where: { chain: networkContext.chain } }); - this.cache.put(`${ALL_TOKENS_CACHE_KEY}:${networkContext.chain}`, tokens, 5 * 60 * 1000); + tokens = await prisma.prismaToken.findMany({ where: { chain: chain } }); + this.cache.put(`${ALL_TOKENS_CACHE_KEY}:${chain}`, tokens, 5 * 60 * 1000); } if (addresses) { return tokens.filter((token) => addresses.includes(token.address)); diff --git a/package.json b/package.json index f02ca2c23..368ab00db 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@aws-sdk/client-secrets-manager": "^3.195.0", "@aws-sdk/client-sqs": "^3.137.0", "@balancer-labs/sdk": "github:beethovenxfi/balancer-sdk#beethovenx-master", + "@balancer/sdk": "^0.3.0", "@ethersproject/address": "^5.6.0", "@ethersproject/bignumber": "^5.6.0", "@ethersproject/constants": "^5.6.0", @@ -89,7 +90,7 @@ "testcontainers": "^8.0.0", "ts-jest": "^28.0.7", "ts-node": "^10.4.0", - "typescript": "^4.5.3", + "typescript": "^5.2.2", "vitest": "^0.32.4", "vitest-mock-extended": "^1.1.3" } diff --git a/prisma/migrations/20230905102723_gyro_sor_fields/migration.sql b/prisma/migrations/20230905102723_gyro_sor_fields/migration.sql new file mode 100644 index 000000000..4202ecbdf --- /dev/null +++ b/prisma/migrations/20230905102723_gyro_sor_fields/migration.sql @@ -0,0 +1,13 @@ +-- AlterTable +ALTER TABLE "PrismaPoolGyroData" ADD COLUMN "c" TEXT, +ADD COLUMN "dSq" TEXT, +ADD COLUMN "lambda" TEXT, +ADD COLUMN "s" TEXT, +ADD COLUMN "tauAlphaX" TEXT, +ADD COLUMN "tauAlphaY" TEXT, +ADD COLUMN "tauBetaX" TEXT, +ADD COLUMN "tauBetaY" TEXT, +ADD COLUMN "u" TEXT, +ADD COLUMN "v" TEXT, +ADD COLUMN "w" TEXT, +ADD COLUMN "z" TEXT; diff --git a/prisma/migrations/20230907135550_gyro_missing_fields/migration.sql b/prisma/migrations/20230907135550_gyro_missing_fields/migration.sql new file mode 100644 index 000000000..c3f5623b1 --- /dev/null +++ b/prisma/migrations/20230907135550_gyro_missing_fields/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "PrismaPoolGyroData" ADD COLUMN "root3Alpha" TEXT, +ADD COLUMN "sqrtAlpha" TEXT, +ADD COLUMN "sqrtBeta" TEXT; diff --git a/prisma/prisma-types.ts b/prisma/prisma-types.ts index 56663de91..6f830da7b 100644 --- a/prisma/prisma-types.ts +++ b/prisma/prisma-types.ts @@ -276,3 +276,22 @@ export const prismaPoolBatchSwapWithSwaps = Prisma.validator; + +export const prismaPoolWithDynamic = Prisma.validator()({ + include: { + stableDynamicData: true, + dynamicData: true, + linearDynamicData: true, + linearData: true, + gyroData: true, + tokens: { + orderBy: { index: 'asc' }, + include: { + token: true, + dynamicData: true, + }, + }, + } +}); + +export type PrismaPoolWithDynamic = Prisma.PrismaPoolGetPayload; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 988be5d51..6e3103627 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -142,6 +142,21 @@ model PrismaPoolGyroData{ alpha String beta String + sqrtAlpha String? + sqrtBeta String? + root3Alpha String? + c String? + s String? + lambda String? + tauAlphaX String? + tauAlphaY String? + tauBetaX String? + tauBetaY String? + u String? + v String? + w String? + z String? + dSq String? } model PrismaPoolDynamicData { diff --git a/tsconfig.json b/tsconfig.json index 817776afd..a9c4bc4b4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,16 @@ { - "compilerOptions": { - "esModuleInterop": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "lib": ["es2018"], - "module": "commonjs", - "strict": true, - "target": "ES2020", - "resolveJsonModule": true, - "outDir": "./dist", - "skipLibCheck": true - }, - "exclude": [ - "node_modules", - "debug", - "**/*.spec.ts", - "**/*.test.ts" - ] + "compilerOptions": { + "esModuleInterop": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": ["es2018"], + "module": "commonjs", + "strict": true, + "target": "ES2020", + "resolveJsonModule": true, + "outDir": "./dist", + "skipLibCheck": true, + "sourceMap": true + }, + "exclude": ["node_modules", "debug", "**/*.spec.ts", "**/*.test.ts"] } diff --git a/worker/worker.ts b/worker/worker.ts index 45cd2e0ed..c5da70337 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -17,7 +17,7 @@ export async function startWorker() { new ProfilingIntegration(), ], tracesSampleRate: 0.2, - profilesSampleRate: 1.0, + profilesSampleRate: 0.1, }); app.use(Sentry.Handlers.requestHandler()); diff --git a/yarn.lock b/yarn.lock index 4c8f3ad55..245f5a8c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" + integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== + "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" @@ -2451,6 +2456,16 @@ graphology "^0.24.1" isomorphic-fetch "^2.2.1" +"@balancer/sdk@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@balancer/sdk/-/sdk-0.3.1.tgz#ca70a0b0082a809d2b0889277dff37c1676d46ef" + integrity sha512-eyhfgjPlVR5nZ6+Az1bDVnmycjkVa2wx7uO5bs/ntb/HYvheeJt+mcoHUOTojMRYvWUSal0WrenfbxWy0pn9Pg== + dependencies: + async-retry "^1.3.3" + decimal.js-light "^2.5.1" + pino "^8.11.0" + viem "^1.9.3" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3851,6 +3866,18 @@ resolved "https://registry.yarnpkg.com/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz#defaebdd31f625bee49e6745934f36312532b2bc" integrity sha512-BTpWy1e+FxN82RnLz4x1+JcEewVdfmUhV1C6/XYD5AjS7PQp9QFF7K8bCD6gzPTr2l+prvqOyVueQhFJxB1vfg== +"@noble/curves@1.2.0", "@noble/curves@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/hashes@1.3.2", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -4014,6 +4041,28 @@ resolved "https://registry.yarnpkg.com/@sanity/timed-out/-/timed-out-4.0.2.tgz#c9f61f9a1609baa1eb3e4235a24ea2a775022cdf" integrity sha512-NBDKGj14g9Z+bopIvZcQKWCzJq5JSrdmzRR1CS+iyA3Gm8SnIWBfZa7I3mTg2X6Nu8LQXG0EPKXdOGozLS4i3w== +"@scure/base@~1.1.0", "@scure/base@~1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.3.tgz#8584115565228290a6c6c4961973e0903bb3df2f" + integrity sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q== + +"@scure/bip32@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" + integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== + dependencies: + "@noble/curves" "~1.2.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.2" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@sentry-internal/tracing@7.56.0": version "7.56.0" resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.56.0.tgz#ba709258f2f0f3d8a36f9740403088b39212b843" @@ -5027,6 +5076,11 @@ abbrev@1, abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abitype@0.9.8: + version "0.9.8" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" + integrity sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -5354,7 +5408,7 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -async-retry@^1.2.1: +async-retry@^1.2.1, async-retry@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== @@ -5742,6 +5796,14 @@ buffer@^5.5.0, buffer@^5.7.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -6439,6 +6501,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decimal.js@^10.3.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -7099,6 +7166,11 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-sta resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-redact@^3.1.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.3.0.tgz#7c83ce3a7be4898241a46560d51de10f653f7634" + integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== + fast-text-encoding@^1.0.0: version "1.0.6" resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" @@ -7872,7 +7944,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -8392,6 +8464,11 @@ isomorphic-ws@4.0.1: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== +isows@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" + integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -9860,6 +9937,11 @@ obliterator@^2.0.2: resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -10128,6 +10210,36 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pino-abstract-transport@v1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz#083d98f966262164504afb989bccd05f665937a8" + integrity sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + +pino-std-serializers@^6.0.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" + integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== + +pino@^8.11.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.16.0.tgz#2465012a1d11fa2e7a0545032f636e203990ae26" + integrity sha512-UUmvQ/7KTZt/vHjhRrnyS7h+J7qPBQnpG80V56xmIC+o9IqYmQOw/UIny9S9zYDfRBR0ClouCr464EkBMIT7Fw== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport v1.1.0 + pino-std-serializers "^6.0.0" + process-warning "^2.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.7.0" + thread-stream "^2.0.0" + pirates@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -10214,6 +10326,16 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-warning@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.2.0.tgz#008ec76b579820a8e5c35d81960525ca64feb626" + integrity sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + progress-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-2.0.0.tgz#fac63a0b3d11deacbb0969abcc93b214bce19ed5" @@ -10307,6 +10429,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + random-words@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/random-words/-/random-words-1.3.0.tgz#00715efb8dd787d244f963c994367707c1e95676" @@ -10378,6 +10505,17 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.2.tgz#e6aced27ad3b9d726d8308515b9a1b98dc1b9d13" + integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readdir-glob@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4" @@ -10392,6 +10530,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" @@ -10636,6 +10779,11 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -10863,6 +11011,13 @@ sonic-boom@^2.1.0: dependencies: atomic-sleep "^1.0.0" +sonic-boom@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.7.0.tgz#b4b7b8049a912986f4a92c51d4660b721b11f2f2" + integrity sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg== + dependencies: + atomic-sleep "^1.0.0" + source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" @@ -10909,6 +11064,11 @@ split-ca@^1.0.1: resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY= +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + sponge-case@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" @@ -11061,7 +11221,7 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -11279,6 +11439,13 @@ testcontainers@^8.0.0: ssh-remote-port-forward "^1.0.4" tar-fs "^2.1.1" +thread-stream@^2.0.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.4.1.tgz#6d588b14f0546e59d3f306614f044bc01ce43351" + integrity sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg== + dependencies: + real-require "^0.2.0" + through2@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -11486,10 +11653,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.5.3: - version "4.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" - integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== +typescript@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== ua-parser-js@^0.7.30: version "0.7.31" @@ -11682,6 +11849,20 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +viem@^1.9.3: + version "1.19.6" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.19.6.tgz#daf1ebf4774a5bb9e87822ab25fcc67420471847" + integrity sha512-WSBHBMurWIWQk2yisOD8hqSA5S56cZu6onty3hzauVjiHMildtVWujF7YT0xjoU40GpFODvJASRR2RFdzgvUUg== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@scure/bip32" "1.3.2" + "@scure/bip39" "1.2.1" + abitype "0.9.8" + isows "1.0.3" + ws "8.13.0" + vite-node@0.32.4: version "0.32.4" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.32.4.tgz#7b3f94af5a87c631fbc380ba662914bafbd04d80" @@ -11894,6 +12075,11 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + ws@8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" From f58ac63c5a107b1eb8cc2ae59366cad470bf2a0b Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 8 Dec 2023 12:57:56 +0100 Subject: [PATCH 132/175] chain param optional --- modules/beethoven/balancer-sdk.gql | 2 +- modules/beethoven/balancer-sdk.resolvers.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/beethoven/balancer-sdk.gql b/modules/beethoven/balancer-sdk.gql index a2d9e8d26..161c685a7 100644 --- a/modules/beethoven/balancer-sdk.gql +++ b/modules/beethoven/balancer-sdk.gql @@ -1,6 +1,6 @@ extend type Query { sorGetSwaps( - chain: GqlChain! + chain: GqlChain tokenIn: String! tokenOut: String! swapType: GqlSorSwapType! diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index ccedce89b..cab3456ca 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -4,10 +4,18 @@ import { tokenService } from '../token/token.service'; import { sorService } from '../sor/sor.service'; import { getTokenAmountHuman } from '../sor/utils'; import { GraphTraversalConfig } from '../sor/types'; +import { headerChain } from '../context/header-chain'; const balancerSdkResolvers: Resolvers = { Query: { sorGetSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.chain && currentChain) { + args.chain = currentChain; + } else if (!args.chain) { + throw new Error('poolGetPool error: Provide "chain" param'); + } + const chain = args.chain; const tokenIn = args.tokenIn.toLowerCase(); const tokenOut = args.tokenOut.toLowerCase(); const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; @@ -18,6 +26,7 @@ const balancerSdkResolvers: Resolvers = { const swaps = await sorService.getBeetsSwaps({ ...args, + chain, tokenIn, tokenOut, graphTraversalConfig, From 52ef7f93fb9012f53fe29a5b2bc60bfc881acf24 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:53:59 +0100 Subject: [PATCH 133/175] Fix/sor (#564) * metrics by ENV * default maxNonBoostedPathDepth * GqlGraphTraversalConfigInput docs --- modules/beethoven/balancer-sdk.gql | 29 ++++++++++++++++++--- modules/beethoven/balancer-sdk.resolvers.ts | 10 +++++-- modules/metrics/sor.metric.ts | 8 +++++- modules/sor/sor.service.ts | 13 +++++---- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/modules/beethoven/balancer-sdk.gql b/modules/beethoven/balancer-sdk.gql index 161c685a7..6c90201ef 100644 --- a/modules/beethoven/balancer-sdk.gql +++ b/modules/beethoven/balancer-sdk.gql @@ -26,10 +26,33 @@ input GqlSorSwapOptionsInput { forceRefresh: Boolean #don't use any cached responses } +""" +Configuration options for SOR V2 +""" input GqlGraphTraversalConfigInput { - maxDepth: Int # default 6 - maxNonBoostedPathDepth: Int # default 3 - maxNonBoostedHopTokensInBoostedPath: Int # default 2 + """ + The max hops in a path. + + Default: 6 + """ + maxDepth: Int + """ + Limit of "non-boosted" pools for efficiency. + + Default: 6 + """ + maxNonBoostedPathDepth: Int + """ + Limit non boosted hop tokens in a boosted path. + + Default: 2 + """ + maxNonBoostedHopTokensInBoostedPath: Int + """ + Max number of paths to return (can be less) + + Default: 5 + """ approxPathsToReturn: Int # default 5 poolIdsToInclude: [String] } diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index cab3456ca..792d06e2e 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -13,7 +13,7 @@ const balancerSdkResolvers: Resolvers = { if (!args.chain && currentChain) { args.chain = currentChain; } else if (!args.chain) { - throw new Error('poolGetPool error: Provide "chain" param'); + throw new Error('sorGetSwaps error: Provide "chain" param'); } const chain = args.chain; const tokenIn = args.tokenIn.toLowerCase(); @@ -22,7 +22,13 @@ const balancerSdkResolvers: Resolvers = { // Use TokenAmount to help follow scaling requirements in later logic // args.swapAmount is HumanScale const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain); - const graphTraversalConfig = args.graphTraversalConfig as GraphTraversalConfig; + const graphTraversalConfig = ( + args.graphTraversalConfig + ? args.graphTraversalConfig + : { + maxNonBoostedPathDepth: 6, + } + ) as GraphTraversalConfig; const swaps = await sorService.getBeetsSwaps({ ...args, diff --git a/modules/metrics/sor.metric.ts b/modules/metrics/sor.metric.ts index bac38295e..fb4b6f42c 100644 --- a/modules/metrics/sor.metric.ts +++ b/modules/metrics/sor.metric.ts @@ -3,7 +3,13 @@ import { CloudwatchMetricsPublisher } from './metrics.client'; const publishers: Record = {}; -export function getSorMetricsPublisher(chain: Chain): CloudwatchMetricsPublisher { +export const publishMetric = (chain: Chain, metricName: string, value: number) => { + if (process.env.NODE_ENV !== 'production') return Promise.resolve(); + + return getSorMetricsPublisher(chain).publish(metricName, value); +}; + +function getSorMetricsPublisher(chain: Chain): CloudwatchMetricsPublisher { if (!publishers[chain]) { console.log(`Creating new SOR publisher for ${chain}`); publishers[chain] = new CloudwatchMetricsPublisher(`Backend-${chain}/Sor`); diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 1602e1d34..98e93fead 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -4,7 +4,7 @@ import { sorV1BeetsService } from './sorV1Beets/sorV1Beets.service'; import { sorV2Service } from './sorV2/sorV2.service'; import { GetSwapsInput, SwapResult, SwapService } from './types'; import { EMPTY_COWSWAP_RESPONSE } from './constants'; -import { getSorMetricsPublisher } from '../metrics/sor.metric'; +import { publishMetric } from '../metrics/sor.metric'; import { Chain } from '@prisma/client'; import { parseUnits, formatUnits } from '@ethersproject/units'; import { tokenService } from '../token/token.service'; @@ -121,9 +121,8 @@ export class SorService { v1Time: number, v2Time: number, ) { - const sorMetricsPublisher = getSorMetricsPublisher(chain); - await sorMetricsPublisher.publish(`SOR_VALID_V1`, v1.isValid ? 1 : 0); - await sorMetricsPublisher.publish(`SOR_VALID_V2`, v2.isValid ? 1 : 0); + await publishMetric(chain, `SOR_VALID_V1`, v1.isValid ? 1 : 0); + await publishMetric(chain, `SOR_VALID_V2`, v2.isValid ? 1 : 0); if (!version) return; @@ -154,9 +153,9 @@ export class SorService { let diff = bn(diffN.toFixed(decimals), decimals); let bestResultAmount = version === 'V1' ? v1ResultAmount : v2ResultAmount; - await sorMetricsPublisher.publish(`SOR_TIME_V1`, v1Time); - await sorMetricsPublisher.publish(`SOR_TIME_V2`, v2Time); - await sorMetricsPublisher.publish(`SOR_V2_PERFORMACE`, v2Perf); + await publishMetric(chain, `SOR_TIME_V1`, v1Time); + await publishMetric(chain, `SOR_TIME_V2`, v2Time); + await publishMetric(chain, `SOR_V2_PERFORMACE`, v2Perf); console.log( [ From fb4733b708fa31870dd195076995651f67f59b64 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Sat, 9 Dec 2023 08:59:37 +0100 Subject: [PATCH 134/175] fix: FX pool failing due to missing latestFXPrice (#566) --- modules/sor/sorV2/sorV2.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 04b7892b0..9a43c9794 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -358,6 +358,7 @@ export class SorV2Service implements SwapService { 'ELEMENT', // not supported by b-sdk 'UNKNOWN', // not supported by b-sdk 'INVESTMENT', // not supported by b-sdk + 'FX', // TODO: FX pool tokens are missing latestFXPrice - needs to be added to the DB ], }, AND: { From 4456c23cc57d00fd0ca787e395a7b38b2b31a222 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:42:04 +0100 Subject: [PATCH 135/175] disabling SOR metrics (temporary) (#567) --- modules/sor/sor.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 98e93fead..51951870d 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -121,8 +121,8 @@ export class SorService { v1Time: number, v2Time: number, ) { - await publishMetric(chain, `SOR_VALID_V1`, v1.isValid ? 1 : 0); - await publishMetric(chain, `SOR_VALID_V2`, v2.isValid ? 1 : 0); + // await publishMetric(chain, `SOR_VALID_V1`, v1.isValid ? 1 : 0); + // await publishMetric(chain, `SOR_VALID_V2`, v2.isValid ? 1 : 0); if (!version) return; @@ -153,9 +153,9 @@ export class SorService { let diff = bn(diffN.toFixed(decimals), decimals); let bestResultAmount = version === 'V1' ? v1ResultAmount : v2ResultAmount; - await publishMetric(chain, `SOR_TIME_V1`, v1Time); - await publishMetric(chain, `SOR_TIME_V2`, v2Time); - await publishMetric(chain, `SOR_V2_PERFORMACE`, v2Perf); + // await publishMetric(chain, `SOR_TIME_V1`, v1Time); + // await publishMetric(chain, `SOR_TIME_V2`, v2Time); + // await publishMetric(chain, `SOR_V2_PERFORMACE`, v2Perf); console.log( [ From 523e4a52b5620c5699678e14662135b317dfde2e Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:57:48 +0100 Subject: [PATCH 136/175] SOR: set pathDepth to 4 (#568) --- modules/beethoven/balancer-sdk.resolvers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index 792d06e2e..beaa1446c 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -26,7 +26,7 @@ const balancerSdkResolvers: Resolvers = { args.graphTraversalConfig ? args.graphTraversalConfig : { - maxNonBoostedPathDepth: 6, + maxNonBoostedPathDepth: 4, } ) as GraphTraversalConfig; From 0cadf3fb9edd2fbb3bcbbd5b65f16633713e880e Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:42:13 +0100 Subject: [PATCH 137/175] add sentry profiling to webApp (#570) --- app.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app.ts b/app.ts index 49b240ff8..6f3a2f4ec 100644 --- a/app.ts +++ b/app.ts @@ -16,6 +16,7 @@ import { resolvers } from './app/gql/resolvers'; import helmet from 'helmet'; import GraphQLJSON from 'graphql-type-json'; import * as Sentry from '@sentry/node'; +import { ProfilingIntegration } from '@sentry/profiling-node'; import { sentryPlugin } from './app/gql/sentry-apollo-plugin'; import { startWorker } from './worker/worker'; import { startScheduler } from './worker/scheduler'; @@ -33,9 +34,12 @@ async function startServer() { // new Tracing.Integrations.Apollo(), // new Tracing.Integrations.GraphQL(), // new Tracing.Integrations.Prisma({ client: prisma }), - // new Tracing.Integrations.Express({ app }), - // new Sentry.Integrations.Http({ tracing: true }), + new Sentry.Integrations.Express({ app }), + new Sentry.Integrations.Http({ tracing: true }), + new ProfilingIntegration(), ], + tracesSampleRate: 0.2, + profilesSampleRate: 0.1, beforeSend(event, hint) { const error = hint.originalException as string; if (error?.toString().includes('Unknown token:')) { @@ -55,7 +59,7 @@ async function startServer() { }); app.use(Sentry.Handlers.requestHandler()); - // app.use(Sentry.Handlers.tracingHandler()); + app.use(Sentry.Handlers.tracingHandler()); // app.use(Sentry.Handlers.errorHandler()); app.use(helmet.dnsPrefetchControl()); From 70a4fee4ff0c47f7ad059cc6eab88a21688abe58 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Tue, 12 Dec 2023 08:34:10 +0100 Subject: [PATCH 138/175] manual sort user balances (#563) manual sort --- modules/pool/lib/pool-gql-loader.service.ts | 30 +++++++++++++++++---- modules/pool/pool.gql | 4 +++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 3c5ac2126..724c15965 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -90,9 +90,18 @@ export class PoolGqlLoaderService { }, }); - return pools.map((pool) => + const gqlPools = pools.map((pool) => this.mapToMinimalGqlPool(pool, pool.userWalletBalances, pool.userStakedBalances), ); + + if (args.orderBy === 'userbalanceUsd') { + if (args.orderDirection === 'asc') { + return gqlPools.sort((a, b) => a.userBalance!.totalBalanceUsd - b.userBalance!.totalBalanceUsd); + } + return gqlPools.sort((a, b) => b.userBalance!.totalBalanceUsd - a.userBalance!.totalBalanceUsd); + } + + return gqlPools; } const pools = await prisma.prismaPool.findMany({ @@ -135,7 +144,7 @@ export class PoolGqlLoaderService { allTokens: this.mapAllTokens(pool), displayTokens: this.mapDisplayTokens(pool), staking: this.getStakingData(pool), - userBalance: this.getUserBalance(userWalletbalances, userStakedBalances), + userBalance: this.getUserBalance(pool, userWalletbalances, userStakedBalances), }; } @@ -667,16 +676,27 @@ export class PoolGqlLoaderService { } private getUserBalance( + pool: PrismaPoolMinimal, userWalletBalances: PrismaUserWalletBalance[], userStakedBalances: PrismaUserStakedBalance[], ): GqlPoolUserBalance { - const stakedNum = parseUnits(userWalletBalances.at(0)?.balance || '0', 18); - const walletNum = parseUnits(userStakedBalances.at(0)?.balance || '0', 18); + let bptPrice = 0; + if (pool.dynamicData && pool.dynamicData.totalLiquidity > 0 && parseFloat(pool.dynamicData.totalShares) > 0) { + bptPrice = pool.dynamicData.totalLiquidity / parseFloat(pool.dynamicData.totalShares); + } + + const walletBalance = parseUnits(userWalletBalances.at(0)?.balance || '0', 18); + const stakedBalance = parseUnits(userStakedBalances.at(0)?.balance || '0', 18); + const walletBalanceNum = userWalletBalances.at(0)?.balanceNum || 0; + const stakedBalanceNum = userStakedBalances.at(0)?.balanceNum || 0; return { walletBalance: userWalletBalances.at(0)?.balance || '0', stakedBalance: userStakedBalances.at(0)?.balance || '0', - totalBalance: formatFixed(stakedNum.add(walletNum), 18), + totalBalance: formatFixed(stakedBalance.add(walletBalance), 18), + walletBalanceUsd: walletBalanceNum * bptPrice, + stakedBalanceUsd: stakedBalanceNum * bptPrice, + totalBalanceUsd: (walletBalanceNum + stakedBalanceNum) * bptPrice, }; } diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index 2d1b84e59..dc601f485 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -81,8 +81,11 @@ type GqlPoolMinimal { type GqlPoolUserBalance { totalBalance: AmountHumanReadable! + totalBalanceUsd: Float! walletBalance: AmountHumanReadable! + walletBalanceUsd: Float! stakedBalance: AmountHumanReadable! + stakedBalanceUsd: Float! } enum GqlPoolMinimalType { @@ -618,6 +621,7 @@ enum GqlPoolOrderBy { volume24h fees24h apr + userbalanceUsd } enum GqlPoolOrderDirection { From 1d1641b51fdc954f2e0851761d2a9e2b40d62f33 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 12 Dec 2023 08:48:19 +0100 Subject: [PATCH 139/175] Refactor/get logs (#558) * update node types to 18 * isolated sync-wallet-balances * fetching logs by array of addresses * remove service binding, add output --------- Co-authored-by: franz --- modules/balancer/balancer.resolvers.ts | 2 +- .../beets-bar-subgraph/beets-bar.service.ts | 31 +-- .../lib/user-sync-wallet-balance.service.ts | 222 +++++++++++------- package.json | 2 +- yarn.lock | 10 +- 5 files changed, 163 insertions(+), 104 deletions(-) diff --git a/modules/balancer/balancer.resolvers.ts b/modules/balancer/balancer.resolvers.ts index a7f0b1843..3369aa1e0 100644 --- a/modules/balancer/balancer.resolvers.ts +++ b/modules/balancer/balancer.resolvers.ts @@ -5,7 +5,7 @@ import { getTokenAmountRaw } from '../sor/utils'; const balancerResolvers: Resolvers = { Query: { sorGetCowSwaps: async (parent, args, context) => { - const amountToken = args.swapType === "EXACT_IN" ? args.tokenIn : args.tokenOut; + const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; // Use TokenAmount to help follow scaling requirements in later logic // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain); diff --git a/modules/subgraphs/beets-bar-subgraph/beets-bar.service.ts b/modules/subgraphs/beets-bar-subgraph/beets-bar.service.ts index 8a14ae4b1..2ff0ce0db 100644 --- a/modules/subgraphs/beets-bar-subgraph/beets-bar.service.ts +++ b/modules/subgraphs/beets-bar-subgraph/beets-bar.service.ts @@ -8,7 +8,6 @@ import { } from './generated/beets-bar-subgraph-types'; import { Cache, CacheClass } from 'memory-cache'; import { twentyFourHoursInMs } from '../../common/time'; -import { networkContext } from '../../network/network-context.service'; const ALL_USERS_CACHE_KEY = `beets-bar-subgraph_all-users`; const BEETS_BAR_CACHE_KEY_PREFIX = `beets-bar`; @@ -17,7 +16,7 @@ const BEETS_BAR_NOW_CACHE_KEY = `beets-bar-now`; export class BeetsBarSubgraphService { cache: CacheClass; - constructor() { + constructor(private subgraphUrl: string, private fbeetsAddress?: string, private chainId = 250) { this.cache = new Cache(); } @@ -42,7 +41,7 @@ export class BeetsBarSubgraphService { }> { const { beetsBarUser, beetsBar, previousBeetsBarUser, previousBeetsBar } = await this.sdk.BeetsBarPortfolioData( { - barId: networkContext.data.fbeets?.address || '', + barId: this.fbeetsAddress || '', userAddress, previousBlockNumber, }, @@ -59,7 +58,7 @@ export class BeetsBarSubgraphService { public async getBeetsBar(block?: number): Promise { if (block) { const cached = this.cache.get( - `${BEETS_BAR_CACHE_KEY_PREFIX}:${networkContext.chainId}:${block}`, + `${BEETS_BAR_CACHE_KEY_PREFIX}:${this.chainId}:${block}`, ) as BeetsBarFragment | null; if (cached) { @@ -68,12 +67,12 @@ export class BeetsBarSubgraphService { } const { bar } = await this.sdk.GetBeetsBar({ - id: networkContext.data.fbeets?.address || '', + id: this.fbeetsAddress || '', block: { number: block }, }); this.cache.put( - `${BEETS_BAR_CACHE_KEY_PREFIX}:${networkContext.chainId}:${block}`, + `${BEETS_BAR_CACHE_KEY_PREFIX}:${this.chainId}:${block}`, bar ?? this.emptyBeetsBar, twentyFourHoursInMs, ); @@ -86,21 +85,19 @@ export class BeetsBarSubgraphService { } public async getBeetsBarNow(): Promise { - const cached = this.cache.get( - `${BEETS_BAR_NOW_CACHE_KEY}:${networkContext.chainId}`, - ) as BeetsBarFragment | null; + const cached = this.cache.get(`${BEETS_BAR_NOW_CACHE_KEY}:${this.chainId}`) as BeetsBarFragment | null; if (cached) { return cached; } - const { bar } = await this.sdk.GetBeetsBar({ id: networkContext.data.fbeets?.address || '' }); + const { bar } = await this.sdk.GetBeetsBar({ id: this.fbeetsAddress || '' }); if (!bar) { return this.emptyBeetsBar; } - this.cache.put(`${BEETS_BAR_NOW_CACHE_KEY}:${networkContext.chainId}`, bar, 60000); + this.cache.put(`${BEETS_BAR_NOW_CACHE_KEY}:${this.chainId}`, bar, 60000); return bar; } @@ -116,7 +113,7 @@ export class BeetsBarSubgraphService { } public async getUserAtBlock(address: string, block: number): Promise { - const cachedUsers = this.cache.get(`${ALL_USERS_CACHE_KEY}:${networkContext.chainId}:${block}`) as + const cachedUsers = this.cache.get(`${ALL_USERS_CACHE_KEY}:${this.chainId}:${block}`) as | BeetsBarUserFragment[] | null; @@ -126,21 +123,21 @@ export class BeetsBarSubgraphService { const users = await this.getAllUsers({ block: { number: block } }); - this.cache.put(`${ALL_USERS_CACHE_KEY}:${networkContext.chainId}:${block}`, users, twentyFourHoursInMs); + this.cache.put(`${ALL_USERS_CACHE_KEY}:${this.chainId}:${block}`, users, twentyFourHoursInMs); return users.find((user) => user.id === address) || null; } private get sdk() { - const client = new GraphQLClient(networkContext.data.subgraphs.beetsBar || ''); + const client = new GraphQLClient(this.subgraphUrl || ''); return getSdk(client); } private get emptyBeetsBar(): BeetsBarFragment { return { - id: networkContext.data.fbeets?.address || '', - address: networkContext.data.fbeets?.address || '', + id: this.fbeetsAddress || '', + address: this.fbeetsAddress || '', block: '', decimals: 19, fBeetsBurned: '0', @@ -156,5 +153,3 @@ export class BeetsBarSubgraphService { }; } } - -export const beetsBarService = new BeetsBarSubgraphService(); diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 571418aa9..340f39781 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -6,17 +6,59 @@ import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { BalancerUserPoolShare } from '../../subgraphs/balancer-subgraph/balancer-subgraph-types'; -import { beetsBarService } from '../../subgraphs/beets-bar-subgraph/beets-bar.service'; +import { BeetsBarSubgraphService } from '../../subgraphs/beets-bar-subgraph/beets-bar.service'; import { BeetsBarUserFragment } from '../../subgraphs/beets-bar-subgraph/generated/beets-bar-subgraph-types'; import { Multicaller, MulticallUserBalance } from '../../web3/multicaller'; import ERC20Abi from '../../web3/abi/ERC20.json'; import { networkContext } from '../../network/network-context.service'; +import { AllNetworkConfigs } from '../../network/network-config'; export class UserSyncWalletBalanceService { - constructor() {} + beetsBarService?: BeetsBarSubgraphService; - private get balancerSubgraphService() { - return networkContext.services.balancerSubgraphService; + constructor(private _chainId?: number) { + if (this.isFantomNetwork) { + this.beetsBarService = new BeetsBarSubgraphService( + AllNetworkConfigs['250'].data.subgraphs.beetsBar!, + this.fbeetsAddress, + ); + } + } + + get isFantomNetwork() { + return String(this.chain) === 'FANTOM'; + } + + get balancerSubgraphService() { + return AllNetworkConfigs[this.chainId].services.balancerSubgraphService; + } + + get chainId() { + return String(this._chainId || networkContext.chainId); + } + + get chain() { + return AllNetworkConfigs[this.chainId].data.chain.prismaId; + } + + get vaultAddress() { + return AllNetworkConfigs[this.chainId].data.balancer.vault; + } + + get fbeetsAddress() { + return AllNetworkConfigs['250'].data.fbeets!.address; + } + + get provider() { + return AllNetworkConfigs[this.chainId].provider; + } + + get multicallAddress() { + return AllNetworkConfigs[this.chainId].data.multicall; + } + + get rpcMaxBlockRange() { + return AllNetworkConfigs[this.chainId].data.rpcMaxBlockRange; } public async initBalancesForPools() { @@ -26,31 +68,32 @@ export class UserSyncWalletBalanceService { let endBlock = block.number; console.log(`Loading balances at block ${endBlock}`); - if (networkContext.isFantomNetwork) { - const { block: beetsBarBlock } = await beetsBarService.getMetadata(); + if (this.beetsBarService) { + const { block: beetsBarBlock } = await this.beetsBarService.getMetadata(); endBlock = Math.min(endBlock, beetsBarBlock.number); } const pools = await prisma.prismaPool.findMany({ select: { id: true, address: true }, - where: { dynamicData: { totalSharesNum: { gt: 0.000000000001 } }, chain: networkContext.chain }, + where: { dynamicData: { totalSharesNum: { gt: 0.000000000001 } }, chain: this.chain }, }); const poolIdsToInit = pools.map((pool) => pool.id); const shares = await this.balancerSubgraphService.getAllPoolSharesWithBalance(poolIdsToInit, [ AddressZero, - networkContext.data.balancer.vault, + this.vaultAddress, ]); console.log(`Found ${poolIdsToInit.length} pools to init`); + console.log(`Found ${shares.length} shares to sync`); let fbeetsHolders: BeetsBarUserFragment[] = []; - if (networkContext.isFantomNetwork) { - fbeetsHolders = await beetsBarService.getAllUsers({ where: { fBeets_not: '0' } }); + if (this.beetsBarService) { + fbeetsHolders = await this.beetsBarService.getAllUsers({ where: { fBeets_not: '0' } }); } let operations: any[] = []; - operations.push(prisma.prismaUserWalletBalance.deleteMany({ where: { chain: networkContext.chain } })); + operations.push(prisma.prismaUserWalletBalance.deleteMany({ where: { chain: this.chain } })); for (const pool of pools) { const poolShares = shares.filter((share) => share.poolAddress.toLowerCase() === pool.address); @@ -76,8 +119,8 @@ export class UserSyncWalletBalanceService { ...operations, ...fbeetsHolders.map((user) => this.getUserWalletBalanceUpsertForFbeets(user.address, user.fBeets)), prisma.prismaUserBalanceSyncStatus.upsert({ - where: { type_chain: { type: 'WALLET', chain: networkContext.chain } }, - create: { type: 'WALLET', blockNumber: endBlock, chain: networkContext.chain }, + where: { type_chain: { type: 'WALLET', chain: this.chain } }, + create: { type: 'WALLET', blockNumber: endBlock, chain: this.chain }, update: { blockNumber: Math.min(block.number, endBlock) }, }), ], @@ -88,19 +131,19 @@ export class UserSyncWalletBalanceService { public async syncChangedBalancesForAllPools() { const erc20Interface = new ethers.utils.Interface(ERC20Abi); - const latestBlock = await networkContext.provider.getBlockNumber(); + const latestBlock = await this.provider.getBlockNumber(); const syncStatus = await prisma.prismaUserBalanceSyncStatus.findUnique({ - where: { type_chain: { type: 'WALLET', chain: networkContext.chain } }, + where: { type_chain: { type: 'WALLET', chain: this.chain } }, }); const response = await prisma.prismaPool.findMany({ select: { id: true, address: true }, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); const poolAddresses = response.map((item) => item.address); - if (networkContext.isFantomNetwork) { - poolAddresses.push(networkContext.data.fbeets!.address); + if (this.isFantomNetwork) { + poolAddresses.push(this.fbeetsAddress); } if (!syncStatus) { @@ -108,55 +151,77 @@ export class UserSyncWalletBalanceService { } const fromBlock = syncStatus.blockNumber + 1; - // as we use the erc20 transfer topic, we use a smaller block range than defined in the network context - - const toBlock = - latestBlock - fromBlock > networkContext.data.rpcMaxBlockRange - ? fromBlock + networkContext.data.rpcMaxBlockRange - : latestBlock; // no new blocks have been minted, needed for slow networks - if (fromBlock > toBlock) { + if (fromBlock > latestBlock) { return; } - const events: ethers.providers.Log[] = []; - - const logPromises: Promise[] = []; + // Split the range into smaller chunks to avoid RPC limits, setting up to 50 times max block range + const toBlock = Math.min(fromBlock + 50 * this.rpcMaxBlockRange, latestBlock); + const range = toBlock - fromBlock; + console.log(`UserWalletBalanceService: syncing balances from ${fromBlock} to ${toBlock}`); + console.log(`user-sync-wallet-balances-for-all-pools-${this.chainId} getLogs of ${poolAddresses.length} pools`); + const events = await Promise.all( + // Getting logs in batches of max blocks allowed by RPC + Array.from({ length: Math.ceil(range / this.rpcMaxBlockRange) }, (_, i) => i).map(async (i) => { + const from = fromBlock + i * this.rpcMaxBlockRange; + const to = Math.min(fromBlock + (i + 1) * this.rpcMaxBlockRange, toBlock); + + // Usually RPCs are handling any number of addresses, but it here batching just to be on the safe side + const logRequests: Promise[] = _.chunk(poolAddresses, 500).map((addresses) => { + // Fetch logs with a raw json request until we support Viem or Ethers6 + const payload = { + jsonrpc: '2.0', + id: 1, + method: 'eth_getLogs', + params: [ + { + address: addresses, + topics: [ethers.utils.id('Transfer(address,address,uint256)')], + fromBlock: '0x' + BigInt(from).toString(16), + toBlock: '0x' + BigInt(to).toString(16), + }, + ], + }; + + return fetch(AllNetworkConfigs[this.chainId].data.rpcUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + .then((response) => response.json() as Promise<{ result: ethers.providers.Log[] }>) + .then(({ result }) => result) + .catch((error) => { + console.error('Error fetching logs:', error); + return []; + }); + + // Fetching logs with Viem + // viemClient.getLogs({ + // address: addresses, + // event: parseAbiItem('event Transfer(address indexed, address indexed, uint256)'), + // fromBlock: BigInt(from), + // toBlock: BigInt(to), + // }) + }); + + const events = await Promise.all(logRequests).then((res) => res.flat()); + + return events; + }), + ).then((res) => res.flat()); console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${poolAddresses.length} pools`, + `user-sync-wallet-balances-for-all-pools-${this.chainId} getLogs of ${poolAddresses.length} pools done`, ); - const chunks = _.chunk(poolAddresses, 400); - let i = 1; - for (const chunk of chunks) { - for (const poolAddress of poolAddresses) { - logPromises.push( - networkContext.provider.getLogs({ - //ERC20 Transfer topic - topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], - fromBlock, - toBlock, - address: poolAddress, - }), - ); - } - - const allResponses = await Promise.all(logPromises); - console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} getLogs of ${chunk.length} pools done.`, - ); - - for (const response of allResponses) { - events.push(...response); - } - i++; - } const relevantERC20Addresses = poolAddresses; - if (networkContext.isFantomNetwork) { - relevantERC20Addresses.push(networkContext.data.fbeets!.address); + if (this.isFantomNetwork) { + relevantERC20Addresses.push(this.fbeetsAddress); } const balancesToFetch = _.uniqBy( @@ -178,13 +243,13 @@ export class UserSyncWalletBalanceService { ); console.log( - `user-sync-wallet-balances-for-all-pools-${networkContext.chainId} got ${balancesToFetch.length} balances to fetch.`, + `user-sync-wallet-balances-for-all-pools-${this.chainId} got ${balancesToFetch.length} balances to fetch.`, ); if (balancesToFetch.length === 0) { await prisma.prismaUserBalanceSyncStatus.upsert({ - where: { type_chain: { type: 'WALLET', chain: networkContext.chain } }, - create: { type: 'WALLET', chain: networkContext.chain, blockNumber: toBlock }, + where: { type_chain: { type: 'WALLET', chain: this.chain } }, + create: { type: 'WALLET', chain: this.chain, blockNumber: toBlock }, update: { blockNumber: toBlock }, }); @@ -192,8 +257,8 @@ export class UserSyncWalletBalanceService { } const balances = await Multicaller.fetchBalances({ - multicallAddress: networkContext.data.multicall, - provider: networkContext.provider, + multicallAddress: this.multicallAddress, + provider: this.provider, balancesToFetch, }); @@ -208,10 +273,7 @@ export class UserSyncWalletBalanceService { ...balances .filter(({ userAddress }) => userAddress !== AddressZero) .map((userBalance) => { - if ( - networkContext.isFantomNetwork && - isSameAddress(userBalance.erc20Address, networkContext.data.fbeets!.address) - ) { + if (this.isFantomNetwork && isSameAddress(userBalance.erc20Address, this.fbeetsAddress)) { return this.getUserWalletBalanceUpsertForFbeets( userBalance.userAddress, formatFixed(userBalance.balance, 18), @@ -221,8 +283,8 @@ export class UserSyncWalletBalanceService { return this.getUserWalletBalanceUpsert(userBalance, poolId!); }), prisma.prismaUserBalanceSyncStatus.upsert({ - where: { type_chain: { type: 'WALLET', chain: networkContext.chain } }, - create: { type: 'WALLET', chain: networkContext.chain, blockNumber: toBlock }, + where: { type_chain: { type: 'WALLET', chain: this.chain } }, + create: { type: 'WALLET', chain: this.chain, blockNumber: toBlock }, update: { blockNumber: toBlock }, }), ], @@ -243,8 +305,8 @@ export class UserSyncWalletBalanceService { }), ...shares.map((share) => this.getPrismaUpsertForPoolShare(poolId, share)), prisma.prismaUserBalanceSyncStatus.upsert({ - where: { type_chain: { type: 'WALLET', chain: networkContext.chain } }, - create: { type: 'WALLET', chain: networkContext.chain, blockNumber: block.number }, + where: { type_chain: { type: 'WALLET', chain: this.chain } }, + create: { type: 'WALLET', chain: this.chain, blockNumber: block.number }, update: { blockNumber: block.number }, }), ], @@ -255,13 +317,13 @@ export class UserSyncWalletBalanceService { public async syncUserBalance(userAddress: string, poolId: string, poolAddresses: string) { const balancesToFetch = [{ erc20Address: poolAddresses, userAddress }]; - if (networkContext.isFantomNetwork && isSameAddress(networkContext.data.fbeets!.poolAddress, poolAddresses)) { - balancesToFetch.push({ erc20Address: networkContext.data.fbeets!.address, userAddress }); + if (this.isFantomNetwork && isSameAddress(this.fbeetsAddress, poolAddresses)) { + balancesToFetch.push({ erc20Address: this.fbeetsAddress, userAddress }); } const balances = await Multicaller.fetchBalances({ - multicallAddress: networkContext.data.multicall, - provider: networkContext.provider, + multicallAddress: this.multicallAddress, + provider: this.provider, balancesToFetch, }); @@ -272,10 +334,10 @@ export class UserSyncWalletBalanceService { private getPrismaUpsertForPoolShare(poolId: string, share: BalancerUserPoolShare) { return prisma.prismaUserWalletBalance.upsert({ - where: { id_chain: { id: `${poolId}-${share.userAddress}`, chain: networkContext.chain } }, + where: { id_chain: { id: `${poolId}-${share.userAddress}`, chain: this.chain } }, create: { id: `${poolId}-${share.userAddress}`, - chain: networkContext.chain, + chain: this.chain, userAddress: share.userAddress, poolId, tokenAddress: share.poolAddress.toLowerCase(), @@ -288,12 +350,12 @@ export class UserSyncWalletBalanceService { private getUserWalletBalanceUpsertForFbeets(userAddress: string, balance: string) { return prisma.prismaUserWalletBalance.upsert({ - where: { id_chain: { id: `fbeets-${userAddress}`, chain: networkContext.chain } }, + where: { id_chain: { id: `fbeets-${userAddress}`, chain: this.chain } }, create: { id: `fbeets-${userAddress}`, - chain: networkContext.chain, + chain: this.chain, userAddress: userAddress, - tokenAddress: networkContext.data.fbeets!.address, + tokenAddress: this.fbeetsAddress, balance, balanceNum: parseFloat(balance), }, @@ -305,10 +367,10 @@ export class UserSyncWalletBalanceService { const { userAddress, balance, erc20Address } = userBalance; return prisma.prismaUserWalletBalance.upsert({ - where: { id_chain: { id: `${poolId}-${userAddress}`, chain: networkContext.chain } }, + where: { id_chain: { id: `${poolId}-${userAddress}`, chain: this.chain } }, create: { id: `${poolId}-${userAddress}`, - chain: networkContext.chain, + chain: this.chain, userAddress, poolId, tokenAddress: erc20Address, diff --git a/package.json b/package.json index 368ab00db..939a84dd0 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@types/jest": "^28.1.6", "@types/lodash": "^4.14.177", "@types/memory-cache": "^0.2.1", - "@types/node": "^16.11.7", + "@types/node": "^18.0.0", "@types/node-cron": "^3.0.0", "@types/pino-http": "^5.8.1", "@types/safe-compare": "^1.1.0", diff --git a/yarn.lock b/yarn.lock index 245f5a8c0..33e32b57c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4863,10 +4863,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^16.11.7": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== +"@types/node@^18.0.0": + version "18.19.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.3.tgz#e4723c4cb385641d61b983f6fe0b716abd5f8fc0" + integrity sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": version "4.0.0" From eed52c03b8d37cb16ffffd53b86969da72521133 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 12 Dec 2023 11:20:55 +0100 Subject: [PATCH 140/175] first and skip for user balance --- modules/pool/lib/pool-gql-loader.service.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 724c15965..0f1be6426 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -42,6 +42,8 @@ import { networkContext } from '../../network/network-context.service'; import { fixedNumber } from '../../view-helpers/fixed-number'; import { parseUnits } from 'ethers/lib/utils'; import { formatFixed } from '@ethersproject/bignumber'; +import { StringDecoder } from 'string_decoder'; +import { SwapKind } from '@balancer/sdk'; export class PoolGqlLoaderService { public async getPool(id: string, chain: Chain): Promise { @@ -64,7 +66,15 @@ export class PoolGqlLoaderService { public async getPools(args: QueryPoolGetPoolsArgs): Promise { // only include wallet and staked balances if the query requests it // this makes sure that we don't load ALL user balances when we don't filter on userAddress + // need to support ordering and paging by userbalanceUsd. Need to take care of that here, as the DB does not (and should not) store the usd balance if (args.where?.userAddress) { + const first = args.first; + const skip = args.skip ? args.skip : 0; + if (args.orderBy === 'userbalanceUsd') { + // we need to retrieve all pools, regardless of paging request as we can't page on a DB level because there is no balance usd stored + args.first = undefined; + args.skip = undefined; + } const pools = await prisma.prismaPool.findMany({ ...this.mapQueryArgsToPoolQuery(args), include: { @@ -95,10 +105,17 @@ export class PoolGqlLoaderService { ); if (args.orderBy === 'userbalanceUsd') { + let sortedPools = []; if (args.orderDirection === 'asc') { - return gqlPools.sort((a, b) => a.userBalance!.totalBalanceUsd - b.userBalance!.totalBalanceUsd); + sortedPools = gqlPools.sort( + (a, b) => a.userBalance!.totalBalanceUsd - b.userBalance!.totalBalanceUsd, + ); + } else { + sortedPools = gqlPools.sort( + (a, b) => b.userBalance!.totalBalanceUsd - a.userBalance!.totalBalanceUsd, + ); } - return gqlPools.sort((a, b) => b.userBalance!.totalBalanceUsd - a.userBalance!.totalBalanceUsd); + return first ? sortedPools.slice(skip, skip + first) : sortedPools.slice(skip, undefined); } return gqlPools; From 360ac857a42b55df3b2181bb2118db471b83efcd Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:02:55 +0100 Subject: [PATCH 141/175] SORv2 depth increments when no path found (#572) --- modules/beethoven/balancer-sdk.resolvers.ts | 8 +---- modules/sor/sorV2/sorV2.service.ts | 36 ++++++++++++++------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index beaa1446c..0d029cdb3 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -22,13 +22,7 @@ const balancerSdkResolvers: Resolvers = { // Use TokenAmount to help follow scaling requirements in later logic // args.swapAmount is HumanScale const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain); - const graphTraversalConfig = ( - args.graphTraversalConfig - ? args.graphTraversalConfig - : { - maxNonBoostedPathDepth: 4, - } - ) as GraphTraversalConfig; + const graphTraversalConfig = args.graphTraversalConfig as GraphTraversalConfig | undefined; const swaps = await sorService.getBeetsSwaps({ ...args, diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 9a43c9794..008400f01 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -270,22 +270,36 @@ export class SorV2Service implements SwapService { this.cache = new Cache(); } - public async getSwapResult({ - chain, - tokenIn, - tokenOut, - swapType, - swapAmount, - graphTraversalConfig, - }: GetSwapsInput): Promise { + public async getSwapResult( + { chain, tokenIn, tokenOut, swapType, swapAmount, graphTraversalConfig }: GetSwapsInput, + maxNonBoostedPathDepth = 4, + ): Promise { try { const poolsFromDb = await this.getBasePools(chain); const tIn = await this.getToken(tokenIn as Address, chain); const tOut = await this.getToken(tokenOut as Address, chain); const swapKind = this.mapSwapType(swapType); - const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, { - graphTraversalConfig, - }); + const config = graphTraversalConfig + ? { + graphTraversalConfig: { + maxNonBoostedPathDepth, + ...graphTraversalConfig, + }, + } + : { + graphTraversalConfig: { + maxNonBoostedPathDepth, + }, + }; + + console.info( + `SOR: Fetching SORv2 on ${chain} for ${tokenIn} -> ${tokenOut} with maxNonBoostedPathDepth`, + maxNonBoostedPathDepth, + ); + const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, config); + if (!swap && maxNonBoostedPathDepth < 6) { + return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); + } return new SwapResultV2(swap); } catch (err: any) { console.error( From 123a906e44b3d8f8271043fad9584258877430fe Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 12 Dec 2023 17:13:50 +0100 Subject: [PATCH 142/175] adding datastudio jobs for balancer chains --- modules/network/arbitrum.ts | 9 ++++++++- modules/network/avalanche.ts | 9 ++++++++- modules/network/base.ts | 25 ++++++++++++++++++++++++- modules/network/gnosis.ts | 9 ++++++++- modules/network/mainnet.ts | 12 ++++++++---- modules/network/polygon.ts | 9 ++++++++- modules/network/zkevm.ts | 11 +++++++++-- 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 5db4ebdcc..3a34524b7 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -253,7 +253,10 @@ export const arbitrumNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(arbitrumNetworkData.subgraphs.balancer, arbitrumNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + arbitrumNetworkData.subgraphs.balancer, + arbitrumNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. @@ -349,5 +352,9 @@ export const arbitrumNetworkConfig: NetworkConfig = { name: 'sync-vebal-totalSupply', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 504f3b373..038f2788d 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -243,7 +243,10 @@ export const avalancheNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(avalancheNetworkData.subgraphs.balancer, avalancheNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + avalancheNetworkData.subgraphs.balancer, + avalancheNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. @@ -339,5 +342,9 @@ export const avalancheNetworkConfig: NetworkConfig = { name: 'sync-vebal-totalSupply', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; diff --git a/modules/network/base.ts b/modules/network/base.ts index b2ddf1291..4509e5b4d 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -115,6 +115,22 @@ const baseNetworkData: NetworkData = { alarmTopicArn: 'arn:aws:sns:eu-central-1:118697801881:api_alarms', }, }, + datastudio: { + main: { + user: 'datafeed-service@datastudio-366113.iam.gserviceaccount.com', + sheetId: '11anHUEb9snGwvB-errb5HvO8TvoLTRJhkDdD80Gxw1Q', + databaseTabName: 'Database v2', + compositionTabName: 'Pool Composition v2', + emissionDataTabName: 'EmissionData', + }, + canary: { + user: 'datafeed-service@datastudio-366113.iam.gserviceaccount.com', + sheetId: '1HnJOuRQXGy06tNgqjYMzQNIsaCSCC01Yxe_lZhXBDpY', + databaseTabName: 'Database v2', + compositionTabName: 'Pool Composition v2', + emissionDataTabName: 'EmissionData', + }, + }, }; export const baseNetworkConfig: NetworkConfig = { @@ -141,7 +157,10 @@ export const baseNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(baseNetworkData.subgraphs.balancer, baseNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + baseNetworkData.subgraphs.balancer, + baseNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. @@ -237,5 +256,9 @@ export const baseNetworkConfig: NetworkConfig = { name: 'sync-vebal-totalSupply', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index d5312029c..360e227c7 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -170,7 +170,10 @@ export const gnosisNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(gnosisNetworkData.subgraphs.balancer, gnosisNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + gnosisNetworkData.subgraphs.balancer, + gnosisNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. @@ -266,5 +269,9 @@ export const gnosisNetworkConfig: NetworkConfig = { name: 'sync-vebal-totalSupply', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 2210eedd2..fe2fd1676 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -111,8 +111,8 @@ const data: NetworkData = { gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), poolIdsToExclude: [ - "0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3", - "0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4", // Linear pools that cause issues with new b-sdk + '0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3', + '0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4', // Linear pools that cause issues with new b-sdk ], }, canary: { @@ -122,8 +122,8 @@ const data: NetworkData = { gasPrice: BigNumber.from(10), swapGas: BigNumber.from('1000000'), poolIdsToExclude: [ - "0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3", - "0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4", // Linear pools that cause issues with new b-sdk + '0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3', + '0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4', // Linear pools that cause issues with new b-sdk ], }, }, @@ -505,5 +505,9 @@ export const mainnetNetworkConfig: NetworkConfig = { name: 'sync-global-coingecko-prices', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(2, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index e641bd715..2420ce629 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -288,7 +288,10 @@ export const polygonNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(polygonNetworkData.subgraphs.balancer, polygonNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + polygonNetworkData.subgraphs.balancer, + polygonNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. @@ -384,5 +387,9 @@ export const polygonNetworkConfig: NetworkConfig = { name: 'sync-vebal-totalSupply', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 32c89798e..68a343a99 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -73,7 +73,7 @@ const zkevmNetworkData: NetworkData = { composableStablePoolFactories: [ '0x8ea89804145c007e7d226001a96955ad53836087', '0x956ccab09898c0af2aca5e6c229c3ad4e93d9288', - '0x577e5993b9cc480f07f98b5ebd055604bd9071c4' + '0x577e5993b9cc480f07f98b5ebd055604bd9071c4', ], weightedPoolV2Factories: ['0x03f3fb107e74f2eac9358862e91ad3c692712054'], swapProtocolFeePercentage: 0.5, @@ -189,7 +189,10 @@ export const zkevmNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(zkevmNetworkData.subgraphs.balancer, zkevmNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + zkevmNetworkData.subgraphs.balancer, + zkevmNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. @@ -285,5 +288,9 @@ export const zkevmNetworkConfig: NetworkConfig = { name: 'sync-vebal-totalSupply', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), }, + { + name: 'feed-data-to-datastudio', + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), + }, ], }; From c4cffddd7dfdb93c35fb9e177254eae484f4dd72 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 12 Dec 2023 17:42:48 +0100 Subject: [PATCH 143/175] make beets price optional --- modules/datastudio/datastudio.service.ts | 35 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/modules/datastudio/datastudio.service.ts b/modules/datastudio/datastudio.service.ts index a13104beb..312ef42f0 100644 --- a/modules/datastudio/datastudio.service.ts +++ b/modules/datastudio/datastudio.service.ts @@ -17,8 +17,37 @@ export class DatastudioService { constructor(private readonly secretsManager: SecretsManager, private readonly jwtClientHelper: GoogleJwtClient) {} public async feedPoolData() { - const privateKey = await this.secretsManager.getSecret('backend-v3-datafeed-privatekey'); - const jwtClient = await this.jwtClientHelper.getAuthorizedSheetsClient(privateKey); + // const privateKey = await this.secretsManager.getSecret('backend-v3-datafeed-privatekey'); + const jwtClient = await this.jwtClientHelper.getAuthorizedSheetsClient( + `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgqKRSmWLafMl4 +52i0ix8qdmjBJFTIGtRst5iq0vXGYQ/NzL6sb1iARrUzMb8bfqL/LU75bY4P+H78 +bg9SVYQkN4up6pDiF7QPiNyDIdLxbueksQuHItQPDrvHcF3m1JpY5/xwncAT4PQQ +0if4FmGkPdoCzcU41dr/EFeOtRrXFn0R12g+BCPhq7GQNDSVw7T6qrtnS7PEmq56 +L19rgtI/GnilOfI901bYsso5RPMli2dg5JPAq2Z9BDsEeq1AqkzMFOacKQRGl1G2 +pF3ZjH0Jaia8YhORhDNJhu8cuis1Zqtz6fzS73OoFPfc4zyg5Tsk+WBQTTbHO4W8 +aOsC1XLzAgMBAAECggEAAuaMHIBTVSCCFAW8NTVeQFiQfg0XUItXBHqUinyOkBJy +/O3gnaF7q+Ya6PMnFnjE8wo/JuF47CJy9c8YN3/FwN6Ham+Y8O+bEIRKm1olwkSU +qdf6rIZAyMN6VYp3CVKhisGZ60cIgUxDNLBRT8BWzkozlf4KLoNJeXYWZ30SrqXc +hpDtYnc2vdboJTd1sMPOlImcXbwtbBpg3172hnhixdeWOT7Bm3O3SeOuJ1uqB0oK +q3eVz9LdeGAzR3UKq6F8YbHzzvQRDD0jHKQsw1dpvLxK9G2pYqmxBCz5gUfvDznV +p7sTrYqGxFmU1YI3yNlEWxCVkHXploSq7wMGpIgAoQKBgQDMfy4nTGnNlervONgX +l5o4OKxA+QOdF5fa1wkWnEJZ/+h6vVe0uCjckab1mJIsuUMgdSuG0YITY7kChUzh +Ds0pr1yn8i0SC4PFj9RINA7fK36UO3uoWceolj4m4jUkgcHV2zKxt8uFGkKk1WlG +41WmYDHQ/JqLnmWq1w85wYjpAwKBgQDJHwcdo/Hq2bZH864Vx8XEg6Q6xUjGlisT +G8r6x1yp9b951+bQ11TU8edmoyRHkzAHnCsC0jcboolLRhxv8+dHfkwTrVpNXysN +/ZISDIkK50Pjsr7XbD8yC9MtI/i0SGCJ9xUISS4zypdKc5iktj3Zf7O8MxfLKNQA +nJEI4T2TUQKBgG3Z3+wiYy+Z2gM5sPMzcBfFcdrRKXzIwL1BCFPKCuaXzsWoUqX0 +y5Z484w4skUuG/4ikwgwIUTTK/5Lpxm6vsEmv4pEUKvB3b5R3gIPXPHLRorQTUQJ +PnS0iJyQpWWAAqUjw96t0bkleylartvyoP7n67rNByP7KbgVIMuwyCALAoGBAJ0F +s4mJuVORfMY6U10/5oEiAjO8WbBqB+FYXqZixk8Pxdm8HcJJjDrhTrgMgUO8f9BB +NjdypoHV6XkymPszIwvnaEAopQspVTUMF2IuFUTFAHUi1erc4Y4SO2LP5dnjdDPj +C7G3aPIYpvwCuldGzY0lLc+u2FHOY47Crl2PdYKxAoGABYMe8KZ0j16KmhM2M8hY +fxkQoLHw9k6eBoRXAUrQYeuwldQHsL6OUzjgPdbOFt47Gtyl8c4eB1vURyiDQU8Z +nhJNukRX5uaoIn1EaThY377nUoQrf2z6y4+mgruhL8SnuVYfBUhfskdMecENd8FA +ksEywRzrXaBtLpGaf9GRWCw= +-----END PRIVATE KEY-----`, + ); const databaseTabName = networkContext.data.datastudio![env.DEPLOYMENT_ENV as DeploymentEnv].databaseTabName; const sheetId = networkContext.data.datastudio![env.DEPLOYMENT_ENV as DeploymentEnv].sheetId; @@ -244,7 +273,7 @@ export class DatastudioService { for (const stake of pool.staking) { const blocksPerDay = await blocksSubgraphService.getBlocksPerDay(); const tokenPrices = await tokenService.getTokenPrices(); - const beetsPrice = await beetsService.getBeetsPrice(); + const beetsPrice = stake.farm || stake.reliquary ? await beetsService.getBeetsPrice() : '0'; if (stake.farm) { const beetsPerDay = parseFloat(stake.farm.beetsPerBlock) * blocksPerDay; const beetsValuePerDay = parseFloat(beetsPrice) * beetsPerDay; From 0b72986f132ee5b761f7d33fe6649eb69431e72c Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 12 Dec 2023 17:53:35 +0100 Subject: [PATCH 144/175] change to use secret, disable compromised key --- modules/datastudio/datastudio.service.ts | 33 ++---------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/modules/datastudio/datastudio.service.ts b/modules/datastudio/datastudio.service.ts index 312ef42f0..8ec97703e 100644 --- a/modules/datastudio/datastudio.service.ts +++ b/modules/datastudio/datastudio.service.ts @@ -17,37 +17,8 @@ export class DatastudioService { constructor(private readonly secretsManager: SecretsManager, private readonly jwtClientHelper: GoogleJwtClient) {} public async feedPoolData() { - // const privateKey = await this.secretsManager.getSecret('backend-v3-datafeed-privatekey'); - const jwtClient = await this.jwtClientHelper.getAuthorizedSheetsClient( - `-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgqKRSmWLafMl4 -52i0ix8qdmjBJFTIGtRst5iq0vXGYQ/NzL6sb1iARrUzMb8bfqL/LU75bY4P+H78 -bg9SVYQkN4up6pDiF7QPiNyDIdLxbueksQuHItQPDrvHcF3m1JpY5/xwncAT4PQQ -0if4FmGkPdoCzcU41dr/EFeOtRrXFn0R12g+BCPhq7GQNDSVw7T6qrtnS7PEmq56 -L19rgtI/GnilOfI901bYsso5RPMli2dg5JPAq2Z9BDsEeq1AqkzMFOacKQRGl1G2 -pF3ZjH0Jaia8YhORhDNJhu8cuis1Zqtz6fzS73OoFPfc4zyg5Tsk+WBQTTbHO4W8 -aOsC1XLzAgMBAAECggEAAuaMHIBTVSCCFAW8NTVeQFiQfg0XUItXBHqUinyOkBJy -/O3gnaF7q+Ya6PMnFnjE8wo/JuF47CJy9c8YN3/FwN6Ham+Y8O+bEIRKm1olwkSU -qdf6rIZAyMN6VYp3CVKhisGZ60cIgUxDNLBRT8BWzkozlf4KLoNJeXYWZ30SrqXc -hpDtYnc2vdboJTd1sMPOlImcXbwtbBpg3172hnhixdeWOT7Bm3O3SeOuJ1uqB0oK -q3eVz9LdeGAzR3UKq6F8YbHzzvQRDD0jHKQsw1dpvLxK9G2pYqmxBCz5gUfvDznV -p7sTrYqGxFmU1YI3yNlEWxCVkHXploSq7wMGpIgAoQKBgQDMfy4nTGnNlervONgX -l5o4OKxA+QOdF5fa1wkWnEJZ/+h6vVe0uCjckab1mJIsuUMgdSuG0YITY7kChUzh -Ds0pr1yn8i0SC4PFj9RINA7fK36UO3uoWceolj4m4jUkgcHV2zKxt8uFGkKk1WlG -41WmYDHQ/JqLnmWq1w85wYjpAwKBgQDJHwcdo/Hq2bZH864Vx8XEg6Q6xUjGlisT -G8r6x1yp9b951+bQ11TU8edmoyRHkzAHnCsC0jcboolLRhxv8+dHfkwTrVpNXysN -/ZISDIkK50Pjsr7XbD8yC9MtI/i0SGCJ9xUISS4zypdKc5iktj3Zf7O8MxfLKNQA -nJEI4T2TUQKBgG3Z3+wiYy+Z2gM5sPMzcBfFcdrRKXzIwL1BCFPKCuaXzsWoUqX0 -y5Z484w4skUuG/4ikwgwIUTTK/5Lpxm6vsEmv4pEUKvB3b5R3gIPXPHLRorQTUQJ -PnS0iJyQpWWAAqUjw96t0bkleylartvyoP7n67rNByP7KbgVIMuwyCALAoGBAJ0F -s4mJuVORfMY6U10/5oEiAjO8WbBqB+FYXqZixk8Pxdm8HcJJjDrhTrgMgUO8f9BB -NjdypoHV6XkymPszIwvnaEAopQspVTUMF2IuFUTFAHUi1erc4Y4SO2LP5dnjdDPj -C7G3aPIYpvwCuldGzY0lLc+u2FHOY47Crl2PdYKxAoGABYMe8KZ0j16KmhM2M8hY -fxkQoLHw9k6eBoRXAUrQYeuwldQHsL6OUzjgPdbOFt47Gtyl8c4eB1vURyiDQU8Z -nhJNukRX5uaoIn1EaThY377nUoQrf2z6y4+mgruhL8SnuVYfBUhfskdMecENd8FA -ksEywRzrXaBtLpGaf9GRWCw= ------END PRIVATE KEY-----`, - ); + const privateKey = await this.secretsManager.getSecret('backend-v3-datafeed-privatekey'); + const jwtClient = await this.jwtClientHelper.getAuthorizedSheetsClient(privateKey); const databaseTabName = networkContext.data.datastudio![env.DEPLOYMENT_ENV as DeploymentEnv].databaseTabName; const sheetId = networkContext.data.datastudio![env.DEPLOYMENT_ENV as DeploymentEnv].sheetId; From 9e7752dd95f10ecd692bd671a5eac9ebf7c1b4dd Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:36:15 +0100 Subject: [PATCH 145/175] Fix/sor (#584) * SDK update * remove SORv2 config option --- modules/beethoven/balancer-sdk.gql | 1 - modules/beethoven/balancer-sdk.resolvers.ts | 3 --- modules/sor/sorV2/sorV2.service.ts | 8 ++++---- package.json | 2 +- yarn.lock | 9 +++++---- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/beethoven/balancer-sdk.gql b/modules/beethoven/balancer-sdk.gql index 6c90201ef..530ab20c2 100644 --- a/modules/beethoven/balancer-sdk.gql +++ b/modules/beethoven/balancer-sdk.gql @@ -6,7 +6,6 @@ extend type Query { swapType: GqlSorSwapType! swapAmount: BigDecimal! #expected in human readable form swapOptions: GqlSorSwapOptionsInput! - graphTraversalConfig: GqlGraphTraversalConfigInput ): GqlSorGetSwapsResponse! sorGetBatchSwapForTokensIn( tokensIn: [GqlTokenAmountHumanReadable!]! diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index 0d029cdb3..3b3ff5dd6 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -3,7 +3,6 @@ import { balancerSorService } from './balancer-sor.service'; import { tokenService } from '../token/token.service'; import { sorService } from '../sor/sor.service'; import { getTokenAmountHuman } from '../sor/utils'; -import { GraphTraversalConfig } from '../sor/types'; import { headerChain } from '../context/header-chain'; const balancerSdkResolvers: Resolvers = { @@ -22,14 +21,12 @@ const balancerSdkResolvers: Resolvers = { // Use TokenAmount to help follow scaling requirements in later logic // args.swapAmount is HumanScale const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain); - const graphTraversalConfig = args.graphTraversalConfig as GraphTraversalConfig | undefined; const swaps = await sorService.getBeetsSwaps({ ...args, chain, tokenIn, tokenOut, - graphTraversalConfig, swapAmount: amount, }); diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 008400f01..be99e23a5 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -249,12 +249,12 @@ class SwapResultV2 implements SwapResult { const swapAmount = swap.swapKind === SwapKind.GivenIn ? inputAmount.amount.toString() : outputAmount.amount.toString(); return { - marketSp: '', // TODO - Check if CowSwap actually use this? Could this be calculate using out/in? + marketSp: '', // CowSwap is not using this field, confirmed. returnAmount, - returnAmountConsideringFees: returnAmount, // TODO - Check if CowSwap actually use this? - returnAmountFromSwaps: returnAmount, // TODO - Check if CowSwap actually use this? + returnAmountConsideringFees: returnAmount, // CowSwap is not using this field, confirmed. + returnAmountFromSwaps: returnAmount, // CowSwap is not using this field, confirmed. swapAmount, - swapAmountForSwaps: swapAmount, // TODO - Check if CowSwap actually use this? + swapAmountForSwaps: swapAmount, // CowSwap is not using this field, confirmed. swaps, tokenAddresses: swap.assets, tokenIn: swap.inputAmount.token.address, diff --git a/package.json b/package.json index 939a84dd0..9be908911 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@aws-sdk/client-secrets-manager": "^3.195.0", "@aws-sdk/client-sqs": "^3.137.0", "@balancer-labs/sdk": "github:beethovenxfi/balancer-sdk#beethovenx-master", - "@balancer/sdk": "^0.3.0", + "@balancer/sdk": "^0.4.0", "@ethersproject/address": "^5.6.0", "@ethersproject/bignumber": "^5.6.0", "@ethersproject/constants": "^5.6.0", diff --git a/yarn.lock b/yarn.lock index 33e32b57c..3374ba4bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,13 +2456,14 @@ graphology "^0.24.1" isomorphic-fetch "^2.2.1" -"@balancer/sdk@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@balancer/sdk/-/sdk-0.3.1.tgz#ca70a0b0082a809d2b0889277dff37c1676d46ef" - integrity sha512-eyhfgjPlVR5nZ6+Az1bDVnmycjkVa2wx7uO5bs/ntb/HYvheeJt+mcoHUOTojMRYvWUSal0WrenfbxWy0pn9Pg== +"@balancer/sdk@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@balancer/sdk/-/sdk-0.4.0.tgz#9fe762e1b7f6cad574a1603bdc33e77c6e1ab426" + integrity sha512-g8ilzNSlk2Pdh7PUAyr88sD78GGDTOUJUpAhKBO5dN77Kk70ivyL6mNJBGpJqrjraoBygv6yQukV8yO5m0kiEw== dependencies: async-retry "^1.3.3" decimal.js-light "^2.5.1" + lodash "^4.17.21" pino "^8.11.0" viem "^1.9.3" From eb2a4290a84186265cebaf60fd0aa1219560f24c Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:57:31 +0100 Subject: [PATCH 146/175] faster logs fetching for staking --- .../lib/user-sync-gauge-balance.service.ts | 81 ++++++++++++------- .../lib/user-sync-wallet-balance.service.ts | 2 +- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/modules/user/lib/user-sync-gauge-balance.service.ts b/modules/user/lib/user-sync-gauge-balance.service.ts index 0ee6df2f3..93dd0efb8 100644 --- a/modules/user/lib/user-sync-gauge-balance.service.ts +++ b/modules/user/lib/user-sync-gauge-balance.service.ts @@ -88,20 +88,16 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { include: { staking: true }, where: { chain: networkContext.chain }, }); - console.log(`user-sync-staked-balances-${networkContext.chainId} got data from db.`); const latestBlock = await networkContext.provider.getBlockNumber(); console.log(`user-sync-staked-balances-${networkContext.chainId} got latest block.`); + const gaugeAddresses = await gaugeSubgraphService.getAllGaugeAddresses(); - console.log(`user-sync-staked-balances-${networkContext.chainId} got ${gaugeAddresses.length} gauges.`); // we sync at most 10k blocks at a time const startBlock = status.blockNumber + 1; - const endBlock = - latestBlock - startBlock > networkContext.data.rpcMaxBlockRange - ? startBlock + networkContext.data.rpcMaxBlockRange - : latestBlock; + const endBlock = latestBlock; // no new blocks have been minted, needed for slow networks if (startBlock > endBlock) { @@ -112,32 +108,63 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { we need to figure out which users have a changed balance on any gauge contract and update their balance, therefore we check all transfer events since the last synced block */ - - const events: ethers.providers.Log[] = []; - const logPromises: Promise[] = []; const erc20Interface = new ethers.utils.Interface(ERC20Abi); - console.log(`user-sync-staked-balances-${networkContext.chainId} getLogs of ${gaugeAddresses.length} gauges`); - - for (const gaugeAddress of gaugeAddresses) { - logPromises.push( - networkContext.provider.getLogs({ - //ERC20 Transfer topic - topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], - fromBlock: startBlock, - toBlock: endBlock, - address: gaugeAddress, - }), - ); - } - const allResponses = await Promise.all(logPromises); + // Split the range into smaller chunks to avoid RPC limits, setting up to 50 times max block range + const maxBlockRange = networkContext.data.rpcMaxBlockRange; + const toBlock = Math.min(startBlock + 50 * maxBlockRange, latestBlock); + const range = toBlock - startBlock; + console.log(`user-sync-staked-balances-${networkContext.chainId} block range from ${startBlock} to ${toBlock}`); console.log( - `user-sync-staked-balances-${networkContext.chainId} getLogs of ${gaugeAddresses.length} gauges done`, + `user-sync-staked-balances-${networkContext.chainId} getLogs for ${_.uniq(gaugeAddresses).length} gauges.`, ); + const events = await Promise.all( + // Getting logs in batches of max blocks allowed by RPC + Array.from({ length: Math.ceil(range / maxBlockRange) }, (_, i) => i).map(async (i) => { + const from = startBlock + i * maxBlockRange; + const to = Math.min(startBlock + (i + 1) * maxBlockRange, toBlock); - for (const response of allResponses) { - events.push(...response); - } + // Usually RPCs are handling any number of addresses, but it here batching just to be on the safe side + const logRequests: Promise[] = _.chunk(gaugeAddresses, 500).map((addresses) => { + // Fetch logs with a raw json request until we support Viem or Ethers6 + const payload = { + jsonrpc: '2.0', + id: 1, + method: 'eth_getLogs', + params: [ + { + address: addresses, + topics: [ethers.utils.id('Transfer(address,address,uint256)')], + fromBlock: '0x' + BigInt(from).toString(16), + toBlock: '0x' + BigInt(to).toString(16), + }, + ], + }; + + return fetch(networkContext.data.rpcUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + .then((response) => response.json() as Promise<{ result: ethers.providers.Log[] }>) + .then(({ result }) => result) + .catch((error) => { + console.error('Error fetching logs:', error); + return []; + }); + }); + + const events = await Promise.all(logRequests).then((res) => res.flat()); + + return events; + }), + ).then((res) => res.flat().filter((event) => event)); + + console.log( + `user-sync-staked-balances-${networkContext.chainId} getLogs for ${gaugeAddresses.length} gauges done`, + ); const balancesToFetch = _.uniqBy( events diff --git a/modules/user/lib/user-sync-wallet-balance.service.ts b/modules/user/lib/user-sync-wallet-balance.service.ts index 340f39781..7389260aa 100644 --- a/modules/user/lib/user-sync-wallet-balance.service.ts +++ b/modules/user/lib/user-sync-wallet-balance.service.ts @@ -212,7 +212,7 @@ export class UserSyncWalletBalanceService { return events; }), - ).then((res) => res.flat()); + ).then((res) => res.flat().filter((event) => event)); console.log( `user-sync-wallet-balances-for-all-pools-${this.chainId} getLogs of ${poolAddresses.length} pools done`, From 67ca94ee9f262b11a94dbe813eeaa88ec818b017 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 14 Dec 2023 08:44:32 +0100 Subject: [PATCH 147/175] adding SORv2 priceImpact in basis points (#585) --- modules/beethoven/balancer-sor.service.ts | 4 +++- modules/sor/sorV2/sorV2.service.ts | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/beethoven/balancer-sor.service.ts b/modules/beethoven/balancer-sor.service.ts index 37106b79a..bc7dac26e 100644 --- a/modules/beethoven/balancer-sor.service.ts +++ b/modules/beethoven/balancer-sor.service.ts @@ -148,6 +148,7 @@ export class BalancerSorService { marketSp: string; swaps: SwapV2[]; tokenAddresses: string[]; + priceImpact?: string; }): GqlSorGetSwapsResponse { const { tokenIn, @@ -164,6 +165,7 @@ export class BalancerSorService { marketSp, swaps, tokenAddresses, + priceImpact: rawPriceImpact, } = swapData; const tokenInAmountFixed = formatFixed(tokenInAmtEvm, this.getTokenDecimals(tokenIn, tokens)); @@ -214,7 +216,7 @@ export class BalancerSorService { })), effectivePrice: effectivePrice.toString(), effectivePriceReversed: effectivePriceReversed.toString(), - priceImpact: priceImpact.toString(), + priceImpact: rawPriceImpact ? rawPriceImpact : priceImpact.toString(), }; } diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index be99e23a5..95adf1993 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -89,6 +89,7 @@ class SwapResultV2 implements SwapResult { ): Promise { const sor = new BalancerSorService(); const tokens = await tokenService.getTokens(); + const priceImpact = swap.priceImpact.percentage.toFixed(4); let poolIds: string[]; if (swap.isBatchSwap) { const swaps = swap.swaps as BatchSwapStep[]; @@ -130,6 +131,7 @@ class SwapResultV2 implements SwapResult { marketSp: '0', // Daniel confirmed returning 0 should be fine here swaps: this.mapSwaps(swap.swaps, swap.assets), tokenAddresses: swap.assets, + priceImpact, }; return sor.formatResponse(swapData); } From 4d60790203da94b1aa4cea58c969f7087c3b18a1 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 14 Dec 2023 09:11:25 +0100 Subject: [PATCH 148/175] fix price impact decimals --- modules/sor/sorV2/sorV2.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 95adf1993..bf4d89c2c 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -89,7 +89,7 @@ class SwapResultV2 implements SwapResult { ): Promise { const sor = new BalancerSorService(); const tokens = await tokenService.getTokens(); - const priceImpact = swap.priceImpact.percentage.toFixed(4); + const priceImpact = swap.priceImpact.decimal.toFixed(4); let poolIds: string[]; if (swap.isBatchSwap) { const swaps = swap.swaps as BatchSwapStep[]; From 7a1a0e7b61311420715c1223fde87160158c3a34 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:13:33 +0100 Subject: [PATCH 149/175] more SOR logging to debug when it's crashing (#591) --- modules/beethoven/balancer-sdk.resolvers.ts | 2 ++ modules/sor/sor.service.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts index 3b3ff5dd6..a755a1e90 100644 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ b/modules/beethoven/balancer-sdk.resolvers.ts @@ -8,6 +8,8 @@ import { headerChain } from '../context/header-chain'; const balancerSdkResolvers: Resolvers = { Query: { sorGetSwaps: async (parent, args, context) => { + console.log('sorGetSwaps args', JSON.stringify(args)); + const currentChain = headerChain(); if (!args.chain && currentChain) { args.chain = currentChain; diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 51951870d..8067c21c3 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -46,6 +46,7 @@ export class SorService { } private async getSwap(input: GetSwapsInput, v1Service: SwapService = sorV1BalancerService) { + console.log(`Running SOR for ${input.swapAmount} ${input.tokenIn} > ${input.tokenOut}`); const v1Start = +new Date(); const swapV1 = await v1Service.getSwapResult(input); const v1Time = +new Date() - v1Start; From 5ce23369588d9d9edcf215405034650796eed6df Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 14 Dec 2023 13:16:16 +0100 Subject: [PATCH 150/175] wip --- modules/pool/lib/pool-gql-loader.service.ts | 46 ++++++++++++++++++--- modules/pool/pool.gql | 4 +- modules/pool/pool.resolvers.ts | 6 +-- modules/pool/pool.service.ts | 5 ++- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 0f1be6426..c0671a828 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -30,6 +30,7 @@ import { GqlPoolUserBalance, GqlPoolWithdrawConfig, GqlPoolWithdrawOption, + InputMaybe, QueryPoolGetPoolsArgs, } from '../../../schema'; import { isSameAddress } from '@balancer-labs/sdk'; @@ -42,14 +43,16 @@ import { networkContext } from '../../network/network-context.service'; import { fixedNumber } from '../../view-helpers/fixed-number'; import { parseUnits } from 'ethers/lib/utils'; import { formatFixed } from '@ethersproject/bignumber'; -import { StringDecoder } from 'string_decoder'; -import { SwapKind } from '@balancer/sdk'; export class PoolGqlLoaderService { - public async getPool(id: string, chain: Chain): Promise { - const pool = await prisma.prismaPool.findUnique({ + public async getPool(id: string, chain: Chain, userAddress?: InputMaybe): Promise { + let pool = undefined; + pool = await prisma.prismaPool.findUnique({ where: { id_chain: { id, chain: chain } }, - include: prismaPoolWithExpandedNesting.include, + include: { + ...prismaPoolWithExpandedNesting.include, + ...this.getUserBalancesInclude(userAddress), + }, }); if (!pool) { @@ -423,7 +426,11 @@ export class PoolGqlLoaderService { }; } - private mapPoolToGqlPool(pool: PrismaPoolWithExpandedNesting): GqlPoolUnion { + private mapPoolToGqlPool( + pool: PrismaPoolWithExpandedNesting, + userWalletbalances: PrismaUserWalletBalance[] = [], + userStakedBalances: PrismaUserStakedBalance[] = [], + ): GqlPoolUnion { const bpt = pool.tokens.find((token) => token.address === pool.address); const mappedData = { @@ -437,6 +444,7 @@ export class PoolGqlLoaderService { tokens: pool.tokens.map((token) => this.mapPoolTokenToGqlUnion(token)), allTokens: this.mapAllTokens(pool), displayTokens: this.mapDisplayTokens(pool), + userBalance: this.getUserBalance(pool, userWalletbalances, userStakedBalances), }; //TODO: may need to build out the types here still @@ -1258,4 +1266,30 @@ export class PoolGqlLoaderService { .toFixed(mainToken.token.decimals)}`, }; } + + private getUserBalancesInclude(userAddress?: InputMaybe) { + if (!userAddress) { + return {}; + } + return { + userWalletBalances: { + where: { + userAddress: { + equals: userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, + }, + userStakedBalances: { + where: { + userAddress: { + equals: userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, + }, + }; + } } diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index dc601f485..d7046df63 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -1,5 +1,5 @@ extend type Query { - poolGetPool(id: String!, chain: GqlChain): GqlPoolBase! + poolGetPool(id: String!, chain: GqlChain, userAddress: String): GqlPoolBase! poolGetPools( first: Int skip: Int @@ -125,6 +125,8 @@ interface GqlPoolBase { dynamicData: GqlPoolDynamicData! staking: GqlPoolStaking + + userBalance: GqlPoolUserBalance } type GqlPoolDynamicData { diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index bb7387b93..8acf48937 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -7,14 +7,14 @@ import { headerChain } from '../context/header-chain'; const balancerResolvers: Resolvers = { Query: { - poolGetPool: async (parent, { id, chain }, context) => { + poolGetPool: async (parent, { id, chain, userAddress }, context) => { const currentChain = headerChain(); if (!chain && currentChain) { chain = currentChain; } else if (!chain) { throw new Error('poolGetPool error: Provide "chain" param'); } - return poolService.getGqlPool(id, chain); + return poolService.getGqlPool(id, chain, userAddress); }, poolGetPools: async (parent, args, context) => { return poolService.getGqlPools(args); @@ -105,7 +105,7 @@ const balancerResolvers: Resolvers = { }, poolGetGyroPools: async () => { return poolService.getGqlGyroPools(); - } + }, }, Mutation: { poolSyncAllPoolsFromSubgraph: async (parent, {}, context) => { diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 704d56364..1ded9c694 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -14,6 +14,7 @@ import { GqlPoolSnapshotDataRange, GqlPoolUnion, GqlPoolUserSwapVolume, + InputMaybe, QueryPoolGetBatchSwapsArgs, QueryPoolGetJoinExitsArgs, QueryPoolGetPoolsArgs, @@ -73,8 +74,8 @@ export class PoolService { return networkContext.services.balancerSubgraphService; } - public async getGqlPool(id: string, chain: GqlChain): Promise { - return this.poolGqlLoaderService.getPool(id, chain); + public async getGqlPool(id: string, chain: GqlChain, userAddress?: InputMaybe): Promise { + return this.poolGqlLoaderService.getPool(id, chain, userAddress); } public async getGqlPools(args: QueryPoolGetPoolsArgs): Promise { From 684f17515946fc7e955dc2eaae1e24754ae456b7 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 14 Dec 2023 13:18:41 +0100 Subject: [PATCH 151/175] more logs --- modules/sor/sor.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 8067c21c3..33bd006a7 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -26,6 +26,7 @@ export class SorService { } async getBeetsSwaps(input: GetSwapsInput): Promise { + console.log('getBeetsSwaps input', JSON.stringify(input)); const swap = await this.getSwap(input, sorV1BeetsService); const emptyResponse = sorV1BeetsService.zeroResponse( input.swapType, From ad6e4d9242214ef1e18635026a0bfd90672898a8 Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 14 Dec 2023 13:31:47 +0100 Subject: [PATCH 152/175] reduce default max hop --- modules/sor/sorV2/sorV2.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index bf4d89c2c..9de44e900 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -274,7 +274,7 @@ export class SorV2Service implements SwapService { public async getSwapResult( { chain, tokenIn, tokenOut, swapType, swapAmount, graphTraversalConfig }: GetSwapsInput, - maxNonBoostedPathDepth = 4, + maxNonBoostedPathDepth = 3, ): Promise { try { const poolsFromDb = await this.getBasePools(chain); From ffd6360bade0100464191946441115e900daa59c Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 14 Dec 2023 13:51:48 +0100 Subject: [PATCH 153/175] reduce max to 5 --- modules/sor/sorV2/sorV2.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 9de44e900..3ddbf24d6 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -299,7 +299,7 @@ export class SorV2Service implements SwapService { maxNonBoostedPathDepth, ); const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, config); - if (!swap && maxNonBoostedPathDepth < 6) { + if (!swap && maxNonBoostedPathDepth < 5) { return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); } return new SwapResultV2(swap); From cc0d6dc9dd304b7288a8b0c3ab2c9643bf00d9cc Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 14 Dec 2023 14:34:09 +0100 Subject: [PATCH 154/175] reduce to 4 max hops --- modules/sor/sorV2/sorV2.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 3ddbf24d6..8cb319cfb 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -299,7 +299,7 @@ export class SorV2Service implements SwapService { maxNonBoostedPathDepth, ); const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, config); - if (!swap && maxNonBoostedPathDepth < 5) { + if (!swap && maxNonBoostedPathDepth < 4) { return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); } return new SwapResultV2(swap); From 875bbec8ee4c7c3be00af1bd49ac8e046419409a Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:18:53 +0100 Subject: [PATCH 155/175] native asset support --- modules/beethoven/balancer-sor.service.ts | 3 +- .../sorV1Balancer/sorV1Balancer.service.ts | 3 +- modules/sor/sorV2/beetsHelpers.ts | 6 +-- modules/sor/sorV2/sorV2.service.ts | 49 ++++++++----------- modules/sor/utils.ts | 30 ++++++++---- 5 files changed, 47 insertions(+), 44 deletions(-) diff --git a/modules/beethoven/balancer-sor.service.ts b/modules/beethoven/balancer-sor.service.ts index bc7dac26e..7452f5ea0 100644 --- a/modules/beethoven/balancer-sor.service.ts +++ b/modules/beethoven/balancer-sor.service.ts @@ -18,6 +18,7 @@ import * as Sentry from '@sentry/node'; import _ from 'lodash'; import { Logger } from '@ethersproject/logger'; import { SwapInfoRoute } from '@balancer-labs/sor'; +import { NATIVE_ADDRESS, ZERO_ADDRESS } from '@balancer/sdk'; interface GetSwapsInput { tokenIn: string; @@ -331,7 +332,7 @@ export class BalancerSorService { } private getTokenDecimals(tokenAddress: string, tokens: PrismaToken[]): number { - if (tokenAddress === '0x0000000000000000000000000000000000000000') { + if (tokenAddress === ZERO_ADDRESS || tokenAddress === NATIVE_ADDRESS) { return 18; } diff --git a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts b/modules/sor/sorV1Balancer/sorV1Balancer.service.ts index 73ad3b5ab..f35d98914 100644 --- a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts +++ b/modules/sor/sorV1Balancer/sorV1Balancer.service.ts @@ -6,7 +6,6 @@ import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse } from '. import { GetSwapsInput, SwapService, SwapResult } from '../types'; import { FundManagement, SwapTypes, SwapV2 } from '@balancer-labs/sdk'; import { env } from '../../../app/env'; -import { networkContext } from '../../network/network-context.service'; import { AllNetworkConfigs, AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; import { DeploymentEnv } from '../../network/network-config-types'; @@ -33,7 +32,7 @@ class SwapResultV1 implements SwapResult { } } - async getCowSwapResponse(chain = networkContext.chain, queryFirst = false): Promise { + async getCowSwapResponse(chain: Chain, queryFirst = false): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); if (queryFirst) { diff --git a/modules/sor/sorV2/beetsHelpers.ts b/modules/sor/sorV2/beetsHelpers.ts index 135276c23..6a7226742 100644 --- a/modules/sor/sorV2/beetsHelpers.ts +++ b/modules/sor/sorV2/beetsHelpers.ts @@ -1,4 +1,4 @@ -import { BatchSwapStep, SingleSwap, SwapKind } from '@balancer/sdk'; +import { BatchSwapStep, NATIVE_ADDRESS, SingleSwap, SwapKind, ZERO_ADDRESS } from '@balancer/sdk'; import { GqlPoolMinimal, GqlSorSwapRoute, GqlSorSwapRouteHop } from '../../../schema'; import { formatFixed } from '@ethersproject/bignumber'; @@ -77,8 +77,8 @@ export function splitPaths( if (kind === SwapKind.GivenOut) { swapsCopy.reverse(); } - const assetInIndex = BigInt(assets.indexOf(assetIn)); - const assetOutIndex = BigInt(assets.indexOf(assetOut)); + const assetInIndex = BigInt(assets.indexOf(assetIn === NATIVE_ADDRESS ? ZERO_ADDRESS : assetIn)); + const assetOutIndex = BigInt(assets.indexOf(assetOut === NATIVE_ADDRESS ? ZERO_ADDRESS : assetOut)); let path: BatchSwapStep[]; let paths: BatchSwapStep[][] = []; swapsCopy.forEach((swap) => { diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 8cb319cfb..a3b96fc2e 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -1,7 +1,6 @@ import { BasePool, sorGetSwapsWithPools, - Token, Address, SwapKind, sorParseRawPools, @@ -14,23 +13,29 @@ import { RawGyro2Pool, RawGyro3Pool, RawGyroEPool, + HumanAmount, + SupportedRawPoolTypes, + SingleSwap, + BatchSwapStep, } from '@balancer/sdk'; -import { GqlSorSwapType, GqlSwap, GqlSorGetSwapsResponse, GqlPoolMinimal, GqlSorSwapRoute } from '../../../schema'; +import { + GqlSorSwapType, + GqlSwap, + GqlSorGetSwapsResponse, + GqlPoolMinimal, + GqlSorSwapRoute, + GqlCowSwapApiResponse, +} from '../../../schema'; import { Chain, PrismaPoolType } from '@prisma/client'; +import { PrismaPoolWithDynamic, prismaPoolWithDynamic } from '../../../prisma/prisma-types'; +import { prisma } from '../../../prisma/prisma-client'; import { GetSwapsInput, SwapResult, SwapService } from '../types'; +import { poolService } from '../../pool/pool.service'; import { tokenService } from '../../token/token.service'; -import { networkContext } from '../../network/network-context.service'; -import { prisma } from '../../../prisma/prisma-client'; -import { PrismaPoolWithDynamic, prismaPoolWithDynamic } from '../../../prisma/prisma-types'; -import { HumanAmount, SupportedRawPoolTypes } from '@balancer/sdk'; +import { BalancerSorService } from '../../beethoven/balancer-sor.service'; import { env } from '../../../app/env'; import { DeploymentEnv } from '../../network/network-config-types'; import { Cache, CacheClass } from 'memory-cache'; -import { GqlCowSwapApiResponse } from '../../../schema'; -import { BalancerSorService } from '../../beethoven/balancer-sor.service'; -import { poolService } from '../../pool/pool.service'; -import { BatchSwapStep } from '@balancer/sdk'; -import { SingleSwap } from '@balancer/sdk'; import { SwapInfoRoute, SwapTypes, Swap, bnum, SwapInfoRouteHop } from '@balancer-labs/sor'; import { BigNumber } from 'ethers'; import { oldBnumScale } from '../../big-number/old-big-number'; @@ -38,6 +43,7 @@ import { mapRoutes } from './beetsHelpers'; import { poolsToIgnore } from '../constants'; import { AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; import * as Sentry from '@sentry/node'; +import { getToken } from '../utils'; const ALL_BASEPOOLS_CACHE_KEY = `basePools:all`; @@ -59,7 +65,7 @@ class SwapResultV2 implements SwapResult { } } - async getCowSwapResponse(chain = networkContext.chain, queryFirst = false): Promise { + async getCowSwapResponse(chain: Chain, queryFirst = false): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); if (!queryFirst) return this.mapResultToCowSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); @@ -278,8 +284,8 @@ export class SorV2Service implements SwapService { ): Promise { try { const poolsFromDb = await this.getBasePools(chain); - const tIn = await this.getToken(tokenIn as Address, chain); - const tOut = await this.getToken(tokenOut as Address, chain); + const tIn = await getToken(tokenIn as Address, chain); + const tOut = await getToken(tokenOut as Address, chain); const swapKind = this.mapSwapType(swapType); const config = graphTraversalConfig ? { @@ -321,21 +327,6 @@ export class SorV2Service implements SwapService { } } - /** - * Gets a b-sdk Token based off tokenAddr. - * @param address - * @param chain - * @returns - */ - private async getToken(address: Address, chain: Chain): Promise { - const token = await tokenService.getToken(address, chain); - if (!token) { - throw new Error('Unknown token: ' + address); - } - const chainId = Number(chainToIdMap[chain]); - return new Token(chainId, address, token.decimals, token.symbol); - } - private mapSwapType(swapType: GqlSorSwapType): SwapKind { return swapType === 'EXACT_IN' ? SwapKind.GivenIn : SwapKind.GivenOut; } diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts index 1be0ad8ba..ab47ae2b2 100644 --- a/modules/sor/utils.ts +++ b/modules/sor/utils.ts @@ -1,20 +1,32 @@ -import { TokenAmount, Token, Address } from '@balancer/sdk'; +import { TokenAmount, Token, Address, NATIVE_ADDRESS, NATIVE_ASSETS } from '@balancer/sdk'; import { tokenService } from '../token/token.service'; import { Chain } from '@prisma/client'; import { chainToIdMap } from '../network/network-config'; export async function getTokenAmountHuman(tokenAddr: string, humanAmount: string, chain: Chain): Promise { - const chainId = Number(chainToIdMap[chain]); - const prismaToken = await tokenService.getToken(tokenAddr, chain); - if (!prismaToken) throw Error(`Missing token from tokenService ${tokenAddr}`); - const token = new Token(chainId, prismaToken.address as Address, prismaToken.decimals); + const token = await getToken(tokenAddr, chain); return TokenAmount.fromHumanAmount(token, humanAmount as `${number}`); } export async function getTokenAmountRaw(tokenAddr: string, rawAmount: string, chain: Chain): Promise { - const chainId = Number(chainToIdMap[chain]); - const prismaToken = await tokenService.getToken(tokenAddr, chain); - if (!prismaToken) throw Error(`Missing token from tokenService ${tokenAddr}`); - const token = new Token(chainId, prismaToken.address as Address, prismaToken.decimals); + const token = await getToken(tokenAddr, chain); return TokenAmount.fromRawAmount(token, rawAmount); } + +/** + * Gets a b-sdk Token based off tokenAddr. + * @param address + * @param chain + * @returns + */ +export const getToken = async (tokenAddr: string, chain: Chain): Promise => { + const chainId = Number(chainToIdMap[chain]); + + if (tokenAddr === NATIVE_ADDRESS && chainId in NATIVE_ASSETS) { + return NATIVE_ASSETS[chainId as keyof typeof NATIVE_ASSETS]; + } else { + const prismaToken = await tokenService.getToken(tokenAddr, chain); + if (!prismaToken) throw Error(`Missing token from tokenService ${tokenAddr}`); + return new Token(chainId, prismaToken.address as Address, prismaToken.decimals); + } +}; From d90388139b364201b301db2276c63f41e23f01c5 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 09:21:05 +0100 Subject: [PATCH 156/175] wip --- modules/balancer/balancer.gql | 39 ------------- modules/balancer/balancer.resolvers.ts | 23 -------- modules/beethoven/balancer-sdk.resolvers.ts | 45 -------------- .../{beethoven => sor}/balancer-sor.test.ts | 0 .../balancer-sdk.gql => sor/sor.gql} | 49 +++++++++------- modules/sor/sor.resolvers.ts | 31 ++++++++++ modules/sor/sor.service.ts | 58 ++++++++++++++----- .../sorV1Beets}/balancer-sor.service.ts | 18 +++--- modules/sor/sorV1Beets/sorV1Beets.service.ts | 2 +- modules/sor/sorV2/sorV2.service.ts | 2 +- modules/sor/utils.ts | 5 +- 11 files changed, 119 insertions(+), 153 deletions(-) delete mode 100644 modules/balancer/balancer.gql delete mode 100644 modules/balancer/balancer.resolvers.ts delete mode 100644 modules/beethoven/balancer-sdk.resolvers.ts rename modules/{beethoven => sor}/balancer-sor.test.ts (100%) rename modules/{beethoven/balancer-sdk.gql => sor/sor.gql} (93%) create mode 100644 modules/sor/sor.resolvers.ts rename modules/{beethoven => sor/sorV1Beets}/balancer-sor.service.ts (96%) diff --git a/modules/balancer/balancer.gql b/modules/balancer/balancer.gql deleted file mode 100644 index 228429a2f..000000000 --- a/modules/balancer/balancer.gql +++ /dev/null @@ -1,39 +0,0 @@ -extend type Mutation { - balancerMutationTest: String! -} - -extend type Query { - sorGetCowSwaps( - chain: GqlChain! - tokenIn: String! - tokenOut: String! - swapType: GqlSorSwapType! - swapAmount: BigDecimal! #expected in raw amount - ): GqlCowSwapApiResponse! -} - -enum GqlSorSwapType { - EXACT_IN - EXACT_OUT -} - -type GqlCowSwapApiResponse { - tokenAddresses: [String!]! - swaps: [GqlSwap!]! - swapAmount: String! - swapAmountForSwaps: String! - returnAmount: String! - returnAmountFromSwaps: String! - returnAmountConsideringFees: String! - tokenIn: String! - tokenOut: String! - marketSp: String! -} - -type GqlSwap { - poolId: String! - assetInIndex: Int! - assetOutIndex: Int! - amount: String! - userData: String! -} diff --git a/modules/balancer/balancer.resolvers.ts b/modules/balancer/balancer.resolvers.ts deleted file mode 100644 index 3369aa1e0..000000000 --- a/modules/balancer/balancer.resolvers.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Resolvers } from '../../schema'; -import { sorService } from '../sor/sor.service'; -import { getTokenAmountRaw } from '../sor/utils'; - -const balancerResolvers: Resolvers = { - Query: { - sorGetCowSwaps: async (parent, args, context) => { - const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; - // Use TokenAmount to help follow scaling requirements in later logic - // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 - const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain); - const swaps = await sorService.getCowSwaps({ ...args, swapAmount: amount, swapOptions: {} }); - return { ...swaps, __typename: 'GqlCowSwapApiResponse' }; - }, - }, - Mutation: { - balancerMutationTest: async (parent, {}, context) => { - return 'test'; - }, - }, -}; - -export default balancerResolvers; diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts deleted file mode 100644 index a755a1e90..000000000 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Resolvers } from '../../schema'; -import { balancerSorService } from './balancer-sor.service'; -import { tokenService } from '../token/token.service'; -import { sorService } from '../sor/sor.service'; -import { getTokenAmountHuman } from '../sor/utils'; -import { headerChain } from '../context/header-chain'; - -const balancerSdkResolvers: Resolvers = { - Query: { - sorGetSwaps: async (parent, args, context) => { - console.log('sorGetSwaps args', JSON.stringify(args)); - - const currentChain = headerChain(); - if (!args.chain && currentChain) { - args.chain = currentChain; - } else if (!args.chain) { - throw new Error('sorGetSwaps error: Provide "chain" param'); - } - const chain = args.chain; - const tokenIn = args.tokenIn.toLowerCase(); - const tokenOut = args.tokenOut.toLowerCase(); - const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; - // Use TokenAmount to help follow scaling requirements in later logic - // args.swapAmount is HumanScale - const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain); - - const swaps = await sorService.getBeetsSwaps({ - ...args, - chain, - tokenIn, - tokenOut, - swapAmount: amount, - }); - - return { ...swaps, __typename: 'GqlSorGetSwapsResponse' }; - }, - sorGetBatchSwapForTokensIn: async (parent, args, context) => { - const tokens = await tokenService.getTokens(); - - return balancerSorService.getBatchSwapForTokensIn({ ...args, tokens }); - }, - }, -}; - -export default balancerSdkResolvers; diff --git a/modules/beethoven/balancer-sor.test.ts b/modules/sor/balancer-sor.test.ts similarity index 100% rename from modules/beethoven/balancer-sor.test.ts rename to modules/sor/balancer-sor.test.ts diff --git a/modules/beethoven/balancer-sdk.gql b/modules/sor/sor.gql similarity index 93% rename from modules/beethoven/balancer-sdk.gql rename to modules/sor/sor.gql index 530ab20c2..716cf02d2 100644 --- a/modules/beethoven/balancer-sdk.gql +++ b/modules/sor/sor.gql @@ -7,6 +7,13 @@ extend type Query { swapAmount: BigDecimal! #expected in human readable form swapOptions: GqlSorSwapOptionsInput! ): GqlSorGetSwapsResponse! + sorGetCowSwaps( + chain: GqlChain! + tokenIn: String! + tokenOut: String! + swapType: GqlSorSwapType! + swapAmount: BigDecimal! #expected in raw amount + ): GqlCowSwapApiResponse! sorGetBatchSwapForTokensIn( tokensIn: [GqlTokenAmountHumanReadable!]! tokenOut: String! @@ -14,6 +21,27 @@ extend type Query { ): GqlSorGetBatchSwapForTokensInResponse! } +type GqlCowSwapApiResponse { + tokenAddresses: [String!]! + swaps: [GqlSwap!]! + swapAmount: String! + swapAmountForSwaps: String! + returnAmount: String! + returnAmountFromSwaps: String! + returnAmountConsideringFees: String! + tokenIn: String! + tokenOut: String! + marketSp: String! +} + +type GqlSwap { + poolId: String! + assetInIndex: Int! + assetOutIndex: Int! + amount: String! + userData: String! +} + enum GqlSorSwapType { EXACT_IN EXACT_OUT @@ -109,24 +137,3 @@ type GqlSorGetBatchSwapForTokensInResponse { swaps: [GqlSorSwap!]! assets: [String!]! } - -type GqlCowSwapApiResponse { - tokenAddresses: [String!]! - swaps: [GqlSwap!]! - swapAmount: String! - swapAmountForSwaps: String! - returnAmount: String! - returnAmountFromSwaps: String! - returnAmountConsideringFees: String! - tokenIn: String! - tokenOut: String! - marketSp: String! -} - -type GqlSwap { - poolId: String! - assetInIndex: Int! - assetOutIndex: Int! - amount: String! - userData: String! -} diff --git a/modules/sor/sor.resolvers.ts b/modules/sor/sor.resolvers.ts new file mode 100644 index 000000000..b4da3403d --- /dev/null +++ b/modules/sor/sor.resolvers.ts @@ -0,0 +1,31 @@ +import { Resolvers } from '../../schema'; +import { balancerSorService } from './sorV1Beets/balancer-sor.service'; +import { tokenService } from '../token/token.service'; +import { sorService } from './sor.service'; +import { getTokenAmountHuman } from './utils'; +import { headerChain } from '../context/header-chain'; + +const balancerSdkResolvers: Resolvers = { + Query: { + sorGetSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.chain && currentChain) { + args.chain = currentChain; + } else if (!args.chain) { + throw new Error('sorGetSwaps error: Provide "chain" param'); + } + + return sorService.getBeetsSwaps(args); + }, + sorGetBatchSwapForTokensIn: async (parent, args, context) => { + const tokens = await tokenService.getTokens(); + + return balancerSorService.getBatchSwapForTokensIn({ ...args, tokens }); + }, + sorGetCowSwaps: async (parent, args, context) => { + return sorService.getCowSwaps(args); + }, + }, +}; + +export default balancerSdkResolvers; diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 33bd006a7..5019bebbb 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -1,4 +1,11 @@ -import { GqlCowSwapApiResponse, GqlSorSwapType, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../schema'; +import { + GqlCowSwapApiResponse, + GqlSorSwapType, + GqlSorGetSwapsResponse, + GqlSorSwapOptionsInput, + QuerySorGetSwapsArgs, + QuerySorGetCowSwapsArgs, +} from '../../schema'; import { sorV1BalancerService } from './sorV1Balancer/sorV1Balancer.service'; import { sorV1BeetsService } from './sorV1Beets/sorV1Beets.service'; import { sorV2Service } from './sorV2/sorV2.service'; @@ -8,32 +15,57 @@ import { publishMetric } from '../metrics/sor.metric'; import { Chain } from '@prisma/client'; import { parseUnits, formatUnits } from '@ethersproject/units'; import { tokenService } from '../token/token.service'; +import { getTokenAmountHuman, getTokenAmountRaw } from './utils'; export class SorService { - async getCowSwaps(input: GetSwapsInput): Promise { - const swap = await this.getSwap({ ...input, swapOptions: {} }); - const emptyResponse = EMPTY_COWSWAP_RESPONSE(input.tokenIn, input.tokenOut, input.swapAmount); + async getCowSwaps(args: QuerySorGetCowSwapsArgs): Promise { + const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; + // Use TokenAmount to help follow scaling requirements in later logic + // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 + const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain!); + + const swap = await this.getSwap({ + chain: args.chain!, + swapAmount: amount, + swapType: args.swapType, + tokenIn: args.tokenIn.toLowerCase(), + tokenOut: args.tokenOut.toLowerCase(), + swapOptions: {}, + }); + const emptyResponse = EMPTY_COWSWAP_RESPONSE(args.tokenIn, args.tokenOut, amount); if (!swap) return emptyResponse; try { // Updates with latest onchain data before returning - return await swap.getCowSwapResponse(input.chain, true); + return await swap.getCowSwapResponse(args.chain!, true); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; } } - async getBeetsSwaps(input: GetSwapsInput): Promise { - console.log('getBeetsSwaps input', JSON.stringify(input)); - const swap = await this.getSwap(input, sorV1BeetsService); - const emptyResponse = sorV1BeetsService.zeroResponse( - input.swapType, - input.tokenIn, - input.tokenOut, - input.swapAmount, + async getBeetsSwaps(args: QuerySorGetSwapsArgs): Promise { + console.log('getBeetsSwaps args', JSON.stringify(args)); + const tokenIn = args.tokenIn.toLowerCase(); + const tokenOut = args.tokenOut.toLowerCase(); + const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; + // Use TokenAmount to help follow scaling requirements in later logic + // args.swapAmount is HumanScale + const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain!); + + const swap = await this.getSwap( + { + chain: args.chain!, + swapAmount: amount, + swapOptions: args.swapOptions, + swapType: args.swapType, + tokenIn: tokenIn, + tokenOut: tokenOut, + }, + sorV1BeetsService, ); + const emptyResponse = sorV1BeetsService.zeroResponse(args.swapType, args.tokenIn, args.tokenOut, amount); if (!swap) return emptyResponse; diff --git a/modules/beethoven/balancer-sor.service.ts b/modules/sor/sorV1Beets/balancer-sor.service.ts similarity index 96% rename from modules/beethoven/balancer-sor.service.ts rename to modules/sor/sorV1Beets/balancer-sor.service.ts index 7452f5ea0..6cf8fefa7 100644 --- a/modules/beethoven/balancer-sor.service.ts +++ b/modules/sor/sorV1Beets/balancer-sor.service.ts @@ -1,19 +1,19 @@ -import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../schema'; +import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../../schema'; import { formatFixed, parseFixed } from '@ethersproject/bignumber'; import { PrismaToken } from '@prisma/client'; -import { poolService } from '../pool/pool.service'; -import { oldBnum } from '../big-number/old-big-number'; +import { poolService } from '../../pool/pool.service'; +import { oldBnum } from '../../big-number/old-big-number'; import axios from 'axios'; import { FundManagement, SwapInfo, SwapTypes, SwapV2 } from '@balancer-labs/sdk'; -import { replaceEthWithZeroAddress, replaceZeroAddressWithEth } from '../web3/addresses'; +import { replaceEthWithZeroAddress, replaceZeroAddressWithEth } from '../../web3/addresses'; import { BigNumber } from 'ethers'; -import { TokenAmountHumanReadable } from '../common/global-types'; +import { TokenAmountHumanReadable } from '../../common/global-types'; import { AddressZero } from '@ethersproject/constants'; import { Contract } from '@ethersproject/contracts'; -import VaultAbi from '../pool/abi/Vault.json'; -import { env } from '../../app/env'; -import { networkContext } from '../network/network-context.service'; -import { DeploymentEnv } from '../network/network-config-types'; +import VaultAbi from '../../pool/abi/Vault.json'; +import { env } from '../../../app/env'; +import { networkContext } from '../../network/network-context.service'; +import { DeploymentEnv } from '../../network/network-config-types'; import * as Sentry from '@sentry/node'; import _ from 'lodash'; import { Logger } from '@ethersproject/logger'; diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index 067e5f87c..e91744ec7 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -1,7 +1,7 @@ import { formatEther } from 'viem'; import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../../schema'; import { GetSwapsInput, SwapService, SwapResult } from '../types'; -import { BalancerSorService } from '../../beethoven/balancer-sor.service'; +import { BalancerSorService } from './balancer-sor.service'; import { tokenService } from '../../token/token.service'; import { TokenAmount } from '@balancer/sdk'; diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index a3b96fc2e..fd442dfae 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -32,7 +32,7 @@ import { prisma } from '../../../prisma/prisma-client'; import { GetSwapsInput, SwapResult, SwapService } from '../types'; import { poolService } from '../../pool/pool.service'; import { tokenService } from '../../token/token.service'; -import { BalancerSorService } from '../../beethoven/balancer-sor.service'; +import { BalancerSorService } from '../sorV1Beets/balancer-sor.service'; import { env } from '../../../app/env'; import { DeploymentEnv } from '../../network/network-config-types'; import { Cache, CacheClass } from 'memory-cache'; diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts index ab47ae2b2..2c53582b9 100644 --- a/modules/sor/utils.ts +++ b/modules/sor/utils.ts @@ -22,7 +22,10 @@ export async function getTokenAmountRaw(tokenAddr: string, rawAmount: string, ch export const getToken = async (tokenAddr: string, chain: Chain): Promise => { const chainId = Number(chainToIdMap[chain]); - if (tokenAddr === NATIVE_ADDRESS && chainId in NATIVE_ASSETS) { + if ( + (tokenAddr === NATIVE_ADDRESS || tokenAddr === '0x0000000000000000000000000000000000001010') && + chainId in NATIVE_ASSETS + ) { return NATIVE_ASSETS[chainId as keyof typeof NATIVE_ASSETS]; } else { const prismaToken = await tokenService.getToken(tokenAddr, chain); From 03f51af83226135885962c1f61c3d0bc22d3a20e Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 11:03:09 +0100 Subject: [PATCH 157/175] rename --- modules/sor/sor.resolvers.ts | 2 +- modules/sor/sor.service.ts | 47 +++--- .../sorV1Balancer/sorV1Balancer.service.ts | 135 ------------------ modules/sor/utils.ts | 1 + 4 files changed, 19 insertions(+), 166 deletions(-) delete mode 100644 modules/sor/sorV1Balancer/sorV1Balancer.service.ts diff --git a/modules/sor/sor.resolvers.ts b/modules/sor/sor.resolvers.ts index b4da3403d..31fb1c044 100644 --- a/modules/sor/sor.resolvers.ts +++ b/modules/sor/sor.resolvers.ts @@ -15,7 +15,7 @@ const balancerSdkResolvers: Resolvers = { throw new Error('sorGetSwaps error: Provide "chain" param'); } - return sorService.getBeetsSwaps(args); + return sorService.getSorSwaps(args); }, sorGetBatchSwapForTokensIn: async (parent, args, context) => { const tokens = await tokenService.getTokens(); diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 5019bebbb..ea1abecfa 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -2,16 +2,13 @@ import { GqlCowSwapApiResponse, GqlSorSwapType, GqlSorGetSwapsResponse, - GqlSorSwapOptionsInput, QuerySorGetSwapsArgs, QuerySorGetCowSwapsArgs, } from '../../schema'; -import { sorV1BalancerService } from './sorV1Balancer/sorV1Balancer.service'; import { sorV1BeetsService } from './sorV1Beets/sorV1Beets.service'; import { sorV2Service } from './sorV2/sorV2.service'; -import { GetSwapsInput, SwapResult, SwapService } from './types'; +import { GetSwapsInput, SwapResult } from './types'; import { EMPTY_COWSWAP_RESPONSE } from './constants'; -import { publishMetric } from '../metrics/sor.metric'; import { Chain } from '@prisma/client'; import { parseUnits, formatUnits } from '@ethersproject/units'; import { tokenService } from '../token/token.service'; @@ -19,12 +16,13 @@ import { getTokenAmountHuman, getTokenAmountRaw } from './utils'; export class SorService { async getCowSwaps(args: QuerySorGetCowSwapsArgs): Promise { + console.log('getCowSwaps args', JSON.stringify(args)); const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; // Use TokenAmount to help follow scaling requirements in later logic // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain!); - const swap = await this.getSwap({ + const swap = await sorV2Service.getSwapResult({ chain: args.chain!, swapAmount: amount, swapType: args.swapType, @@ -38,15 +36,15 @@ export class SorService { try { // Updates with latest onchain data before returning - return await swap.getCowSwapResponse(args.chain!, true); + return await swap.getCowSwapResponse(true); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; } } - async getBeetsSwaps(args: QuerySorGetSwapsArgs): Promise { - console.log('getBeetsSwaps args', JSON.stringify(args)); + async getSorSwaps(args: QuerySorGetSwapsArgs): Promise { + console.log('getSorSwaps args', JSON.stringify(args)); const tokenIn = args.tokenIn.toLowerCase(); const tokenOut = args.tokenOut.toLowerCase(); const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; @@ -54,34 +52,30 @@ export class SorService { // args.swapAmount is HumanScale const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain!); - const swap = await this.getSwap( - { - chain: args.chain!, - swapAmount: amount, - swapOptions: args.swapOptions, - swapType: args.swapType, - tokenIn: tokenIn, - tokenOut: tokenOut, - }, - sorV1BeetsService, - ); + const swap = await this.getComparingSwap({ + chain: args.chain!, + swapAmount: amount, + swapOptions: args.swapOptions, + swapType: args.swapType, + tokenIn: tokenIn, + tokenOut: tokenOut, + }); const emptyResponse = sorV1BeetsService.zeroResponse(args.swapType, args.tokenIn, args.tokenOut, amount); if (!swap) return emptyResponse; try { // Updates with latest onchain data before returning - return swap.getBeetsSwapResponse(true); + return swap.getSorSwapResponse(true); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; } } - private async getSwap(input: GetSwapsInput, v1Service: SwapService = sorV1BalancerService) { - console.log(`Running SOR for ${input.swapAmount} ${input.tokenIn} > ${input.tokenOut}`); + private async getComparingSwap(input: GetSwapsInput) { const v1Start = +new Date(); - const swapV1 = await v1Service.getSwapResult(input); + const swapV1 = await sorV1BeetsService.getSwapResult(input); const v1Time = +new Date() - v1Start; const v2Start = +new Date(); @@ -155,9 +149,6 @@ export class SorService { v1Time: number, v2Time: number, ) { - // await publishMetric(chain, `SOR_VALID_V1`, v1.isValid ? 1 : 0); - // await publishMetric(chain, `SOR_VALID_V2`, v2.isValid ? 1 : 0); - if (!version) return; let v1ResultAmount = v1.inputAmount; @@ -187,10 +178,6 @@ export class SorService { let diff = bn(diffN.toFixed(decimals), decimals); let bestResultAmount = version === 'V1' ? v1ResultAmount : v2ResultAmount; - // await publishMetric(chain, `SOR_TIME_V1`, v1Time); - // await publishMetric(chain, `SOR_TIME_V2`, v2Time); - // await publishMetric(chain, `SOR_V2_PERFORMACE`, v2Perf); - console.log( [ 'SOR_RESULT', diff --git a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts b/modules/sor/sorV1Balancer/sorV1Balancer.service.ts deleted file mode 100644 index f35d98914..000000000 --- a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts +++ /dev/null @@ -1,135 +0,0 @@ -import axios from 'axios'; -import * as Sentry from '@sentry/node'; -import { AddressZero } from '@ethersproject/constants'; -import { Contract } from '@ethersproject/contracts'; -import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse } from '../../../schema'; -import { GetSwapsInput, SwapService, SwapResult } from '../types'; -import { FundManagement, SwapTypes, SwapV2 } from '@balancer-labs/sdk'; -import { env } from '../../../app/env'; -import { AllNetworkConfigs, AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; -import { DeploymentEnv } from '../../network/network-config-types'; - -import VaultAbi from '../../pool/abi/Vault.json'; -import { BigNumber } from 'ethers'; -import { TokenAmount } from '@balancer/sdk'; -import { Chain } from '@prisma/client'; - -type CowSwapSwapType = 'buy' | 'sell'; - -class SwapResultV1 implements SwapResult { - public inputAmount: bigint = BigInt(0); - public outputAmount: bigint = BigInt(0); - public isValid: boolean; - - constructor(private swap: GqlCowSwapApiResponse | null, private swapType: GqlSorSwapType) { - if (swap === null) { - this.isValid = false; - this.swap = null; - } else { - this.inputAmount = swapType === 'EXACT_IN' ? BigInt(swap.swapAmount) : BigInt(swap.returnAmount); - this.outputAmount = swapType === 'EXACT_IN' ? BigInt(swap.returnAmount) : BigInt(swap.swapAmount); - this.isValid = swap.swaps.length === 0 ? false : true; - } - } - - async getCowSwapResponse(chain: Chain, queryFirst = false): Promise { - if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); - - if (queryFirst) { - const swapType = this.mapSwapType(this.swapType); - const deltas = await this.queryBatchSwap(swapType, this.swap.swaps, this.swap.tokenAddresses, chain); - const tokenInAmount = deltas[this.swap.tokenAddresses.indexOf(this.swap.tokenIn)].toString(); - const tokenOutAmount = deltas[this.swap.tokenAddresses.indexOf(this.swap.tokenOut)].abs().toString(); - // console.log(`UPDATE:`, this.inputAmount, this.outputAmount, tokenInAmount, tokenOutAmount, deltas.toString()); - return { - ...this.swap, - returnAmount: swapType === SwapTypes.SwapExactIn ? tokenOutAmount : tokenInAmount, - swapAmount: swapType === SwapTypes.SwapExactIn ? tokenInAmount : tokenOutAmount, - }; - } - return this.swap; - } - - async getBeetsSwapResponse(queryFirst: boolean): Promise { - throw new Error('Use Beets service.'); - } - - private queryBatchSwap(swapType: SwapTypes, swaps: SwapV2[], assets: string[], chain: Chain): Promise { - const vault = AllNetworkConfigsKeyedOnChain[chain].data.balancer.vault; - const provider = AllNetworkConfigsKeyedOnChain[chain].provider; - - const vaultContract = new Contract(vault, VaultAbi, provider); - const funds: FundManagement = { - sender: AddressZero, - recipient: AddressZero, - fromInternalBalance: false, - toInternalBalance: false, - }; - - return vaultContract.queryBatchSwap(swapType, swaps, assets, funds); - } - - private mapSwapType(swapType: GqlSorSwapType): SwapTypes { - return swapType === 'EXACT_IN' ? SwapTypes.SwapExactIn : SwapTypes.SwapExactOut; - } -} -export class SorV1BalancerService implements SwapService { - public async getSwapResult({ chain, tokenIn, tokenOut, swapType, swapAmount }: GetSwapsInput): Promise { - try { - const swap = await this.querySorBalancer(chain, swapType, tokenIn, tokenOut, swapAmount); - return new SwapResultV1(swap, swapType); - } catch (err: any) { - console.error( - `SOR_V1_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, - ); - Sentry.captureException(err.message, { - tags: { - service: 'sorV1', - tokenIn, - tokenOut, - swapAmount: swapAmount.amount, - swapType, - chain, - }, - }); - return new SwapResultV1(null, swapType); - } - } - - /** - * Query Balancer API CowSwap/SOR endpoint. - * @param swapType - * @param tokenIn - * @param tokenOut - * @param swapAmountScaled - * @param swapOptions - * @returns - */ - private async querySorBalancer( - chain: Chain, - swapType: GqlSorSwapType, - tokenIn: string, - tokenOut: string, - swapAmount: TokenAmount, - ): Promise { - const chainId = chainToIdMap[chain]; - const endPoint = `https://api.balancer.fi/sor/${chainId}`; - const gasPrice = AllNetworkConfigs[chainId].data.sor[env.DEPLOYMENT_ENV as DeploymentEnv].gasPrice.toString(); - const swapData = { - orderKind: this.mapSwapType(swapType), - sellToken: tokenIn, - buyToken: tokenOut, - amount: swapAmount.amount.toString(), - gasPrice, - }; - - const { data } = await axios.post(endPoint, swapData); - return data; - } - - private mapSwapType(swapType: GqlSorSwapType): CowSwapSwapType { - return swapType === 'EXACT_IN' ? 'sell' : 'buy'; - } -} - -export const sorV1BalancerService = new SorV1BalancerService(); diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts index 2c53582b9..4e10b55c8 100644 --- a/modules/sor/utils.ts +++ b/modules/sor/utils.ts @@ -22,6 +22,7 @@ export async function getTokenAmountRaw(tokenAddr: string, rawAmount: string, ch export const getToken = async (tokenAddr: string, chain: Chain): Promise => { const chainId = Number(chainToIdMap[chain]); + // also check for the polygon native asset if ( (tokenAddr === NATIVE_ADDRESS || tokenAddr === '0x0000000000000000000000000000000000001010') && chainId in NATIVE_ASSETS From 55fd61c234a4f78d72f4d6c5a97b39f40831ca16 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 11:03:59 +0100 Subject: [PATCH 158/175] query first for v2 --- modules/sor/sorV1Beets/sorV1Beets.service.ts | 8 ++--- modules/sor/sorV2/sorV2.service.ts | 34 +++++++++++++------- modules/sor/types.ts | 4 +-- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index e91744ec7..3b299996f 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -23,11 +23,11 @@ class SwapResultV1 implements SwapResult { } } - async getCowSwapResponse(chain = 'MAINNET', queryFirst = false): Promise { + async getCowSwapResponse(queryFirst = false): Promise { throw new Error('Use Balancer Service'); } - async getBeetsSwapResponse(queryFirst: boolean): Promise { + async getSorSwapResponse(queryFirst: boolean): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); // Beets service is already querying onchain return this.swap; @@ -42,7 +42,7 @@ export class SorV1BeetsService implements SwapService { public async getSwapResult(input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }): Promise { try { - const swap = await this.querySorBeets(input); + const swap = await this.querySorV1(input); return new SwapResultV1(swap, input.swapType); } catch (err) { console.log(`sorV1 Service Error`, err); @@ -59,7 +59,7 @@ export class SorV1BeetsService implements SwapService { return this.sorService.zeroResponse(swapType, tokenIn, tokenOut, formatEther(swapAmount.scale18)); } - private async querySorBeets( + private async querySorV1( input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }, ): Promise { const tokens = await tokenService.getTokens(); diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index fd442dfae..e747531b7 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -49,43 +49,53 @@ const ALL_BASEPOOLS_CACHE_KEY = `basePools:all`; class SwapResultV2 implements SwapResult { private swap: SwapSdk | null; + private chain: Chain; public inputAmount: bigint = BigInt(0); public outputAmount: bigint = BigInt(0); public isValid: boolean; - constructor(swap: SwapSdk | null) { + constructor(swap: SwapSdk | null, chain: Chain) { if (swap === null) { this.isValid = false; this.swap = null; + this.chain = chain; } else { this.isValid = true; this.swap = swap; this.inputAmount = swap.inputAmount.amount; this.outputAmount = swap.outputAmount.amount; + this.chain = chain; } } - async getCowSwapResponse(chain: Chain, queryFirst = false): Promise { + async getCowSwapResponse(queryFirst = false): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); if (!queryFirst) return this.mapResultToCowSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); else { - const rpcUrl = AllNetworkConfigsKeyedOnChain[chain].data.rpcUrl; - // Needs node >= 18 (https://github.com/wagmi-dev/viem/discussions/147) + const rpcUrl = AllNetworkConfigsKeyedOnChain[this.chain].data.rpcUrl; const updatedResult = await this.swap.query(rpcUrl); - // console.log(`UPDATE:`, this.swap.quote.amount.toString(), updatedResult.amount.toString()); - const ip = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; - const op = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; + const inputAmount = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; + const outputAmount = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; - return this.mapResultToCowSwap(this.swap, ip, op); + return this.mapResultToCowSwap(this.swap, inputAmount, outputAmount); } } - async getBeetsSwapResponse(queryFirst: boolean): Promise { + async getSorSwapResponse(queryFirst = false): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); - return await this.mapResultToBeetsSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); + if (!queryFirst) return this.mapResultToBeetsSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); + else { + const rpcUrl = AllNetworkConfigsKeyedOnChain[this.chain].data.rpcUrl; + const updatedResult = await this.swap.query(rpcUrl); + + const inputAmount = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; + const outputAmount = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; + + return this.mapResultToBeetsSwap(this.swap, inputAmount, outputAmount); + } } private async mapResultToBeetsSwap( @@ -308,7 +318,7 @@ export class SorV2Service implements SwapService { if (!swap && maxNonBoostedPathDepth < 4) { return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); } - return new SwapResultV2(swap); + return new SwapResultV2(swap, chain); } catch (err: any) { console.error( `SOR_V2_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, @@ -323,7 +333,7 @@ export class SorV2Service implements SwapService { chain, }, }); - return new SwapResultV2(null); + return new SwapResultV2(null, chain); } } diff --git a/modules/sor/types.ts b/modules/sor/types.ts index 564647c84..2059332ec 100644 --- a/modules/sor/types.ts +++ b/modules/sor/types.ts @@ -19,8 +19,8 @@ export interface GraphTraversalConfig { } export interface SwapResult { - getCowSwapResponse(chain: Chain, queryFirst: boolean): Promise; - getBeetsSwapResponse(queryFirst: boolean): Promise; + getCowSwapResponse(queryFirst: boolean): Promise; + getSorSwapResponse(queryFirst: boolean): Promise; isValid: boolean; outputAmount: bigint; inputAmount: bigint; From 1c1e46476e11d080a3229cc7a0ed8f4c31560be5 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 11:52:18 +0100 Subject: [PATCH 159/175] add polygon native address --- modules/sor/sorV1Beets/balancer-sor.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/sor/sorV1Beets/balancer-sor.service.ts b/modules/sor/sorV1Beets/balancer-sor.service.ts index 6cf8fefa7..19d5c2b3a 100644 --- a/modules/sor/sorV1Beets/balancer-sor.service.ts +++ b/modules/sor/sorV1Beets/balancer-sor.service.ts @@ -332,7 +332,11 @@ export class BalancerSorService { } private getTokenDecimals(tokenAddress: string, tokens: PrismaToken[]): number { - if (tokenAddress === ZERO_ADDRESS || tokenAddress === NATIVE_ADDRESS) { + if ( + tokenAddress === ZERO_ADDRESS || + tokenAddress === NATIVE_ADDRESS || + tokenAddress === '0x0000000000000000000000000000000000001010' + ) { return 18; } From 0c9d1989ef819821ec2e5fbf5102479dffc21d7e Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 15 Dec 2023 12:17:08 +0100 Subject: [PATCH 160/175] isolate networkContext in the getters --- .../lib/user-sync-gauge-balance.service.ts | 89 +++++++++++-------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/modules/user/lib/user-sync-gauge-balance.service.ts b/modules/user/lib/user-sync-gauge-balance.service.ts index 93dd0efb8..9c99c4f60 100644 --- a/modules/user/lib/user-sync-gauge-balance.service.ts +++ b/modules/user/lib/user-sync-gauge-balance.service.ts @@ -14,6 +14,30 @@ import { gaugeSubgraphService } from '../../subgraphs/gauge-subgraph/gauge-subgr import { AddressZero } from '@ethersproject/constants'; export class UserSyncGaugeBalanceService implements UserStakedBalanceService { + get chain() { + return networkContext.chain; + } + + get chainId() { + return networkContext.chainId; + } + + get provider() { + return networkContext.provider; + } + + get rpcUrl() { + return networkContext.data.rpcUrl; + } + + get rpcMaxBlockRange() { + return networkContext.data.rpcMaxBlockRange; + } + + get multicallAddress() { + return networkContext.data.multicall; + } + public async initStakedBalances(stakingTypes: PrismaPoolStakingType[]): Promise { if (!stakingTypes.includes('GAUGE')) { return; @@ -25,7 +49,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { console.log('initStakedBalances: loading pools...'); const pools = await prisma.prismaPool.findMany({ select: { id: true }, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); const filteredGaugeShares = gaugeShares.filter((share) => { @@ -45,14 +69,14 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { data: userAddresses.map((userAddress) => ({ address: userAddress })), skipDuplicates: true, }), - prisma.prismaUserStakedBalance.deleteMany({ where: { chain: networkContext.chain } }), + prisma.prismaUserStakedBalance.deleteMany({ where: { chain: this.chain } }), prisma.prismaUserStakedBalance.createMany({ data: filteredGaugeShares.map((share) => { const pool = pools.find((pool) => pool.id === share.gauge.poolId); return { id: `${share.gauge.id}-${share.user.id}`, - chain: networkContext.chain, + chain: this.chain, balance: share.balance, balanceNum: parseFloat(share.balance), userAddress: share.user.id, @@ -63,8 +87,8 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { }), }), prisma.prismaUserBalanceSyncStatus.upsert({ - where: { type_chain: { type: 'STAKED', chain: networkContext.chain } }, - create: { type: 'STAKED', chain: networkContext.chain, blockNumber: block.number }, + where: { type_chain: { type: 'STAKED', chain: this.chain } }, + create: { type: 'STAKED', chain: this.chain, blockNumber: block.number }, update: { blockNumber: block.number }, }), ], @@ -77,7 +101,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { public async syncChangedStakedBalances(): Promise { // we always store the latest synced block const status = await prisma.prismaUserBalanceSyncStatus.findUnique({ - where: { type_chain: { type: 'STAKED', chain: networkContext.chain } }, + where: { type_chain: { type: 'STAKED', chain: this.chain } }, }); if (!status) { @@ -86,12 +110,12 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { const pools = await prisma.prismaPool.findMany({ include: { staking: true }, - where: { chain: networkContext.chain }, + where: { chain: this.chain }, }); - console.log(`user-sync-staked-balances-${networkContext.chainId} got data from db.`); + console.log(`user-sync-staked-balances-${this.chainId} got data from db.`); - const latestBlock = await networkContext.provider.getBlockNumber(); - console.log(`user-sync-staked-balances-${networkContext.chainId} got latest block.`); + const latestBlock = await this.provider.getBlockNumber(); + console.log(`user-sync-staked-balances-${this.chainId} got latest block.`); const gaugeAddresses = await gaugeSubgraphService.getAllGaugeAddresses(); @@ -111,18 +135,15 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { const erc20Interface = new ethers.utils.Interface(ERC20Abi); // Split the range into smaller chunks to avoid RPC limits, setting up to 50 times max block range - const maxBlockRange = networkContext.data.rpcMaxBlockRange; - const toBlock = Math.min(startBlock + 50 * maxBlockRange, latestBlock); + const toBlock = Math.min(startBlock + 50 * this.rpcMaxBlockRange, latestBlock); const range = toBlock - startBlock; - console.log(`user-sync-staked-balances-${networkContext.chainId} block range from ${startBlock} to ${toBlock}`); - console.log( - `user-sync-staked-balances-${networkContext.chainId} getLogs for ${_.uniq(gaugeAddresses).length} gauges.`, - ); + console.log(`user-sync-staked-balances-${this.chainId} block range from ${startBlock} to ${toBlock}`); + console.log(`user-sync-staked-balances-${this.chainId} getLogs for ${_.uniq(gaugeAddresses).length} gauges.`); const events = await Promise.all( // Getting logs in batches of max blocks allowed by RPC - Array.from({ length: Math.ceil(range / maxBlockRange) }, (_, i) => i).map(async (i) => { - const from = startBlock + i * maxBlockRange; - const to = Math.min(startBlock + (i + 1) * maxBlockRange, toBlock); + Array.from({ length: Math.ceil(range / this.rpcMaxBlockRange) }, (_, i) => i).map(async (i) => { + const from = startBlock + i * this.rpcMaxBlockRange; + const to = Math.min(startBlock + (i + 1) * this.rpcMaxBlockRange, toBlock); // Usually RPCs are handling any number of addresses, but it here batching just to be on the safe side const logRequests: Promise[] = _.chunk(gaugeAddresses, 500).map((addresses) => { @@ -141,7 +162,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { ], }; - return fetch(networkContext.data.rpcUrl, { + return fetch(this.rpcUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -162,9 +183,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { }), ).then((res) => res.flat().filter((event) => event)); - console.log( - `user-sync-staked-balances-${networkContext.chainId} getLogs for ${gaugeAddresses.length} gauges done`, - ); + console.log(`user-sync-staked-balances-${this.chainId} getLogs for ${gaugeAddresses.length} gauges done`); const balancesToFetch = _.uniqBy( events @@ -180,13 +199,11 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { (entry) => entry.erc20Address + entry.userAddress, ); - console.log( - `user-sync-staked-balances-${networkContext.chainId} got ${balancesToFetch.length} balances to fetch.`, - ); + console.log(`user-sync-staked-balances-${this.chainId} got ${balancesToFetch.length} balances to fetch.`); if (balancesToFetch.length === 0) { await prisma.prismaUserBalanceSyncStatus.update({ - where: { type_chain: { type: 'STAKED', chain: networkContext.chain } }, + where: { type_chain: { type: 'STAKED', chain: this.chain } }, data: { blockNumber: endBlock }, }); @@ -194,14 +211,12 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { } const balances = await Multicaller.fetchBalances({ - multicallAddress: networkContext.data.multicall, - provider: networkContext.provider, + multicallAddress: this.multicallAddress, + provider: this.provider, balancesToFetch, }); - console.log( - `user-sync-staked-balances-${networkContext.chainId} got ${balancesToFetch.length} balances to fetch done.`, - ); + console.log(`user-sync-staked-balances-${this.chainId} got ${balancesToFetch.length} balances to fetch done.`); await prismaBulkExecuteOperations( [ @@ -220,7 +235,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { where: { id_chain: { id: `${userBalance.erc20Address}-${userBalance.userAddress}`, - chain: networkContext.chain, + chain: this.chain, }, }, update: { @@ -229,7 +244,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { }, create: { id: `${userBalance.erc20Address}-${userBalance.userAddress}`, - chain: networkContext.chain, + chain: this.chain, balance: formatFixed(userBalance.balance, 18), balanceNum: parseFloat(formatFixed(userBalance.balance, 18)), userAddress: userBalance.userAddress, @@ -243,7 +258,7 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { where: { type_chain: { type: 'STAKED', - chain: networkContext.chain, + chain: this.chain, }, }, data: { blockNumber: endBlock }, @@ -259,14 +274,14 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { const amount = formatFixed(balance, 18); await prisma.prismaUserStakedBalance.upsert({ - where: { id_chain: { id: `${staking.address}-${userAddress}`, chain: networkContext.chain } }, + where: { id_chain: { id: `${staking.address}-${userAddress}`, chain: this.chain } }, update: { balance: amount, balanceNum: parseFloat(amount), }, create: { id: `${staking.address}-${userAddress}`, - chain: networkContext.chain, + chain: this.chain, balance: amount, balanceNum: parseFloat(amount), userAddress: userAddress, From e475429d1f21b5b7dcc5c585a425ab8073847205 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 14:15:24 +0100 Subject: [PATCH 161/175] decimals for native asset --- modules/sor/sor.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 33bd006a7..dc5ac98ce 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -47,7 +47,6 @@ export class SorService { } private async getSwap(input: GetSwapsInput, v1Service: SwapService = sorV1BalancerService) { - console.log(`Running SOR for ${input.swapAmount} ${input.tokenIn} > ${input.tokenOut}`); const v1Start = +new Date(); const swapV1 = await v1Service.getSwapResult(input); const v1Time = +new Date() - v1Start; @@ -144,7 +143,7 @@ export class SorService { const fp = (a: bigint, d: number) => Number(formatUnits(String(a), d)); const bn = (a: string, d: number) => BigInt(String(parseUnits(a, d))); const prismaToken = await tokenService.getToken(resultToken, chain); - const decimals = prismaToken!.decimals; + const decimals = prismaToken ? prismaToken.decimals : 18; // most probably native asset let v2Perf = version === 'V1' ? 1 - fp(v1ResultAmount, decimals) / fp(v2ResultAmount, decimals) // negative perf means V1 is better From 05c0e45abc946b7927f310d17040d77e059a5da9 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 14:22:49 +0100 Subject: [PATCH 162/175] bump bsdk --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9be908911..ce3785b5f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@aws-sdk/client-secrets-manager": "^3.195.0", "@aws-sdk/client-sqs": "^3.137.0", "@balancer-labs/sdk": "github:beethovenxfi/balancer-sdk#beethovenx-master", - "@balancer/sdk": "^0.4.0", + "@balancer/sdk": "^0.5.0", "@ethersproject/address": "^5.6.0", "@ethersproject/bignumber": "^5.6.0", "@ethersproject/constants": "^5.6.0", diff --git a/yarn.lock b/yarn.lock index 3374ba4bd..1f593b651 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,10 +2456,10 @@ graphology "^0.24.1" isomorphic-fetch "^2.2.1" -"@balancer/sdk@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@balancer/sdk/-/sdk-0.4.0.tgz#9fe762e1b7f6cad574a1603bdc33e77c6e1ab426" - integrity sha512-g8ilzNSlk2Pdh7PUAyr88sD78GGDTOUJUpAhKBO5dN77Kk70ivyL6mNJBGpJqrjraoBygv6yQukV8yO5m0kiEw== +"@balancer/sdk@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@balancer/sdk/-/sdk-0.5.0.tgz#098b2fd019a0460c2ad13c20f1f6a058573a08d2" + integrity sha512-2ys18xZAfGvwJpQmStmX2O6b/QkIQQWHpm09/qk2oHDg/lhnYYY5otfs14QuEQubdlA3Kth0U0uAxMlc4NxN/w== dependencies: async-retry "^1.3.3" decimal.js-light "^2.5.1" From 52a4c53c01443dc0afb3c605b0e91e235fd03187 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 18 Dec 2023 13:15:18 +0100 Subject: [PATCH 163/175] change to new balancer subgraph code --- modules/network/fantom.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index ca6112e98..486301112 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -39,7 +39,7 @@ const fantomNetworkData: NetworkData = { }, subgraphs: { startDate: '2021-10-08', - balancer: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx', + balancer: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx-v2-fantom', beetsBar: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beets-bar', blocks: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/fantom-blocks', masterchef: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2', @@ -349,7 +349,10 @@ export const fantomNetworkConfig: NetworkConfig = { new UserSyncReliquaryFarmBalanceService(fantomNetworkData.reliquary!.address), ], services: { - balancerSubgraphService: new BalancerSubgraphService(fantomNetworkData.subgraphs.balancer, fantomNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + fantomNetworkData.subgraphs.balancer, + fantomNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. From 1612fe7af51cb15fe5423fd3473214f0c14bdd05 Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 18 Dec 2023 15:33:17 +0100 Subject: [PATCH 164/175] add field to all pools, fix error --- modules/pool/lib/pool-gql-loader.service.ts | 26 ++++----------------- modules/pool/pool.gql | 16 +++++++++++++ modules/pool/pool.resolvers.ts | 2 +- modules/pool/pool.service.ts | 4 +--- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index c0671a828..a2b366265 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -30,7 +30,6 @@ import { GqlPoolUserBalance, GqlPoolWithdrawConfig, GqlPoolWithdrawOption, - InputMaybe, QueryPoolGetPoolsArgs, } from '../../../schema'; import { isSameAddress } from '@balancer-labs/sdk'; @@ -45,7 +44,7 @@ import { parseUnits } from 'ethers/lib/utils'; import { formatFixed } from '@ethersproject/bignumber'; export class PoolGqlLoaderService { - public async getPool(id: string, chain: Chain, userAddress?: InputMaybe): Promise { + public async getPool(id: string, chain: Chain, userAddress?: string): Promise { let pool = undefined; pool = await prisma.prismaPool.findUnique({ where: { id_chain: { id, chain: chain } }, @@ -63,7 +62,7 @@ export class PoolGqlLoaderService { throw new Error('Pool exists, but has an unknown type'); } - return this.mapPoolToGqlPool(pool); + return this.mapPoolToGqlPool(pool, pool.userWalletBalances, pool.userStakedBalances); } public async getPools(args: QueryPoolGetPoolsArgs): Promise { @@ -82,24 +81,7 @@ export class PoolGqlLoaderService { ...this.mapQueryArgsToPoolQuery(args), include: { ...prismaPoolMinimal.include, - userWalletBalances: { - where: { - userAddress: { - equals: args.where?.userAddress, - mode: 'insensitive' as const, - }, - balanceNum: { gt: 0 }, - }, - }, - userStakedBalances: { - where: { - userAddress: { - equals: args.where?.userAddress, - mode: 'insensitive' as const, - }, - balanceNum: { gt: 0 }, - }, - }, + ...this.getUserBalancesInclude(args.where.userAddress), }, }); @@ -1267,7 +1249,7 @@ export class PoolGqlLoaderService { }; } - private getUserBalancesInclude(userAddress?: InputMaybe) { + private getUserBalancesInclude(userAddress?: string) { if (!userAddress) { return {}; } diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index d7046df63..f1b047f04 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -232,6 +232,8 @@ type GqlPoolWeighted implements GqlPoolBase { staking: GqlPoolStaking type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolGyro implements GqlPoolBase { @@ -274,6 +276,8 @@ type GqlPoolGyro implements GqlPoolBase { staking: GqlPoolStaking type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolLiquidityBootstrapping implements GqlPoolBase { @@ -297,6 +301,8 @@ type GqlPoolLiquidityBootstrapping implements GqlPoolBase { staking: GqlPoolStaking type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolStable implements GqlPoolBase { @@ -320,6 +326,8 @@ type GqlPoolStable implements GqlPoolBase { staking: GqlPoolStaking type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolMetaStable implements GqlPoolBase { @@ -343,6 +351,8 @@ type GqlPoolMetaStable implements GqlPoolBase { staking: GqlPoolStaking type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolPhantomStable implements GqlPoolBase { @@ -367,6 +377,8 @@ type GqlPoolPhantomStable implements GqlPoolBase { bptPriceRate: BigDecimal! type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolElement implements GqlPoolBase { @@ -393,6 +405,8 @@ type GqlPoolElement implements GqlPoolBase { staking: GqlPoolStaking type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolLinear implements GqlPoolBase { @@ -421,6 +435,8 @@ type GqlPoolLinear implements GqlPoolBase { bptPriceRate: BigDecimal! type: String! version: Int! + + userBalance: GqlPoolUserBalance } type GqlPoolLinearNested { diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 8acf48937..c6478733d 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -14,7 +14,7 @@ const balancerResolvers: Resolvers = { } else if (!chain) { throw new Error('poolGetPool error: Provide "chain" param'); } - return poolService.getGqlPool(id, chain, userAddress); + return poolService.getGqlPool(id, chain, userAddress ? userAddress : undefined); }, poolGetPools: async (parent, args, context) => { return poolService.getGqlPools(args); diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 1ded9c694..7a6d7da27 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -13,8 +13,6 @@ import { GqlPoolMinimal, GqlPoolSnapshotDataRange, GqlPoolUnion, - GqlPoolUserSwapVolume, - InputMaybe, QueryPoolGetBatchSwapsArgs, QueryPoolGetJoinExitsArgs, QueryPoolGetPoolsArgs, @@ -74,7 +72,7 @@ export class PoolService { return networkContext.services.balancerSubgraphService; } - public async getGqlPool(id: string, chain: GqlChain, userAddress?: InputMaybe): Promise { + public async getGqlPool(id: string, chain: GqlChain, userAddress?: string): Promise { return this.poolGqlLoaderService.getPool(id, chain, userAddress); } From 93125b9a36ee490f0d87b58ccdd816b428e205b4 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Mon, 18 Dec 2023 16:07:53 +0100 Subject: [PATCH 165/175] fix: use DB instead of slow subgraph --- modules/user/lib/user-sync-gauge-balance.service.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/user/lib/user-sync-gauge-balance.service.ts b/modules/user/lib/user-sync-gauge-balance.service.ts index 9c99c4f60..04b79bd9e 100644 --- a/modules/user/lib/user-sync-gauge-balance.service.ts +++ b/modules/user/lib/user-sync-gauge-balance.service.ts @@ -117,7 +117,13 @@ export class UserSyncGaugeBalanceService implements UserStakedBalanceService { const latestBlock = await this.provider.getBlockNumber(); console.log(`user-sync-staked-balances-${this.chainId} got latest block.`); - const gaugeAddresses = await gaugeSubgraphService.getAllGaugeAddresses(); + // Get gauge addresses + const gaugeAddresses = ( + await prisma.prismaPoolStakingGauge.findMany({ + select: { gaugeAddress: true }, + where: { chain: this.chain }, + }) + ).map((gauge) => gauge.gaugeAddress); // we sync at most 10k blocks at a time const startBlock = status.blockNumber + 1; From 99159390053b425dfe1443fef4f09f381865441e Mon Sep 17 00:00:00 2001 From: franz Date: Mon, 18 Dec 2023 17:15:09 +0100 Subject: [PATCH 166/175] change fantom rpc --- modules/network/fantom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 486301112..e0efc4c8b 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -96,8 +96,8 @@ const fantomNetworkData: NetworkData = { }, rpcUrl: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' - ? `https://rpc.fantom.gateway.fm` - : `https://rpc.fantom.network`, + ? `https://rpc.ankr.com/fantom` + : `https://rpc.fantom.gateway.fm`, rpcMaxBlockRange: 1000, sanity: { projectId: '1g2ag2hb', From 80c3ea12d1349f06233150250631e86e2567dc71 Mon Sep 17 00:00:00 2001 From: franz Date: Tue, 19 Dec 2023 16:20:37 +0100 Subject: [PATCH 167/175] add composable type to supported types --- modules/pool/lib/pool-on-chain-data.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 2a0e70d9d..3957329eb 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -15,6 +15,7 @@ const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ 'STABLE', 'META_STABLE', 'PHANTOM_STABLE', + 'COMPOSABLE_STABLE', 'LINEAR', 'LIQUIDITY_BOOTSTRAPPING', 'ELEMENT', From a815e8c53dead366eb73ef84f310abfe878873ca Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:10:39 +0100 Subject: [PATCH 168/175] add composable type --- modules/sor/sorV2/sorV2.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index e747531b7..b972462b7 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -521,6 +521,8 @@ export class SorV2Service implements SwapService { case PrismaPoolType.PHANTOM_STABLE: // Composablestables are PHANTOM_STABLE in Prisma. b-sdk treats Phantoms as ComposableStable. return 'ComposableStable'; + case PrismaPoolType.COMPOSABLE_STABLE: + return 'ComposableStable'; case PrismaPoolType.GYRO: return 'Gyro2'; case PrismaPoolType.GYRO3: From c812f68659e4b89186307c6aefb3961cb3e076c2 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:16:52 +0100 Subject: [PATCH 169/175] only use pools with more th $50 liquidity --- modules/sor/sorV2/sorV2.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index b972462b7..2c983445d 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -364,6 +364,9 @@ export class SorV2Service implements SwapService { gt: 0.000000000001, }, swapEnabled: true, + totalLiquidity: { + gt: 50, + }, }, id: { notIn: [...poolIdsToExclude, ...poolsToIgnore], From 5a777bb8a613310e9797a617f99fffb519efe064 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:20:12 +0100 Subject: [PATCH 170/175] increase one hop --- modules/sor/sorV2/sorV2.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 2c983445d..56caa5bb4 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -290,7 +290,7 @@ export class SorV2Service implements SwapService { public async getSwapResult( { chain, tokenIn, tokenOut, swapType, swapAmount, graphTraversalConfig }: GetSwapsInput, - maxNonBoostedPathDepth = 3, + maxNonBoostedPathDepth = 4, ): Promise { try { const poolsFromDb = await this.getBasePools(chain); From 27ca9aa88e799a781a03b727879985c501879d47 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:51:22 +0100 Subject: [PATCH 171/175] remove unused cowswap fields --- modules/sor/constants.ts | 11 +++++------ modules/sor/sor.gql | 4 ---- modules/sor/sorV2/sorV2.service.ts | 4 ---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/modules/sor/constants.ts b/modules/sor/constants.ts index 0fa69e5be..8c73cdc23 100644 --- a/modules/sor/constants.ts +++ b/modules/sor/constants.ts @@ -1,14 +1,14 @@ import { TokenAmount } from '@balancer/sdk'; import { GqlCowSwapApiResponse, GqlSorSwapType } from '../../schema'; -export const EMPTY_COWSWAP_RESPONSE = (assetIn: string, assetOut: string, amount: TokenAmount): GqlCowSwapApiResponse => { +export const EMPTY_COWSWAP_RESPONSE = ( + assetIn: string, + assetOut: string, + amount: TokenAmount, +): GqlCowSwapApiResponse => { return { - marketSp: '0', returnAmount: '0', - returnAmountConsideringFees: '0', - returnAmountFromSwaps: '0', swapAmount: amount.amount.toString(), - swapAmountForSwaps: '0', swaps: [], tokenAddresses: [], tokenIn: assetIn, @@ -352,4 +352,3 @@ export const poolsToIgnore = [ '0xbfd65c6160cfd638a85c645e6e6d8acac5dac935000000000000000000000004', '0xe274c9deb6ed34cfe4130f8d0a8a948dea5bb28600000000000000000000000d', ]; - diff --git a/modules/sor/sor.gql b/modules/sor/sor.gql index 716cf02d2..9bee51655 100644 --- a/modules/sor/sor.gql +++ b/modules/sor/sor.gql @@ -25,13 +25,9 @@ type GqlCowSwapApiResponse { tokenAddresses: [String!]! swaps: [GqlSwap!]! swapAmount: String! - swapAmountForSwaps: String! returnAmount: String! - returnAmountFromSwaps: String! - returnAmountConsideringFees: String! tokenIn: String! tokenOut: String! - marketSp: String! } type GqlSwap { diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 56caa5bb4..3f0ff71f7 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -267,12 +267,8 @@ class SwapResultV2 implements SwapResult { const swapAmount = swap.swapKind === SwapKind.GivenIn ? inputAmount.amount.toString() : outputAmount.amount.toString(); return { - marketSp: '', // CowSwap is not using this field, confirmed. returnAmount, - returnAmountConsideringFees: returnAmount, // CowSwap is not using this field, confirmed. - returnAmountFromSwaps: returnAmount, // CowSwap is not using this field, confirmed. swapAmount, - swapAmountForSwaps: swapAmount, // CowSwap is not using this field, confirmed. swaps, tokenAddresses: swap.assets, tokenIn: swap.inputAmount.token.address, From 18c0ec9efcc0fc7df72194018d403276aa4b841b Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:47:07 +0100 Subject: [PATCH 172/175] sync-latest-fx-prices --- modules/network/avalanche.ts | 4 ++ modules/network/mainnet.ts | 4 ++ modules/network/optimism.ts | 5 +- modules/network/polygon.ts | 4 ++ modules/pool/pool.prisma | 1 + .../balancer-subgraph-queries.graphql | 4 ++ modules/token/latest-fx-price.ts | 49 +++++++++++++++++++ modules/token/token.gql | 1 + modules/token/token.resolvers.ts | 10 ++++ .../migration.sql | 2 + prisma/schema.prisma | 1 + worker/job-handlers.ts | 16 ++++++ 12 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 modules/token/latest-fx-price.ts create mode 100644 prisma/migrations/20231215151625_latest_fx_price/migration.sql diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 038f2788d..8a543c455 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -346,5 +346,9 @@ export const avalancheNetworkConfig: NetworkConfig = { name: 'feed-data-to-datastudio', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), }, + { + name: 'sync-latest-fx-prices', + interval: every(10, 'minutes'), + }, ], }; diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index fe2fd1676..8592b1739 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -509,5 +509,9 @@ export const mainnetNetworkConfig: NetworkConfig = { name: 'feed-data-to-datastudio', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), }, + { + name: 'sync-latest-fx-prices', + interval: every(10, 'minutes'), + }, ], }; diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index c0f3beccb..f104162b8 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -298,7 +298,10 @@ export const optimismNetworkConfig: NetworkConfig = { ], userStakedBalanceServices: [new UserSyncGaugeBalanceService()], services: { - balancerSubgraphService: new BalancerSubgraphService(optimismNetworkData.subgraphs.balancer, optimismNetworkData.chain.id), + balancerSubgraphService: new BalancerSubgraphService( + optimismNetworkData.subgraphs.balancer, + optimismNetworkData.chain.id, + ), }, /* For sub-minute jobs we set the alarmEvaluationPeriod and alarmDatapointsToAlarm to 1 instead of the default 3. diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index 2420ce629..357f35df8 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -391,5 +391,9 @@ export const polygonNetworkConfig: NetworkConfig = { name: 'feed-data-to-datastudio', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(1, 'minutes'), }, + { + name: 'sync-latest-fx-prices', + interval: every(10, 'minutes'), + }, ], }; diff --git a/modules/pool/pool.prisma b/modules/pool/pool.prisma index 6781f3727..a03ce9e54 100644 --- a/modules/pool/pool.prisma +++ b/modules/pool/pool.prisma @@ -229,6 +229,7 @@ model PrismaPoolTokenDynamicData { balanceUSD Float weight String? priceRate String + latestFxPrice Float? } model PrismaPoolSwap { diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql index f154888d1..bdf1a2171 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql @@ -141,6 +141,7 @@ fragment BalancerToken on Token { id symbol address + latestFXPrice latestUSDPrice totalVolumeNotional totalVolumeUSD @@ -213,6 +214,9 @@ fragment BalancerPoolToken on PoolToken { weight priceRate index + token { + latestFXPrice + } } query BalancerPools( diff --git a/modules/token/latest-fx-price.ts b/modules/token/latest-fx-price.ts new file mode 100644 index 000000000..2eb090661 --- /dev/null +++ b/modules/token/latest-fx-price.ts @@ -0,0 +1,49 @@ +import { GraphQLClient } from 'graphql-request'; +import { getSdk } from '../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; +import { prisma } from '../../prisma/prisma-client'; +import { Chain } from '@prisma/client'; + +/** + * 'Latest FX Price' is relevant only to FX pools. It is sourced from offchain platforms, like Chainlink. + * The subgraph actively indexes this price by listening to the 'Answer Updated' events emitted by Chainlink oracles. + * For reference and more details, see the code at: + * https://github.com/balancer/balancer-subgraph-v2/blob/master/src/mappings/pricing.ts#L373 + * + * Note: 'LatestFXPrice' is a dependency of SORv2. + */ +export const syncLatestFXPrices = async (subgraphUrl: string, chain: Chain) => { + const { pools } = await fetchFxPools(subgraphUrl); + + for (const pool of pools) { + const { tokens } = pool; + if (!tokens) continue; + + for (const token of tokens) { + try { + await prisma.prismaPoolTokenDynamicData.update({ + where: { + id_chain: { + id: token.id, + chain, + }, + }, + data: { + latestFxPrice: token.token.latestFXPrice ? parseFloat(token.token.latestFXPrice) : undefined, + }, + }); + } catch (e) { + console.error(`Error updating latest FX price for token ${token.id} on chain ${chain}: ${e}`); + } + } + } + + return true; +}; + +const fetchFxPools = (subgraphUrl: string) => { + const sdk = getSdk(new GraphQLClient(subgraphUrl)); + + return sdk.BalancerPools({ + where: { poolType: 'FX' }, + }); +}; diff --git a/modules/token/token.gql b/modules/token/token.gql index 5481e271b..3adbb8db1 100644 --- a/modules/token/token.gql +++ b/modules/token/token.gql @@ -20,6 +20,7 @@ extend type Mutation { tokenReloadTokenPrices: Boolean tokenSyncTokenDefinitions: String! tokenSyncTokenDynamicData: String! + tokenSyncLatestFxPrices(chain: GqlChain!): String! tokenInitChartData(tokenAddress: String!): String! tokenDeletePrice(tokenAddress: String!, timestamp: Int!): Boolean! tokenDeleteTokenType(tokenAddress: String!, type: GqlTokenType!): String! diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index 8bc79db4c..7554d5c9c 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -3,6 +3,8 @@ import _ from 'lodash'; import { isAdminRoute } from '../auth/auth-context'; import { tokenService } from './token.service'; import { headerChain } from '../context/header-chain'; +import { syncLatestFXPrices } from './latest-fx-price'; +import { AllNetworkConfigsKeyedOnChain } from '../network/network-config'; const resolvers: Resolvers = { Query: { @@ -137,6 +139,14 @@ const resolvers: Resolvers = { return 'success'; }, + tokenSyncLatestFxPrices: async (parent, { chain }, context) => { + isAdminRoute(context); + const subgraphUrl = AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer; + + await syncLatestFXPrices(subgraphUrl, chain); + + return 'success'; + }, tokenInitChartData: async (parent, { tokenAddress }, context) => { isAdminRoute(context); diff --git a/prisma/migrations/20231215151625_latest_fx_price/migration.sql b/prisma/migrations/20231215151625_latest_fx_price/migration.sql new file mode 100644 index 000000000..3f97ae378 --- /dev/null +++ b/prisma/migrations/20231215151625_latest_fx_price/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "PrismaPoolTokenDynamicData" ADD COLUMN "latestFxPrice" DOUBLE PRECISION; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6e3103627..3dc68f074 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -275,6 +275,7 @@ model PrismaPoolTokenDynamicData { balanceUSD Float weight String? priceRate String + latestFxPrice Float? } model PrismaPoolSwap { diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 9b6102541..d2b3d2143 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -14,6 +14,8 @@ import { veBalVotingListService } from '../modules/vebal/vebal-voting-list.servi import { cronsMetricPublisher } from '../modules/metrics/metrics.client'; import moment from 'moment'; import { cronsDurationMetricPublisher } from '../modules/metrics/cron-duration-metrics.client'; +import { syncLatestFXPrices } from '../modules/token/latest-fx-price'; +import { AllNetworkConfigs } from '../modules/network/network-config'; const runningJobs: Set = new Set(); @@ -278,6 +280,20 @@ export function configureWorkerRoutes(app: Express) { next, ); break; + case 'sync-latest-fx-prices': + await runIfNotAlreadyRunning( + job.name, + chainId, + () => { + const config = AllNetworkConfigs[chainId].data; + const subgraphUrl = config.subgraphs.balancer; + const chain = config.chain.prismaId; + return syncLatestFXPrices(subgraphUrl, chain); + }, + res, + next, + ); + break; default: res.sendStatus(400); throw new Error(`Unhandled job type ${job.name}`); From 8ccf9d5d1488c2db1ca74b4916cf844333b00237 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:50:48 +0100 Subject: [PATCH 173/175] include FX pools in the SOR --- modules/sor/sorV2/sorV2.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index a3b96fc2e..733d8f659 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -365,7 +365,6 @@ export class SorV2Service implements SwapService { 'ELEMENT', // not supported by b-sdk 'UNKNOWN', // not supported by b-sdk 'INVESTMENT', // not supported by b-sdk - 'FX', // TODO: FX pool tokens are missing latestFXPrice - needs to be added to the DB ], }, AND: { From 1c38aabadc238fc8bfd08fdd283637b698041dfb Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 16:13:47 +0100 Subject: [PATCH 174/175] can go one level deeper than maxdepth, also retry on no candidate paths --- modules/sor/sorV2/sorV2.service.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 0c8f72f6d..d78eeecf1 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -288,6 +288,7 @@ export class SorV2Service implements SwapService { { chain, tokenIn, tokenOut, swapType, swapAmount, graphTraversalConfig }: GetSwapsInput, maxNonBoostedPathDepth = 4, ): Promise { + const MAX_INCREASED_PATH_DEPTH = maxNonBoostedPathDepth + 1; try { const poolsFromDb = await this.getBasePools(chain); const tIn = await getToken(tokenIn as Address, chain); @@ -311,11 +312,17 @@ export class SorV2Service implements SwapService { maxNonBoostedPathDepth, ); const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, config); - if (!swap && maxNonBoostedPathDepth < 4) { + if (!swap && maxNonBoostedPathDepth < MAX_INCREASED_PATH_DEPTH) { return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); } return new SwapResultV2(swap, chain); } catch (err: any) { + if ( + err.message.includes('No potential swap paths provided') && + maxNonBoostedPathDepth < MAX_INCREASED_PATH_DEPTH + ) { + return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); + } console.error( `SOR_V2_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, ); From 349f69ac9004119c319568a4aeb7100f17523ee7 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 16:26:31 +0100 Subject: [PATCH 175/175] remove 7d metrics --- modules/protocol/protocol.gql | 4 ---- modules/protocol/protocol.service.ts | 17 ----------------- 2 files changed, 21 deletions(-) diff --git a/modules/protocol/protocol.gql b/modules/protocol/protocol.gql index c9af32d44..f1a2f2d8c 100644 --- a/modules/protocol/protocol.gql +++ b/modules/protocol/protocol.gql @@ -16,8 +16,6 @@ type GqlProtocolMetricsAggregated { swapFee24h: BigDecimal! swapVolume24h: BigDecimal! yieldCapture24h: BigDecimal! - swapFee7d: BigDecimal! - swapVolume7d: BigDecimal! numLiquidityProviders: BigInt! chains: [GqlProtocolMetricsChain!]! } @@ -31,8 +29,6 @@ type GqlProtocolMetricsChain { swapFee24h: BigDecimal! swapVolume24h: BigDecimal! yieldCapture24h: BigDecimal! - swapFee7d: BigDecimal! - swapVolume7d: BigDecimal! numLiquidityProviders: BigInt! } diff --git a/modules/protocol/protocol.service.ts b/modules/protocol/protocol.service.ts index 45af70a3f..0dc5cf39a 100644 --- a/modules/protocol/protocol.service.ts +++ b/modules/protocol/protocol.service.ts @@ -40,8 +40,6 @@ export class ProtocolService { const swapVolume24h = _.sumBy(chainMetrics, (metrics) => parseFloat(metrics.swapVolume24h)); const swapFee24h = _.sumBy(chainMetrics, (metrics) => parseFloat(metrics.swapFee24h)); const yieldCapture24h = _.sumBy(chainMetrics, (metrics) => parseFloat(metrics.yieldCapture24h)); - const swapVolume7d = _.sumBy(chainMetrics, (metrics) => parseFloat(metrics.swapVolume7d)); - const swapFee7d = _.sumBy(chainMetrics, (metrics) => parseFloat(metrics.swapFee7d)); const numLiquidityProviders = _.sumBy(chainMetrics, (metrics) => parseInt(metrics.numLiquidityProviders)); return { @@ -52,8 +50,6 @@ export class ProtocolService { swapVolume24h: `${swapVolume24h}`, swapFee24h: `${swapFee24h}`, yieldCapture24h: `${yieldCapture24h}`, - swapVolume7d: `${swapVolume7d}`, - swapFee7d: `${swapFee7d}`, numLiquidityProviders: `${numLiquidityProviders}`, chains: chainMetrics, }; @@ -71,8 +67,6 @@ export class ProtocolService { public async cacheProtocolMetrics(chain: Chain): Promise { const oneDayAgo = moment().subtract(24, 'hours').unix(); - const startOfDay = moment().startOf('day').unix(); - const sevenDayRange = moment().startOf('day').subtract(7, 'days').unix(); const client = new GraphQLClient(AllNetworkConfigsKeyedOnChain[chain].data.subgraphs.balancer); const subgraphClient = getSdk(client); @@ -115,15 +109,6 @@ export class ProtocolService { const yieldCapture24h = _.sumBy(pools, (pool) => (!pool.dynamicData ? 0 : pool.dynamicData.yieldCapture24h)); - //we take the aggregate of the last 7 days previous to today, since today's values grow throughout the day - const snapshotQueryResponse = await prisma.prismaPoolSnapshot.aggregate({ - _sum: { fees24h: true, volume24h: true }, - where: { - chain, - timestamp: { gte: sevenDayRange, lt: startOfDay }, - }, - }); - const balancerV1Tvl = await this.getBalancerV1Tvl(`${AllNetworkConfigsKeyedOnChain[chain].data.chain.id}`); const protocolData = { @@ -135,8 +120,6 @@ export class ProtocolService { swapVolume24h: `${swapVolume24h}`, swapFee24h: `${swapFee24h}`, yieldCapture24h: `${yieldCapture24h}`, - swapVolume7d: `${snapshotQueryResponse._sum.volume24h}`, - swapFee7d: `${snapshotQueryResponse._sum.fees24h}`, numLiquidityProviders: `${holdersQueryResponse._sum.holdersCount || '0'}`, };