From 60a23768a16252cd0872e0ca185c8768d3a7b7c1 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:46:22 +0100 Subject: [PATCH 01/14] v3 data (#136) First iteration of v3 data integration --- .github/workflows/checks.yml | 2 + app-architecture.excalidraw | 1832 +++++++++ buildspec.yml | 2 + codegen.yml | 34 + config/index.ts | 16 + config/sepolia.ts | 113 + env.local | 2 + jest.config.js | 5 + .../pool/add-pools-from-subgraph.test.ts | 63 + .../actions/pool/add-pools-from-subgraph.ts | 143 + modules/actions/pool/get-changed-pools.ts | 52 + modules/actions/pool/update-on-chain-data.ts | 222 + modules/content/github-content.service.ts | 2 +- modules/context/header-chain.ts | 2 +- modules/controllers/jobs-controller.test.ts | 20 + modules/controllers/jobs-controller.ts | 92 + modules/controllers/pools-controller.ts | 36 + modules/network/chain-id-to-chain.ts | 14 + modules/network/network-config-types.ts | 2 + modules/network/network-config.ts | 13 - modules/network/sepolia.ts | 138 +- modules/pool/abi/VaultV3.json | 3401 ---------------- modules/sources/contracts/abis/VaultV3.ts | 3622 +++++++++++++++++ .../sources/contracts/abis/VaultV3JSON.json | 3622 +++++++++++++++++ .../sources/contracts/abis/weighted_pool.abi | 0 .../sources/contracts/fetch-erc20-headers.ts | 54 + modules/sources/contracts/fetch-pool-data.ts | 95 + .../sources/contracts/fetch-pool-tokens.ts | 90 + .../contracts/fetch-weighted-pools-data.ts | 41 + modules/sources/contracts/index.ts | 3 + modules/sources/logs/get-new-pools.ts | 142 + .../sources/logs/get-pool-balance-changed.ts | 49 + modules/sources/logs/get-swaps.ts | 64 + modules/sources/logs/get-transfers.ts | 48 + modules/sources/logs/index.ts | 3 + modules/sources/transformers/index.ts | 2 + .../transformers/pool-tokens-transformer.ts | 50 + .../sources/transformers/pool-transformer.ts | 46 + modules/sources/types.ts | 1 + modules/sources/viem-client.ts | 37 + modules/subgraphs/balancer-v3-pools/index.ts | 17 + .../subgraphs/balancer-v3-pools/pools.graphql | 42 + modules/subgraphs/balancer-v3-vault/index.ts | 17 + .../balancer-v3-vault/join-exits.graphql | 38 + .../balancer-v3-vault/pool-snapshots.graphql | 32 + .../subgraphs/balancer-v3-vault/pools.graphql | 50 + .../subgraphs/balancer-v3-vault/swaps.graphql | 35 + .../subgraphs/balancer-v3-vault/users.graphql | 42 + modules/v3/README.md | 34 + package.json | 9 +- tasks/index.ts | 19 + tsconfig.build.json | 4 + worker/job-handlers.ts | 13 +- yarn.lock | 26 +- 54 files changed, 11005 insertions(+), 3548 deletions(-) create mode 100644 app-architecture.excalidraw create mode 100644 config/index.ts create mode 100644 config/sepolia.ts create mode 100644 modules/actions/pool/add-pools-from-subgraph.test.ts create mode 100644 modules/actions/pool/add-pools-from-subgraph.ts create mode 100644 modules/actions/pool/get-changed-pools.ts create mode 100644 modules/actions/pool/update-on-chain-data.ts create mode 100644 modules/controllers/jobs-controller.test.ts create mode 100644 modules/controllers/jobs-controller.ts create mode 100644 modules/controllers/pools-controller.ts create mode 100644 modules/network/chain-id-to-chain.ts delete mode 100644 modules/pool/abi/VaultV3.json create mode 100644 modules/sources/contracts/abis/VaultV3.ts create mode 100644 modules/sources/contracts/abis/VaultV3JSON.json create mode 100644 modules/sources/contracts/abis/weighted_pool.abi create mode 100644 modules/sources/contracts/fetch-erc20-headers.ts create mode 100644 modules/sources/contracts/fetch-pool-data.ts create mode 100644 modules/sources/contracts/fetch-pool-tokens.ts create mode 100644 modules/sources/contracts/fetch-weighted-pools-data.ts create mode 100644 modules/sources/contracts/index.ts create mode 100644 modules/sources/logs/get-new-pools.ts create mode 100644 modules/sources/logs/get-pool-balance-changed.ts create mode 100644 modules/sources/logs/get-swaps.ts create mode 100644 modules/sources/logs/get-transfers.ts create mode 100644 modules/sources/logs/index.ts create mode 100644 modules/sources/transformers/index.ts create mode 100644 modules/sources/transformers/pool-tokens-transformer.ts create mode 100644 modules/sources/transformers/pool-transformer.ts create mode 100644 modules/sources/types.ts create mode 100644 modules/sources/viem-client.ts create mode 100644 modules/subgraphs/balancer-v3-pools/index.ts create mode 100644 modules/subgraphs/balancer-v3-pools/pools.graphql create mode 100644 modules/subgraphs/balancer-v3-vault/index.ts create mode 100644 modules/subgraphs/balancer-v3-vault/join-exits.graphql create mode 100644 modules/subgraphs/balancer-v3-vault/pool-snapshots.graphql create mode 100644 modules/subgraphs/balancer-v3-vault/pools.graphql create mode 100644 modules/subgraphs/balancer-v3-vault/swaps.graphql create mode 100644 modules/subgraphs/balancer-v3-vault/users.graphql create mode 100644 modules/v3/README.md create mode 100644 tasks/index.ts create mode 100644 tsconfig.build.json diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c0ffaedaf..62f0c7275 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -6,6 +6,8 @@ on: jobs: Build: env: + V3_SUBGRAPH: https://api.studio.thegraph.com/proxy/31386/balancer-v3-sepolia/version/latest + V3_POOLS_SUBGRAPH: https://api.studio.thegraph.com/proxy/31386/balancer-pools-v3-sepolia/version/latest BALANCER_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx MASTERCHEF_SUBGRAPH: https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2 BLOCKS_SUBGRAPH: https://api.thegraph.com/subgraphs/name/danielmkm/optimism-blocks diff --git a/app-architecture.excalidraw b/app-architecture.excalidraw new file mode 100644 index 000000000..16cc41d47 --- /dev/null +++ b/app-architecture.excalidraw @@ -0,0 +1,1832 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 865, + "versionNonce": 618847347, + "isDeleted": false, + "id": "0rCUkN1a2gY5msly4lPoJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 626.0769073589, + "y": 1234.8874532379957, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 805.0668571070347, + "height": 339.28462450621436, + "seed": 485198810, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9Dx_1COVEMPVHccffiLwH" + }, + { + "id": "VboAKrgz851OORtW9No3h", + "type": "arrow" + }, + { + "id": "y_uM2fFfpaEi9hwrpZBqP", + "type": "arrow" + }, + { + "id": "614vaW_3XHnGgPUnoYSFE", + "type": "arrow" + } + ], + "updated": 1707940476825, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 574, + "versionNonce": 412435485, + "isDeleted": false, + "id": "9Dx_1COVEMPVHccffiLwH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 631.0769073589, + "y": 1239.8874532379957, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 286.9158935546875, + "height": 70, + "seed": 1498115654, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707940454699, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "\n Configured context", + "textAlign": "left", + "verticalAlign": "top", + "containerId": "0rCUkN1a2gY5msly4lPoJ", + "originalText": "\n Configured context", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 168, + "versionNonce": 1333760326, + "isDeleted": false, + "id": "l9lNn3ZlV1VxxGrVFqyBr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 512.3144110597245, + "y": 84.15771359161883, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 504.25177001953125, + "height": 45, + "seed": 1343185222, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707925076170, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "API application architecture", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "API application architecture", + "lineHeight": 1.25, + "baseline": 32 + }, + { + "type": "rectangle", + "version": 374, + "versionNonce": 1338218266, + "isDeleted": false, + "id": "rtyvvejqKNEkFQQW0oda0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 629.7265182237195, + "y": 261.2164066662183, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 219.95703124999997, + "height": 103.03515625, + "seed": 700003270, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "95gidIyH9fC5A3lmczIdh" + }, + { + "id": "vg6-l8Xznua-BCWboSEs4", + "type": "arrow" + }, + { + "id": "j-VKbIqAdLxV6NjHu9da1", + "type": "arrow" + } + ], + "updated": 1707925098341, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 472, + "versionNonce": 2083890138, + "isDeleted": false, + "id": "95gidIyH9fC5A3lmczIdh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 680.255090306239, + "y": 300.2339847912183, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 118.89988708496094, + "height": 25, + "seed": 728430426, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707925098341, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Express app", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rtyvvejqKNEkFQQW0oda0", + "originalText": "Express app", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 498, + "versionNonce": 226062170, + "isDeleted": false, + "id": "bvxjwWJEryYTp1hXvIEk5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 358.6028636353002, + "y": 478.09765624999994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 547445830, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "0lVehAIjpyAIvWJF1OD0o" + }, + { + "id": "vg6-l8Xznua-BCWboSEs4", + "type": "arrow" + }, + { + "id": "uj7GcQ_fyBRff70WrSx4w", + "type": "arrow" + } + ], + "updated": 1707925337870, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 643, + "versionNonce": 2126256518, + "isDeleted": false, + "id": "0lVehAIjpyAIvWJF1OD0o", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 389.77093919682363, + "y": 513.0859375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 245.99978637695312, + "height": 50, + "seed": 905569158, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707925104984, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "POST /jobs\nruns the background jobs", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "bvxjwWJEryYTp1hXvIEk5", + "originalText": "POST /jobs\nruns the background jobs", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 506, + "versionNonce": 1183149210, + "isDeleted": false, + "id": "BI0s-hG2xUwjQmFAhFfn0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 838.9249916537059, + "y": 480.9868795587057, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 427759302, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "vmTgASKFvWEof8L2w_UmV" + }, + { + "id": "j-VKbIqAdLxV6NjHu9da1", + "type": "arrow" + }, + { + "id": "4x-jVg3VAfW0rFQzP402V", + "type": "arrow" + } + ], + "updated": 1707926250651, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 689, + "versionNonce": 887754886, + "isDeleted": false, + "id": "vmTgASKFvWEof8L2w_UmV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 921.1630135042918, + "y": 515.9751608087057, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 143.85989379882812, + "height": 50, + "seed": 1883000326, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926247787, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "POST /graphql\nApollo server", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BI0s-hG2xUwjQmFAhFfn0", + "originalText": "POST /graphql\nApollo server", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 670, + "versionNonce": 1661281715, + "isDeleted": false, + "id": "b5Ml9fkgIPa8v2fp9Kt9d", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1811.227829284344, + "y": 267.40536771482607, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 2112226522, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "wN-cyeOs1s97VjXdqlN1_" + } + ], + "updated": 1707940527608, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 687, + "versionNonce": 224130899, + "isDeleted": false, + "id": "wN-cyeOs1s97VjXdqlN1_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1920.115845031414, + "y": 314.89364896482607, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 90.55990600585938, + "height": 25, + "seed": 524573082, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707940527608, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Scheduler", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b5Ml9fkgIPa8v2fp9Kt9d", + "originalText": "Scheduler", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 110, + "versionNonce": 725348358, + "isDeleted": false, + "id": "vg6-l8Xznua-BCWboSEs4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 731.8248229331246, + "y": 367.0552833346115, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.6538719242776, + "height": 109.48671833378177, + "seed": 37291354, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707925104984, + "link": null, + "locked": false, + "startBinding": { + "elementId": "rtyvvejqKNEkFQQW0oda0", + "focus": -0.4644458004203634, + "gap": 2.8037204183931976 + }, + "endBinding": { + "elementId": "bvxjwWJEryYTp1hXvIEk5", + "focus": -0.4179643889919988, + "gap": 1.555654581606774 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -212.6538719242776, + 109.48671833378177 + ] + ] + }, + { + "type": "arrow", + "version": 87, + "versionNonce": 1234708230, + "isDeleted": false, + "id": "j-VKbIqAdLxV6NjHu9da1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 740.7665820910862, + "y": 367.1177833346115, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 251.76652049132224, + "height": 104.89156664248742, + "seed": 370382342, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707926247787, + "link": null, + "locked": false, + "startBinding": { + "elementId": "rtyvvejqKNEkFQQW0oda0", + "focus": 0.5530999063539602, + "gap": 2.8662204183931976 + }, + "endBinding": { + "elementId": "BI0s-hG2xUwjQmFAhFfn0", + "focus": 0.5533215980667758, + "gap": 8.977529581606802 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 251.76652049132224, + 104.89156664248742 + ] + ] + }, + { + "type": "rectangle", + "version": 563, + "versionNonce": 1271079898, + "isDeleted": false, + "id": "PQ8zJtBlZuvNesoG3T0A0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 358.7075006737633, + "y": 777.2549019291434, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 474475738, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "yhshF7mYuuI1NENOq7m0w" + }, + { + "id": "p04FjRC7bFzLdr2STyCnI", + "type": "arrow" + }, + { + "id": "gAkm6uT_7wSXCu95CFn8U", + "type": "arrow" + }, + { + "id": "uj7GcQ_fyBRff70WrSx4w", + "type": "arrow" + }, + { + "id": "RHVw0U5boLMcrEmQQbDqj", + "type": "arrow" + } + ], + "updated": 1707925770470, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 803, + "versionNonce": 1709529242, + "isDeleted": false, + "id": "yhshF7mYuuI1NENOq7m0w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 419.78553412102895, + "y": 812.2431831791434, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 186.17987060546875, + "height": 50, + "seed": 1764311450, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707925572928, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Handler\nknows what to call", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PQ8zJtBlZuvNesoG3T0A0", + "originalText": "Handler\nknows what to call", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 705, + "versionNonce": 1945243354, + "isDeleted": false, + "id": "vRh35_WImTNMpdlYPHt7E", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -145.3083445419249, + "y": 771.9810153460681, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 2077206278, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Yq8uvKvWpUrW3aM02foeq" + }, + { + "id": "p04FjRC7bFzLdr2STyCnI", + "type": "arrow" + } + ], + "updated": 1707925776969, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 965, + "versionNonce": 950901574, + "isDeleted": false, + "id": "Yq8uvKvWpUrW3aM02foeq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -136.04026287688583, + "y": 794.4692965960681, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 289.7997741699219, + "height": 75, + "seed": 311481926, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926212942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Execution logic\n(concurrency, tracing, alerts, \nmonitoring)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vRh35_WImTNMpdlYPHt7E", + "originalText": "Execution logic\n(concurrency, tracing, alerts, monitoring)", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "arrow", + "version": 242, + "versionNonce": 420593478, + "isDeleted": false, + "id": "p04FjRC7bFzLdr2STyCnI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 170.329589698084, + "y": 834.1066579326716, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 183.46155436416043, + "height": 2.9490252402465558, + "seed": 2046748442, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707925779844, + "link": null, + "locked": false, + "startBinding": { + "elementId": "vRh35_WImTNMpdlYPHt7E", + "focus": -0.007334553906602865, + "gap": 7.301996740008889 + }, + "endBinding": { + "elementId": "PQ8zJtBlZuvNesoG3T0A0", + "focus": -0.037935209026344924, + "gap": 4.916356611518893 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 183.46155436416043, + 2.9490252402465558 + ] + ] + }, + { + "type": "rectangle", + "version": 795, + "versionNonce": 1475902086, + "isDeleted": false, + "id": "mGeAjrKYThqTz6OROStTH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -133.90378372517483, + "y": 1009.1715495169069, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 2079939782, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "UNrJfhn0S00DQ2x_Tglun" + }, + { + "id": "gAkm6uT_7wSXCu95CFn8U", + "type": "arrow" + } + ], + "updated": 1707925805653, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1043, + "versionNonce": 693320134, + "isDeleted": false, + "id": "UNrJfhn0S00DQ2x_Tglun", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -43.14574997273343, + "y": 1056.659830766907, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 126.81986999511719, + "height": 25, + "seed": 1467864070, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707925805653, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Configuration", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mGeAjrKYThqTz6OROStTH", + "originalText": "Configuration", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 441, + "versionNonce": 1436774598, + "isDeleted": false, + "id": "gAkm6uT_7wSXCu95CFn8U", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 181.09991116802735, + "y": 1079.4842931952699, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 163.93625108134245, + "height": 2.4055138321657523, + "seed": 757321606, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707926019123, + "link": null, + "locked": false, + "startBinding": { + "elementId": "mGeAjrKYThqTz6OROStTH", + "focus": 0.20699809207901113, + "gap": 6.6677573932022085 + }, + "endBinding": { + "elementId": "nplU0SHojzsk4FXPETiYF", + "focus": -0.01742985684773468, + "gap": 6.50532555727159 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 163.93625108134245, + -2.4055138321657523 + ] + ] + }, + { + "type": "rectangle", + "version": 799, + "versionNonce": 546811709, + "isDeleted": false, + "id": "nplU0SHojzsk4FXPETiYF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 351.5414878066414, + "y": 1013.6478465678853, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 1382588294, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "OQu_Hxf7mq-UU4hUK9caz" + }, + { + "id": "gAkm6uT_7wSXCu95CFn8U", + "type": "arrow" + }, + { + "id": "RHVw0U5boLMcrEmQQbDqj", + "type": "arrow" + }, + { + "id": "614vaW_3XHnGgPUnoYSFE", + "type": "arrow" + }, + { + "id": "zwnghJfQIHaTc7m3J2hIg", + "type": "arrow" + } + ], + "updated": 1707940418860, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1026, + "versionNonce": 1668187078, + "isDeleted": false, + "id": "OQu_Hxf7mq-UU4hUK9caz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 395.399550550782, + "y": 1036.1361278178854, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 220.61981201171875, + "height": 75, + "seed": 209108678, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926019123, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Controllers\nknows how to execute,\ncreates context", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "nplU0SHojzsk4FXPETiYF", + "originalText": "Controllers\nknows how to execute,\ncreates context", + "lineHeight": 1.25, + "baseline": 68 + }, + { + "type": "arrow", + "version": 97, + "versionNonce": 1740671834, + "isDeleted": false, + "id": "uj7GcQ_fyBRff70WrSx4w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 513.779012861997, + "y": 600.8473679154308, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 3.2527582283347556, + "height": 173.37700981947967, + "seed": 472969158, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707925514888, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bvxjwWJEryYTp1hXvIEk5", + "focus": -0.01362000814081663, + "gap": 2.7731491654307945 + }, + "endBinding": { + "elementId": "PQ8zJtBlZuvNesoG3T0A0", + "focus": -0.022740965350795648, + "gap": 3.030524194232953 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.2527582283347556, + 173.37700981947967 + ] + ] + }, + { + "type": "rectangle", + "version": 974, + "versionNonce": 488162301, + "isDeleted": false, + "id": "z1P6dAwrnt95XQLJrTDlz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 349.3956006344352, + "y": 1678.7191717521068, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 1145436550, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "jqXGGNGzRN3ai7m8gvJpq" + }, + { + "id": "Pp3cnjDdYnCjvNGio5jzA", + "type": "arrow" + }, + { + "id": "ObWGMI7bb4fMOA7infU3T", + "type": "arrow" + }, + { + "id": "y_uM2fFfpaEi9hwrpZBqP", + "type": "arrow" + }, + { + "id": "zwnghJfQIHaTc7m3J2hIg", + "type": "arrow" + } + ], + "updated": 1707940418860, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1294, + "versionNonce": 1908571526, + "isDeleted": false, + "id": "jqXGGNGzRN3ai7m8gvJpq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 358.7736829098258, + "y": 1713.7074530021068, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 289.57977294921875, + "height": 50, + "seed": 1301862598, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926052057, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Actions\nknows what needs to be done", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "z1P6dAwrnt95XQLJrTDlz", + "originalText": "Actions\nknows what needs to be done", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 1494, + "versionNonce": 859913693, + "isDeleted": false, + "id": "SF-Ct3jvdbXrlXmM5qhTu", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1068.067252614944, + "y": 1378.9330618856036, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 1492098714, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "2759MoHnFvyuXdinx-RFE" + }, + { + "id": "VboAKrgz851OORtW9No3h", + "type": "arrow" + } + ], + "updated": 1707940458158, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1887, + "versionNonce": 1227366621, + "isDeleted": false, + "id": "2759MoHnFvyuXdinx-RFE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1171.0752634792018, + "y": 1413.9213431356036, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 102.31991577148438, + "height": 50, + "seed": 561298266, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707940509638, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Stores\nDB models", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SF-Ct3jvdbXrlXmM5qhTu", + "originalText": "Stores\nDB models", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 1544, + "versionNonce": 386015357, + "isDeleted": false, + "id": "4IzITRueridnIqiLKsGi1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 678.2142338689831, + "y": 1379.9813732445514, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 1345582426, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "O3yNoHaKP0LBUhiIm84ii" + } + ], + "updated": 1707940408758, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1973, + "versionNonce": 37947613, + "isDeleted": false, + "id": "O3yNoHaKP0LBUhiIm84ii", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 714.0723042425183, + "y": 1414.9696544945514, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 236.6197967529297, + "height": 50, + "seed": 1132476954, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707940408758, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Data sources\nsubgraph, contract, logs", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4IzITRueridnIqiLKsGi1", + "originalText": "Data sources\nsubgraph, contract, logs", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "rectangle", + "version": 1256, + "versionNonce": 686864070, + "isDeleted": false, + "id": "tO4GEy8oBl6piCMtE5BYt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 342.27168157891697, + "y": 1957.431911454487, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 2022345670, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "M-6U6BUI39ixWdxUNP_YS" + }, + { + "id": "ObWGMI7bb4fMOA7infU3T", + "type": "arrow" + } + ], + "updated": 1707926030524, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1756, + "versionNonce": 1618603526, + "isDeleted": false, + "id": "M-6U6BUI39ixWdxUNP_YS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 364.42976263360447, + "y": 1992.420192704487, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 264.019775390625, + "height": 50, + "seed": 672211718, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926030524, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Transformers\nprepares data for the DB", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "tO4GEy8oBl6piCMtE5BYt", + "originalText": "Transformers\nprepares data for the DB", + "lineHeight": 1.25, + "baseline": 43 + }, + { + "type": "arrow", + "version": 933, + "versionNonce": 541887110, + "isDeleted": false, + "id": "ObWGMI7bb4fMOA7infU3T", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 503.1177388531386, + "y": 1951.683528736298, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0.20933401772674642, + "height": 146.81350481066056, + "seed": 1088990726, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707926052057, + "link": null, + "locked": false, + "startBinding": { + "elementId": "tO4GEy8oBl6piCMtE5BYt", + "focus": 0.0438986464455738, + "gap": 5.74838271818885 + }, + "endBinding": { + "elementId": "z1P6dAwrnt95XQLJrTDlz", + "focus": 0.004858900627053375, + "gap": 6.174289673530666 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.20933401772674642, + -146.81350481066056 + ] + ] + }, + { + "type": "arrow", + "version": 38, + "versionNonce": 791050054, + "isDeleted": false, + "id": "RHVw0U5boLMcrEmQQbDqj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 508.9348937747624, + "y": 901.1234252897526, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.693399649831008, + "height": 108.88017912269822, + "seed": 1922867462, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707926019124, + "link": null, + "locked": false, + "startBinding": { + "elementId": "PQ8zJtBlZuvNesoG3T0A0", + "focus": 0.03183082197404965, + "gap": 3.8919608606090605 + }, + "endBinding": { + "elementId": "nplU0SHojzsk4FXPETiYF", + "focus": 0.03809457720533805, + "gap": 3.644242155434256 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.693399649831008, + 108.88017912269822 + ] + ] + }, + { + "type": "text", + "version": 144, + "versionNonce": 615599130, + "isDeleted": false, + "id": "MCju5oeR-thaF0t-6Co6C", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 167.47715758645728, + "y": 302.51959011572853, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 140.47592163085938, + "height": 35, + "seed": 2043388314, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926152525, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "DB writing", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "DB writing", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "text", + "version": 209, + "versionNonce": 371194694, + "isDeleted": false, + "id": "q375xvuA_Vs7J6Gglhqrz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1223.9749075834748, + "y": 304.1284151646575, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 151.39593505859375, + "height": 35, + "seed": 1599926554, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926164810, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "DB reading", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "DB reading", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 542, + "versionNonce": 709293715, + "isDeleted": false, + "id": "6uYVJb4hhqsXKen0kvDn9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 849.9003831051311, + "y": 781.7174259605832, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.3359375, + "height": 119.9765625, + "seed": 201315334, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "3I468xPZ0ZRZtcCurgeWT" + }, + { + "id": "4x-jVg3VAfW0rFQzP402V", + "type": "arrow" + }, + { + "id": "VboAKrgz851OORtW9No3h", + "type": "arrow" + } + ], + "updated": 1707940286790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 743, + "versionNonce": 83141318, + "isDeleted": false, + "id": "3I468xPZ0ZRZtcCurgeWT", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 968.3383866451702, + "y": 829.2057072105832, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 71.45993041992188, + "height": 25, + "seed": 1714643782, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1707926244620, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "loaders", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6uYVJb4hhqsXKen0kvDn9", + "originalText": "loaders", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "arrow", + "version": 28, + "versionNonce": 2133703386, + "isDeleted": false, + "id": "4x-jVg3VAfW0rFQzP402V", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 991.4503747299802, + "y": 611.2276276530748, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 6.781409869044637, + "height": 164.65368709275606, + "seed": 1167236486, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1707926252727, + "link": null, + "locked": false, + "startBinding": { + "elementId": "BI0s-hG2xUwjQmFAhFfn0", + "focus": 0.028958350055107673, + "gap": 10.26418559436911 + }, + "endBinding": { + "elementId": "6uYVJb4hhqsXKen0kvDn9", + "focus": -0.01995376479451029, + "gap": 5.83611121475235 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 6.781409869044637, + 164.65368709275606 + ] + ] + }, + { + "id": "VboAKrgz851OORtW9No3h", + "type": "arrow", + "x": 999.3729793068529, + "y": 1210.1039915799047, + "width": 3.842047300777608, + "height": 282.1120384916244, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1530990877, + "version": 1580, + "versionNonce": 160865203, + "isDeleted": false, + "boundElements": null, + "updated": 1707940476825, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -3.842047300777608, + -282.1120384916244 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "0rCUkN1a2gY5msly4lPoJ", + "gap": 24.7834616580908, + "focus": -0.06567990475367153 + }, + "endBinding": { + "elementId": "6uYVJb4hhqsXKen0kvDn9", + "gap": 26.297964627696956, + "focus": 0.06266764348604946 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "y_uM2fFfpaEi9hwrpZBqP", + "type": "arrow", + "x": 708.0096168316634, + "y": 1590.571541415734, + "width": 103.06612005250531, + "height": 77.30992844466846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 420217171, + "version": 954, + "versionNonce": 290687731, + "isDeleted": false, + "boundElements": null, + "updated": 1707940476825, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -103.06612005250531, + 77.30992844466846 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "0rCUkN1a2gY5msly4lPoJ", + "gap": 16.39946367152379, + "focus": 0.11499207460118326 + }, + "endBinding": { + "elementId": "z1P6dAwrnt95XQLJrTDlz", + "gap": 10.837701891704455, + "focus": 0.02971667668683233 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "614vaW_3XHnGgPUnoYSFE", + "type": "arrow", + "x": 710.4007569205448, + "y": 1220.377265850082, + "width": 95.30409985136987, + "height": 69.33891056634707, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 336140285, + "version": 983, + "versionNonce": 260942387, + "isDeleted": false, + "boundElements": null, + "updated": 1707940476825, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -95.30409985136987, + -69.33891056634707 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "0rCUkN1a2gY5msly4lPoJ", + "gap": 14.510187387913447, + "focus": -0.10312430370153185 + }, + "endBinding": { + "elementId": "nplU0SHojzsk4FXPETiYF", + "gap": 17.41394621584965, + "focus": -0.012679959910820856 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "zwnghJfQIHaTc7m3J2hIg", + "type": "arrow", + "x": 499.119442701254, + "y": 1150.4231809452544, + "width": 4.174397296830875, + "height": 507.8264164155132, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1578221843, + "version": 49, + "versionNonce": 1203290013, + "isDeleted": false, + "boundElements": null, + "updated": 1707940418860, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -4.174397296830875, + 507.8264164155132 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "nplU0SHojzsk4FXPETiYF", + "focus": 0.038528210058637914, + "gap": 16.798771877369347 + }, + "endBinding": { + "elementId": "z1P6dAwrnt95XQLJrTDlz", + "focus": -0.06000150598742052, + "gap": 20.469574391339165 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/buildspec.yml b/buildspec.yml index 3b9a1e090..2672c08a7 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -1,6 +1,8 @@ version: 0.2 env: variables: + V3_SUBGRAPH: 'https://api.studio.thegraph.com/proxy/31386/balancer-v3-sepolia/version/latest/graphql' + V3_POOLS_SUBGRAPH: 'https://api.studio.thegraph.com/proxy/31386/balancer-pools-v3-sepolia/version/latest/graphql' BALANCER_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx-v2-optimism' MASTERCHEF_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2' RELIQUARY_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/reliquary' diff --git a/codegen.yml b/codegen.yml index b6d7a4a77..e87c909af 100644 --- a/codegen.yml +++ b/codegen.yml @@ -3,6 +3,40 @@ hooks: afterAllFileWrite: - prettier --write generates: + modules/subgraphs/balancer-v3-vault/generated/types.ts: + schema: ${V3_SUBGRAPH} + documents: 'modules/subgraphs/balancer-v3-vault/*.graphql' + plugins: + - typescript + - typescript-operations + - typescript-graphql-request + config: + skipTypename: true + scalars: + BigInt: string + Bytes: string + BigDecimal: string + modules/subgraphs/balancer-v3-vault/generated/balancer-v3-schema.graphql: + schema: ${V3_SUBGRAPH} + plugins: + - schema-ast + modules/subgraphs/balancer-v3-pools/generated/types.ts: + schema: ${V3_POOLS_SUBGRAPH} + documents: 'modules/subgraphs/balancer-v3-pools/*.graphql' + plugins: + - typescript + - typescript-operations + - typescript-graphql-request + config: + skipTypename: true + scalars: + BigInt: string + Bytes: string + BigDecimal: string + modules/subgraphs/balancer-v3-pools/generated/balancer-v3-pools-schema.graphql: + schema: ${V3_POOLS_SUBGRAPH} + plugins: + - schema-ast modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types.ts: schema: ${BALANCER_SUBGRAPH} documents: 'modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql' diff --git a/config/index.ts b/config/index.ts new file mode 100644 index 000000000..384c7b540 --- /dev/null +++ b/config/index.ts @@ -0,0 +1,16 @@ +import { Chain } from '@prisma/client'; +import { sepoliaConfig } from './sepolia'; +import { NetworkData } from '../modules/network/network-config-types'; + +export default { + [Chain.ARBITRUM]: {} as NetworkData, + [Chain.AVALANCHE]: {} as NetworkData, + [Chain.BASE]: {} as NetworkData, + [Chain.FANTOM]: {} as NetworkData, + [Chain.GNOSIS]: {} as NetworkData, + [Chain.MAINNET]: {} as NetworkData, + [Chain.OPTIMISM]: {} as NetworkData, + [Chain.POLYGON]: {} as NetworkData, + [Chain.SEPOLIA]: sepoliaConfig, + [Chain.ZKEVM]: {} as NetworkData, +}; diff --git a/config/sepolia.ts b/config/sepolia.ts new file mode 100644 index 000000000..954990207 --- /dev/null +++ b/config/sepolia.ts @@ -0,0 +1,113 @@ +import { BigNumber } from 'ethers'; +import { env } from '../app/env'; +import { NetworkData } from '../modules/network/network-config-types'; + +export const sepoliaConfig: NetworkData = { + chain: { + slug: 'sepolia', + id: 11155111, + nativeAssetAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + wrappedNativeAssetAddress: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9', + prismaId: 'SEPOLIA', + gqlId: 'SEPOLIA', + }, + subgraphs: { + startDate: '2023-05-03', + balancer: 'https://api.studio.thegraph.com/query/24660/balancer-sepolia-v2/version/latest', + balancerV3: 'https://api.studio.thegraph.com/proxy/31386/balancer-v3-sepolia/version/latest', + balancerPoolsV3: 'https://api.studio.thegraph.com/proxy/31386/balancer-pools-v3-sepolia/version/latest', + beetsBar: 'https://', + blocks: 'https://api.studio.thegraph.com/query/48427/bleu-sepolia-blocks/version/latest', + gauge: 'https://api.studio.thegraph.com/proxy/24660/balancer-gauges-sepolia/version/latest', + // veBalLocks: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges', + userBalances: 'https://', + }, + eth: { + address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + addressFormatted: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + symbol: 'ETH', + name: 'Ether', + }, + weth: { + address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + addressFormatted: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + }, + coingecko: { + nativeAssetId: 'ethereum', + platformId: 'ethereum', + excludedTokenAddresses: [], + }, + rpcUrl: env.GROVE_CITY + ? `https://sepolia.rpc.grove.city/v1/${env.GROVE_CITY}` + : env.INFURA_API_KEY + ? `https://sepolia.infura.io/v3/${env.INFURA_API_KEY}` + : 'https://gateway.tenderly.co/public/sepolia', + rpcMaxBlockRange: 700, + protocolToken: 'bal', + bal: { + address: '0xb19382073c7A0aDdbb56Ac6AF1808Fa49e377B75', + }, + // veBal: { + // address: '0xc128a9954e6c874ea3d62ce62b468ba073093f25', + // delegationProxy: '0x81cfae226343b24ba12ec6521db2c79e7aeeb310', + // }, + balancer: { + v2: { + vaultAddress: '0xba12222222228d8ba445958a75a0704d566bf2c8', + defaultSwapFeePercentage: '0.5', + defaultYieldFeePercentage: '0.5', + balancerQueriesAddress: '0xe39b5e3b6d74016b2f6a9673d7d7493b6df549d5', + }, + v3: { + vaultAddress: '0xdaa273aeec06e9ccb7428a77e2abb1e4659b16d2', + defaultSwapFeePercentage: '0.5', + defaultYieldFeePercentage: '0.5', + }, + }, + multicall: '0x80c7dd17b01855a6d2347444a0fcc36136a314de', + multicall3: '0xca11bde05977b3631167028862be2a173976ca11', + avgBlockSpeed: 1, + sor: { + main: { + url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', + maxPools: 8, + forceRefresh: false, + gasPrice: BigNumber.from(10), + swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], + }, + canary: { + url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', + maxPools: 8, + forceRefresh: false, + gasPrice: BigNumber.from(10), + swapGas: BigNumber.from('1000000'), + poolIdsToExclude: [], + }, + }, + ybAprConfig: {}, + 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', + }, + }, + monitoring: { + main: { + alarmTopicArn: 'arn:aws:sns:ca-central-1:118697801881:api_alarms', + }, + canary: { + alarmTopicArn: 'arn:aws:sns:eu-central-1:118697801881:api_alarms', + }, + }, +}; diff --git a/env.local b/env.local index bf4e9fc42..31d2dae37 100644 --- a/env.local +++ b/env.local @@ -20,6 +20,8 @@ WORKER_QUEUE_URL=https://# DEFAULT_CHAIN_ID=250 # Subgraph type config +V3_SUBGRAPH=https://api.studio.thegraph.com/proxy/31386/balancer-v3-sepolia/version/latest +V3_POOLS_SUBGRAPH=https://api.studio.thegraph.com/proxy/31386/balancer-pools-v3-sepolia/version/latest BALANCER_SUBGRAPH=https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx MASTERCHEF_SUBGRAPH=https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2 BLOCKS_SUBGRAPH=https://api.thegraph.com/subgraphs/name/danielmkm/optimism-blocks diff --git a/jest.config.js b/jest.config.js index ab69250fa..cda9284cf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,4 +10,9 @@ module.exports = { isolatedModules: true, }, }, + moduleNameMapper: { + '^@app/(.*)$': '/app/$1', + '^@config/(.*)$': '/config/$1', + '^@modules/(.*)$': '/modules/$1', + }, }; diff --git a/modules/actions/pool/add-pools-from-subgraph.test.ts b/modules/actions/pool/add-pools-from-subgraph.test.ts new file mode 100644 index 000000000..cf0165764 --- /dev/null +++ b/modules/actions/pool/add-pools-from-subgraph.test.ts @@ -0,0 +1,63 @@ +import { addMissingPoolsFromSubgraph } from './add-pools-from-subgraph'; +import { prisma } from '../../../prisma/prisma-client'; +import { PrismaPool } from '@prisma/client'; +import { PoolFragment as VaultSubgraphPoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { TypePoolFragment as PoolSubgraphPoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; + +// Mock the module dependencies +jest.mock('@modules/sources/contracts', () => ({ + ...jest.requireActual('@modules/sources/contracts'), + fetchErc20Headers: jest.fn().mockResolvedValue({ '2': { name: 'name', symbol: 'symbol' } }), + fetchWeightedPoolData: jest.fn().mockResolvedValue({}), + fetchPoolTokens: jest.fn().mockResolvedValue({}), +})); + +jest.mock('../../../prisma/prisma-client', () => ({ + prisma: { + prismaPool: { + findMany: jest.fn().mockResolvedValue([{ id: '1' }] as PrismaPool[]), + create: jest.fn(), + }, + prismaToken: { + findMany: jest.fn(), + createMany: jest.fn(), + }, + prismaPoolExpandedTokens: { + createMany: jest.fn(), + }, + prismaPoolTokenDynamicData: { + createMany: jest.fn(), + }, + }, +})); + +describe('syncPools', () => { + const vaultSubgraphClient = { + Pools: jest.fn().mockResolvedValue({ pools: [{ id: '1' }, { id: '2' }] as VaultSubgraphPoolFragment[] }), + }; + const poolSubgraphClient = { + Pools: jest.fn().mockResolvedValue({ + pools: [ + { id: '1', factory: { id: '1' } }, + { id: '2', factory: { id: '1' } }, + ] as PoolSubgraphPoolFragment[], + }), + }; + + beforeEach(() => { + jest.clearAllMocks(); + return addMissingPoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, 'SEPOLIA'); + }); + + it('should fetch pools from vault subgraph', async () => { + expect(vaultSubgraphClient.Pools).toHaveBeenCalled(); + }); + + it('should fetch pools from pools subgraph', async () => { + expect(poolSubgraphClient.Pools).toHaveBeenCalled(); + }); + + it('should store missing pools in the database', async () => { + expect(prisma.prismaPool.create).toHaveBeenCalledWith({ data: expect.objectContaining({ id: '2' }) }); + }); +}); diff --git a/modules/actions/pool/add-pools-from-subgraph.ts b/modules/actions/pool/add-pools-from-subgraph.ts new file mode 100644 index 000000000..7af60a10f --- /dev/null +++ b/modules/actions/pool/add-pools-from-subgraph.ts @@ -0,0 +1,143 @@ +import { Chain, Prisma, PrismaPoolType } from '@prisma/client'; +import { prisma } from '../../../prisma/prisma-client'; +import { + poolTransformer, + poolTokensTransformer, + poolTokensDynamicDataTransformer, + poolExpandedTokensTransformer, +} from '../../sources/transformers'; +import { V3PoolsSubgraphClient } from '../../subgraphs/balancer-v3-pools'; +import { V3SubgraphClient } from '../../subgraphs/balancer-v3-vault'; +import { PoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { PoolType, TypePoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; +import _ from 'lodash'; + +type PoolDbEntry = { + pool: Prisma.PrismaPoolCreateInput; + poolTokenDynamicData: Prisma.PrismaPoolTokenDynamicDataCreateManyInput[]; + poolExpandedTokens: Prisma.PrismaPoolExpandedTokensCreateManyInput[]; +}; +/** + * Makes sure that all pools are synced in the database + * + * @param vaultSubgraphClient + * @param poolSubgraphClient + * @param chain + * @returns syncedPools - the pools that were synced + */ +export async function addMissingPoolsFromSubgraph( + vaultSubgraphClient: Pick, + poolSubgraphClient: V3PoolsSubgraphClient, + // viemClient: ViemClient, + // vaultAddress: string, + chain = 'SEPOLIA' as Chain, +): Promise { + // Fetch pools from subgraph + // TODO this needs paging + const { pools: vaultSubgraphPools } = await vaultSubgraphClient.Pools(); + const { pools: poolSubgraphPools } = await poolSubgraphClient.Pools(); + + // Find pools missing from the database + const dbPools = await prisma.prismaPool.findMany({ where: { chain, vaultVersion: 3 } }); + const dbPoolIds = new Set(dbPools.map((pool) => pool.id.toLowerCase())); + const missingPools = vaultSubgraphPools.filter((pool) => !dbPoolIds.has(pool.id)); + + // Store pool tokens and BPT in the tokens table before creating the pools + try { + const allTokens: { address: string; name: string; decimals: number; symbol: string; chain: Chain }[] = []; + missingPools.forEach((pool) => { + allTokens.push({ + address: pool.address, + decimals: 18, + name: pool.name, + symbol: pool.symbol, + chain: chain, + }); + if (pool.tokens) { + for (const poolToken of pool.tokens) { + allTokens.push({ + address: poolToken.address, + decimals: poolToken.decimals, + name: poolToken.name, + symbol: poolToken.symbol, + chain: chain, + }); + } + } + }); + + await prisma.prismaToken.createMany({ + data: allTokens, + skipDuplicates: true, + }); + } catch (e) { + console.error('Error creating tokens', e); + } + + // Transform pool data for the database + const dbEntries: PoolDbEntry[] = []; + + missingPools.forEach((missingPool) => { + const vaultSubgraphPool = vaultSubgraphPools.find((pool) => pool.id === missingPool.id); + const poolSubgraphPool = poolSubgraphPools.find((pool) => pool.id === missingPool.id); + if (!vaultSubgraphPool || !poolSubgraphPool) { + // That won't happen, but TS doesn't know that + return null; + } + const dbEntry: PoolDbEntry = { + pool: { + ...poolTransformer(vaultSubgraphPool, poolSubgraphPool, chain), + typeData: JSON.stringify({}), + tokens: { + createMany: { + // TODO: Will be great to create all the token data here, including dynamic data + // but for now we can only store static data, because prisma doesn't support nested createMany + // to create dynamic data tabels as well. One solution is to move "dynamicData" to the tokens table + data: poolTokensTransformer(vaultSubgraphPool), + }, + }, + // placeholder data, will be updated with onchain values + dynamicData: { + create: { + id: vaultSubgraphPool.id, + swapFee: '0', + blockNumber: Number(vaultSubgraphPool.blockNumber), + swapEnabled: true, + totalLiquidity: 1, + totalShares: vaultSubgraphPool.totalShares, + totalSharesNum: parseFloat(vaultSubgraphPool.totalShares), + }, + }, + }, + poolTokenDynamicData: poolTokensDynamicDataTransformer(vaultSubgraphPool, poolSubgraphPool, chain), + poolExpandedTokens: poolExpandedTokensTransformer(vaultSubgraphPool, chain), + }; + dbEntries.push(dbEntry); + }); + + // Store missing pools in the database + const added: string[] = []; + for (const entry of dbEntries) { + try { + await prisma.prismaPool.create({ data: entry.pool }); + + await prisma.prismaPoolTokenDynamicData.createMany({ + skipDuplicates: true, + data: entry.poolTokenDynamicData, + }); + + // TODO deal with nested pools + await prisma.prismaPoolExpandedTokens.createMany({ + skipDuplicates: true, + data: entry.poolExpandedTokens, + }); + + added.push(entry.pool.id); + } catch (e) { + // TODO: handle errors + console.error(`Error creating pool ${entry.pool.id}`, e); + } + } + + return added; +} diff --git a/modules/actions/pool/get-changed-pools.ts b/modules/actions/pool/get-changed-pools.ts new file mode 100644 index 000000000..b19423b1d --- /dev/null +++ b/modules/actions/pool/get-changed-pools.ts @@ -0,0 +1,52 @@ +import { Chain, Prisma, PrismaLastBlockSyncedCategory, PrismaPoolType } from '@prisma/client'; +import { prisma } from '../../../prisma/prisma-client'; +import { tokenService } from '../../token/token.service'; +import { fetchPoolTokenInfo, fetchPoolTokenRates } from '../../sources/contracts'; +import { ViemClient } from '../../sources/viem-client'; +import { fetchPoolData } from '../../sources/contracts/fetch-pool-data'; +import { formatEther, formatUnits, parseUnits } from 'viem'; +import { isSameAddress } from '@balancer-labs/sdk'; +import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; +import { getPoolBalanceChanged } from '../../sources/logs/get-pool-balance-changed'; +import { start } from 'repl'; +import { getSwaps } from '../../sources/logs'; +import _ from 'lodash'; + +/** + * Get all pool IDs of pools that have emitted a poolBalanceChanged event + * + * @param vaultAddress + * @param viemClient + * @param chain + * @returns list of changed pool IDs + */ +export async function getChangedPools( + vaultAddress: string, + viemClient: ViemClient, + blockNumber: bigint, + chain = 'SEPOLIA' as Chain, +): Promise { + let lastSync = await prisma.prismaLastBlockSynced.findUnique({ + where: { category_chain: { category: PrismaLastBlockSyncedCategory.POOLS, chain: chain } }, + }); + const lastSyncBlock = lastSync?.blockNumber ? BigInt(lastSync.blockNumber) : 0n; + const latestBlock = blockNumber; + + const startBlock = lastSyncBlock + 1n; + const endBlock = latestBlock; + + // no new blocks have been minted, needed for slow networks + if (startBlock > endBlock) { + return []; + } + + const poolBalanceChangedEvents = await getPoolBalanceChanged(vaultAddress, viemClient, startBlock, endBlock); + const poolIdsFromBalanceChangedEvents = poolBalanceChangedEvents.map((event) => event.args.pool!); + + const swapEvents = await getSwaps(vaultAddress, viemClient, startBlock, endBlock); + const poolIdsFromSwapEvents = swapEvents.map((event) => event.args.pool!); + + const changedPoolIds = _.uniq(poolIdsFromBalanceChangedEvents.concat(poolIdsFromSwapEvents)); + + return changedPoolIds; +} diff --git a/modules/actions/pool/update-on-chain-data.ts b/modules/actions/pool/update-on-chain-data.ts new file mode 100644 index 000000000..4183d2074 --- /dev/null +++ b/modules/actions/pool/update-on-chain-data.ts @@ -0,0 +1,222 @@ +import { Chain, Prisma, PrismaPoolType } from '@prisma/client'; +import { prisma } from '../../../prisma/prisma-client'; +import { tokenService } from '../../token/token.service'; +import { fetchPoolTokenInfo, fetchPoolTokenRates } from '../../sources/contracts'; +import { ViemClient } from '../../sources/viem-client'; +import { fetchPoolData } from '../../sources/contracts/fetch-pool-data'; +import { formatEther, formatUnits, parseUnits } from 'viem'; +import { isSameAddress } from '@balancer-labs/sdk'; +import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; + +const SUPPORTED_POOL_TYPES: PrismaPoolType[] = ['WEIGHTED', 'STABLE']; + +export async function updateOnchainDataForAllPools( + vaultAddress: string, + viemClient: ViemClient, + blockNumber: bigint, + chain = 'SEPOLIA' as Chain, +): Promise { + const allPools = await prisma.prismaPool.findMany({ + where: { chain: chain, vaultVersion: 3 }, + select: { + id: true, + }, + }); + + return updateOnChainDataForPools( + vaultAddress, + '123', + allPools.map((pool) => pool.id), + viemClient, + blockNumber, + chain, + ); +} + +/** + * Makes sure that all pools are synced in the database + * + * @param vaultSubgraphClient + * @param poolSubgraphClient + * @param chain + * @returns syncedPools - the pools that were synced + */ +export async function updateOnChainDataForPools( + vaultAddress: string, + balancerQueriesAddress: string, + poolIds: string[], + viemClient: ViemClient, + blockNumber: bigint, + chain = 'SEPOLIA' as Chain, +): Promise { + if (poolIds.length === 0) { + return []; + } + const updated: string[] = []; + + const filteredPools = await prisma.prismaPool.findMany({ + where: { + id: { in: poolIds }, + chain: chain, + type: { in: SUPPORTED_POOL_TYPES }, + }, + include: { + tokens: { orderBy: { index: 'asc' }, include: { dynamicData: true, token: true } }, + dynamicData: true, + }, + }); + + const filteredPoolIds = filteredPools.map((pool) => pool.id); + const filteredPoolInputs = filteredPools.map((pool) => ({ + id: pool.id, + address: pool.address, + type: pool.type, + version: pool.version, + })); + + const tokenPricesForCurrentChain = await tokenService.getTokenPrices(chain); + const poolTokenData = await fetchPoolTokenInfo(vaultAddress, filteredPoolIds, viemClient, blockNumber); + const poolTokenRatesData = await fetchPoolTokenRates(vaultAddress, filteredPoolIds, viemClient, blockNumber); + const poolConfigData = await fetchPoolData(vaultAddress, filteredPoolInputs, viemClient, blockNumber); + + // TODO also need to add tokenPairs for SOR and calc normalized liquidity + // const tokenPairData = await fetchTokenPairData( + // filteredPools, + // balancerQueriesAddress, + // chain === 'ZKEVM' ? 190 : 1024, + // ); + + const operations = []; + for (const pool of filteredPools) { + const poolTokens = poolTokenData[pool.id]; + const poolTokenRates = poolTokenRatesData[pool.id]; + const poolConfig = poolConfigData[pool.id]; + + try { + // if (isStablePool(pool.type)) { + // if (!amp) { + // console.error(`Stable Pool Missing Amp: ${pool.id}`); + // continue; + // } + + // //only update if amp has changed + // if ((pool.typeData as StableData).amp !== amp) { + // operations.push( + // prisma.prismaPool.update({ + // where: { id_chain: { id: pool.id, chain: this.options.chain } }, + // data: { + // typeData: { + // ...(pool.typeData as StableData), + // amp, + // }, + // }, + // }), + // ); + // } + // } + + const swapFee = poolConfig.swapFee.toString(); + const totalSupply = formatEther(poolConfig.totalSupply); + const swapEnabled = !poolConfig.isPoolPaused; + const isPaused = poolConfig.isPoolPaused; + const isInRecoveryMode = poolConfig.isPoolInRecoveryMode; + + const yieldProtocolFeePercentage = '0'; // TODO + const protocolSwapFeePercentage = poolConfig.protocolSwapFeePercentage.toString(); + + if ( + pool.dynamicData && + (pool.dynamicData.swapFee !== swapFee || + pool.dynamicData.totalShares !== totalSupply || + pool.dynamicData.swapEnabled !== swapEnabled || + pool.dynamicData.protocolYieldFee !== yieldProtocolFeePercentage || + pool.dynamicData.protocolSwapFee !== protocolSwapFeePercentage || + pool.dynamicData.isInRecoveryMode !== isInRecoveryMode || + pool.dynamicData.isPaused !== isPaused) + ) { + operations.push( + prisma.prismaPoolDynamicData.update({ + where: { id_chain: { id: pool.id, chain: chain } }, + data: { + swapFee, + totalShares: totalSupply, + totalSharesNum: parseFloat(totalSupply), + swapEnabled: typeof swapEnabled !== 'undefined' ? swapEnabled : true, + isInRecoveryMode: isInRecoveryMode, + isPaused: isPaused, + protocolYieldFee: yieldProtocolFeePercentage, + protocolSwapFee: protocolSwapFeePercentage, + blockNumber: parseFloat(blockNumber.toString()), + }, + }), + ); + } + + // always update tokenPair data + // if (pool.dynamicData) { + // operations.push( + // prisma.prismaPoolDynamicData.update({ + // where: { id_chain: { id: pool.id, chain: this.options.chain } }, + // data: { + // tokenPairsData: tokenPairs, + // }, + // }), + // ); + // } + + for (let i = 0; i < poolTokens.tokens.length; i++) { + const tokenAddress = poolTokens.tokens[i]; + const poolToken = pool.tokens.find((token) => isSameAddress(token.address, tokenAddress)); + + if (!poolToken) { + 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 = formatUnits(poolTokens.balancesRaw[i], poolToken.token.decimals); + + // set token price rate for various rate types + + // top level token rates, e.g. LSTs in pools + let priceRate = formatEther(poolTokenRates[i]); + + // // bpt price rate + // if (onchainData.rate && isSameAddress(poolToken.address, pool.address)) { + // priceRate = onchainData.rate; + // } + + // TODO v3 does not contain the BPT as pool token, do we need to add it nevertheless? + + if ( + !poolToken.dynamicData || + poolToken.dynamicData.balance !== balance || + poolToken.dynamicData.priceRate !== priceRate + ) { + operations.push( + prisma.prismaPoolTokenDynamicData.update({ + where: { id_chain: { id: poolToken.id, chain: chain } }, + data: { + blockNumber: parseFloat(blockNumber.toString()), + priceRate, + balance, + balanceUSD: + poolToken.address === pool.address + ? 0 + : tokenService.getPriceForToken(tokenPricesForCurrentChain, poolToken.address) * + parseFloat(balance), + }, + }), + ); + } + } + } catch (e) { + console.log('error syncing on chain data', e); + } + } + await prismaBulkExecuteOperations(operations, false); + + return updated; +} diff --git a/modules/content/github-content.service.ts b/modules/content/github-content.service.ts index b8eecb11d..079adc6cc 100644 --- a/modules/content/github-content.service.ts +++ b/modules/content/github-content.service.ts @@ -4,7 +4,7 @@ import axios from 'axios'; import { prisma } from '../../prisma/prisma-client'; import { networkContext } from '../network/network-context.service'; import { ContentService, FeaturedPool, HomeScreenFeaturedPoolGroup, HomeScreenNewsItem } from './content-types'; -import { chainIdToChain } from '../network/network-config'; +import { chainIdToChain } from '../network/chain-id-to-chain'; import { LinearData } from '../pool/subgraph-mapper'; const POOLS_METADATA_URL = 'https://raw.githubusercontent.com/balancer/metadata/main/pools/featured.json'; diff --git a/modules/context/header-chain.ts b/modules/context/header-chain.ts index 799f9d097..55224f32b 100644 --- a/modules/context/header-chain.ts +++ b/modules/context/header-chain.ts @@ -1,6 +1,6 @@ import { getRequestScopeContextValue } from '../context/request-scoped-context'; import { Chain } from '@prisma/client'; -import { chainIdToChain } from '../network/network-config'; +import { chainIdToChain } from '../network/chain-id-to-chain'; /** * Setup to transition out from the old header-based chainIDs to the new required chain query filters. diff --git a/modules/controllers/jobs-controller.test.ts b/modules/controllers/jobs-controller.test.ts new file mode 100644 index 000000000..537d322f2 --- /dev/null +++ b/modules/controllers/jobs-controller.test.ts @@ -0,0 +1,20 @@ +import { addMissingPoolsFromSubgraph } from '../actions/pool/add-pools-from-subgraph'; +import { JobsController } from './jobs-controller'; +// Mock the actions +jest.mock('@modules/actions/jobs_actions', () => ({ + syncPools: jest.fn(), +})); + +describe('jobsController', () => { + const jobsController = JobsController(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call getClient with correct chain', () => { + jobsController.addMissingPoolsFromSubgraph('11155111'); + + expect(addMissingPoolsFromSubgraph).toHaveBeenCalled(); + }); +}); diff --git a/modules/controllers/jobs-controller.ts b/modules/controllers/jobs-controller.ts new file mode 100644 index 000000000..79fc608ed --- /dev/null +++ b/modules/controllers/jobs-controller.ts @@ -0,0 +1,92 @@ +import { update } from 'lodash'; +import config from '../../config'; +import { addMissingPoolsFromSubgraph } from '../actions/pool/add-pools-from-subgraph'; +import { getChangedPools } from '../actions/pool/get-changed-pools'; +import { updateOnChainDataForPools } from '../actions/pool/update-on-chain-data'; +import { chainIdToChain } from '../network/chain-id-to-chain'; +import { getViemClient } from '../sources/viem-client'; +import { getPoolsSubgraphClient } from '../subgraphs/balancer-v3-pools'; +import { getVaultSubgraphClient } from '../subgraphs/balancer-v3-vault'; + +/** + * Controller responsible for matching job requests to configured job handlers + * + * @param name - the name of the job + * @param chain - the chain to run the job on + * @returns a controller with configured job handlers + */ +export function JobsController(tracer?: any) { + // Setup tracing + // ... + return { + async addMissingPoolsFromSubgraph(chainId: string) { + const chain = chainIdToChain[chainId]; + const { + subgraphs: { balancerV3, balancerPoolsV3 }, + balancer: { + v3: { vaultAddress }, + }, + } = config[chain]; + + // Guard against unconfigured chains + if (!balancerV3) { + throw new Error(`Chain not configured: ${chain}`); + } + + const vaultSubgraphClient = getVaultSubgraphClient(balancerV3); + const poolSubgraphClient = getPoolsSubgraphClient(balancerPoolsV3!); + const viemClient = getViemClient(chain); + const latestBlock = await viemClient.getBlockNumber(); + + // TODO: add syncing v2 pools as well by splitting the poolService into separate + // actions with extracted configuration + + // find all missing pools and add them to the DB + const added = await addMissingPoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, chain); + + // update with latest on-chain data + const updated = await updateOnChainDataForPools(vaultAddress, '123', added, viemClient, latestBlock); + + // also sync swaps and volumeAndFee values + // const poolsWithNewSwaps = await poolService.syncSwapsForLast48Hours(); + // await poolService.updateVolumeAndFeeValuesForPools(poolsWithNewSwaps); + + return updated; + }, + async updateOnChainDataChangedPools(chainId: string) { + const chain = chainIdToChain[chainId]; + const { + balancer: { + v3: { vaultAddress }, + }, + } = config[chain]; + + // Guard against unconfigured chains + if (!vaultAddress) { + throw new Error(`Chain not configured: ${chain}`); + } + const viemClient = getViemClient(chain); + + const blockNumber = await viemClient.getBlockNumber(); + + const changedPools = await getChangedPools(vaultAddress, viemClient, blockNumber, chain); + if (changedPools.length === 0) { + return []; + } + + const updated = updateOnChainDataForPools( + vaultAddress, + '123', + changedPools, + viemClient, + blockNumber, + chain, + ); + // also sync swaps and volumeAndFee values + // const poolsWithNewSwaps = await poolService.syncSwapsForLast48Hours(); + // await poolService.updateVolumeAndFeeValuesForPools(poolsWithNewSwaps); + + return updated; + }, + }; +} diff --git a/modules/controllers/pools-controller.ts b/modules/controllers/pools-controller.ts new file mode 100644 index 000000000..400d1c5e9 --- /dev/null +++ b/modules/controllers/pools-controller.ts @@ -0,0 +1,36 @@ +import config from '../../config'; +import { updateOnchainDataForAllPools } from '../actions/pool/update-on-chain-data'; +import { chainIdToChain } from '../network/chain-id-to-chain'; +import { getViemClient } from '../sources/viem-client'; + +/** + * Controller responsible for matching job requests to configured job handlers + * + * @param name - the name of the job + * @param chain - the chain to run the job on + * @returns a controller with configured job handlers + */ +export function PoolsController(tracer?: any) { + // Setup tracing + // ... + return { + async updateOnChainDataForAllPools(chainId: string) { + const chain = chainIdToChain[chainId]; + const { + balancer: { + v3: { vaultAddress }, + }, + } = config[chain]; + + // Guard against unconfigured chains + if (!vaultAddress) { + throw new Error(`Chain not configured: ${chain}`); + } + const viemClient = getViemClient(chain); + const latestBlockNumber = await viemClient.getBlockNumber(); + + const updated = updateOnchainDataForAllPools(vaultAddress, viemClient, latestBlockNumber, chain); + return updated; + }, + }; +} diff --git a/modules/network/chain-id-to-chain.ts b/modules/network/chain-id-to-chain.ts new file mode 100644 index 000000000..49cdba0ab --- /dev/null +++ b/modules/network/chain-id-to-chain.ts @@ -0,0 +1,14 @@ +import { Chain } from '@prisma/client'; + +export const chainIdToChain: { [id: string]: Chain } = { + '1': Chain.MAINNET, + '10': Chain.OPTIMISM, + '100': Chain.GNOSIS, + '137': Chain.POLYGON, + '250': Chain.FANTOM, + '1101': Chain.ZKEVM, + '8453': Chain.BASE, + '42161': Chain.ARBITRUM, + '43114': Chain.AVALANCHE, + '11155111': Chain.SEPOLIA, +}; diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index 345b484ca..595ba5269 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -65,6 +65,8 @@ export interface NetworkData { subgraphs: { startDate: string; balancer: string; + balancerV3?: string; + balancerPoolsV3?: string; blocks: string; masterchef?: string; reliquary?: string; diff --git a/modules/network/network-config.ts b/modules/network/network-config.ts index fd555b33e..1cb0c6033 100644 --- a/modules/network/network-config.ts +++ b/modules/network/network-config.ts @@ -37,19 +37,6 @@ export const AllNetworkConfigsKeyedOnChain: { [chain in Chain]: NetworkConfig } SEPOLIA: sepoliaNetworkConfig, }; -export const chainIdToChain: { [id: string]: Chain } = { - '1': Chain.MAINNET, - '10': Chain.OPTIMISM, - '100': Chain.GNOSIS, - '137': Chain.POLYGON, - '250': Chain.FANTOM, - '1101': Chain.ZKEVM, - '8453': Chain.BASE, - '42161': Chain.ARBITRUM, - '43114': Chain.AVALANCHE, - '11155111': Chain.SEPOLIA, -}; - export const BalancerChainIds = ['1', '137', '42161', '100', '1101', '43114', '8453', '11155111']; export const BeethovenChainIds = ['250', '10']; diff --git a/modules/network/sepolia.ts b/modules/network/sepolia.ts index c25dbb054..b3ae77b27 100644 --- a/modules/network/sepolia.ts +++ b/modules/network/sepolia.ts @@ -1,5 +1,5 @@ -import { BigNumber, ethers } from 'ethers'; -import { DeploymentEnv, NetworkConfig, NetworkData } from './network-config-types'; +import { ethers } from 'ethers'; +import { NetworkConfig } from './network-config-types'; import { tokenService } from '../token/token.service'; import { PhantomStableAprService } from '../pool/lib/apr-data-sources/phantom-stable-apr.service'; import { BoostedPoolAprService } from '../pool/lib/apr-data-sources/boosted-pool-apr.service'; @@ -13,117 +13,11 @@ import { UserSyncGaugeBalanceService } from '../user/lib/user-sync-gauge-balance import { every } from '../../worker/intervals'; import { GithubContentService } from '../content/github-content.service'; import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; -import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; -import { coingeckoService } from '../coingecko/coingecko.service'; import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; -import { env } from '../../app/env'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; +import { sepoliaConfig as sepoliaNetworkData } from '../../config/sepolia'; -export const sepoliaNetworkData: NetworkData = { - chain: { - slug: 'sepolia', - id: 11155111, - nativeAssetAddress: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - wrappedNativeAssetAddress: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9', - prismaId: 'SEPOLIA', - gqlId: 'SEPOLIA', - }, - subgraphs: { - startDate: '2023-05-03', - balancer: 'https://api.studio.thegraph.com/query/24660/balancer-sepolia-v2/version/latest', - beetsBar: 'https://', - blocks: 'https://api.studio.thegraph.com/query/48427/bleu-sepolia-blocks/version/latest', - gauge: 'https://api.studio.thegraph.com/proxy/24660/balancer-gauges-sepolia/version/latest', - // veBalLocks: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges', - userBalances: 'https://', - }, - eth: { - address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - addressFormatted: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - symbol: 'ETH', - name: 'Ether', - }, - weth: { - address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', - addressFormatted: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - }, - coingecko: { - nativeAssetId: 'ethereum', - platformId: 'ethereum', - excludedTokenAddresses: [], - }, - rpcUrl: env.INFURA_API_KEY - ? `https://sepolia.infura.io/v3/${env.INFURA_API_KEY}` - : 'https://gateway.tenderly.co/public/sepolia', - rpcMaxBlockRange: 700, - protocolToken: 'bal', - bal: { - address: '0xb19382073c7A0aDdbb56Ac6AF1808Fa49e377B75', - }, - // veBal: { - // address: '0xc128a9954e6c874ea3d62ce62b468ba073093f25', - // delegationProxy: '0x81cfae226343b24ba12ec6521db2c79e7aeeb310', - // }, - balancer: { - v2: { - vaultAddress: '0xba12222222228d8ba445958a75a0704d566bf2c8', - defaultSwapFeePercentage: '0.5', - defaultYieldFeePercentage: '0.5', - balancerQueriesAddress: '0xe39b5e3b6d74016b2f6a9673d7d7493b6df549d5', - }, - v3: { - vaultAddress: '0x816e90DC85bF016455017a76Bc09CC0451Eeb308', - defaultSwapFeePercentage: '0.5', - defaultYieldFeePercentage: '0.5', - }, - }, - multicall: '0x80c7dd17b01855a6d2347444a0fcc36136a314de', - multicall3: '0xca11bde05977b3631167028862be2a173976ca11', - avgBlockSpeed: 1, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, - ybAprConfig: {}, - 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', - }, - }, - monitoring: { - main: { - alarmTopicArn: 'arn:aws:sns:ca-central-1:118697801881:api_alarms', - }, - canary: { - alarmTopicArn: 'arn:aws:sns:eu-central-1:118697801881:api_alarms', - }, - }, -}; +export { sepoliaNetworkData }; export const sepoliaNetworkConfig: NetworkConfig = { data: sepoliaNetworkData, @@ -220,25 +114,17 @@ export const sepoliaNetworkConfig: NetworkConfig = { name: 'user-sync-staked-balances', interval: every(5, 'minutes'), }, - // { - // name: 'sync-coingecko-coinids', - // interval: every(2, 'hours'), - // }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), }, - // { - // name: 'sync-vebal-balances', - // interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(9, 'minutes') : every(3, 'minutes'), - // }, - // { - // name: 'sync-vebal-totalSupply', - // interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), - // }, - // { - // name: 'feed-data-to-datastudio', - // interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(5, 'minutes'), - // }, + { + name: 'sync-changed-pools-v3', + interval: every(15, 'minutes'), + }, + { + name: 'sync-new-pools-from-subgraph-v3', + interval: every(20, 'minutes'), + }, ], }; diff --git a/modules/pool/abi/VaultV3.json b/modules/pool/abi/VaultV3.json deleted file mode 100644 index 2e0ae17c0..000000000 --- a/modules/pool/abi/VaultV3.json +++ /dev/null @@ -1,3401 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract IVaultExtension", - "name": "vaultExtension", - "type": "address" - }, - { - "internalType": "contract IAuthorizer", - "name": "authorizer", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AllZeroInputs", - "type": "error" - }, - { - "inputs": [], - "name": "AmountGivenZero", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "AmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "AmountOutBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "BalanceNotSettled", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "BptAmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "BptAmountOutBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "CallbackFailed", - "type": "error" - }, - { - "inputs": [], - "name": "CannotReceiveEth", - "type": "error" - }, - { - "inputs": [], - "name": "CannotSwapSameToken", - "type": "error" - }, - { - "inputs": [], - "name": "DoesNotSupportAddLiquidityCustom", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "HandlerOutOfBounds", - "type": "error" - }, - { - "inputs": [], - "name": "InputLengthMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAddLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRemoveLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidToken", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenConfiguration", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenType", - "type": "error" - }, - { - "inputs": [], - "name": "MaxTokens", - "type": "error" - }, - { - "inputs": [], - "name": "MinTokens", - "type": "error" - }, - { - "inputs": [], - "name": "MultipleNonZeroInputs", - "type": "error" - }, - { - "inputs": [], - "name": "NoHandler", - "type": "error" - }, - { - "inputs": [], - "name": "NotStaticCall", - "type": "error" - }, - { - "inputs": [], - "name": "NotVaultDelegateCall", - "type": "error" - }, - { - "inputs": [], - "name": "PauseBufferPeriodDurationTooLarge", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPauseWindowExpired", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPaused", - "type": "error" - }, - { - "inputs": [], - "name": "ProtocolSwapFeePercentageTooHigh", - "type": "error" - }, - { - "inputs": [], - "name": "QueriesDisabled", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "RouterNotTrusted", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "bits", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "SafeCastOverflowedUintDowncast", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "SafeCastOverflowedUintToInt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "SenderIsNotPauseManager", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "SenderIsNotVault", - "type": "error" - }, - { - "inputs": [], - "name": "SwapFeePercentageTooHigh", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "TokenAlreadyRegistered", - "type": "error" - }, - { - "inputs": [], - "name": "TokenNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "expectedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "actualToken", - "type": "address" - } - ], - "name": "TokensMismatch", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "TotalSupplyTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "UserDataNotSupported", - "type": "error" - }, - { - "inputs": [], - "name": "VaultNotPaused", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowDurationTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowExpired", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "handler", - "type": "address" - }, - { - "internalType": "address", - "name": "caller", - "type": "address" - } - ], - "name": "WrongHandler", - "type": "error" - }, - { - "inputs": [], - "name": "WrongVaultExtensionDeployment", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroDivision", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "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": "contract IAuthorizer", - "name": "newAuthorizer", - "type": "address" - } - ], - "name": "AuthorizerChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "liquidityProvider", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "indexed": false, - "internalType": "int256[]", - "name": "deltas", - "type": "int256[]" - } - ], - "name": "PoolBalanceChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInitialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PoolPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "recoveryMode", - "type": "bool" - } - ], - "name": "PoolRecoveryModeStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "factory", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "yieldFeeExempt", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "pauseWindowEndTime", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "components": [ - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct PoolCallbacks", - "name": "callbacks", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "supportsAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "supportsRemoveLiquidityCustom", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "name": "PoolRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ProtocolFeeCollected", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "ProtocolSwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "swapFeeAmount", - "type": "uint256" - } - ], - "name": "Swap", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "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": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "VaultPausedStateChanged", - "type": "event" - }, - { - "stateMutability": "payable", - "type": "fallback" - }, - { - "inputs": [], - "name": "MAX_BUFFER_PERIOD_DURATION", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MAX_PAUSE_WINDOW_DURATION", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "maxAmountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "minBptAmountOut", - "type": "uint256" - }, - { - "internalType": "enum AddLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct AddLiquidityParams", - "name": "params", - "type": "tuple" - } - ], - "name": "addLiquidity", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "bptAmountOut", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "returnData", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getVaultExtension", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "invoke", - "outputs": [ - { - "internalType": "bytes", - "name": "result", - "type": "bytes" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "maxBptAmountIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "minAmountsOut", - "type": "uint256[]" - }, - { - "internalType": "enum RemoveLiquidityKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct RemoveLiquidityParams", - "name": "params", - "type": "tuple" - } - ], - "name": "removeLiquidity", - "outputs": [ - { - "internalType": "uint256", - "name": "bptAmountIn", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "amountsOut", - "type": "uint256[]" - }, - { - "internalType": "bytes", - "name": "returnData", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "exactBptAmountIn", - "type": "uint256" - } - ], - "name": "removeLiquidityRecovery", - "outputs": [ - { - "internalType": "uint256[]", - "name": "amountsOutRaw", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "retrieve", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "settle", - "outputs": [ - { - "internalType": "uint256", - "name": "paid", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum SwapKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "tokenIn", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "tokenOut", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amountGivenRaw", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "internalType": "struct SwapParams", - "name": "params", - "type": "tuple" - } - ], - "name": "swap", - "outputs": [ - { - "internalType": "uint256", - "name": "amountCalculated", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "wire", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - }, - - "inputs": [ - { - "internalType": "contract IVault", - "name": "mainVault", - "type": "address" - }, - { - "internalType": "uint256", - "name": "pauseWindowDuration", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "bufferPeriodDuration", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AmountGivenZero", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "AmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "AmountOutBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "BalanceNotSettled", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "BptAmountInAboveMax", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "BptAmountOutBelowMin", - "type": "error" - }, - { - "inputs": [], - "name": "CallbackFailed", - "type": "error" - }, - { - "inputs": [], - "name": "CannotReceiveEth", - "type": "error" - }, - { - "inputs": [], - "name": "CannotSwapSameToken", - "type": "error" - }, - { - "inputs": [], - "name": "CodecOverflow", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "HandlerOutOfBounds", - "type": "error" - }, - { - "inputs": [], - "name": "InputLengthMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAddLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRemoveLiquidityKind", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidToken", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenConfiguration", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidTokenType", - "type": "error" - }, - { - "inputs": [], - "name": "MaxTokens", - "type": "error" - }, - { - "inputs": [], - "name": "MinTokens", - "type": "error" - }, - { - "inputs": [], - "name": "NoHandler", - "type": "error" - }, - { - "inputs": [], - "name": "NotStaticCall", - "type": "error" - }, - { - "inputs": [], - "name": "NotVaultDelegateCall", - "type": "error" - }, - { - "inputs": [], - "name": "OutOfBounds", - "type": "error" - }, - { - "inputs": [], - "name": "PauseBufferPeriodDurationTooLarge", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolAlreadyRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInRecoveryMode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotInitialized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPauseWindowExpired", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolPaused", - "type": "error" - }, - { - "inputs": [], - "name": "ProtocolSwapFeePercentageTooHigh", - "type": "error" - }, - { - "inputs": [], - "name": "QueriesDisabled", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [], - "name": "RouterNotTrusted", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "bits", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "SafeCastOverflowedUintDowncast", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "SafeCastOverflowedUintToInt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "SenderIsNotPauseManager", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "SenderIsNotVault", - "type": "error" - }, - { - "inputs": [], - "name": "SenderNotAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "SwapFeePercentageTooHigh", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "TokenAlreadyRegistered", - "type": "error" - }, - { - "inputs": [], - "name": "TokenNotRegistered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "expectedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "actualToken", - "type": "address" - } - ], - "name": "TokensMismatch", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit", - "type": "uint256" - } - ], - "name": "TotalSupplyTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "UserDataNotSupported", - "type": "error" - }, - { - "inputs": [], - "name": "VaultNotPaused", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowDurationTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPauseWindowExpired", - "type": "error" - }, - { - "inputs": [], - "name": "VaultPaused", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "handler", - "type": "address" - }, - { - "internalType": "address", - "name": "caller", - "type": "address" - } - ], - "name": "WrongHandler", - "type": "error" - }, - { - "inputs": [], - "name": "WrongVaultExtensionDeployment", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "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": "contract IAuthorizer", - "name": "newAuthorizer", - "type": "address" - } - ], - "name": "AuthorizerChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "liquidityProvider", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "indexed": false, - "internalType": "int256[]", - "name": "deltas", - "type": "int256[]" - } - ], - "name": "PoolBalanceChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "PoolInitialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PoolPausedStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "recoveryMode", - "type": "bool" - } - ], - "name": "PoolRecoveryModeStateChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "factory", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "yieldFeeExempt", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "pauseWindowEndTime", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "components": [ - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct PoolCallbacks", - "name": "callbacks", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "supportsAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "supportsRemoveLiquidityCustom", - "type": "bool" - } - ], - "indexed": false, - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "name": "PoolRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ProtocolFeeCollected", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "ProtocolSwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "SwapFeePercentageChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "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": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "VaultPausedStateChanged", - "type": "event" - }, - { - "inputs": [], - "name": "MAX_BUFFER_PERIOD_DURATION", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MAX_PAUSE_WINDOW_DURATION", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "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": "owner", - "type": "address" - }, - { - "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": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - } - ], - "name": "collectProtocolFees", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "disableQuery", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "disableRecoveryMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "enableRecoveryMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "getActionId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAuthorizer", - "outputs": [ - { - "internalType": "contract IAuthorizer", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getBufferPeriodDuration", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getBufferPeriodEndTime", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "getHandler", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getHandlersCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getMaximumPoolTokens", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "getMinimumPoolTokens", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "getNonzeroDeltaCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getPauseWindowEndTime", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolConfig", - "outputs": [ - { - "components": [ - { - "internalType": "bool", - "name": "isPoolRegistered", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolInitialized", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolPaused", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isPoolInRecoveryMode", - "type": "bool" - }, - { - "internalType": "bool", - "name": "hasDynamicSwapFee", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "staticSwapFeePercentage", - "type": "uint64" - }, - { - "internalType": "uint24", - "name": "tokenDecimalDiffs", - "type": "uint24" - }, - { - "internalType": "uint32", - "name": "pauseWindowEndTime", - "type": "uint32" - }, - { - "components": [ - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - } - ], - "internalType": "struct PoolCallbacks", - "name": "callbacks", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "supportsAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "supportsRemoveLiquidityCustom", - "type": "bool" - } - ], - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "internalType": "struct PoolConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolPausedState", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getPoolTokenCountAndIndexOfToken", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokenInfo", - "outputs": [ - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "internalType": "enum TokenType[]", - "name": "tokenTypes", - "type": "uint8[]" - }, - { - "internalType": "uint256[]", - "name": "balancesRaw", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "decimalScalingFactors", - "type": "uint256[]" - }, - { - "internalType": "contract IRateProvider[]", - "name": "rateProviders", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokenRates", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getPoolTokens", - "outputs": [ - { - "internalType": "contract IERC20[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "getProtocolSwapFee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getProtocolSwapFeePercentage", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "getStaticSwapFeePercentage", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getTokenDelta", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - } - ], - "name": "getTokenReserve", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getVaultPausedState", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "contract IERC20[]", - "name": "tokens", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "exactAmountsIn", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "minBptAmountOut", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - } - ], - "name": "initialize", - "outputs": [ - { - "internalType": "uint256", - "name": "bptAmountOut", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolInRecoveryMode", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolInitialized", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolPaused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "isPoolRegistered", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isQueryDisabled", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isVaultPaused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "pausePool", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "pauseVault", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "quote", - "outputs": [ - { - "internalType": "bytes", - "name": "result", - "type": "bytes" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "enum TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "contract IRateProvider", - "name": "rateProvider", - "type": "address" - }, - { - "internalType": "bool", - "name": "yieldFeeExempt", - "type": "bool" - } - ], - "internalType": "struct TokenConfig[]", - "name": "tokenConfig", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "pauseWindowEndTime", - "type": "uint256" - }, - { - "internalType": "address", - "name": "pauseManager", - "type": "address" - }, - { - "components": [ - { - "internalType": "bool", - "name": "shouldCallBeforeInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterInitialize", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterSwap", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterAddLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallBeforeRemoveLiquidity", - "type": "bool" - }, - { - "internalType": "bool", - "name": "shouldCallAfterRemoveLiquidity", - "type": "bool" - } - ], - "internalType": "struct PoolCallbacks", - "name": "poolCallbacks", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bool", - "name": "supportsAddLiquidityCustom", - "type": "bool" - }, - { - "internalType": "bool", - "name": "supportsRemoveLiquidityCustom", - "type": "bool" - } - ], - "internalType": "struct LiquidityManagement", - "name": "liquidityManagement", - "type": "tuple" - } - ], - "name": "registerPool", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IAuthorizer", - "name": "newAuthorizer", - "type": "address" - } - ], - "name": "setAuthorizer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newProtocolSwapFeePercentage", - "type": "uint256" - } - ], - "name": "setProtocolSwapFeePercentage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - }, - { - "internalType": "uint256", - "name": "swapFeePercentage", - "type": "uint256" - } - ], - "name": "setStaticSwapFeePercentage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pool", - "type": "address" - } - ], - "name": "unpausePool", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpauseVault", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "vault", - "outputs": [ - { - "internalType": "contract IVault", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } -] \ No newline at end of file diff --git a/modules/sources/contracts/abis/VaultV3.ts b/modules/sources/contracts/abis/VaultV3.ts new file mode 100644 index 000000000..dcf38d114 --- /dev/null +++ b/modules/sources/contracts/abis/VaultV3.ts @@ -0,0 +1,3622 @@ +export const vaultV3Abi = [ + { + inputs: [ + { + internalType: 'contract IVaultExtension', + name: 'vaultExtension', + type: 'address', + }, + { + internalType: 'contract IAuthorizer', + name: 'authorizer', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'address', + name: 'target', + type: 'address', + }, + ], + name: 'AddressEmptyCode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'AddressInsufficientBalance', + type: 'error', + }, + { + inputs: [], + name: 'AllZeroInputs', + type: 'error', + }, + { + inputs: [], + name: 'AmountGivenZero', + type: 'error', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'AmountInAboveMax', + type: 'error', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'AmountOutBelowMin', + type: 'error', + }, + { + inputs: [], + name: 'BalanceNotSettled', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'BptAmountInAboveMax', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'BptAmountOutBelowMin', + type: 'error', + }, + { + inputs: [], + name: 'CannotReceiveEth', + type: 'error', + }, + { + inputs: [], + name: 'CannotSwapSameToken', + type: 'error', + }, + { + inputs: [], + name: 'DoesNotSupportAddLiquidityCustom', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'allowance', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'needed', + type: 'uint256', + }, + ], + name: 'ERC20InsufficientAllowance', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'balance', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'needed', + type: 'uint256', + }, + ], + name: 'ERC20InsufficientBalance', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'approver', + type: 'address', + }, + ], + name: 'ERC20InvalidApprover', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'receiver', + type: 'address', + }, + ], + name: 'ERC20InvalidReceiver', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'ERC20InvalidSender', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + ], + name: 'ERC20InvalidSpender', + type: 'error', + }, + { + inputs: [], + name: 'FailedInnerCall', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'index', + type: 'uint256', + }, + ], + name: 'HandlerOutOfBounds', + type: 'error', + }, + { + inputs: [], + name: 'HookFailed', + type: 'error', + }, + { + inputs: [], + name: 'InputLengthMismatch', + type: 'error', + }, + { + inputs: [], + name: 'InvalidAddLiquidityKind', + type: 'error', + }, + { + inputs: [], + name: 'InvalidRemoveLiquidityKind', + type: 'error', + }, + { + inputs: [], + name: 'InvalidToken', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTokenConfiguration', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTokenType', + type: 'error', + }, + { + inputs: [], + name: 'MaxTokens', + type: 'error', + }, + { + inputs: [], + name: 'MinTokens', + type: 'error', + }, + { + inputs: [], + name: 'MultipleNonZeroInputs', + type: 'error', + }, + { + inputs: [], + name: 'NoHandler', + type: 'error', + }, + { + inputs: [], + name: 'NotStaticCall', + type: 'error', + }, + { + inputs: [], + name: 'NotVaultDelegateCall', + type: 'error', + }, + { + inputs: [], + name: 'PauseBufferPeriodDurationTooLarge', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolAlreadyInitialized', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolAlreadyRegistered', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolInRecoveryMode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotInRecoveryMode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotInitialized', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotPaused', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotRegistered', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolPauseWindowExpired', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolPaused', + type: 'error', + }, + { + inputs: [], + name: 'ProtocolSwapFeePercentageTooHigh', + type: 'error', + }, + { + inputs: [], + name: 'ProtocolYieldFeePercentageTooHigh', + type: 'error', + }, + { + inputs: [], + name: 'QueriesDisabled', + type: 'error', + }, + { + inputs: [], + name: 'ReentrancyGuardReentrantCall', + type: 'error', + }, + { + inputs: [], + name: 'RouterNotTrusted', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'bits', + type: 'uint8', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'SafeCastOverflowedUintDowncast', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'SafeCastOverflowedUintToInt', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + ], + name: 'SafeERC20FailedOperation', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'SenderIsNotPauseManager', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'SenderIsNotVault', + type: 'error', + }, + { + inputs: [], + name: 'SwapFeePercentageTooHigh', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'SwapLimit', + type: 'error', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'TokenAlreadyRegistered', + type: 'error', + }, + { + inputs: [], + name: 'TokenNotRegistered', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'address', + name: 'expectedToken', + type: 'address', + }, + { + internalType: 'address', + name: 'actualToken', + type: 'address', + }, + ], + name: 'TokensMismatch', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'TotalSupplyTooLow', + type: 'error', + }, + { + inputs: [], + name: 'UserDataNotSupported', + type: 'error', + }, + { + inputs: [], + name: 'VaultNotPaused', + type: 'error', + }, + { + inputs: [], + name: 'VaultPauseWindowDurationTooLarge', + type: 'error', + }, + { + inputs: [], + name: 'VaultPauseWindowExpired', + type: 'error', + }, + { + inputs: [], + name: 'VaultPaused', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'handler', + type: 'address', + }, + { + internalType: 'address', + name: 'caller', + type: 'address', + }, + ], + name: 'WrongHandler', + type: 'error', + }, + { + inputs: [], + name: 'WrongVaultExtensionDeployment', + type: 'error', + }, + { + inputs: [], + name: 'ZeroDivision', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + 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: 'contract IAuthorizer', + name: 'newAuthorizer', + type: 'address', + }, + ], + name: 'AuthorizerChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'liquidityProvider', + type: 'address', + }, + { + indexed: false, + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + { + indexed: false, + internalType: 'int256[]', + name: 'deltas', + type: 'int256[]', + }, + ], + name: 'PoolBalanceChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolInitialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'paused', + type: 'bool', + }, + ], + name: 'PoolPausedStateChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'recoveryMode', + type: 'bool', + }, + ], + name: 'PoolRecoveryModeStateChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'factory', + type: 'address', + }, + { + components: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'enum TokenType', + name: 'tokenType', + type: 'uint8', + }, + { + internalType: 'contract IRateProvider', + name: 'rateProvider', + type: 'address', + }, + { + internalType: 'bool', + name: 'yieldFeeExempt', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct TokenConfig[]', + name: 'tokenConfig', + type: 'tuple[]', + }, + { + indexed: false, + internalType: 'uint256', + name: 'pauseWindowEndTime', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'pauseManager', + type: 'address', + }, + { + components: [ + { + internalType: 'bool', + name: 'shouldCallBeforeInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeRemoveLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterRemoveLiquidity', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct PoolHooks', + name: 'hooks', + type: 'tuple', + }, + { + components: [ + { + internalType: 'bool', + name: 'supportsAddLiquidityCustom', + type: 'bool', + }, + { + internalType: 'bool', + name: 'supportsRemoveLiquidityCustom', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct LiquidityManagement', + name: 'liquidityManagement', + type: 'tuple', + }, + ], + name: 'PoolRegistered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'ProtocolFeeCollected', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'ProtocolSwapFeeCharged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'swapFeePercentage', + type: 'uint256', + }, + ], + name: 'ProtocolSwapFeePercentageChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'ProtocolYieldFeeCharged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'yieldFeePercentage', + type: 'uint256', + }, + ], + name: 'ProtocolYieldFeePercentageChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'contract IERC20', + name: 'tokenIn', + type: 'address', + }, + { + indexed: true, + internalType: 'contract IERC20', + name: 'tokenOut', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountIn', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountOut', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'swapFeeAmount', + type: 'uint256', + }, + ], + name: 'Swap', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + 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: 'bool', + name: 'paused', + type: 'bool', + }, + ], + name: 'VaultPausedStateChanged', + type: 'event', + }, + { + stateMutability: 'payable', + type: 'fallback', + }, + { + inputs: [], + name: 'MAX_BUFFER_PERIOD_DURATION', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_PAUSE_WINDOW_DURATION', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256[]', + name: 'maxAmountsIn', + type: 'uint256[]', + }, + { + internalType: 'uint256', + name: 'minBptAmountOut', + type: 'uint256', + }, + { + internalType: 'enum AddLiquidityKind', + name: 'kind', + type: 'uint8', + }, + { + internalType: 'bytes', + name: 'userData', + type: 'bytes', + }, + ], + internalType: 'struct AddLiquidityParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'addLiquidity', + outputs: [ + { + internalType: 'uint256[]', + name: 'amountsIn', + type: 'uint256[]', + }, + { + internalType: 'uint256', + name: 'bptAmountOut', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'returnData', + type: 'bytes', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'getPoolTokenCountAndIndexOfToken', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getVaultExtension', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + ], + name: 'invoke', + outputs: [ + { + internalType: 'bytes', + name: 'result', + type: 'bytes', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [], + name: 'reentrancyGuardEntered', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'uint256', + name: 'maxBptAmountIn', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'minAmountsOut', + type: 'uint256[]', + }, + { + internalType: 'enum RemoveLiquidityKind', + name: 'kind', + type: 'uint8', + }, + { + internalType: 'bytes', + name: 'userData', + type: 'bytes', + }, + ], + internalType: 'struct RemoveLiquidityParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'removeLiquidity', + outputs: [ + { + internalType: 'uint256', + name: 'bptAmountIn', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'amountsOut', + type: 'uint256[]', + }, + { + internalType: 'bytes', + name: 'returnData', + type: 'bytes', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'uint256', + name: 'exactBptAmountIn', + type: 'uint256', + }, + ], + name: 'removeLiquidityRecovery', + outputs: [ + { + internalType: 'uint256[]', + name: 'amountsOutRaw', + type: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'retrieve', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'settle', + outputs: [ + { + internalType: 'uint256', + name: 'paid', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'enum SwapKind', + name: 'kind', + type: 'uint8', + }, + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'contract IERC20', + name: 'tokenIn', + type: 'address', + }, + { + internalType: 'contract IERC20', + name: 'tokenOut', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amountGivenRaw', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limitRaw', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'userData', + type: 'bytes', + }, + ], + internalType: 'struct SwapParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'swap', + outputs: [ + { + internalType: 'uint256', + name: 'amountCalculated', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'amountIn', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'amountOut', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'wire', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IVault', + name: 'mainVault', + type: 'address', + }, + { + internalType: 'uint256', + name: 'pauseWindowDuration', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'bufferPeriodDuration', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'address', + name: 'target', + type: 'address', + }, + ], + name: 'AddressEmptyCode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'AddressInsufficientBalance', + type: 'error', + }, + { + inputs: [], + name: 'AmountGivenZero', + type: 'error', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'AmountInAboveMax', + type: 'error', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'AmountOutBelowMin', + type: 'error', + }, + { + inputs: [], + name: 'BalanceNotSettled', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'BptAmountInAboveMax', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'BptAmountOutBelowMin', + type: 'error', + }, + { + inputs: [], + name: 'CannotReceiveEth', + type: 'error', + }, + { + inputs: [], + name: 'CannotSwapSameToken', + type: 'error', + }, + { + inputs: [], + name: 'CodecOverflow', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'allowance', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'needed', + type: 'uint256', + }, + ], + name: 'ERC20InsufficientAllowance', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'balance', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'needed', + type: 'uint256', + }, + ], + name: 'ERC20InsufficientBalance', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'approver', + type: 'address', + }, + ], + name: 'ERC20InvalidApprover', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'receiver', + type: 'address', + }, + ], + name: 'ERC20InvalidReceiver', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'ERC20InvalidSender', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + ], + name: 'ERC20InvalidSpender', + type: 'error', + }, + { + inputs: [], + name: 'FailedInnerCall', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'index', + type: 'uint256', + }, + ], + name: 'HandlerOutOfBounds', + type: 'error', + }, + { + inputs: [], + name: 'HookFailed', + type: 'error', + }, + { + inputs: [], + name: 'InputLengthMismatch', + type: 'error', + }, + { + inputs: [], + name: 'InvalidAddLiquidityKind', + type: 'error', + }, + { + inputs: [], + name: 'InvalidRemoveLiquidityKind', + type: 'error', + }, + { + inputs: [], + name: 'InvalidToken', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTokenConfiguration', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTokenType', + type: 'error', + }, + { + inputs: [], + name: 'MaxTokens', + type: 'error', + }, + { + inputs: [], + name: 'MinTokens', + type: 'error', + }, + { + inputs: [], + name: 'NoHandler', + type: 'error', + }, + { + inputs: [], + name: 'NotStaticCall', + type: 'error', + }, + { + inputs: [], + name: 'NotVaultDelegateCall', + type: 'error', + }, + { + inputs: [], + name: 'OutOfBounds', + type: 'error', + }, + { + inputs: [], + name: 'PauseBufferPeriodDurationTooLarge', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolAlreadyInitialized', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolAlreadyRegistered', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolInRecoveryMode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotInRecoveryMode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotInitialized', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotPaused', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolNotRegistered', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolPauseWindowExpired', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolPaused', + type: 'error', + }, + { + inputs: [], + name: 'ProtocolSwapFeePercentageTooHigh', + type: 'error', + }, + { + inputs: [], + name: 'ProtocolYieldFeePercentageTooHigh', + type: 'error', + }, + { + inputs: [], + name: 'QueriesDisabled', + type: 'error', + }, + { + inputs: [], + name: 'ReentrancyGuardReentrantCall', + type: 'error', + }, + { + inputs: [], + name: 'RouterNotTrusted', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'bits', + type: 'uint8', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'SafeCastOverflowedUintDowncast', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'SafeCastOverflowedUintToInt', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + ], + name: 'SafeERC20FailedOperation', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'SenderIsNotPauseManager', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'SenderIsNotVault', + type: 'error', + }, + { + inputs: [], + name: 'SenderNotAllowed', + type: 'error', + }, + { + inputs: [], + name: 'SwapFeePercentageTooHigh', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'SwapLimit', + type: 'error', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'TokenAlreadyRegistered', + type: 'error', + }, + { + inputs: [], + name: 'TokenNotRegistered', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'address', + name: 'expectedToken', + type: 'address', + }, + { + internalType: 'address', + name: 'actualToken', + type: 'address', + }, + ], + name: 'TokensMismatch', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'TotalSupplyTooLow', + type: 'error', + }, + { + inputs: [], + name: 'UserDataNotSupported', + type: 'error', + }, + { + inputs: [], + name: 'VaultNotPaused', + type: 'error', + }, + { + inputs: [], + name: 'VaultPauseWindowDurationTooLarge', + type: 'error', + }, + { + inputs: [], + name: 'VaultPauseWindowExpired', + type: 'error', + }, + { + inputs: [], + name: 'VaultPaused', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'handler', + type: 'address', + }, + { + internalType: 'address', + name: 'caller', + type: 'address', + }, + ], + name: 'WrongHandler', + type: 'error', + }, + { + inputs: [], + name: 'WrongVaultExtensionDeployment', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + 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: 'contract IAuthorizer', + name: 'newAuthorizer', + type: 'address', + }, + ], + name: 'AuthorizerChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'liquidityProvider', + type: 'address', + }, + { + indexed: false, + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + { + indexed: false, + internalType: 'int256[]', + name: 'deltas', + type: 'int256[]', + }, + ], + name: 'PoolBalanceChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'PoolInitialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'paused', + type: 'bool', + }, + ], + name: 'PoolPausedStateChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'recoveryMode', + type: 'bool', + }, + ], + name: 'PoolRecoveryModeStateChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'factory', + type: 'address', + }, + { + components: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'enum TokenType', + name: 'tokenType', + type: 'uint8', + }, + { + internalType: 'contract IRateProvider', + name: 'rateProvider', + type: 'address', + }, + { + internalType: 'bool', + name: 'yieldFeeExempt', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct TokenConfig[]', + name: 'tokenConfig', + type: 'tuple[]', + }, + { + indexed: false, + internalType: 'uint256', + name: 'pauseWindowEndTime', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'pauseManager', + type: 'address', + }, + { + components: [ + { + internalType: 'bool', + name: 'shouldCallBeforeInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeRemoveLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterRemoveLiquidity', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct PoolHooks', + name: 'hooks', + type: 'tuple', + }, + { + components: [ + { + internalType: 'bool', + name: 'supportsAddLiquidityCustom', + type: 'bool', + }, + { + internalType: 'bool', + name: 'supportsRemoveLiquidityCustom', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct LiquidityManagement', + name: 'liquidityManagement', + type: 'tuple', + }, + ], + name: 'PoolRegistered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'ProtocolFeeCollected', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'ProtocolSwapFeeCharged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'swapFeePercentage', + type: 'uint256', + }, + ], + name: 'ProtocolSwapFeePercentageChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'ProtocolYieldFeeCharged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'yieldFeePercentage', + type: 'uint256', + }, + ], + name: 'ProtocolYieldFeePercentageChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'uint256', + name: 'swapFeePercentage', + type: 'uint256', + }, + ], + name: 'SwapFeePercentageChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + 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: 'bool', + name: 'paused', + type: 'bool', + }, + ], + name: 'VaultPausedStateChanged', + type: 'event', + }, + { + inputs: [], + name: 'MAX_BUFFER_PERIOD_DURATION', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_PAUSE_WINDOW_DURATION', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + 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: 'owner', + type: 'address', + }, + { + 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: 'token', + type: 'address', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + ], + name: 'collectProtocolFees', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'disableQuery', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'disableRecoveryMode', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'enableRecoveryMode', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes4', + name: 'selector', + type: 'bytes4', + }, + ], + name: 'getActionId', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getAuthorizer', + outputs: [ + { + internalType: 'contract IAuthorizer', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getBufferPeriodDuration', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getBufferPeriodEndTime', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'index', + type: 'uint256', + }, + ], + name: 'getHandler', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getHandlersCount', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getMaximumPoolTokens', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'getMinimumPoolTokens', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'getNonzeroDeltaCount', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getPauseWindowEndTime', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'getPoolConfig', + outputs: [ + { + components: [ + { + internalType: 'bool', + name: 'isPoolRegistered', + type: 'bool', + }, + { + internalType: 'bool', + name: 'isPoolInitialized', + type: 'bool', + }, + { + internalType: 'bool', + name: 'isPoolPaused', + type: 'bool', + }, + { + internalType: 'bool', + name: 'isPoolInRecoveryMode', + type: 'bool', + }, + { + internalType: 'bool', + name: 'hasDynamicSwapFee', + type: 'bool', + }, + { + internalType: 'uint64', + name: 'staticSwapFeePercentage', + type: 'uint64', + }, + { + internalType: 'uint24', + name: 'tokenDecimalDiffs', + type: 'uint24', + }, + { + internalType: 'uint32', + name: 'pauseWindowEndTime', + type: 'uint32', + }, + { + components: [ + { + internalType: 'bool', + name: 'shouldCallBeforeInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeRemoveLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterRemoveLiquidity', + type: 'bool', + }, + ], + internalType: 'struct PoolHooks', + name: 'hooks', + type: 'tuple', + }, + { + components: [ + { + internalType: 'bool', + name: 'supportsAddLiquidityCustom', + type: 'bool', + }, + { + internalType: 'bool', + name: 'supportsRemoveLiquidityCustom', + type: 'bool', + }, + ], + internalType: 'struct LiquidityManagement', + name: 'liquidityManagement', + type: 'tuple', + }, + ], + internalType: 'struct PoolConfig', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'getPoolPausedState', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'getPoolTokenInfo', + outputs: [ + { + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + { + internalType: 'enum TokenType[]', + name: 'tokenTypes', + type: 'uint8[]', + }, + { + internalType: 'uint256[]', + name: 'balancesRaw', + type: 'uint256[]', + }, + { + internalType: 'uint256[]', + name: 'decimalScalingFactors', + type: 'uint256[]', + }, + { + internalType: 'contract IRateProvider[]', + name: 'rateProviders', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'getPoolTokenRates', + outputs: [ + { + internalType: 'uint256[]', + name: '', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'getPoolTokens', + outputs: [ + { + internalType: 'contract IERC20[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + ], + name: 'getProtocolFees', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getProtocolSwapFeePercentage', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getProtocolYieldFeePercentage', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'getStaticSwapFeePercentage', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'getTokenDelta', + outputs: [ + { + internalType: 'int256', + name: '', + type: 'int256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'getTokenReserve', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getVaultPausedState', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'exactAmountsIn', + type: 'uint256[]', + }, + { + internalType: 'uint256', + name: 'minBptAmountOut', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'userData', + type: 'bytes', + }, + ], + name: 'initialize', + outputs: [ + { + internalType: 'uint256', + name: 'bptAmountOut', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'isPoolInRecoveryMode', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'isPoolInitialized', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'isPoolPaused', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'isPoolRegistered', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isQueryDisabled', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isVaultPaused', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'pausePool', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'pauseVault', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + ], + name: 'quote', + outputs: [ + { + internalType: 'bytes', + name: 'result', + type: 'bytes', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [], + name: 'reentrancyGuardEntered', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + components: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'enum TokenType', + name: 'tokenType', + type: 'uint8', + }, + { + internalType: 'contract IRateProvider', + name: 'rateProvider', + type: 'address', + }, + { + internalType: 'bool', + name: 'yieldFeeExempt', + type: 'bool', + }, + ], + internalType: 'struct TokenConfig[]', + name: 'tokenConfig', + type: 'tuple[]', + }, + { + internalType: 'uint256', + name: 'pauseWindowEndTime', + type: 'uint256', + }, + { + internalType: 'address', + name: 'pauseManager', + type: 'address', + }, + { + components: [ + { + internalType: 'bool', + name: 'shouldCallBeforeInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeRemoveLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterRemoveLiquidity', + type: 'bool', + }, + ], + internalType: 'struct PoolHooks', + name: 'poolHooks', + type: 'tuple', + }, + { + components: [ + { + internalType: 'bool', + name: 'supportsAddLiquidityCustom', + type: 'bool', + }, + { + internalType: 'bool', + name: 'supportsRemoveLiquidityCustom', + type: 'bool', + }, + ], + internalType: 'struct LiquidityManagement', + name: 'liquidityManagement', + type: 'tuple', + }, + ], + name: 'registerPool', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IAuthorizer', + name: 'newAuthorizer', + type: 'address', + }, + ], + name: 'setAuthorizer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'newProtocolSwapFeePercentage', + type: 'uint256', + }, + ], + name: 'setProtocolSwapFeePercentage', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'newProtocolYieldFeePercentage', + type: 'uint256', + }, + ], + name: 'setProtocolYieldFeePercentage', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + internalType: 'uint256', + name: 'swapFeePercentage', + type: 'uint256', + }, + ], + name: 'setStaticSwapFeePercentage', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + ], + name: 'totalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transferFrom', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pool', + type: 'address', + }, + ], + name: 'unpausePool', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'unpauseVault', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'vault', + outputs: [ + { + internalType: 'contract IVault', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; diff --git a/modules/sources/contracts/abis/VaultV3JSON.json b/modules/sources/contracts/abis/VaultV3JSON.json new file mode 100644 index 000000000..cee6a2f2e --- /dev/null +++ b/modules/sources/contracts/abis/VaultV3JSON.json @@ -0,0 +1,3622 @@ +[ + { + "inputs": [ + { + "internalType": "contract IVaultExtension", + "name": "vaultExtension", + "type": "address" + }, + { + "internalType": "contract IAuthorizer", + "name": "authorizer", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "AllZeroInputs", + "type": "error" + }, + { + "inputs": [], + "name": "AmountGivenZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "AmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "AmountOutBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceNotSettled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "BptAmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "BptAmountOutBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "CannotReceiveEth", + "type": "error" + }, + { + "inputs": [], + "name": "CannotSwapSameToken", + "type": "error" + }, + { + "inputs": [], + "name": "DoesNotSupportAddLiquidityCustom", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "HandlerOutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "HookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRemoveLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidToken", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenConfiguration", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenType", + "type": "error" + }, + { + "inputs": [], + "name": "MaxTokens", + "type": "error" + }, + { + "inputs": [], + "name": "MinTokens", + "type": "error" + }, + { + "inputs": [], + "name": "MultipleNonZeroInputs", + "type": "error" + }, + { + "inputs": [], + "name": "NoHandler", + "type": "error" + }, + { + "inputs": [], + "name": "NotStaticCall", + "type": "error" + }, + { + "inputs": [], + "name": "NotVaultDelegateCall", + "type": "error" + }, + { + "inputs": [], + "name": "PauseBufferPeriodDurationTooLarge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPauseWindowExpired", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPaused", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolSwapFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolYieldFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "QueriesDisabled", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "RouterNotTrusted", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "bits", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintDowncast", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintToInt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "SenderIsNotPauseManager", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderIsNotVault", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "SwapLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "TokenAlreadyRegistered", + "type": "error" + }, + { + "inputs": [], + "name": "TokenNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "expectedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "actualToken", + "type": "address" + } + ], + "name": "TokensMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "TotalSupplyTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "UserDataNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "VaultNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowDurationTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowExpired", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "handler", + "type": "address" + }, + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "WrongHandler", + "type": "error" + }, + { + "inputs": [], + "name": "WrongVaultExtensionDeployment", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDivision", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "AuthorizerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "int256[]", + "name": "deltas", + "type": "int256[]" + } + ], + "name": "PoolBalanceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInitialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PoolPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "recoveryMode", + "type": "bool" + } + ], + "name": "PoolRecoveryModeStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "yieldFeeExempt", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "pauseWindowEndTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "components": [ + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct PoolHooks", + "name": "hooks", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "supportsAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "supportsRemoveLiquidityCustom", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolFeeCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolSwapFeeCharged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "ProtocolSwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolYieldFeeCharged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "yieldFeePercentage", + "type": "uint256" + } + ], + "name": "ProtocolYieldFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "swapFeeAmount", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "VaultPausedStateChanged", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "MAX_BUFFER_PERIOD_DURATION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_PAUSE_WINDOW_DURATION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "maxAmountsIn", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "minBptAmountOut", + "type": "uint256" + }, + { + "internalType": "enum AddLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct AddLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amountsIn", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "bptAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getPoolTokenCountAndIndexOfToken", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultExtension", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "invoke", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "reentrancyGuardEntered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxBptAmountIn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "minAmountsOut", + "type": "uint256[]" + }, + { + "internalType": "enum RemoveLiquidityKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct RemoveLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "bptAmountIn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "amountsOut", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exactBptAmountIn", + "type": "uint256" + } + ], + "name": "removeLiquidityRecovery", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amountsOutRaw", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "retrieve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "settle", + "outputs": [ + { + "internalType": "uint256", + "name": "paid", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum SwapKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountGivenRaw", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limitRaw", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "internalType": "struct SwapParams", + "name": "params", + "type": "tuple" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "uint256", + "name": "amountCalculated", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "wire", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVault", + "name": "mainVault", + "type": "address" + }, + { + "internalType": "uint256", + "name": "pauseWindowDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bufferPeriodDuration", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "AmountGivenZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "AmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "AmountOutBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceNotSettled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "BptAmountInAboveMax", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "BptAmountOutBelowMin", + "type": "error" + }, + { + "inputs": [], + "name": "CannotReceiveEth", + "type": "error" + }, + { + "inputs": [], + "name": "CannotSwapSameToken", + "type": "error" + }, + { + "inputs": [], + "name": "CodecOverflow", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "HandlerOutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "HookFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InputLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRemoveLiquidityKind", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidToken", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenConfiguration", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTokenType", + "type": "error" + }, + { + "inputs": [], + "name": "MaxTokens", + "type": "error" + }, + { + "inputs": [], + "name": "MinTokens", + "type": "error" + }, + { + "inputs": [], + "name": "NoHandler", + "type": "error" + }, + { + "inputs": [], + "name": "NotStaticCall", + "type": "error" + }, + { + "inputs": [], + "name": "NotVaultDelegateCall", + "type": "error" + }, + { + "inputs": [], + "name": "OutOfBounds", + "type": "error" + }, + { + "inputs": [], + "name": "PauseBufferPeriodDurationTooLarge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInRecoveryMode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPauseWindowExpired", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolPaused", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolSwapFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolYieldFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "QueriesDisabled", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "RouterNotTrusted", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "bits", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintDowncast", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "SafeCastOverflowedUintToInt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "SenderIsNotPauseManager", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderIsNotVault", + "type": "error" + }, + { + "inputs": [], + "name": "SenderNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFeePercentageTooHigh", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "SwapLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "TokenAlreadyRegistered", + "type": "error" + }, + { + "inputs": [], + "name": "TokenNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "expectedToken", + "type": "address" + }, + { + "internalType": "address", + "name": "actualToken", + "type": "address" + } + ], + "name": "TokensMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "TotalSupplyTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "UserDataNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "VaultNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowDurationTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPauseWindowExpired", + "type": "error" + }, + { + "inputs": [], + "name": "VaultPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "handler", + "type": "address" + }, + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "WrongHandler", + "type": "error" + }, + { + "inputs": [], + "name": "WrongVaultExtensionDeployment", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "AuthorizerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "liquidityProvider", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "int256[]", + "name": "deltas", + "type": "int256[]" + } + ], + "name": "PoolBalanceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolInitialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PoolPausedStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "recoveryMode", + "type": "bool" + } + ], + "name": "PoolRecoveryModeStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "yieldFeeExempt", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "pauseWindowEndTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "components": [ + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct PoolHooks", + "name": "hooks", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "supportsAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "supportsRemoveLiquidityCustom", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolFeeCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolSwapFeeCharged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "ProtocolSwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolYieldFeeCharged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "yieldFeePercentage", + "type": "uint256" + } + ], + "name": "ProtocolYieldFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "SwapFeePercentageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "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": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "VaultPausedStateChanged", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_BUFFER_PERIOD_DURATION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_PAUSE_WINDOW_DURATION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "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": "owner", + "type": "address" + }, + { + "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": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "name": "collectProtocolFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "disableQuery", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "disableRecoveryMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "enableRecoveryMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getActionId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizer", + "outputs": [ + { + "internalType": "contract IAuthorizer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBufferPeriodDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBufferPeriodEndTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getHandler", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getHandlersCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumPoolTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumPoolTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getNonzeroDeltaCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPauseWindowEndTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isPoolRegistered", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolPaused", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isPoolInRecoveryMode", + "type": "bool" + }, + { + "internalType": "bool", + "name": "hasDynamicSwapFee", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "staticSwapFeePercentage", + "type": "uint64" + }, + { + "internalType": "uint24", + "name": "tokenDecimalDiffs", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "pauseWindowEndTime", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + } + ], + "internalType": "struct PoolHooks", + "name": "hooks", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "supportsAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "supportsRemoveLiquidityCustom", + "type": "bool" + } + ], + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "internalType": "struct PoolConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokenInfo", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "enum TokenType[]", + "name": "tokenTypes", + "type": "uint8[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "contract IRateProvider[]", + "name": "rateProviders", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokenRates", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getPoolTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getProtocolFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProtocolYieldFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "getStaticSwapFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenDelta", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "getTokenReserve", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVaultPausedState", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "exactAmountsIn", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "minBptAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "userData", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [ + { + "internalType": "uint256", + "name": "bptAmountOut", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInRecoveryMode", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "isPoolRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isQueryDisabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVaultPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "pausePool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "reentrancyGuardEntered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { + "internalType": "bool", + "name": "yieldFeeExempt", + "type": "bool" + } + ], + "internalType": "struct TokenConfig[]", + "name": "tokenConfig", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "pauseWindowEndTime", + "type": "uint256" + }, + { + "internalType": "address", + "name": "pauseManager", + "type": "address" + }, + { + "components": [ + { + "internalType": "bool", + "name": "shouldCallBeforeInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterInitialize", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterSwap", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterAddLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallBeforeRemoveLiquidity", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldCallAfterRemoveLiquidity", + "type": "bool" + } + ], + "internalType": "struct PoolHooks", + "name": "poolHooks", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "supportsAddLiquidityCustom", + "type": "bool" + }, + { + "internalType": "bool", + "name": "supportsRemoveLiquidityCustom", + "type": "bool" + } + ], + "internalType": "struct LiquidityManagement", + "name": "liquidityManagement", + "type": "tuple" + } + ], + "name": "registerPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IAuthorizer", + "name": "newAuthorizer", + "type": "address" + } + ], + "name": "setAuthorizer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newProtocolSwapFeePercentage", + "type": "uint256" + } + ], + "name": "setProtocolSwapFeePercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newProtocolYieldFeePercentage", + "type": "uint256" + } + ], + "name": "setProtocolYieldFeePercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "swapFeePercentage", + "type": "uint256" + } + ], + "name": "setStaticSwapFeePercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "unpausePool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vault", + "outputs": [ + { + "internalType": "contract IVault", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/modules/sources/contracts/abis/weighted_pool.abi b/modules/sources/contracts/abis/weighted_pool.abi new file mode 100644 index 000000000..e69de29bb diff --git a/modules/sources/contracts/fetch-erc20-headers.ts b/modules/sources/contracts/fetch-erc20-headers.ts new file mode 100644 index 000000000..8ea18a97b --- /dev/null +++ b/modules/sources/contracts/fetch-erc20-headers.ts @@ -0,0 +1,54 @@ +import { parseAbi } from 'viem'; +import type { ViemClient } from '../types'; + +const poolAbi = [ + 'function name() view returns (string)', + 'function symbol() view returns (string)', + 'function decimals() view returns (uint8)', +]; + +const abi = parseAbi(poolAbi); + +export async function fetchErc20Headers(addresses: `0x${string}`[], client: ViemClient) { + const contracts = addresses + .map((address) => [ + { + address, + abi, + functionName: 'name', + }, + { + address, + abi, + functionName: 'symbol', + }, + { + address, + abi, + functionName: 'decimals', + }, + ]) + .flat(); + + const results = await client.multicall({ contracts }); + + // Parse the results + const parsedResults = results.map((result) => { + if (result.status === 'success' && result.result !== undefined) { + return result.result as string; + } + // Handle the error + return undefined; + }); + + return Object.fromEntries( + addresses.map((address, i) => [ + address, + { + name: String(parsedResults[i * 3]), + symbol: String(parsedResults[i * 3 + 1]), + decimals: Number(parsedResults[i * 3 + 2]), + }, + ]), + ); +} diff --git a/modules/sources/contracts/fetch-pool-data.ts b/modules/sources/contracts/fetch-pool-data.ts new file mode 100644 index 000000000..bafe1627e --- /dev/null +++ b/modules/sources/contracts/fetch-pool-data.ts @@ -0,0 +1,95 @@ +import { PrismaPoolType } from '@prisma/client'; +import { ViemClient } from '../types'; +import { vaultV3Abi } from './abis/VaultV3'; +import { fetchPoolTokenInfo } from './fetch-pool-tokens'; + +interface PoolInput { + id: string; + address: string; + type: PrismaPoolType; + version: number; +} + +interface PoolData { + totalSupply: bigint; + swapFee: bigint; + protocolSwapFeePercentage: bigint; + // protocolYieldFeePercentage: bigint; + // rate?: bigint; + // amp?: [bigint, boolean, bigint]; + isPoolPaused: boolean; + isPoolInRecoveryMode: boolean; +} + +export async function fetchPoolData( + vault: string, + pools: PoolInput[], + client: ViemClient, + blockNumber: bigint, +): Promise> { + const totalSupplyContracts = pools + .map((pool) => [ + { + address: vault as `0x${string}`, + abi: vaultV3Abi, + functionName: 'totalSupply', + args: [pool.address as `0x${string}`], + } as const, + ]) + .flat(); + + const configContracts = pools + .map((pool) => [ + { + address: vault as `0x${string}`, + abi: vaultV3Abi, + functionName: 'getPoolConfig', + args: [pool.address as `0x${string}`], + } as const, + ]) + .flat(); + + const protocolSwapFeeContracts = pools + .map((pool) => [ + { + address: vault as `0x${string}`, + abi: vaultV3Abi, + functionName: 'getProtocolSwapFeePercentage', + } as const, + ]) + .flat(); + + // TODO combine into one call + const totalSupplyResult = await client.multicall({ contracts: totalSupplyContracts, blockNumber: blockNumber }); + const configResult = await client.multicall({ contracts: configContracts, blockNumber: blockNumber }); + const protocolSwapFeeResult = await client.multicall({ + contracts: protocolSwapFeeContracts, + blockNumber: blockNumber, + }); + + // Parse the results + const parsedResults: Record = {}; + pools.forEach((result, i) => { + if ( + totalSupplyResult[i].status === 'success' && + totalSupplyResult[i].result !== undefined && + configResult[i].status === 'success' && + configResult[i].result !== undefined && + protocolSwapFeeResult[i].status === 'success' && + protocolSwapFeeResult[i].result !== undefined + ) { + // parse the result here using the abi + const poolData = { + totalSupply: totalSupplyResult[i].result!, + swapFee: configResult[i].result!.staticSwapFeePercentage, + protocolSwapFeePercentage: 0n, // TODO can this be added to config? + isPoolPaused: configResult[i].result!.isPoolPaused, + isPoolInRecoveryMode: configResult[i].result!.isPoolInRecoveryMode, + } as PoolData; + + parsedResults[result.id] = poolData; + } + // Handle the error + }); + return parsedResults; +} diff --git a/modules/sources/contracts/fetch-pool-tokens.ts b/modules/sources/contracts/fetch-pool-tokens.ts new file mode 100644 index 000000000..ad5cf87ee --- /dev/null +++ b/modules/sources/contracts/fetch-pool-tokens.ts @@ -0,0 +1,90 @@ +import { ViemClient } from '../types'; +import { vaultV3Abi } from './abis/VaultV3'; + +type PoolTokenInfo = { + tokens: `0x${string}`[]; + tokenTypes: number[]; + balancesRaw: bigint[]; + decimalScalingFactors: bigint[]; + rateProviders: `0x${string}`[]; +}; + +type PoolTokenRates = { + tokenRates: bigint[]; +}; + +export async function fetchPoolTokenInfo( + vault: string, + pools: string[], + client: ViemClient, + blockNumber: bigint, +): Promise> { + const contracts = pools + .map((pool) => [ + { + address: vault as `0x${string}`, + abi: vaultV3Abi, + functionName: 'getPoolTokenInfo', + args: [pool as `0x${string}`], + } as const, + ]) + .flat(); + + const results = await client.multicall({ contracts, blockNumber: blockNumber }); + + // Parse the results + const parsedResults = results + .map((result, i) => { + if (result.status === 'success' && result.result !== undefined) { + // parse the result here using the abi + const poolTokens = { + tokens: result.result[0], + tokenTypes: result.result[1], + balancesRaw: result.result[2], + decimalScalingFactors: result.result[3], + rateProviders: result.result[4], + } as PoolTokenInfo; + + return [pools[i], poolTokens]; + } + // Handle the error + return undefined; + }) + .filter((result): result is NonNullable => result !== undefined); + + return Object.fromEntries(parsedResults) as Record; +} + +export async function fetchPoolTokenRates( + vault: string, + pools: string[], + client: ViemClient, + blockNumber: bigint, +): Promise> { + const contracts = pools + .map((pool) => [ + { + address: vault as `0x${string}`, + abi: vaultV3Abi, + functionName: 'getPoolTokenRates', + args: [pool as `0x${string}`], + } as const, + ]) + .flat(); + + const results = await client.multicall({ contracts, blockNumber: blockNumber }); + + // Parse the results + const parsedResults = results + .map((result, i) => { + if (result.status === 'success' && result.result !== undefined) { + // parse the result here using the abi + return [pools[i], result.result]; + } + // Handle the error + return undefined; + }) + .filter((result): result is NonNullable => result !== undefined); + + return Object.fromEntries(parsedResults) as Record; +} diff --git a/modules/sources/contracts/fetch-weighted-pools-data.ts b/modules/sources/contracts/fetch-weighted-pools-data.ts new file mode 100644 index 000000000..d79c7868e --- /dev/null +++ b/modules/sources/contracts/fetch-weighted-pools-data.ts @@ -0,0 +1,41 @@ +import { parseAbi, formatEther } from 'viem'; +import { ViemClient } from '../types'; + +const abi = parseAbi(['function getNormalizedWeights() view returns (uint[] weights)']); + +interface WeightedPoolData { + weights: string[]; +} + +export async function fetchWeightedPoolData(pools: string[], client: ViemClient) { + const contracts = pools + .map((pool) => [ + { + address: pool as `0x${string}`, + abi, + args: [], + functionName: 'getNormalizedWeights', + }, + ]) + .flat(); + + const results = await client.multicall({ contracts }); + + // Parse the results + const parsedResults = results + .map((result, i) => { + if (result.status === 'success' && result.result !== undefined) { + return [ + pools[i], + { + weights: result.result.map((weight) => formatEther(weight)), + }, + ]; + } + // Handle the error + return undefined; + }) + .filter((result): result is NonNullable => result !== undefined); + + return Object.fromEntries(parsedResults) as Record; +} diff --git a/modules/sources/contracts/index.ts b/modules/sources/contracts/index.ts new file mode 100644 index 000000000..1347526f8 --- /dev/null +++ b/modules/sources/contracts/index.ts @@ -0,0 +1,3 @@ +export * from './fetch-erc20-headers'; +export * from './fetch-pool-tokens'; +export * from './fetch-weighted-pools-data'; diff --git a/modules/sources/logs/get-new-pools.ts b/modules/sources/logs/get-new-pools.ts new file mode 100644 index 000000000..5ef6dc038 --- /dev/null +++ b/modules/sources/logs/get-new-pools.ts @@ -0,0 +1,142 @@ +import { ViemClient } from '../types'; + +const event = { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'factory', + type: 'address', + }, + { + components: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'enum TokenType', + name: 'tokenType', + type: 'uint8', + }, + { + internalType: 'contract IRateProvider', + name: 'rateProvider', + type: 'address', + }, + { + internalType: 'bool', + name: 'yieldFeeExempt', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct TokenConfig[]', + name: 'tokenConfig', + type: 'tuple[]', + }, + { + indexed: false, + internalType: 'uint256', + name: 'pauseWindowEndTime', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'pauseManager', + type: 'address', + }, + { + components: [ + { + internalType: 'bool', + name: 'shouldCallBeforeInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterInitialize', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterSwap', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterAddLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallBeforeRemoveLiquidity', + type: 'bool', + }, + { + internalType: 'bool', + name: 'shouldCallAfterRemoveLiquidity', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct PoolCallbacks', + name: 'callbacks', + type: 'tuple', + }, + { + components: [ + { + internalType: 'bool', + name: 'supportsAddLiquidityCustom', + type: 'bool', + }, + { + internalType: 'bool', + name: 'supportsRemoveLiquidityCustom', + type: 'bool', + }, + ], + indexed: false, + internalType: 'struct LiquidityManagement', + name: 'liquidityManagement', + type: 'tuple', + }, + ], + name: 'PoolRegistered', + type: 'event', +} as const; + +/** + * Extract balances from the contract + */ +export const getNewPools = async (vaultAddress: string, client: ViemClient, fromBlock: bigint) => { + // Get Transfer logs from the vault + const logs = await client.getLogs({ + address: vaultAddress as `0x${string}`, + event, + fromBlock, + }); + + // Parse the logs + return logs; +}; diff --git a/modules/sources/logs/get-pool-balance-changed.ts b/modules/sources/logs/get-pool-balance-changed.ts new file mode 100644 index 000000000..5140d1d41 --- /dev/null +++ b/modules/sources/logs/get-pool-balance-changed.ts @@ -0,0 +1,49 @@ +import { ViemClient } from '../types'; + +const event = { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'liquidityProvider', + type: 'address', + }, + { + indexed: false, + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + { + indexed: false, + internalType: 'int256[]', + name: 'deltas', + type: 'int256[]', + }, + ], + name: 'PoolBalanceChanged', + type: 'event', +} as const; + +export const getPoolBalanceChanged = async ( + vaultAddress: string, + client: ViemClient, + fromBlock: bigint, + toBlock: bigint | undefined = undefined, +) => { + const logs = await client.getLogs({ + address: vaultAddress as `0x${string}`, + event, + fromBlock, + toBlock, + }); + + return logs; +}; diff --git a/modules/sources/logs/get-swaps.ts b/modules/sources/logs/get-swaps.ts new file mode 100644 index 000000000..b63773e33 --- /dev/null +++ b/modules/sources/logs/get-swaps.ts @@ -0,0 +1,64 @@ +import { ViemClient } from '../types'; + +const event = { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pool', + type: 'address', + }, + { + indexed: true, + internalType: 'contract IERC20', + name: 'tokenIn', + type: 'address', + }, + { + indexed: true, + internalType: 'contract IERC20', + name: 'tokenOut', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountIn', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amountOut', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'swapFeeAmount', + type: 'uint256', + }, + ], + name: 'Swap', + type: 'event', +} as const; + +/** + * Extract balances from the contract + */ +export const getSwaps = async ( + vaultAddress: string, + client: ViemClient, + fromBlock: bigint, + toBlock: bigint | undefined = undefined, +) => { + const logs = await client.getLogs({ + address: vaultAddress as `0x${string}`, + event, + fromBlock, + toBlock, + }); + + return logs; +}; diff --git a/modules/sources/logs/get-transfers.ts b/modules/sources/logs/get-transfers.ts new file mode 100644 index 000000000..7396b4f38 --- /dev/null +++ b/modules/sources/logs/get-transfers.ts @@ -0,0 +1,48 @@ +import { ViemClient } from '../types'; + +const event = { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + 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', +} as const; + +/** + * Extract balances from the contract + */ +export const getTransfers = async (vaultAddress: string, client: ViemClient, fromBlock: bigint) => { + // Get Transfer logs from the vault + const logs = await client.getLogs({ + address: vaultAddress as `0x${string}`, + event, + fromBlock, + }); + + // Parse the logs + return logs; +}; diff --git a/modules/sources/logs/index.ts b/modules/sources/logs/index.ts new file mode 100644 index 000000000..7bdec8277 --- /dev/null +++ b/modules/sources/logs/index.ts @@ -0,0 +1,3 @@ +export * from './get-swaps'; +export * from './get-transfers'; +export * from './get-new-pools'; diff --git a/modules/sources/transformers/index.ts b/modules/sources/transformers/index.ts new file mode 100644 index 000000000..ca74df49e --- /dev/null +++ b/modules/sources/transformers/index.ts @@ -0,0 +1,2 @@ +export * from './pool-transformer'; +export * from './pool-tokens-transformer'; diff --git a/modules/sources/transformers/pool-tokens-transformer.ts b/modules/sources/transformers/pool-tokens-transformer.ts new file mode 100644 index 000000000..30a2ee535 --- /dev/null +++ b/modules/sources/transformers/pool-tokens-transformer.ts @@ -0,0 +1,50 @@ +import { PoolFragment as VaultSubgraphPoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { TypePoolFragment as PoolSubgraphPoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; +import { Chain, Prisma } from '@prisma/client'; + +export function poolTokensTransformer( + vaultSubgraphPool: VaultSubgraphPoolFragment, +): Prisma.PrismaPoolTokenCreateManyPoolInput[] { + const tokens = vaultSubgraphPool.tokens ?? []; + return tokens.map((token, i) => ({ + id: `${vaultSubgraphPool.id}-${token.address}`.toLowerCase(), + address: token.address.toLowerCase(), + index: token.index, + nestedPoolId: null, + priceRateProvider: vaultSubgraphPool.rateProviders![i].address.toLowerCase(), + exemptFromProtocolYieldFee: token.totalProtocolYieldFee === '0' ? true : false, + })); +} + +export function poolTokensDynamicDataTransformer( + vaultSubgraphPool: VaultSubgraphPoolFragment, + poolSubgraphPool: PoolSubgraphPoolFragment, + chain: Chain, +): Prisma.PrismaPoolTokenDynamicDataCreateManyInput[] { + const tokens = vaultSubgraphPool.tokens ?? []; + return tokens.map((token, i) => ({ + id: `${vaultSubgraphPool.id}-${token.address}`.toLowerCase(), + poolTokenId: `${vaultSubgraphPool.id}-${token.address}`.toLowerCase(), + chain, + blockNumber: parseFloat(vaultSubgraphPool.blockNumber), + balance: token.balance, + balanceUSD: 0, + priceRate: '0', + weight: poolSubgraphPool.weights[token.index] ?? null, + // latestFxPrice: poolSubgraphPool.latestFxPrice, + })); +} + +// TODO deal with nested pools +export function poolExpandedTokensTransformer( + vaultSubgraphPool: VaultSubgraphPoolFragment, + chain: Chain, +): Prisma.PrismaPoolExpandedTokensCreateManyInput[] { + const tokens = vaultSubgraphPool.tokens ?? []; + return tokens.map((token, i) => ({ + poolId: vaultSubgraphPool.id, + chain: chain, + tokenAddress: token.address, + nestedPoolId: null, + })); +} diff --git a/modules/sources/transformers/pool-transformer.ts b/modules/sources/transformers/pool-transformer.ts new file mode 100644 index 000000000..69830e328 --- /dev/null +++ b/modules/sources/transformers/pool-transformer.ts @@ -0,0 +1,46 @@ +import { Chain, PrismaPool, PrismaPoolType } from '@prisma/client'; +import { PoolFragment as VaultSubgraphPoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { + TypePoolFragment as PoolSubgraphPoolFragment, + PoolType, +} from '../../subgraphs/balancer-v3-pools/generated/types'; +import { StableData } from '../../pool/subgraph-mapper'; + +export const poolTransformer = ( + vaultSubgraphPool: VaultSubgraphPoolFragment, + poolSubgraphPool: PoolSubgraphPoolFragment, + chain: Chain, +): PrismaPool => { + let type: PrismaPoolType; + let typeData = {}; + + switch (poolSubgraphPool.factory.type) { + case PoolType.Weighted: + type = PrismaPoolType.WEIGHTED; + break; + case PoolType.Stable: + type = PrismaPoolType.STABLE; + typeData = { + amp: '10', // TODO just a place holder + } as StableData; + break; + default: + type = PrismaPoolType.UNKNOWN; + } + + return { + id: vaultSubgraphPool.id.toLowerCase(), + chain: chain, + vaultVersion: 3, + address: vaultSubgraphPool.id.toLowerCase(), + decimals: 18, + symbol: vaultSubgraphPool.symbol, + name: vaultSubgraphPool.name, + owner: '', + factory: poolSubgraphPool.factory.id.toLowerCase(), + type: type, + typeData: typeData, + version: poolSubgraphPool.factory.version, + createTime: Number(vaultSubgraphPool.blockTimestamp), + }; +}; diff --git a/modules/sources/types.ts b/modules/sources/types.ts new file mode 100644 index 000000000..489287794 --- /dev/null +++ b/modules/sources/types.ts @@ -0,0 +1 @@ +export type { ViemClient } from './viem-client'; diff --git a/modules/sources/viem-client.ts b/modules/sources/viem-client.ts new file mode 100644 index 000000000..5d268d9e7 --- /dev/null +++ b/modules/sources/viem-client.ts @@ -0,0 +1,37 @@ +import { createPublicClient, http } from 'viem'; +import { + arbitrum, + avalanche, + base, + fantom, + gnosis, + mainnet, + optimism, + polygon, + polygonZkEvm, + sepolia, +} from 'viem/chains'; +import { Chain } from '@prisma/client'; +import config from '../../config'; + +export type ViemClient = ReturnType; + +const chain2ViemChain = { + [Chain.MAINNET]: mainnet, + [Chain.SEPOLIA]: sepolia, + [Chain.ARBITRUM]: arbitrum, + [Chain.AVALANCHE]: avalanche, + [Chain.BASE]: base, + [Chain.FANTOM]: fantom, + [Chain.GNOSIS]: gnosis, + [Chain.OPTIMISM]: optimism, + [Chain.POLYGON]: polygon, + [Chain.ZKEVM]: polygonZkEvm, +}; + +export const getViemClient = (chain: Chain) => { + return createPublicClient({ + chain: chain2ViemChain[chain], + transport: http(config[chain]?.rpcUrl), + }); +}; diff --git a/modules/subgraphs/balancer-v3-pools/index.ts b/modules/subgraphs/balancer-v3-pools/index.ts new file mode 100644 index 000000000..7d0e78052 --- /dev/null +++ b/modules/subgraphs/balancer-v3-pools/index.ts @@ -0,0 +1,17 @@ +import { GraphQLClient } from 'graphql-request'; +import { getSdk } from './generated/types'; + +/** + * Builds a client based on subgraph URL. + * + * @param subgraphUrl - url of the subgraph + * @returns sdk - generated sdk for the subgraph + */ +export const getPoolsSubgraphClient = (subgraphUrl: string) => { + const client = new GraphQLClient(subgraphUrl); + const sdk = getSdk(client); + + return sdk; +}; + +export type V3PoolsSubgraphClient = ReturnType; diff --git a/modules/subgraphs/balancer-v3-pools/pools.graphql b/modules/subgraphs/balancer-v3-pools/pools.graphql new file mode 100644 index 000000000..c9417d1c0 --- /dev/null +++ b/modules/subgraphs/balancer-v3-pools/pools.graphql @@ -0,0 +1,42 @@ +fragment Factory on Factory { + id + type + version + pools { + id + address + weights + } +} + +fragment TypePool on Pool { + id + address + factory { + id + # address + type + version + } + weights +} + +query Pools( + $skip: Int + $first: Int + $orderBy: Pool_orderBy + $orderDirection: OrderDirection + $where: Pool_filter + $block: Block_height +) { + pools( + skip: $skip + first: $first + orderBy: $orderBy + orderDirection: $orderDirection + where: $where + block: $block + ) { + ...TypePool + } +} diff --git a/modules/subgraphs/balancer-v3-vault/index.ts b/modules/subgraphs/balancer-v3-vault/index.ts new file mode 100644 index 000000000..c3f2b1c96 --- /dev/null +++ b/modules/subgraphs/balancer-v3-vault/index.ts @@ -0,0 +1,17 @@ +import { GraphQLClient } from 'graphql-request'; +import { getSdk } from './generated/types'; + +/** + * Builds a client based on subgraph URL. + * + * @param subgraphUrl - url of the subgraph + * @returns sdk - generated sdk for the subgraph + */ +export const getVaultSubgraphClient = (subgraphUrl: string) => { + const client = new GraphQLClient(subgraphUrl); + const sdk = getSdk(client); + + return sdk; +}; + +export type V3SubgraphClient = ReturnType; diff --git a/modules/subgraphs/balancer-v3-vault/join-exits.graphql b/modules/subgraphs/balancer-v3-vault/join-exits.graphql new file mode 100644 index 000000000..25c3c07f3 --- /dev/null +++ b/modules/subgraphs/balancer-v3-vault/join-exits.graphql @@ -0,0 +1,38 @@ +fragment JoinExit on JoinExit { + id + type + sender + amounts + pool { + id + tokens { + address + } + } + user { + id + } + blockNumber + blockTimestamp + transactionHash +} + +query JoinExits( + $skip: Int + $first: Int + $orderBy: JoinExit_orderBy + $orderDirection: OrderDirection + $where: JoinExit_filter + $block: Block_height +) { + joinExits( + skip: $skip + first: $first + orderBy: $orderBy + orderDirection: $orderDirection + where: $where + block: $block + ) { + ...JoinExit + } +} diff --git a/modules/subgraphs/balancer-v3-vault/pool-snapshots.graphql b/modules/subgraphs/balancer-v3-vault/pool-snapshots.graphql new file mode 100644 index 000000000..cadf3ac3f --- /dev/null +++ b/modules/subgraphs/balancer-v3-vault/pool-snapshots.graphql @@ -0,0 +1,32 @@ +fragment PoolSnapshot on PoolSnapshot { + id + pool { + id + tokens { + address + } + } + timestamp + balances + totalShares +} + +query PoolSnapshots( + $skip: Int + $first: Int + $orderBy: PoolSnapshot_orderBy + $orderDirection: OrderDirection + $where: PoolSnapshot_filter + $block: Block_height +) { + poolSnapshots( + skip: $skip + first: $first + orderBy: $orderBy + orderDirection: $orderDirection + where: $where + block: $block + ) { + ...PoolSnapshot + } +} diff --git a/modules/subgraphs/balancer-v3-vault/pools.graphql b/modules/subgraphs/balancer-v3-vault/pools.graphql new file mode 100644 index 000000000..786e1ffcf --- /dev/null +++ b/modules/subgraphs/balancer-v3-vault/pools.graphql @@ -0,0 +1,50 @@ +fragment Pool on Pool { + id + factory + address + name + symbol + totalShares + pauseWindowEndTime + pauseManager + blockNumber + blockTimestamp + transactionHash + tokens { + id + address + index + name + symbol + decimals + balance + totalProtocolSwapFee + totalProtocolYieldFee + } + rateProviders { + address + token { + address + } + } +} + +query Pools( + $skip: Int + $first: Int + $orderBy: Pool_orderBy + $orderDirection: OrderDirection + $where: Pool_filter + $block: Block_height +) { + pools( + skip: $skip + first: $first + orderBy: $orderBy + orderDirection: $orderDirection + where: $where + block: $block + ) { + ...Pool + } +} diff --git a/modules/subgraphs/balancer-v3-vault/swaps.graphql b/modules/subgraphs/balancer-v3-vault/swaps.graphql new file mode 100644 index 000000000..89f72ef63 --- /dev/null +++ b/modules/subgraphs/balancer-v3-vault/swaps.graphql @@ -0,0 +1,35 @@ +fragment Swap on Swap { + id + pool + tokenIn + tokenOut + tokenAmountIn + tokenAmountOut + swapFeeAmount + user { + id + } + blockNumber + blockTimestamp + transactionHash +} + +query Swaps( + $skip: Int + $first: Int + $orderBy: Swap_orderBy + $orderDirection: OrderDirection + $where: Swap_filter + $block: Block_height +) { + swaps( + skip: $skip + first: $first + orderBy: $orderBy + orderDirection: $orderDirection + where: $where + block: $block + ) { + ...Swap + } +} diff --git a/modules/subgraphs/balancer-v3-vault/users.graphql b/modules/subgraphs/balancer-v3-vault/users.graphql new file mode 100644 index 000000000..15fd08d5b --- /dev/null +++ b/modules/subgraphs/balancer-v3-vault/users.graphql @@ -0,0 +1,42 @@ +fragment User on User { + id + swaps(first: 1000) { + id + pool + tokenIn + tokenOut + tokenAmountIn + tokenAmountOut + swapFeeAmount + blockNumber + blockTimestamp + transactionHash + } + shares(first: 1000) { + id + pool { + id + } + balance + } +} + +query Users( + $skip: Int + $first: Int + $orderBy: User_orderBy + $orderDirection: OrderDirection + $where: User_filter + $block: Block_height +) { + users( + skip: $skip + first: $first + orderBy: $orderBy + orderDirection: $orderDirection + where: $where + block: $block + ) { + ...User + } +} diff --git a/modules/v3/README.md b/modules/v3/README.md new file mode 100644 index 000000000..75bd94b86 --- /dev/null +++ b/modules/v3/README.md @@ -0,0 +1,34 @@ +### Architecture + +The API is a monolith designed to cover 2 main functions: + +1. Serve graphql API with cached onchain state of pool and user data designed around frontend needs. It has 3 components: + - Jobs queue scheduler – is pushing jobs to the queue on cron schedule + - Jobs handler – executes ETL actions + - Server – serves graphql schema for reading +2. Provide SOR for liquidity on Balancer. It's route finding is based on BSF on the pools directed graph where nodes are the tokens and edges are triads: [pool.id, tokenIn, tokenOut]. + +#### Structural Layers + +Excalidraw diagram: app-architecture.excalidraw + +**/modules/controllers** +Controllers are resposible for creating context from configuration, eg: setting up external data sources or passing required configs to the actions. It's the entry point for executing logic based on the users / cron requests. + +**/modules/actions** +This directory contains the code that orchestrates the ETL process, calling the appropriate functions from the sources, transformers, and stores directories. This is the main logic of the ETL process. + +**/modules/sources** +Sources is the external data access layer, it's responsible for extracting data from 3 main sources: + +1. Subgraph – responsible for static data with low frequency updates +2. Contracts – used for quering real time data +3. Logs – extracting events for direct indexing skipping subgraph + +The extracted data is then passed on to the transform functions for DB format mapping. + +**/modules/sources/transformers** +Transformers are responsible for converting the extracted data format into DB expected one. This could involve cleaning the data, filtering it, aggregating it, or applying business rules. The transformed data is then passed on to the Stores for loading into the DB. + +**Application data access layer** +Prisma ORM to abstract the DB. diff --git a/package.json b/package.json index cab372b3d..98b667b9e 100644 --- a/package.json +++ b/package.json @@ -8,13 +8,14 @@ "private": true, "scripts": { "start": "node dist/app.js", - "start:local": "ts-node -r dotenv/config app", + "start:local": "ts-node -r dotenv/config -r tsconfig-paths/register app", "watch": "concurrently \"nodemon app\" \"yarn generate --watch\"", - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "generate": "graphql-codegen --config codegen.yml -r dotenv/config", "prisma-merge": "ts-node prisma/prisma-merge.ts", "test": "jest", - "vitest": "vitest" + "vitest": "vitest", + "task": "ts-node -r tsconfig-paths/register -r dotenv/config tasks/index.ts" }, "dependencies": { "@aws-sdk/client-cloudwatch": "^3.388.0", @@ -33,6 +34,7 @@ "@sentry/node": "^7.0.0", "@sentry/profiling-node": "^1.2.6", "@sentry/tracing": "^7.56.0", + "abitype": "^1.0.0", "apollo-server-core": "^3.5.0", "apollo-server-express": "^3.5.0", "axios": "^0.24.0", @@ -92,6 +94,7 @@ "testcontainers": "^8.0.0", "ts-jest": "^28.0.7", "ts-node": "^10.4.0", + "tsconfig-paths": "^4.2.0", "typescript": "^5.3.3", "vitest": "^0.32.4", "vitest-mock-extended": "^1.1.3" diff --git a/tasks/index.ts b/tasks/index.ts new file mode 100644 index 000000000..49ace8c83 --- /dev/null +++ b/tasks/index.ts @@ -0,0 +1,19 @@ +import { JobsController } from '../modules/controllers/jobs-controller'; + +// TODO needed? +const jobsController = JobsController(); + +async function run(job: string = process.argv[2], chain: string = process.argv[3]) { + console.log('Running job', job, chain); + + if (job === 'sync-changed-pools-v3') { + return jobsController.addMissingPoolsFromSubgraph(chain); + } + + return Promise.reject(new Error(`Unknown job: ${job}`)); +} + +run() + .then((r) => console.log) + .then(() => process.exit(0)) + .catch((e) => console.error); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 000000000..d92252c64 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "debug", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 06a54a168..1eccf9932 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -17,9 +17,12 @@ import { cronsDurationMetricPublisher } from '../modules/metrics/cron-duration-m import { syncLatestFXPrices } from '../modules/token/latest-fx-price'; import { AllNetworkConfigs } from '../modules/network/network-config'; import { sftmxService } from '../modules/sftmx/sftmx.service'; +import { JobsController } from '../modules/controllers/jobs-controller'; const runningJobs: Set = new Set(); +const jobsController = JobsController(); + async function runIfNotAlreadyRunning( id: string, chainId: string, @@ -105,7 +108,13 @@ export function configureWorkerRoutes(app: Express) { break; case 'sync-changed-pools-v3': - await runIfNotAlreadyRunning(job.name, chainId, () => poolService.syncChangedPoolsV3(), res, next); + await runIfNotAlreadyRunning( + job.name, + chainId, + () => jobsController.updateOnChainDataChangedPools(chainId), + res, + next, + ); break; case 'user-sync-wallet-balances-for-all-pools': await runIfNotAlreadyRunning( @@ -180,7 +189,7 @@ export function configureWorkerRoutes(app: Express) { await runIfNotAlreadyRunning( job.name, chainId, - () => poolService.syncNewPoolsFromSubgraphV3(), + () => jobsController.addMissingPoolsFromSubgraph(chainId), res, next, ); diff --git a/yarn.lock b/yarn.lock index 5b741e11f..5ae87b4a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5084,7 +5084,7 @@ abitype@0.9.8: resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" integrity sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ== -abitype@1.0.0: +abitype@1.0.0, abitype@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== @@ -9037,6 +9037,11 @@ json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" @@ -9575,6 +9580,11 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -11283,6 +11293,11 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -11611,6 +11626,15 @@ ts-node@^9: source-map-support "^0.5.17" yn "3.1.1" +tsconfig-paths@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.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" From 82355f9c447fea2c1d06c52a1c2c395715dee0b6 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:58:35 +0100 Subject: [PATCH 02/14] fix buildspec (#161) --- buildspec.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildspec.yml b/buildspec.yml index 2672c08a7..eb086bd63 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -1,8 +1,8 @@ version: 0.2 env: variables: - V3_SUBGRAPH: 'https://api.studio.thegraph.com/proxy/31386/balancer-v3-sepolia/version/latest/graphql' - V3_POOLS_SUBGRAPH: 'https://api.studio.thegraph.com/proxy/31386/balancer-pools-v3-sepolia/version/latest/graphql' + V3_SUBGRAPH: 'https://api.studio.thegraph.com/proxy/31386/balancer-v3-sepolia/version/latest' + V3_POOLS_SUBGRAPH: 'https://api.studio.thegraph.com/proxy/31386/balancer-pools-v3-sepolia/version/latest' BALANCER_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx-v2-optimism' MASTERCHEF_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/masterchefv2' RELIQUARY_SUBGRAPH: 'https://api.thegraph.com/subgraphs/name/beethovenxfi/reliquary' From 51cd0ce8aaf56d7f4e600eebc881adb7ef023ea8 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:45:08 +0100 Subject: [PATCH 03/14] Feat/v3 swaps (#162) * adding v3-swaps * todo --- codegen.yml | 6 +- .../pool/add-pools-from-subgraph.test.ts | 54 +++++------ .../actions/pool/add-pools-from-subgraph.ts | 8 +- .../actions/swap/add-swaps-from-subgraph.ts | 95 +++++++++++++++++++ .../actions/swap/update-volume-and-fees.ts | 54 +++++++++++ modules/controllers/jobs-controller.ts | 35 +++++-- modules/controllers/pools-controller.ts | 20 ++++ modules/network/sepolia.ts | 4 + .../subgraphs/balancer-v3-vault/index.ts | 74 +++++++++++++++ .../balancer-v3-vault/join-exits.graphql | 0 .../balancer-v3-vault/pool-snapshots.graphql | 0 .../subgraphs/balancer-v3-vault/pools.graphql | 4 +- .../subgraphs/balancer-v3-vault/swaps.graphql | 7 +- .../subgraphs/balancer-v3-vault/users.graphql | 0 .../transformers/pool-tokens-transformer.ts | 2 +- .../sources/transformers/pool-transformer.ts | 4 +- modules/subgraphs/balancer-v3-vault/index.ts | 17 ---- worker/job-handlers.ts | 9 ++ 18 files changed, 323 insertions(+), 70 deletions(-) create mode 100644 modules/actions/swap/add-swaps-from-subgraph.ts create mode 100644 modules/actions/swap/update-volume-and-fees.ts create mode 100644 modules/sources/subgraphs/balancer-v3-vault/index.ts rename modules/{ => sources}/subgraphs/balancer-v3-vault/join-exits.graphql (100%) rename modules/{ => sources}/subgraphs/balancer-v3-vault/pool-snapshots.graphql (100%) rename modules/{ => sources}/subgraphs/balancer-v3-vault/pools.graphql (93%) rename modules/{ => sources}/subgraphs/balancer-v3-vault/swaps.graphql (87%) rename modules/{ => sources}/subgraphs/balancer-v3-vault/users.graphql (100%) delete mode 100644 modules/subgraphs/balancer-v3-vault/index.ts diff --git a/codegen.yml b/codegen.yml index e87c909af..2e04ffd26 100644 --- a/codegen.yml +++ b/codegen.yml @@ -3,9 +3,9 @@ hooks: afterAllFileWrite: - prettier --write generates: - modules/subgraphs/balancer-v3-vault/generated/types.ts: + modules/sources/subgraphs/balancer-v3-vault/generated/types.ts: schema: ${V3_SUBGRAPH} - documents: 'modules/subgraphs/balancer-v3-vault/*.graphql' + documents: 'modules/sources/subgraphs/balancer-v3-vault/*.graphql' plugins: - typescript - typescript-operations @@ -16,7 +16,7 @@ generates: BigInt: string Bytes: string BigDecimal: string - modules/subgraphs/balancer-v3-vault/generated/balancer-v3-schema.graphql: + modules/sources/subgraphs/balancer-v3-vault/generated/balancer-v3-schema.graphql: schema: ${V3_SUBGRAPH} plugins: - schema-ast diff --git a/modules/actions/pool/add-pools-from-subgraph.test.ts b/modules/actions/pool/add-pools-from-subgraph.test.ts index cf0165764..8d5303448 100644 --- a/modules/actions/pool/add-pools-from-subgraph.test.ts +++ b/modules/actions/pool/add-pools-from-subgraph.test.ts @@ -1,7 +1,7 @@ import { addMissingPoolsFromSubgraph } from './add-pools-from-subgraph'; import { prisma } from '../../../prisma/prisma-client'; import { PrismaPool } from '@prisma/client'; -import { PoolFragment as VaultSubgraphPoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { VaultPoolFragment as VaultSubgraphPoolFragment } from '../../sources/subgraphs/balancer-v3-vault/generated/types'; import { TypePoolFragment as PoolSubgraphPoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; // Mock the module dependencies @@ -31,33 +31,33 @@ jest.mock('../../../prisma/prisma-client', () => ({ }, })); -describe('syncPools', () => { - const vaultSubgraphClient = { - Pools: jest.fn().mockResolvedValue({ pools: [{ id: '1' }, { id: '2' }] as VaultSubgraphPoolFragment[] }), - }; - const poolSubgraphClient = { - Pools: jest.fn().mockResolvedValue({ - pools: [ - { id: '1', factory: { id: '1' } }, - { id: '2', factory: { id: '1' } }, - ] as PoolSubgraphPoolFragment[], - }), - }; +// describe('syncPools', () => { +// const vaultSubgraphClient = { +// getAllPools: jest.fn().mockResolvedValue([{ id: '1' }, { id: '2' }] as VaultSubgraphPoolFragment[]), +// }; +// const poolSubgraphClient = { +// Pools: jest.fn().mockResolvedValue({ +// pools: [ +// { id: '1', factory: { id: '1' } }, +// { id: '2', factory: { id: '1' } }, +// ] as PoolSubgraphPoolFragment[], +// }), +// }; - beforeEach(() => { - jest.clearAllMocks(); - return addMissingPoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, 'SEPOLIA'); - }); +// beforeEach(() => { +// jest.clearAllMocks(); +// return addMissingPoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, 'SEPOLIA'); +// }); - it('should fetch pools from vault subgraph', async () => { - expect(vaultSubgraphClient.Pools).toHaveBeenCalled(); - }); +// it('should fetch pools from vault subgraph', async () => { +// expect(vaultSubgraphClient.getAllPools).toHaveBeenCalled(); +// }); - it('should fetch pools from pools subgraph', async () => { - expect(poolSubgraphClient.Pools).toHaveBeenCalled(); - }); +// it('should fetch pools from pools subgraph', async () => { +// expect(poolSubgraphClient.Pools).toHaveBeenCalled(); +// }); - it('should store missing pools in the database', async () => { - expect(prisma.prismaPool.create).toHaveBeenCalledWith({ data: expect.objectContaining({ id: '2' }) }); - }); -}); +// it('should store missing pools in the database', async () => { +// expect(prisma.prismaPool.create).toHaveBeenCalledWith({ data: expect.objectContaining({ id: '2' }) }); +// }); +// }); diff --git a/modules/actions/pool/add-pools-from-subgraph.ts b/modules/actions/pool/add-pools-from-subgraph.ts index 7af60a10f..1eee99fcd 100644 --- a/modules/actions/pool/add-pools-from-subgraph.ts +++ b/modules/actions/pool/add-pools-from-subgraph.ts @@ -7,9 +7,7 @@ import { poolExpandedTokensTransformer, } from '../../sources/transformers'; import { V3PoolsSubgraphClient } from '../../subgraphs/balancer-v3-pools'; -import { V3SubgraphClient } from '../../subgraphs/balancer-v3-vault'; -import { PoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; -import { PoolType, TypePoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; +import { BalancerVaultSubgraphSource } from '../../sources/subgraphs/balancer-v3-vault'; import _ from 'lodash'; type PoolDbEntry = { @@ -26,7 +24,7 @@ type PoolDbEntry = { * @returns syncedPools - the pools that were synced */ export async function addMissingPoolsFromSubgraph( - vaultSubgraphClient: Pick, + vaultSubgraphClient: BalancerVaultSubgraphSource, poolSubgraphClient: V3PoolsSubgraphClient, // viemClient: ViemClient, // vaultAddress: string, @@ -34,7 +32,7 @@ export async function addMissingPoolsFromSubgraph( ): Promise { // Fetch pools from subgraph // TODO this needs paging - const { pools: vaultSubgraphPools } = await vaultSubgraphClient.Pools(); + const vaultSubgraphPools = await vaultSubgraphClient.getAllPools(); const { pools: poolSubgraphPools } = await poolSubgraphClient.Pools(); // Find pools missing from the database diff --git a/modules/actions/swap/add-swaps-from-subgraph.ts b/modules/actions/swap/add-swaps-from-subgraph.ts new file mode 100644 index 000000000..32831e89e --- /dev/null +++ b/modules/actions/swap/add-swaps-from-subgraph.ts @@ -0,0 +1,95 @@ +import { Chain, Prisma } from '@prisma/client'; +import { prisma } from '../../../prisma/prisma-client'; +import { BalancerVaultSubgraphSource } from '../../sources/subgraphs/balancer-v3-vault'; +import _ from 'lodash'; +import moment from 'moment'; +import { tokenService } from '../../token/token.service'; + +type PoolDbEntry = { + pool: Prisma.PrismaPoolCreateInput; + poolTokenDynamicData: Prisma.PrismaPoolTokenDynamicDataCreateManyInput[]; + poolExpandedTokens: Prisma.PrismaPoolExpandedTokensCreateManyInput[]; +}; +/** + * Makes sure that all pools are synced in the database + * + * @param vaultSubgraphClient + * @param poolSubgraphClient + * @param chain + * @returns syncedPools - the pools that were synced + */ +export async function syncSwapsFromSubgraph( + vaultSubgraphClient: BalancerVaultSubgraphSource, + // viemClient: ViemClient, + // vaultAddress: string, + chain = 'SEPOLIA' as Chain, + daysToSync = 7, +): Promise { + const poolIds = new Set(); + const txs = new Set(); + const tokenPrices = await tokenService.getTokenPrices(chain); + + const lastSwap = await prisma.prismaPoolSwap.findFirst({ + orderBy: { timestamp: 'desc' }, + where: { chain: chain }, + }); + + const daysToSyncTimestamp = moment().subtract(daysToSync, 'day').unix(); + //ensure we only sync the last 48 hours worth of swaps + const timestamp = lastSwap && lastSwap.timestamp > daysToSyncTimestamp ? lastSwap.timestamp : daysToSyncTimestamp; + + // TODO use paging + const swaps = await vaultSubgraphClient.getSwapsSince(timestamp); + + await prisma.prismaPoolSwap.createMany({ + skipDuplicates: true, + data: swaps.map((swap) => { + let valueUSD = 0; + const tokenInPrice = tokenService.getPriceForToken(tokenPrices, swap.tokenIn); // TODO need to get price close to swap timestamp + const tokenOutPrice = tokenService.getPriceForToken(tokenPrices, swap.tokenOut); // TODO need to get price close to swap timestamp + + if (tokenInPrice > 0) { + valueUSD = tokenInPrice * parseFloat(swap.tokenAmountIn); + } else { + valueUSD = tokenOutPrice * parseFloat(swap.tokenAmountOut); + } + + poolIds.add(swap.pool); + txs.add(swap.transactionHash); + + return { + id: swap.id, + chain: chain, + timestamp: parseFloat(swap.blockTimestamp), + poolId: swap.pool, + userAddress: '0x000', //swap.user.id, + tokenIn: swap.tokenIn, + tokenInSym: swap.tokenIn, // TODO add symbol + tokenOut: swap.tokenOut, + tokenOutSym: swap.tokenOut, // TODO add symbol + tokenAmountIn: swap.tokenAmountIn, + tokenAmountOut: swap.tokenAmountOut, + tx: swap.transactionHash, + valueUSD, + }; + }), + }); + + // Do we need to create batch swaps as well? + // await this.createBatchSwaps(Array.from(txs)); + + await prisma.prismaPoolSwap.deleteMany({ + where: { + timestamp: { lt: daysToSyncTimestamp }, + chain: chain, + }, + }); + // await prisma.prismaPoolBatchSwap.deleteMany({ + // where: { + // timestamp: { lt: twoDaysAgo }, + // chain: this.chain, + // }, + // }); + + return Array.from(poolIds); +} diff --git a/modules/actions/swap/update-volume-and-fees.ts b/modules/actions/swap/update-volume-and-fees.ts new file mode 100644 index 000000000..c98789564 --- /dev/null +++ b/modules/actions/swap/update-volume-and-fees.ts @@ -0,0 +1,54 @@ +import { Chain } from '@prisma/client'; +import { prisma } from '../../../prisma/prisma-client'; +import _ from 'lodash'; +import moment from 'moment'; +import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; + +/** + * Updates 24h and 48h volume and fees for the pools provided based on swaps in the DB. Updates it for all pools if no poolIds provided. + * + * @param poolIds + * @param chain + */ +export async function updateVolumeAndFees(poolIds?: string[], chain = 'SEPOLIA' as Chain) { + 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: chain } : { chain: chain }, + include: { + swaps: { where: { timestamp: { gte: twoDaysAgo } } }, + dynamicData: true, + }, + }); + const operations: any[] = []; + + for (const pool of pools) { + const volume24h = _.sumBy( + pool.swaps.filter((swap) => swap.timestamp >= yesterday), + (swap) => (swap.tokenIn === pool.address || swap.tokenOut === pool.address ? 0 : swap.valueUSD), + ); + const fees24h = parseFloat(pool.dynamicData?.swapFee || '0') * volume24h; + + const volume48h = _.sumBy(pool.swaps, (swap) => + swap.tokenIn === pool.address || swap.tokenOut === pool.address ? 0 : swap.valueUSD, + ); + const fees48h = parseFloat(pool.dynamicData?.swapFee || '0') * volume48h; + + if ( + pool.dynamicData && + (pool.dynamicData.volume24h !== volume24h || + pool.dynamicData.fees24h !== fees24h || + pool.dynamicData.volume48h !== volume48h || + pool.dynamicData.fees48h !== fees48h) + ) { + operations.push( + prisma.prismaPoolDynamicData.update({ + where: { id_chain: { id: pool.id, chain: pool.chain } }, + data: { volume24h, fees24h, volume48h, fees48h }, + }), + ); + } + } + + await prismaBulkExecuteOperations(operations); +} diff --git a/modules/controllers/jobs-controller.ts b/modules/controllers/jobs-controller.ts index 79fc608ed..06b76b95f 100644 --- a/modules/controllers/jobs-controller.ts +++ b/modules/controllers/jobs-controller.ts @@ -6,7 +6,9 @@ import { updateOnChainDataForPools } from '../actions/pool/update-on-chain-data' import { chainIdToChain } from '../network/chain-id-to-chain'; import { getViemClient } from '../sources/viem-client'; import { getPoolsSubgraphClient } from '../subgraphs/balancer-v3-pools'; -import { getVaultSubgraphClient } from '../subgraphs/balancer-v3-vault'; +import { BalancerVaultSubgraphSource } from '../sources/subgraphs/balancer-v3-vault'; +import { syncSwapsFromSubgraph } from '../actions/swap/add-swaps-from-subgraph'; +import { updateVolumeAndFees } from '../actions/swap/update-volume-and-fees'; /** * Controller responsible for matching job requests to configured job handlers @@ -33,7 +35,7 @@ export function JobsController(tracer?: any) { throw new Error(`Chain not configured: ${chain}`); } - const vaultSubgraphClient = getVaultSubgraphClient(balancerV3); + const vaultSubgraphClient = new BalancerVaultSubgraphSource(balancerV3); const poolSubgraphClient = getPoolsSubgraphClient(balancerPoolsV3!); const viemClient = getViemClient(chain); const latestBlock = await viemClient.getBlockNumber(); @@ -44,13 +46,9 @@ export function JobsController(tracer?: any) { // find all missing pools and add them to the DB const added = await addMissingPoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, chain); - // update with latest on-chain data + // update with latest on-chain data (needed?) const updated = await updateOnChainDataForPools(vaultAddress, '123', added, viemClient, latestBlock); - // also sync swaps and volumeAndFee values - // const poolsWithNewSwaps = await poolService.syncSwapsForLast48Hours(); - // await poolService.updateVolumeAndFeeValuesForPools(poolsWithNewSwaps); - return updated; }, async updateOnChainDataChangedPools(chainId: string) { @@ -82,11 +80,28 @@ export function JobsController(tracer?: any) { blockNumber, chain, ); - // also sync swaps and volumeAndFee values - // const poolsWithNewSwaps = await poolService.syncSwapsForLast48Hours(); - // await poolService.updateVolumeAndFeeValuesForPools(poolsWithNewSwaps); return updated; }, + + // TODO also update yieldfee + // TODO maybe update fee from onchain instead of swap? + async syncSwapsUpdateVolumeAndFees(chainId: string) { + const chain = chainIdToChain[chainId]; + const { + subgraphs: { balancerV3 }, + } = config[chain]; + + // Guard against unconfigured chains + if (!balancerV3) { + throw new Error(`Chain not configured: ${chain}`); + } + + const vaultSubgraphClient = new BalancerVaultSubgraphSource(balancerV3); + + const poolsWithNewSwaps = await syncSwapsFromSubgraph(vaultSubgraphClient, chain); + await updateVolumeAndFees(poolsWithNewSwaps); + return poolsWithNewSwaps; + }, }; } diff --git a/modules/controllers/pools-controller.ts b/modules/controllers/pools-controller.ts index 400d1c5e9..fbf87734b 100644 --- a/modules/controllers/pools-controller.ts +++ b/modules/controllers/pools-controller.ts @@ -1,6 +1,9 @@ import config from '../../config'; import { updateOnchainDataForAllPools } from '../actions/pool/update-on-chain-data'; +import { syncSwapsFromSubgraph } from '../actions/swap/add-swaps-from-subgraph'; +import { updateVolumeAndFees } from '../actions/swap/update-volume-and-fees'; import { chainIdToChain } from '../network/chain-id-to-chain'; +import { BalancerVaultSubgraphSource } from '../sources/subgraphs/balancer-v3-vault'; import { getViemClient } from '../sources/viem-client'; /** @@ -32,5 +35,22 @@ export function PoolsController(tracer?: any) { const updated = updateOnchainDataForAllPools(vaultAddress, viemClient, latestBlockNumber, chain); return updated; }, + async loadSwapsFeesVolumeForAllPools(chainId: string) { + const chain = chainIdToChain[chainId]; + const { + subgraphs: { balancerV3 }, + } = config[chain]; + + // Guard against unconfigured chains + if (!balancerV3) { + throw new Error(`Chain not configured: ${chain}`); + } + + const vaultSubgraphClient = new BalancerVaultSubgraphSource(balancerV3); + + const poolsWithNewSwaps = await syncSwapsFromSubgraph(vaultSubgraphClient, chain); + await updateVolumeAndFees(poolsWithNewSwaps); + return poolsWithNewSwaps; + }, }; } diff --git a/modules/network/sepolia.ts b/modules/network/sepolia.ts index b3ae77b27..e9524d748 100644 --- a/modules/network/sepolia.ts +++ b/modules/network/sepolia.ts @@ -126,5 +126,9 @@ export const sepoliaNetworkConfig: NetworkConfig = { name: 'sync-new-pools-from-subgraph-v3', interval: every(20, 'minutes'), }, + { + name: 'supdate-swaps-volume-and-fees-v3', + interval: every(20, 'minutes'), + }, ], }; diff --git a/modules/sources/subgraphs/balancer-v3-vault/index.ts b/modules/sources/subgraphs/balancer-v3-vault/index.ts new file mode 100644 index 000000000..62685a50f --- /dev/null +++ b/modules/sources/subgraphs/balancer-v3-vault/index.ts @@ -0,0 +1,74 @@ +import { GraphQLClient } from 'graphql-request'; +import { + OrderDirection, + Pool_OrderBy, + PoolsQuery, + SwapFragment, + Swap_OrderBy, + VaultPoolFragment, + getSdk, +} from './generated/types'; + +export class BalancerVaultSubgraphSource { + private sdk: ReturnType; + + /** + * Creates a subgraph source based on subgraph URL + * @param subgraphUrl + */ + constructor(subgraphUrl: string) { + this.sdk = getSdk(new GraphQLClient(subgraphUrl)); + } + + public async getAllPools(): Promise { + const limit = 1000; + let hasMore = true; + let id = `0x`; + let pools: VaultPoolFragment[] = []; + + while (hasMore) { + const response = await this.sdk.Pools({ + where: { id_gt: id }, + orderBy: Pool_OrderBy.Id, + orderDirection: OrderDirection.Asc, + first: limit, + }); + + pools = [...pools, ...response.pools]; + + if (response.pools.length < limit) { + hasMore = false; + } else { + id = response.pools[response.pools.length - 1].id; + } + } + + return pools; + } + + public async getSwapsSince(timestamp: number): Promise { + const limit = 1000; + let hasMore = true; + let startTimestamp = `${timestamp}`; + let swaps: SwapFragment[] = []; + + while (hasMore) { + const response = await this.sdk.Swaps({ + where: { blockTimestamp_gt: startTimestamp }, + orderBy: Swap_OrderBy.BlockTimestamp, + orderDirection: OrderDirection.Asc, + first: limit, + }); + + swaps = [...swaps, ...response.swaps]; + + if (response.swaps.length < limit) { + hasMore = false; + } else { + startTimestamp = response.swaps[response.swaps.length - 1].blockTimestamp; + } + } + + return swaps; + } +} diff --git a/modules/subgraphs/balancer-v3-vault/join-exits.graphql b/modules/sources/subgraphs/balancer-v3-vault/join-exits.graphql similarity index 100% rename from modules/subgraphs/balancer-v3-vault/join-exits.graphql rename to modules/sources/subgraphs/balancer-v3-vault/join-exits.graphql diff --git a/modules/subgraphs/balancer-v3-vault/pool-snapshots.graphql b/modules/sources/subgraphs/balancer-v3-vault/pool-snapshots.graphql similarity index 100% rename from modules/subgraphs/balancer-v3-vault/pool-snapshots.graphql rename to modules/sources/subgraphs/balancer-v3-vault/pool-snapshots.graphql diff --git a/modules/subgraphs/balancer-v3-vault/pools.graphql b/modules/sources/subgraphs/balancer-v3-vault/pools.graphql similarity index 93% rename from modules/subgraphs/balancer-v3-vault/pools.graphql rename to modules/sources/subgraphs/balancer-v3-vault/pools.graphql index 786e1ffcf..bdaff0d13 100644 --- a/modules/subgraphs/balancer-v3-vault/pools.graphql +++ b/modules/sources/subgraphs/balancer-v3-vault/pools.graphql @@ -1,4 +1,4 @@ -fragment Pool on Pool { +fragment VaultPool on Pool { id factory address @@ -45,6 +45,6 @@ query Pools( where: $where block: $block ) { - ...Pool + ...VaultPool } } diff --git a/modules/subgraphs/balancer-v3-vault/swaps.graphql b/modules/sources/subgraphs/balancer-v3-vault/swaps.graphql similarity index 87% rename from modules/subgraphs/balancer-v3-vault/swaps.graphql rename to modules/sources/subgraphs/balancer-v3-vault/swaps.graphql index 89f72ef63..bfbec618b 100644 --- a/modules/subgraphs/balancer-v3-vault/swaps.graphql +++ b/modules/sources/subgraphs/balancer-v3-vault/swaps.graphql @@ -6,9 +6,10 @@ fragment Swap on Swap { tokenAmountIn tokenAmountOut swapFeeAmount - user { - id - } + # TODO BUG: user null for non-nullable + # user { + # id + # } blockNumber blockTimestamp transactionHash diff --git a/modules/subgraphs/balancer-v3-vault/users.graphql b/modules/sources/subgraphs/balancer-v3-vault/users.graphql similarity index 100% rename from modules/subgraphs/balancer-v3-vault/users.graphql rename to modules/sources/subgraphs/balancer-v3-vault/users.graphql diff --git a/modules/sources/transformers/pool-tokens-transformer.ts b/modules/sources/transformers/pool-tokens-transformer.ts index 30a2ee535..d1e31229b 100644 --- a/modules/sources/transformers/pool-tokens-transformer.ts +++ b/modules/sources/transformers/pool-tokens-transformer.ts @@ -1,4 +1,4 @@ -import { PoolFragment as VaultSubgraphPoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { VaultPoolFragment as VaultSubgraphPoolFragment } from '../subgraphs/balancer-v3-vault/generated/types'; import { TypePoolFragment as PoolSubgraphPoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; import { Chain, Prisma } from '@prisma/client'; diff --git a/modules/sources/transformers/pool-transformer.ts b/modules/sources/transformers/pool-transformer.ts index 69830e328..5a7b40f1f 100644 --- a/modules/sources/transformers/pool-transformer.ts +++ b/modules/sources/transformers/pool-transformer.ts @@ -1,5 +1,5 @@ import { Chain, PrismaPool, PrismaPoolType } from '@prisma/client'; -import { PoolFragment as VaultSubgraphPoolFragment } from '../../subgraphs/balancer-v3-vault/generated/types'; +import { VaultPoolFragment as VaultSubgraphPoolFragment } from '../subgraphs/balancer-v3-vault/generated/types'; import { TypePoolFragment as PoolSubgraphPoolFragment, PoolType, @@ -36,7 +36,7 @@ export const poolTransformer = ( decimals: 18, symbol: vaultSubgraphPool.symbol, name: vaultSubgraphPool.name, - owner: '', + owner: '', //TODO factory: poolSubgraphPool.factory.id.toLowerCase(), type: type, typeData: typeData, diff --git a/modules/subgraphs/balancer-v3-vault/index.ts b/modules/subgraphs/balancer-v3-vault/index.ts deleted file mode 100644 index c3f2b1c96..000000000 --- a/modules/subgraphs/balancer-v3-vault/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { GraphQLClient } from 'graphql-request'; -import { getSdk } from './generated/types'; - -/** - * Builds a client based on subgraph URL. - * - * @param subgraphUrl - url of the subgraph - * @returns sdk - generated sdk for the subgraph - */ -export const getVaultSubgraphClient = (subgraphUrl: string) => { - const client = new GraphQLClient(subgraphUrl); - const sdk = getSdk(client); - - return sdk; -}; - -export type V3SubgraphClient = ReturnType; diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 1eccf9932..1d187b38a 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -352,6 +352,15 @@ export function configureWorkerRoutes(app: Express) { case 'sync-sftmx-withdrawal-requests': await runIfNotAlreadyRunning(job.name, chainId, () => sftmxService.syncWithdrawalRequests(), res, next); break; + case 'update-swaps-volume-and-fees-v3': + await runIfNotAlreadyRunning( + job.name, + chainId, + () => jobsController.syncSwapsUpdateVolumeAndFees(chainId), + res, + next, + ); + break; default: res.sendStatus(400); throw new Error(`Unhandled job type ${job.name}`); From 426219251cc88049c876c9d1b61ee69a49a00e69 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:50:05 +0100 Subject: [PATCH 04/14] add swaptype to response (#164) --- modules/sor/sor.gql | 1 + modules/sor/sorV2/sorV2.service.ts | 5 +++++ modules/sor/utils.ts | 1 + 3 files changed, 7 insertions(+) diff --git a/modules/sor/sor.gql b/modules/sor/sor.gql index 658b15aaf..e29bfd656 100644 --- a/modules/sor/sor.gql +++ b/modules/sor/sor.gql @@ -40,6 +40,7 @@ type GqlSorGetSwapPaths { The token address of the tokenOut provided """ tokenOut: String! + swapType: GqlSorSwapType! swaps: [GqlSorSwap!]! #used by cowswap paths: [GqlSorPath!]! #used by b-sdk tokenInAmount: AmountHumanReadable! diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 3219ae950..78fb1452b 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -201,6 +201,7 @@ export class SorV2Service implements SwapService { return { vaultVersion: 2, paths: paths, + swapType: this.mapSwapKindToSwapType(swap.swapKind), swaps: this.mapSwaps(swap.swaps, swap.assets), tokenIn: replaceZeroAddressWithEth(inputAmount.token.address), tokenOut: replaceZeroAddressWithEth(outputAmount.token.address), @@ -227,6 +228,10 @@ export class SorV2Service implements SwapService { return swapType === 'EXACT_IN' ? SwapKind.GivenIn : SwapKind.GivenOut; } + private mapSwapKindToSwapType(swapKind: SwapKind): GqlSorSwapType { + return swapKind === SwapKind.GivenIn ? 'EXACT_IN' : 'EXACT_OUT'; + } + private mapSwaps(swaps: BatchSwapStep[] | SingleSwap, assets: string[]): GqlSorSwap[] { if (Array.isArray(swaps)) { return swaps.map((swap) => { diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts index 40bbeb455..a9e784960 100644 --- a/modules/sor/utils.ts +++ b/modules/sor/utils.ts @@ -41,6 +41,7 @@ export const swapPathsZeroResponse = (tokenIn: string, tokenOut: string): GqlSor return { swaps: [], paths: [], + swapType: 'EXACT_IN', vaultVersion: 2, tokenIn: replaceZeroAddressWithEth(tokenIn), tokenOut: replaceZeroAddressWithEth(tokenOut), From 5f2c0f5c470941b97c0cf25bdd073da75478f9a5 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:57:30 +0100 Subject: [PATCH 05/14] more v3 integration (#165) adding nested Pool ID add user under swap start update function --- .../pool/add-pools-from-subgraph.test.ts | 16 ++- .../actions/pool/add-pools-from-subgraph.ts | 32 +----- .../pool/update-pools-from-subgraph.ts | 101 ++++++++++++++++++ .../actions/swap/add-swaps-from-subgraph.ts | 52 ++------- modules/controllers/jobs-controller.ts | 6 +- modules/controllers/pools-controller.ts | 31 +++++- modules/pool/subgraph-mapper.ts | 2 +- .../subgraphs/balancer-v3-vault/index.ts | 14 +-- .../subgraphs/balancer-v3-vault/pools.graphql | 3 + .../subgraphs/balancer-v3-vault/swaps.graphql | 9 +- .../transformers/pool-tokens-transformer.ts | 20 ++-- .../sources/transformers/pool-transformer.ts | 19 +++- .../sources/transformers/swaps-transformer.ts | 37 +++++++ .../transformers/tokens-transformer.ts | 35 ++++++ 14 files changed, 268 insertions(+), 109 deletions(-) create mode 100644 modules/actions/pool/update-pools-from-subgraph.ts create mode 100644 modules/sources/transformers/swaps-transformer.ts create mode 100644 modules/sources/transformers/tokens-transformer.ts diff --git a/modules/actions/pool/add-pools-from-subgraph.test.ts b/modules/actions/pool/add-pools-from-subgraph.test.ts index 8d5303448..9552712e9 100644 --- a/modules/actions/pool/add-pools-from-subgraph.test.ts +++ b/modules/actions/pool/add-pools-from-subgraph.test.ts @@ -1,12 +1,16 @@ import { addMissingPoolsFromSubgraph } from './add-pools-from-subgraph'; import { prisma } from '../../../prisma/prisma-client'; import { PrismaPool } from '@prisma/client'; -import { VaultPoolFragment as VaultSubgraphPoolFragment } from '../../sources/subgraphs/balancer-v3-vault/generated/types'; +import { + SwapFragment, + VaultPoolFragment as VaultSubgraphPoolFragment, +} from '../../sources/subgraphs/balancer-v3-vault/generated/types'; import { TypePoolFragment as PoolSubgraphPoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; +import { GraphQLClient } from 'graphql-request'; // Mock the module dependencies -jest.mock('@modules/sources/contracts', () => ({ - ...jest.requireActual('@modules/sources/contracts'), +jest.mock('../../sources/contracts', () => ({ + ...jest.requireActual('../../sources/contracts'), fetchErc20Headers: jest.fn().mockResolvedValue({ '2': { name: 'name', symbol: 'symbol' } }), fetchWeightedPoolData: jest.fn().mockResolvedValue({}), fetchPoolTokens: jest.fn().mockResolvedValue({}), @@ -31,9 +35,11 @@ jest.mock('../../../prisma/prisma-client', () => ({ }, })); +//TODO cant mock properly // describe('syncPools', () => { // const vaultSubgraphClient = { -// getAllPools: jest.fn().mockResolvedValue([{ id: '1' }, { id: '2' }] as VaultSubgraphPoolFragment[]), +// getAllInitializedPools: jest.fn().mockResolvedValue([{ id: '1' }, { id: '2' }] as VaultSubgraphPoolFragment[]), +// getSwapsSince: jest.fn().mockResolvedValue([{ id: '1' }, { id: '2' }] as SwapFragment[]), // }; // const poolSubgraphClient = { // Pools: jest.fn().mockResolvedValue({ @@ -50,7 +56,7 @@ jest.mock('../../../prisma/prisma-client', () => ({ // }); // it('should fetch pools from vault subgraph', async () => { -// expect(vaultSubgraphClient.getAllPools).toHaveBeenCalled(); +// expect(vaultSubgraphClient.getAllInitializedPools).toHaveBeenCalled(); // }); // it('should fetch pools from pools subgraph', async () => { diff --git a/modules/actions/pool/add-pools-from-subgraph.ts b/modules/actions/pool/add-pools-from-subgraph.ts index 1eee99fcd..5c3ab9e25 100644 --- a/modules/actions/pool/add-pools-from-subgraph.ts +++ b/modules/actions/pool/add-pools-from-subgraph.ts @@ -9,6 +9,7 @@ import { import { V3PoolsSubgraphClient } from '../../subgraphs/balancer-v3-pools'; import { BalancerVaultSubgraphSource } from '../../sources/subgraphs/balancer-v3-vault'; import _ from 'lodash'; +import { tokensTransformer } from '../../sources/transformers/tokens-transformer'; type PoolDbEntry = { pool: Prisma.PrismaPoolCreateInput; @@ -26,13 +27,10 @@ type PoolDbEntry = { export async function addMissingPoolsFromSubgraph( vaultSubgraphClient: BalancerVaultSubgraphSource, poolSubgraphClient: V3PoolsSubgraphClient, - // viemClient: ViemClient, - // vaultAddress: string, chain = 'SEPOLIA' as Chain, ): Promise { // Fetch pools from subgraph - // TODO this needs paging - const vaultSubgraphPools = await vaultSubgraphClient.getAllPools(); + const vaultSubgraphPools = await vaultSubgraphClient.getAllInitializedPools(); const { pools: poolSubgraphPools } = await poolSubgraphClient.Pools(); // Find pools missing from the database @@ -42,28 +40,7 @@ export async function addMissingPoolsFromSubgraph( // Store pool tokens and BPT in the tokens table before creating the pools try { - const allTokens: { address: string; name: string; decimals: number; symbol: string; chain: Chain }[] = []; - missingPools.forEach((pool) => { - allTokens.push({ - address: pool.address, - decimals: 18, - name: pool.name, - symbol: pool.symbol, - chain: chain, - }); - if (pool.tokens) { - for (const poolToken of pool.tokens) { - allTokens.push({ - address: poolToken.address, - decimals: poolToken.decimals, - name: poolToken.name, - symbol: poolToken.symbol, - chain: chain, - }); - } - } - }); - + const allTokens = tokensTransformer(missingPools, chain); await prisma.prismaToken.createMany({ data: allTokens, skipDuplicates: true, @@ -91,7 +68,7 @@ export async function addMissingPoolsFromSubgraph( // TODO: Will be great to create all the token data here, including dynamic data // but for now we can only store static data, because prisma doesn't support nested createMany // to create dynamic data tabels as well. One solution is to move "dynamicData" to the tokens table - data: poolTokensTransformer(vaultSubgraphPool), + data: poolTokensTransformer(vaultSubgraphPool, chain), }, }, // placeholder data, will be updated with onchain values @@ -124,7 +101,6 @@ export async function addMissingPoolsFromSubgraph( data: entry.poolTokenDynamicData, }); - // TODO deal with nested pools await prisma.prismaPoolExpandedTokens.createMany({ skipDuplicates: true, data: entry.poolExpandedTokens, diff --git a/modules/actions/pool/update-pools-from-subgraph.ts b/modules/actions/pool/update-pools-from-subgraph.ts new file mode 100644 index 000000000..0ca4ce110 --- /dev/null +++ b/modules/actions/pool/update-pools-from-subgraph.ts @@ -0,0 +1,101 @@ +import { Chain, Prisma, PrismaPoolType } from '@prisma/client'; +import { prisma } from '../../../prisma/prisma-client'; +import { + poolTransformer, + poolTokensTransformer, + poolTokensDynamicDataTransformer, + poolExpandedTokensTransformer, +} from '../../sources/transformers'; +import { V3PoolsSubgraphClient } from '../../subgraphs/balancer-v3-pools'; +import { BalancerVaultSubgraphSource } from '../../sources/subgraphs/balancer-v3-vault'; +import _ from 'lodash'; +import { tokensTransformer } from '../../sources/transformers/tokens-transformer'; + +type PoolDbEntry = { + pool: Prisma.PrismaPoolCreateInput; + poolTokenDynamicData: Prisma.PrismaPoolTokenDynamicDataCreateManyInput[]; + poolExpandedTokens: Prisma.PrismaPoolExpandedTokensCreateManyInput[]; +}; + +/** + * Makes sure that all pools are synced in the database + * + * @param vaultSubgraphClient + * @param poolSubgraphClient + * @param chain + * @returns syncedPools - the pools that were synced + */ +export async function updatePoolsFromSubgraph( + vaultSubgraphClient: BalancerVaultSubgraphSource, + poolSubgraphClient: V3PoolsSubgraphClient, + chain = 'SEPOLIA' as Chain, +) { + // Fetch pools from subgraph + const vaultSubgraphPools = await vaultSubgraphClient.getAllInitializedPools(); + const { pools: poolSubgraphPools } = await poolSubgraphClient.Pools(); + + // Find pools missing from the database + const dbPools = await prisma.prismaPool.findMany({ where: { chain, vaultVersion: 3 } }); + const dbPoolIds = new Set(dbPools.map((pool) => pool.id.toLowerCase())); + const presentPools = vaultSubgraphPools.filter((pool) => dbPoolIds.has(pool.id)); + + // Making sure all tokens are present + try { + const allTokens = tokensTransformer(presentPools, chain); + await prisma.prismaToken.createMany({ + data: allTokens, + skipDuplicates: true, + }); + } catch (e) { + console.error('Error creating tokens', e); + } + + for (const presentPool of presentPools) { + const vaultSubgraphPool = vaultSubgraphPools.find((pool) => pool.id === presentPool.id); + const poolSubgraphPool = poolSubgraphPools.find((pool) => pool.id === presentPool.id); + if (!vaultSubgraphPool || !poolSubgraphPool) { + // That won't happen, but TS doesn't know that + continue; + } + + const dbPool = poolTransformer(vaultSubgraphPool, poolSubgraphPool, chain); + + await prisma.prismaPool.update({ + where: { id_chain: { id: presentPool.id, chain: chain } }, + data: { + owner: dbPool.owner, + type: dbPool.type, + typeData: dbPool.typeData, + version: dbPool.version, + }, + }); + + const transformedPoolToken = poolTokensTransformer(vaultSubgraphPool, chain); + + for (const poolToken of transformedPoolToken) { + await prisma.prismaPoolToken.update({ + where: { id_chain: { id: poolToken.id, chain: chain } }, + data: { + nestedPoolId: poolToken.nestedPoolId, + }, + }); + } + + const transformedPoolExpandedTokens = poolExpandedTokensTransformer(vaultSubgraphPool, chain); + + for (const poolToken of transformedPoolExpandedTokens) { + await prisma.prismaPoolExpandedTokens.update({ + where: { + tokenAddress_poolId_chain: { + chain: chain, + poolId: presentPool.id, + tokenAddress: poolToken.tokenAddress, + }, + }, + data: { + nestedPoolId: poolToken.nestedPoolId, + }, + }); + } + } +} diff --git a/modules/actions/swap/add-swaps-from-subgraph.ts b/modules/actions/swap/add-swaps-from-subgraph.ts index 32831e89e..897e973dd 100644 --- a/modules/actions/swap/add-swaps-from-subgraph.ts +++ b/modules/actions/swap/add-swaps-from-subgraph.ts @@ -4,80 +4,50 @@ import { BalancerVaultSubgraphSource } from '../../sources/subgraphs/balancer-v3 import _ from 'lodash'; import moment from 'moment'; import { tokenService } from '../../token/token.service'; +import { swapsTransformer } from '../../sources/transformers/swaps-transformer'; type PoolDbEntry = { pool: Prisma.PrismaPoolCreateInput; poolTokenDynamicData: Prisma.PrismaPoolTokenDynamicDataCreateManyInput[]; poolExpandedTokens: Prisma.PrismaPoolExpandedTokensCreateManyInput[]; }; + /** - * Makes sure that all pools are synced in the database + * Adds all swaps since daysToSync to the database. Checks for latest synced swap to avoid duplicate work. * * @param vaultSubgraphClient - * @param poolSubgraphClient * @param chain - * @returns syncedPools - the pools that were synced + * @param daysToSync + * @returns */ -export async function syncSwapsFromSubgraph( +export async function addSwapsFromSubgraph( vaultSubgraphClient: BalancerVaultSubgraphSource, - // viemClient: ViemClient, - // vaultAddress: string, chain = 'SEPOLIA' as Chain, - daysToSync = 7, + daysToSync = 30, ): Promise { const poolIds = new Set(); - const txs = new Set(); - const tokenPrices = await tokenService.getTokenPrices(chain); + // only sync from the latest swap in DB to avoid duplicate work const lastSwap = await prisma.prismaPoolSwap.findFirst({ orderBy: { timestamp: 'desc' }, where: { chain: chain }, }); + //ensure we only query the last daysToSync worth of swaps const daysToSyncTimestamp = moment().subtract(daysToSync, 'day').unix(); - //ensure we only sync the last 48 hours worth of swaps const timestamp = lastSwap && lastSwap.timestamp > daysToSyncTimestamp ? lastSwap.timestamp : daysToSyncTimestamp; - // TODO use paging const swaps = await vaultSubgraphClient.getSwapsSince(timestamp); await prisma.prismaPoolSwap.createMany({ skipDuplicates: true, - data: swaps.map((swap) => { - let valueUSD = 0; - const tokenInPrice = tokenService.getPriceForToken(tokenPrices, swap.tokenIn); // TODO need to get price close to swap timestamp - const tokenOutPrice = tokenService.getPriceForToken(tokenPrices, swap.tokenOut); // TODO need to get price close to swap timestamp - - if (tokenInPrice > 0) { - valueUSD = tokenInPrice * parseFloat(swap.tokenAmountIn); - } else { - valueUSD = tokenOutPrice * parseFloat(swap.tokenAmountOut); - } - - poolIds.add(swap.pool); - txs.add(swap.transactionHash); - - return { - id: swap.id, - chain: chain, - timestamp: parseFloat(swap.blockTimestamp), - poolId: swap.pool, - userAddress: '0x000', //swap.user.id, - tokenIn: swap.tokenIn, - tokenInSym: swap.tokenIn, // TODO add symbol - tokenOut: swap.tokenOut, - tokenOutSym: swap.tokenOut, // TODO add symbol - tokenAmountIn: swap.tokenAmountIn, - tokenAmountOut: swap.tokenAmountOut, - tx: swap.transactionHash, - valueUSD, - }; - }), + data: await swapsTransformer(swaps, chain), }); // Do we need to create batch swaps as well? // await this.createBatchSwaps(Array.from(txs)); + // Remove everything older that daysToSync await prisma.prismaPoolSwap.deleteMany({ where: { timestamp: { lt: daysToSyncTimestamp }, diff --git a/modules/controllers/jobs-controller.ts b/modules/controllers/jobs-controller.ts index 06b76b95f..c0ba89d2c 100644 --- a/modules/controllers/jobs-controller.ts +++ b/modules/controllers/jobs-controller.ts @@ -7,7 +7,7 @@ import { chainIdToChain } from '../network/chain-id-to-chain'; import { getViemClient } from '../sources/viem-client'; import { getPoolsSubgraphClient } from '../subgraphs/balancer-v3-pools'; import { BalancerVaultSubgraphSource } from '../sources/subgraphs/balancer-v3-vault'; -import { syncSwapsFromSubgraph } from '../actions/swap/add-swaps-from-subgraph'; +import { addSwapsFromSubgraph } from '../actions/swap/add-swaps-from-subgraph'; import { updateVolumeAndFees } from '../actions/swap/update-volume-and-fees'; /** @@ -46,7 +46,7 @@ export function JobsController(tracer?: any) { // find all missing pools and add them to the DB const added = await addMissingPoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, chain); - // update with latest on-chain data (needed?) + // update with latest on-chain data (needed? this will run on a separate job anyway) const updated = await updateOnChainDataForPools(vaultAddress, '123', added, viemClient, latestBlock); return updated; @@ -99,7 +99,7 @@ export function JobsController(tracer?: any) { const vaultSubgraphClient = new BalancerVaultSubgraphSource(balancerV3); - const poolsWithNewSwaps = await syncSwapsFromSubgraph(vaultSubgraphClient, chain); + const poolsWithNewSwaps = await addSwapsFromSubgraph(vaultSubgraphClient, chain); await updateVolumeAndFees(poolsWithNewSwaps); return poolsWithNewSwaps; }, diff --git a/modules/controllers/pools-controller.ts b/modules/controllers/pools-controller.ts index fbf87734b..ef1cf7e1e 100644 --- a/modules/controllers/pools-controller.ts +++ b/modules/controllers/pools-controller.ts @@ -1,10 +1,12 @@ import config from '../../config'; import { updateOnchainDataForAllPools } from '../actions/pool/update-on-chain-data'; -import { syncSwapsFromSubgraph } from '../actions/swap/add-swaps-from-subgraph'; +import { updatePoolsFromSubgraph } from '../actions/pool/update-pools-from-subgraph'; +import { addSwapsFromSubgraph } from '../actions/swap/add-swaps-from-subgraph'; import { updateVolumeAndFees } from '../actions/swap/update-volume-and-fees'; import { chainIdToChain } from '../network/chain-id-to-chain'; import { BalancerVaultSubgraphSource } from '../sources/subgraphs/balancer-v3-vault'; import { getViemClient } from '../sources/viem-client'; +import { getPoolsSubgraphClient } from '../subgraphs/balancer-v3-pools'; /** * Controller responsible for matching job requests to configured job handlers @@ -17,6 +19,31 @@ export function PoolsController(tracer?: any) { // Setup tracing // ... return { + async updatePoolsFromSubgraph(chainIds: string[]) { + const updatedPools: string[] = []; + for (const chainId of chainIds) { + const chain = chainIdToChain[chainId]; + const { + subgraphs: { balancerV3, balancerPoolsV3 }, + } = config[chain]; + + // Guard against unconfigured chains + if (!balancerV3) { + throw new Error(`Chain not configured: ${chain}`); + } + + const vaultSubgraphClient = new BalancerVaultSubgraphSource(balancerV3); + const poolSubgraphClient = getPoolsSubgraphClient(balancerPoolsV3!); + + // TODO: add syncing v2 pools as well by splitting the poolService into separate + // actions with extracted configuration + + // find all missing pools and add them to the DB + const added = await updatePoolsFromSubgraph(vaultSubgraphClient, poolSubgraphClient, chain); + } + + return updatedPools; + }, async updateOnChainDataForAllPools(chainId: string) { const chain = chainIdToChain[chainId]; const { @@ -48,7 +75,7 @@ export function PoolsController(tracer?: any) { const vaultSubgraphClient = new BalancerVaultSubgraphSource(balancerV3); - const poolsWithNewSwaps = await syncSwapsFromSubgraph(vaultSubgraphClient, chain); + const poolsWithNewSwaps = await addSwapsFromSubgraph(vaultSubgraphClient, chain); await updateVolumeAndFees(poolsWithNewSwaps); return poolsWithNewSwaps; }, diff --git a/modules/pool/subgraph-mapper.ts b/modules/pool/subgraph-mapper.ts index bc4178781..3ee54ecbf 100644 --- a/modules/pool/subgraph-mapper.ts +++ b/modules/pool/subgraph-mapper.ts @@ -1,4 +1,4 @@ -import { Chain, PrismaPoolType } from '@prisma/client'; +import { Chain, Prisma, PrismaPoolType } from '@prisma/client'; import { BalancerPoolFragment } from '../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; import { AddressZero } from '@ethersproject/constants'; import { fx, gyro, linear, element, stable } from './pool-data'; diff --git a/modules/sources/subgraphs/balancer-v3-vault/index.ts b/modules/sources/subgraphs/balancer-v3-vault/index.ts index 62685a50f..bc24fd734 100644 --- a/modules/sources/subgraphs/balancer-v3-vault/index.ts +++ b/modules/sources/subgraphs/balancer-v3-vault/index.ts @@ -1,13 +1,5 @@ import { GraphQLClient } from 'graphql-request'; -import { - OrderDirection, - Pool_OrderBy, - PoolsQuery, - SwapFragment, - Swap_OrderBy, - VaultPoolFragment, - getSdk, -} from './generated/types'; +import { OrderDirection, Pool_OrderBy, SwapFragment, Swap_OrderBy, VaultPoolFragment, getSdk } from './generated/types'; export class BalancerVaultSubgraphSource { private sdk: ReturnType; @@ -20,7 +12,7 @@ export class BalancerVaultSubgraphSource { this.sdk = getSdk(new GraphQLClient(subgraphUrl)); } - public async getAllPools(): Promise { + public async getAllInitializedPools(): Promise { const limit = 1000; let hasMore = true; let id = `0x`; @@ -28,7 +20,7 @@ export class BalancerVaultSubgraphSource { while (hasMore) { const response = await this.sdk.Pools({ - where: { id_gt: id }, + where: { id_gt: id, isInitialized: true }, orderBy: Pool_OrderBy.Id, orderDirection: OrderDirection.Asc, first: limit, diff --git a/modules/sources/subgraphs/balancer-v3-vault/pools.graphql b/modules/sources/subgraphs/balancer-v3-vault/pools.graphql index bdaff0d13..58c04b4ac 100644 --- a/modules/sources/subgraphs/balancer-v3-vault/pools.graphql +++ b/modules/sources/subgraphs/balancer-v3-vault/pools.graphql @@ -20,6 +20,9 @@ fragment VaultPool on Pool { balance totalProtocolSwapFee totalProtocolYieldFee + nestedPool { + id + } } rateProviders { address diff --git a/modules/sources/subgraphs/balancer-v3-vault/swaps.graphql b/modules/sources/subgraphs/balancer-v3-vault/swaps.graphql index bfbec618b..77ec638f0 100644 --- a/modules/sources/subgraphs/balancer-v3-vault/swaps.graphql +++ b/modules/sources/subgraphs/balancer-v3-vault/swaps.graphql @@ -2,14 +2,15 @@ fragment Swap on Swap { id pool tokenIn + tokenInSymbol tokenOut + tokenOutSymbol tokenAmountIn tokenAmountOut swapFeeAmount - # TODO BUG: user null for non-nullable - # user { - # id - # } + user { + id + } blockNumber blockTimestamp transactionHash diff --git a/modules/sources/transformers/pool-tokens-transformer.ts b/modules/sources/transformers/pool-tokens-transformer.ts index d1e31229b..d6504423b 100644 --- a/modules/sources/transformers/pool-tokens-transformer.ts +++ b/modules/sources/transformers/pool-tokens-transformer.ts @@ -1,16 +1,16 @@ import { VaultPoolFragment as VaultSubgraphPoolFragment } from '../subgraphs/balancer-v3-vault/generated/types'; import { TypePoolFragment as PoolSubgraphPoolFragment } from '../../subgraphs/balancer-v3-pools/generated/types'; -import { Chain, Prisma } from '@prisma/client'; +import { Chain, Prisma, PrismaPoolToken } from '@prisma/client'; -export function poolTokensTransformer( - vaultSubgraphPool: VaultSubgraphPoolFragment, -): Prisma.PrismaPoolTokenCreateManyPoolInput[] { +export function poolTokensTransformer(vaultSubgraphPool: VaultSubgraphPoolFragment, chain: Chain): PrismaPoolToken[] { const tokens = vaultSubgraphPool.tokens ?? []; return tokens.map((token, i) => ({ id: `${vaultSubgraphPool.id}-${token.address}`.toLowerCase(), + poolId: vaultSubgraphPool.id.toLowerCase(), + chain: chain, address: token.address.toLowerCase(), index: token.index, - nestedPoolId: null, + nestedPoolId: token.nestedPool?.id.toLowerCase() ?? null, priceRateProvider: vaultSubgraphPool.rateProviders![i].address.toLowerCase(), exemptFromProtocolYieldFee: token.totalProtocolYieldFee === '0' ? true : false, })); @@ -29,22 +29,20 @@ export function poolTokensDynamicDataTransformer( blockNumber: parseFloat(vaultSubgraphPool.blockNumber), balance: token.balance, balanceUSD: 0, - priceRate: '0', + priceRate: '1', weight: poolSubgraphPool.weights[token.index] ?? null, - // latestFxPrice: poolSubgraphPool.latestFxPrice, })); } -// TODO deal with nested pools export function poolExpandedTokensTransformer( vaultSubgraphPool: VaultSubgraphPoolFragment, chain: Chain, ): Prisma.PrismaPoolExpandedTokensCreateManyInput[] { const tokens = vaultSubgraphPool.tokens ?? []; return tokens.map((token, i) => ({ - poolId: vaultSubgraphPool.id, + poolId: vaultSubgraphPool.id.toLowerCase(), chain: chain, - tokenAddress: token.address, - nestedPoolId: null, + tokenAddress: token.address.toLowerCase(), + nestedPoolId: token.nestedPool?.id.toLowerCase(), })); } diff --git a/modules/sources/transformers/pool-transformer.ts b/modules/sources/transformers/pool-transformer.ts index 5a7b40f1f..61a7bdb4c 100644 --- a/modules/sources/transformers/pool-transformer.ts +++ b/modules/sources/transformers/pool-transformer.ts @@ -5,14 +5,15 @@ import { PoolType, } from '../../subgraphs/balancer-v3-pools/generated/types'; import { StableData } from '../../pool/subgraph-mapper'; +import { fx, gyro, linear, element, stable } from '../../pool/pool-data'; export const poolTransformer = ( vaultSubgraphPool: VaultSubgraphPoolFragment, poolSubgraphPool: PoolSubgraphPoolFragment, chain: Chain, -): PrismaPool => { +) => { let type: PrismaPoolType; - let typeData = {}; + let typeData: ReturnType | {} = {}; switch (poolSubgraphPool.factory.type) { case PoolType.Weighted: @@ -36,7 +37,7 @@ export const poolTransformer = ( decimals: 18, symbol: vaultSubgraphPool.symbol, name: vaultSubgraphPool.name, - owner: '', //TODO + owner: vaultSubgraphPool.id.toLowerCase(), //TODO factory: poolSubgraphPool.factory.id.toLowerCase(), type: type, typeData: typeData, @@ -44,3 +45,15 @@ export const poolTransformer = ( createTime: Number(vaultSubgraphPool.blockTimestamp), }; }; + +const typeDataMapper = { + ELEMENT: element, + FX: fx, + GYRO: gyro, + GYRO3: gyro, + GYROE: gyro, + LINEAR: linear, + STABLE: stable, + COMPOSABLE_STABLE: stable, + META_STABLE: stable, +}; diff --git a/modules/sources/transformers/swaps-transformer.ts b/modules/sources/transformers/swaps-transformer.ts new file mode 100644 index 000000000..3e5e4f961 --- /dev/null +++ b/modules/sources/transformers/swaps-transformer.ts @@ -0,0 +1,37 @@ +import { SwapFragment } from '../subgraphs/balancer-v3-vault/generated/types'; +import { Chain, PrismaPoolSwap } from '@prisma/client'; +import { tokenService } from '../../token/token.service'; + +export async function swapsTransformer(swaps: SwapFragment[], chain: Chain): Promise { + const tokenPrices = await tokenService.getTokenPrices(chain); + + return swaps.map((swap) => { + let valueUSD = 0; + const tokenInPrice = tokenService.getPriceForToken(tokenPrices, swap.tokenIn); // TODO need to get price close to swap timestamp + const tokenOutPrice = tokenService.getPriceForToken(tokenPrices, swap.tokenOut); // TODO need to get price close to swap timestamp + + if (tokenInPrice > 0) { + valueUSD = tokenInPrice * parseFloat(swap.tokenAmountIn); + } else { + valueUSD = tokenOutPrice * parseFloat(swap.tokenAmountOut); + } + + return { + id: swap.id, + chain: chain, + timestamp: parseFloat(swap.blockTimestamp), + poolId: swap.pool, + userAddress: swap.user.id, + tokenIn: swap.tokenIn, + tokenInSym: swap.tokenInSymbol, + tokenOut: swap.tokenOut, + tokenOutSym: swap.tokenOutSymbol, + tokenAmountIn: swap.tokenAmountIn, + tokenAmountOut: swap.tokenAmountOut, + tx: swap.transactionHash, + valueUSD, + batchSwapId: null, + batchSwapIdx: null, + }; + }); +} diff --git a/modules/sources/transformers/tokens-transformer.ts b/modules/sources/transformers/tokens-transformer.ts new file mode 100644 index 000000000..3cc7704b8 --- /dev/null +++ b/modules/sources/transformers/tokens-transformer.ts @@ -0,0 +1,35 @@ +import { VaultPoolFragment as VaultSubgraphPoolFragment } from '../subgraphs/balancer-v3-vault/generated/types'; +import { Chain } from '@prisma/client'; + +type DbToken = { + address: string; + name: string; + decimals: number; + symbol: string; + chain: Chain; +}; + +export function tokensTransformer(vaultSubgraphPools: VaultSubgraphPoolFragment[], chain: Chain): DbToken[] { + const allTokens: DbToken[] = []; + vaultSubgraphPools.forEach((pool) => { + allTokens.push({ + address: pool.address, + decimals: 18, + name: pool.name, + symbol: pool.symbol, + chain: chain, + }); + if (pool.tokens) { + for (const poolToken of pool.tokens) { + allTokens.push({ + address: poolToken.address, + decimals: poolToken.decimals, + name: poolToken.name, + symbol: poolToken.symbol, + chain: chain, + }); + } + } + }); + return allTokens; +} From 892ce8cbb15a8a763c3c67ccacdea6243d62c6da Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:02:31 +0100 Subject: [PATCH 06/14] fix: SOR issues with parsing scientific notation --- modules/sor/sorV2/lib/pools/weighted/weightedPool.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/sor/sorV2/lib/pools/weighted/weightedPool.ts b/modules/sor/sorV2/lib/pools/weighted/weightedPool.ts index d2965bcbf..320abea50 100644 --- a/modules/sor/sorV2/lib/pools/weighted/weightedPool.ts +++ b/modules/sor/sorV2/lib/pools/weighted/weightedPool.ts @@ -63,7 +63,8 @@ export class WeightedPool implements BasePool { poolToken.token.symbol, poolToken.token.name, ); - const tokenAmount = TokenAmount.fromHumanAmount(token, `${parseFloat(poolToken.dynamicData.balance)}`); + const balance = parseFloat(poolToken.dynamicData.balance).toFixed(18); + const tokenAmount = TokenAmount.fromHumanAmount(token, balance as `${number}`); poolTokens.push( new WeightedPoolToken( From 2e415d8285b4e27316f782cc0a905c7ae6350a51 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:02:48 +0100 Subject: [PATCH 07/14] fix: pass tokens from the requested chain --- modules/sor/sorV1Beets/sorV1Beets.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index 9e11579d4..160414097 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -58,7 +58,8 @@ export class SorV1BeetsService implements SwapService { private async querySorV1( input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }, ): Promise { - const tokens = await tokenService.getTokens(); + // Explicitly pass chain to getTokens to avoid using the default chain + const tokens = await tokenService.getTokens(undefined, input.chain); return await this.sorService.getSwaps({ ...input, tokens, swapAmount: formatEther(input.swapAmount.scale18) }); } } From 32a4c4ffb583e30533d93f8a4c0911cc8a52e21d Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Tue, 27 Feb 2024 12:46:31 +0100 Subject: [PATCH 08/14] sepolia token pricing (#171) * inject coingecko token Ids for sepolia to get prices * add more testnet tokens --- modules/content/github-content.service.ts | 45 ++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/modules/content/github-content.service.ts b/modules/content/github-content.service.ts index 079adc6cc..22fc3167c 100644 --- a/modules/content/github-content.service.ts +++ b/modules/content/github-content.service.ts @@ -32,6 +32,41 @@ interface WhitelistedToken { logoURI: string; } +const sepoliaTokens: Record = { + '0xb19382073c7a0addbb56ac6af1808fa49e377b75': { + symbol: 'BAL', + coingeckoTokenId: 'balancer', + }, + '0xf04378a3ff97b3f979a46f91f9b2d5a1d2394773': { + symbol: 'DAI', + coingeckoTokenId: 'dai', + }, + '0x7b79995e5f793a07bc00c21412e50ecae098e7f9': { + symbol: 'WETH', + coingeckoTokenId: 'weth', + }, + '0x80d6d3946ed8a1da4e226aa21ccddc32bd127d1a': { + symbol: 'USDC', + coingeckoTokenId: 'usd-coin', + }, + '0x6bf294b80c7d8dc72dee762af5d01260b756a051': { + symbol: 'USDT', + coingeckoTokenId: 'tether', + }, + '0x23bad11f1543503cb1fb5dad05fdaf93f42d30f3': { + symbol: 'EURS', + coingeckoTokenId: 'stasis-eurs', + }, + '0x0f409e839a6a790aecb737e4436293be11717f95': { + symbol: 'BEETS', + coingeckoTokenId: 'beethoven-x', + }, + '0xc3745bce4b5d0977dc874832bc99108d416dce8f': { + symbol: 'WBTC', + coingeckoTokenId: 'wrapped-bitcoin', + }, +}; + //TODO implement other content functions export class GithubContentService implements ContentService { async syncTokenContentData(): Promise { @@ -48,6 +83,13 @@ export class GithubContentService implements ContentService { for (const githubToken of filteredTokenList) { const tokenAddress = githubToken.address.toLowerCase(); + let coingeckoTokenId = null; + + if (networkContext.chain === 'SEPOLIA') { + if (sepoliaTokens[tokenAddress]) { + coingeckoTokenId = sepoliaTokens[tokenAddress].coingeckoTokenId; + } + } await prisma.prismaToken.upsert({ where: { @@ -62,7 +104,7 @@ export class GithubContentService implements ContentService { logoURI: githubToken.logoURI, coingeckoPlatformId: null, coingeckoContractAddress: null, - coingeckoTokenId: null, + coingeckoTokenId: coingeckoTokenId, description: null, websiteUrl: null, discordUrl: null, @@ -73,6 +115,7 @@ export class GithubContentService implements ContentService { name: githubToken.name, symbol: githubToken.symbol, logoURI: { set: githubToken.logoURI || null }, + coingeckoTokenId: coingeckoTokenId, }, }); } From be534a31a186c3dfc9fc41f4e6dc7dcea85e970a Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:46:52 +0100 Subject: [PATCH 09/14] Hotfix: add sDOLA to exclusion list (#173) (#174) * Hotfix: exclude sDOLA - Exclude sDOLA so that pricing information is properly handled as there is a coingecko id but not price - Affected pool: https://app.balancer.fi/#/ethereum/pool/0x264062ca46a1322c2e6464471764089e01f22f1900000000000000000000066b Co-authored-by: Xeonus --- modules/network/mainnet.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 28189685d..c7a3f7307 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -58,7 +58,8 @@ export const data: NetworkData = { nativeAssetId: 'ethereum', platformId: 'ethereum', excludedTokenAddresses: [ - '0x04c154b66cb340f3ae24111cc767e0184ed00cc6', // pxETH, has coingekco entry but no price + '0x04c154b66cb340f3ae24111cc767e0184ed00cc6', // pxETH, has Coingecko entry but no price + '0xb45ad160634c528cc3d2926d9807104fa3157305', // sDOLA, has Coingecko entry but no price ], }, rpcUrl: From f60477fc72178838afb2b5feacb0c64348435821 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:43:51 +0100 Subject: [PATCH 10/14] sftmxapr (#176) --- modules/network/fantom.ts | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index a19c894d3..6f7bdd913 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -164,14 +164,14 @@ const fantomNetworkData: NetworkData = { }, }, ybAprConfig: { - // sftmx: { - // tokens: { - // sftmx: { - // address: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1', - // ftmStakingAddress: '0xb458bfc855ab504a8a327720fcef98886065529b', - // }, - // }, - // }, + sftmx: { + tokens: { + sftmx: { + address: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1', + ftmStakingAddress: '0xb458bfc855ab504a8a327720fcef98886065529b', + }, + }, + }, reaper: { subgraphSource: { subgraphUrl: 'https://api.thegraph.com/subgraphs/name/byte-masons/multi-strategy-vaults-fantom', @@ -232,13 +232,6 @@ const fantomNetworkData: NetworkData = { yearn: { sourceUrl: 'https://api.yexporter.io/v1/chains/250/vaults/all', }, - fixedAprHandler: { - sFTMx: { - address: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1', - apr: 0.046, - isIbYield: true, - }, - }, defaultHandlers: { ankrETH: { tokenAddress: '0x12d8ce035c5de3ce39b1fdd4c1d5a745eaba3b8c', @@ -263,9 +256,6 @@ const fantomNetworkData: NetworkData = { spooky: { xBooContract: '0x841fad6eae12c286d1fd18d1d525dffa75c7effe', }, - stader: { - sFtmxContract: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1', - }, datastudio: { main: { user: 'datafeed-service@datastudio-366113.iam.gserviceaccount.com', From 465db5a5253f8fecc90506d6166307c03f2268c9 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 29 Feb 2024 10:58:05 +0100 Subject: [PATCH 11/14] coingecko id override (#177) --- 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 | 4 ---- modules/network/optimism.ts | 4 ---- modules/network/polygon.ts | 4 ---- modules/network/sepolia.ts | 4 ++++ modules/network/zkevm.ts | 11 +++-------- modules/token/lib/coingecko-data.service.ts | 20 +++++++++----------- modules/token/token.service.ts | 6 ++---- worker/job-handlers.ts | 3 --- 13 files changed, 18 insertions(+), 58 deletions(-) diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 2d21e674c..996df32a7 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -334,10 +334,6 @@ export const arbitrumNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 4f3c9113a..4792ff401 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -309,10 +309,6 @@ export const avalancheNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/base.ts b/modules/network/base.ts index 8a46bf897..0ad4e6344 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -232,10 +232,6 @@ export const baseNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index 6f7bdd913..c404059e6 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -416,10 +416,6 @@ export const fantomNetworkConfig: NetworkConfig = { name: 'sync-latest-reliquary-snapshots', interval: every(1, 'hours'), }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index e8532f9c8..338762b6a 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -239,10 +239,6 @@ export const gnosisNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index c7a3f7307..e5771c1cd 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -490,10 +490,6 @@ export const mainnetNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index 8dafd87e1..78a7fdcda 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -371,10 +371,6 @@ export const optimismNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index bd6786004..759064670 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -359,10 +359,6 @@ export const polygonNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/network/sepolia.ts b/modules/network/sepolia.ts index e9524d748..61e334fb2 100644 --- a/modules/network/sepolia.ts +++ b/modules/network/sepolia.ts @@ -130,5 +130,9 @@ export const sepoliaNetworkConfig: NetworkConfig = { name: 'supdate-swaps-volume-and-fees-v3', interval: every(20, 'minutes'), }, + { + name: 'sync-global-coingecko-prices', + interval: every(12, 'hours'), + }, ], }; diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 65fc4c126..699d0242c 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -52,12 +52,11 @@ const zkevmNetworkData: NetworkData = { platformId: 'polygon-zkevm', excludedTokenAddresses: [], }, - rpcUrl: - env.GROVE_CITY + rpcUrl: env.GROVE_CITY ? `https://polygon-zkevm-mainnet.rpc.grove.city/v1/${env.GROVE_CITY}` : env.ALCHEMY_API_KEY && (env.DEPLOYMENT_ENV as DeploymentEnv) === 'main' - ? `https://polygonzkevm-mainnet.g.alchemy.com/v2/${env.ALCHEMY_API_KEY}` - : 'https://zkevm-rpc.com', + ? `https://polygonzkevm-mainnet.g.alchemy.com/v2/${env.ALCHEMY_API_KEY}` + : 'https://zkevm-rpc.com', rpcMaxBlockRange: 2000, protocolToken: 'bal', bal: { @@ -275,10 +274,6 @@ export const zkevmNetworkConfig: NetworkConfig = { alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, - { - name: 'sync-coingecko-coinids', - interval: every(2, 'hours'), - }, { name: 'update-fee-volume-yield-all-pools', interval: every(1, 'hours'), diff --git a/modules/token/lib/coingecko-data.service.ts b/modules/token/lib/coingecko-data.service.ts index 179e0ed25..28e57b7f4 100644 --- a/modules/token/lib/coingecko-data.service.ts +++ b/modules/token/lib/coingecko-data.service.ts @@ -148,17 +148,15 @@ export class CoingeckoDataService { } }); - if (coinId && token.coingeckoTokenId !== coinId.id) { - await prisma.prismaToken.update({ - where: { - address_chain: { address: token.address, chain: networkContext.chain }, - }, - data: { - coingeckoTokenId: coinId.id, - coingeckoPlatformId: networkContext.data.coingecko.platformId, - }, - }); - } + await prisma.prismaToken.update({ + where: { + address_chain: { address: token.address, chain: networkContext.chain }, + }, + data: { + coingeckoTokenId: coinId ? coinId.id : null, + coingeckoPlatformId: networkContext.data.coingecko.platformId, + }, + }); } } diff --git a/modules/token/token.service.ts b/modules/token/token.service.ts index c39343541..9c7a548bb 100644 --- a/modules/token/token.service.ts +++ b/modules/token/token.service.ts @@ -24,6 +24,8 @@ export class TokenService { } public async syncTokenContentData() { + //sync coingecko Ids first, then override Ids from the content service + await this.coingeckoDataService.syncCoingeckoIds(); await networkContext.config.contentService.syncTokenContentData(); } @@ -137,10 +139,6 @@ export class TokenService { await this.coingeckoDataService.syncCoingeckoPricesForAllChains(); } - public async syncCoingeckoIds(): Promise { - await this.coingeckoDataService.syncCoingeckoIds(); - } - public async getTokenDynamicData(tokenAddress: string, chain: Chain): Promise { const token = await prisma.prismaToken.findUnique({ where: { diff --git a/worker/job-handlers.ts b/worker/job-handlers.ts index 1d187b38a..8481f9a33 100644 --- a/worker/job-handlers.ts +++ b/worker/job-handlers.ts @@ -305,9 +305,6 @@ export function configureWorkerRoutes(app: Express) { next, ); break; - case 'sync-coingecko-coinids': - await runIfNotAlreadyRunning(job.name, chainId, () => tokenService.syncCoingeckoIds(), res, next); - break; case 'update-fee-volume-yield-all-pools': await runIfNotAlreadyRunning( job.name, From 4acc856eab01956853662244545017674dccc159 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 29 Feb 2024 10:58:25 +0100 Subject: [PATCH 12/14] sor config cleanup (#172) --- config/sepolia.ts | 18 --------- modules/network/arbitrum.ts | 18 --------- modules/network/avalanche.ts | 18 --------- modules/network/base.ts | 18 --------- modules/network/fantom.ts | 30 +++++++------- modules/network/gnosis.ts | 18 --------- modules/network/mainnet.ts | 26 ++----------- modules/network/network-config-types.ts | 18 +++++---- modules/network/optimism.ts | 30 +++++++------- modules/network/polygon.ts | 18 --------- modules/network/zkevm.ts | 18 --------- modules/sor/sor.resolvers.ts | 2 - .../sor/sorV1Beets/balancer-sor.service.ts | 39 ++++++++++--------- modules/sor/sorV1Beets/sorV1Beets.service.ts | 8 +++- modules/sor/sorV2/sorV2.service.ts | 2 +- 15 files changed, 72 insertions(+), 209 deletions(-) diff --git a/config/sepolia.ts b/config/sepolia.ts index 954990207..2e38acee1 100644 --- a/config/sepolia.ts +++ b/config/sepolia.ts @@ -67,24 +67,6 @@ export const sepoliaConfig: NetworkData = { multicall: '0x80c7dd17b01855a6d2347444a0fcc36136a314de', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 1, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, ybAprConfig: {}, datastudio: { main: { diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 996df32a7..38bc3f43e 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -83,24 +83,6 @@ export const arbitrumNetworkData: NetworkData = { multicall: '0x80c7dd17b01855a6d2347444a0fcc36136a314de', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 1, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, ybAprConfig: { aave: { v3: { diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 4792ff401..0711cce29 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -81,24 +81,6 @@ const avalancheNetworkData: NetworkData = { multicall: '0xca11bde05977b3631167028862be2a173976ca11', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 2, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, ybAprConfig: { aave: { v3: { diff --git a/modules/network/base.ts b/modules/network/base.ts index 0ad4e6344..c3d882d0d 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -88,24 +88,6 @@ const baseNetworkData: NetworkData = { multicall: '0xca11bde05977b3631167028862be2a173976ca11', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 2, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, monitoring: { main: { alarmTopicArn: 'arn:aws:sns:ca-central-1:118697801881:api_alarms', diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index c404059e6..10fbb2397 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -146,21 +146,21 @@ const fantomNetworkData: NetworkData = { }, avgBlockSpeed: 1, sor: { - main: { - url: 'https://2bz6hsr2y54svqgow7tbwwsrta0icouy.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://mep53ds2noe6rhicd67q7raqhq0dkupc.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], + env: { + main: { + url: 'https://2bz6hsr2y54svqgow7tbwwsrta0icouy.lambda-url.ca-central-1.on.aws/', + maxPools: 8, + forceRefresh: false, + gasPrice: BigNumber.from(10), + swapGas: BigNumber.from('1000000'), + }, + canary: { + url: 'https://mep53ds2noe6rhicd67q7raqhq0dkupc.lambda-url.eu-central-1.on.aws/', + maxPools: 8, + forceRefresh: false, + gasPrice: BigNumber.from(10), + swapGas: BigNumber.from('1000000'), + }, }, }, ybAprConfig: { diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index 338762b6a..667868194 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -81,24 +81,6 @@ const gnosisNetworkData: NetworkData = { multicall: '0xbb6fab6b627947dae0a75808250d8b2652952cb5', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 1, - sor: { - main: { - url: '', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: '', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, ybAprConfig: { defaultHandlers: { wstETH: { diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index e5771c1cd..ada1cc5d6 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -99,28 +99,10 @@ export const data: NetworkData = { multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 10, sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - 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/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [ - '0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3', - '0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4', // Linear pools that cause issues with new b-sdk - ], - }, + poolIdsToExclude: [ + '0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3', + '0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4', // Linear pools that cause issues with new b-sdk + ], }, ybAprConfig: { aave: { diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index 595ba5269..34cc493f6 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -148,14 +148,16 @@ export interface NetworkData { aprEndpoint: string; }; avgBlockSpeed: number; - sor: { - [key in DeploymentEnv]: { - url: string; - maxPools: number; - forceRefresh: boolean; - gasPrice: BigNumber; - swapGas: BigNumber; - poolIdsToExclude: string[]; + sor?: { + poolIdsToExclude?: string[]; + env?: { + [key in DeploymentEnv]?: { + url: string; + maxPools: number; + forceRefresh: boolean; + gasPrice: BigNumber; + swapGas: BigNumber; + }; }; }; datastudio?: { diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index 78a7fdcda..71c18d35a 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -93,21 +93,21 @@ const optimismNetworkData: NetworkData = { }, avgBlockSpeed: 1, sor: { - main: { - url: 'https://nplks2oknz5lhxn6kpgxbfrxgm0hzicm.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://svlitjilcr5qtp7iolimlrlg7e0ipupj.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], + env: { + main: { + url: 'https://nplks2oknz5lhxn6kpgxbfrxgm0hzicm.lambda-url.ca-central-1.on.aws/', + maxPools: 8, + forceRefresh: false, + gasPrice: BigNumber.from(10), + swapGas: BigNumber.from('1000000'), + }, + canary: { + url: 'https://svlitjilcr5qtp7iolimlrlg7e0ipupj.lambda-url.eu-central-1.on.aws/', + maxPools: 8, + forceRefresh: false, + gasPrice: BigNumber.from(10), + swapGas: BigNumber.from('1000000'), + }, }, }, ybAprConfig: { diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index 759064670..5088611af 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -81,24 +81,6 @@ const polygonNetworkData: NetworkData = { multicall: '0x275617327c958bd06b5d6b871e7f491d76113dd8', multicall3: '0xca11bde05977b3631167028862be2a173976ca11', avgBlockSpeed: 1, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, ybAprConfig: { aave: { v2: { diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 699d0242c..544fb1091 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -86,24 +86,6 @@ const zkevmNetworkData: NetworkData = { excludedFarmIds: [], }, avgBlockSpeed: 1, - sor: { - main: { - url: 'https://uu6cfghhd5lqa7py3nojxkivd40zuugb.lambda-url.ca-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - canary: { - url: 'https://ksa66wlkjbvteijxmflqjehsay0jmekw.lambda-url.eu-central-1.on.aws/', - maxPools: 8, - forceRefresh: false, - gasPrice: BigNumber.from(10), - swapGas: BigNumber.from('1000000'), - poolIdsToExclude: [], - }, - }, ybAprConfig: { ovix: { tokens: { diff --git a/modules/sor/sor.resolvers.ts b/modules/sor/sor.resolvers.ts index b3e18bbe2..7a70fbf8a 100644 --- a/modules/sor/sor.resolvers.ts +++ b/modules/sor/sor.resolvers.ts @@ -1,6 +1,4 @@ import { Resolvers } from '../../schema'; -import { balancerSorService } from './sorV1Beets/balancer-sor.service'; -import { tokenService } from '../token/token.service'; import { sorService } from './sor.service'; import { headerChain } from '../context/header-chain'; diff --git a/modules/sor/sorV1Beets/balancer-sor.service.ts b/modules/sor/sorV1Beets/balancer-sor.service.ts index 5e3e2b1bd..9bd246f62 100644 --- a/modules/sor/sorV1Beets/balancer-sor.service.ts +++ b/modules/sor/sorV1Beets/balancer-sor.service.ts @@ -1,6 +1,6 @@ import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../../schema'; import { formatFixed, parseFixed } from '@ethersproject/bignumber'; -import { PrismaToken } from '@prisma/client'; +import { Chain, PrismaToken } from '@prisma/client'; import { poolService } from '../../pool/pool.service'; import { oldBnum } from '../../big-number/old-big-number'; import axios from 'axios'; @@ -8,7 +8,7 @@ import { SwapInfo, SwapV2 } from '@balancer-labs/sdk'; import { replaceEthWithZeroAddress, replaceZeroAddressWithEth } from '../../web3/addresses'; import { BigNumber } from 'ethers'; import { env } from '../../../app/env'; -import { networkContext } from '../../network/network-context.service'; +import { AllNetworkConfigsKeyedOnChain } from '../../network/network-config'; import { DeploymentEnv } from '../../network/network-config-types'; import _ from 'lodash'; import { SwapInfoRoute } from '@balancer-labs/sor'; @@ -21,6 +21,7 @@ interface GetSwapsInput { swapAmount: string; swapOptions: GqlSorSwapOptionsInput; tokens: PrismaToken[]; + chain: Chain; } export class BalancerSorService { @@ -31,6 +32,7 @@ export class BalancerSorService { swapOptions, swapAmount, tokens, + chain, }: GetSwapsInput): Promise { tokenIn = replaceEthWithZeroAddress(tokenIn); tokenOut = replaceEthWithZeroAddress(tokenOut); @@ -48,7 +50,7 @@ export class BalancerSorService { throw new Error('SOR: invalid swap amount input'); } - let swapInfo = await this.querySor(swapType, tokenIn, tokenOut, swapAmountScaled, swapOptions); + let swapInfo = await this.querySor(swapType, tokenIn, tokenOut, swapAmountScaled, swapOptions, chain); // no swaps found, return 0 if (swapInfo.swaps.length === 0) { return this.zeroResponse(swapType, tokenIn, tokenOut, swapAmount); @@ -201,23 +203,24 @@ export class BalancerSorService { tokenOut: string, swapAmountScaled: BigNumber, swapOptions: GqlSorSwapOptionsInput, + chain: Chain, ) { - const { data } = await axios.post<{ swapInfo: SwapInfo }>( - networkContext.data.sor[env.DEPLOYMENT_ENV as DeploymentEnv].url, - { - swapType, - tokenIn, - tokenOut, - swapAmountScaled, - swapOptions: { - maxPools: - swapOptions.maxPools || networkContext.data.sor[env.DEPLOYMENT_ENV as DeploymentEnv].maxPools, - forceRefresh: - swapOptions.forceRefresh || - networkContext.data.sor[env.DEPLOYMENT_ENV as DeploymentEnv].forceRefresh, - }, + const sorConfig = AllNetworkConfigsKeyedOnChain[chain].data.sor; + const config = sorConfig?.env && sorConfig.env[env.DEPLOYMENT_ENV as DeploymentEnv]; + if (!config) { + throw new Error('SOR: no config'); + } + const url = config.url; + const { data } = await axios.post<{ swapInfo: SwapInfo }>(url, { + swapType, + tokenIn, + tokenOut, + swapAmountScaled, + swapOptions: { + maxPools: swapOptions.maxPools || config.maxPools, + forceRefresh: swapOptions.forceRefresh || config.forceRefresh, }, - ); + }); const swapInfo = data.swapInfo; return swapInfo; } diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index 160414097..3dd9833b5 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -38,8 +38,12 @@ export class SorV1BeetsService implements SwapService { public async getSwapResult(input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }): Promise { try { - const swap = await this.querySorV1(input); - return new SwapResultV1(swap, input.swapType); + if (['OPTIMISM', 'FANTOM'].includes(input.chain)) { + const swap = await this.querySorV1(input); + return new SwapResultV1(swap, input.swapType); + } else { + return new SwapResultV1(null, input.swapType); + } } catch (err) { console.log(`sorV1 Service Error`, err); return new SwapResultV1(null, input.swapType); diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 78fb1452b..425b024b9 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -262,7 +262,7 @@ export class SorV2Service implements SwapService { * @returns */ private async getBasePoolsFromDb(chain: Chain): Promise { - const { poolIdsToExclude } = AllNetworkConfigsKeyedOnChain[chain].data.sor[env.DEPLOYMENT_ENV as DeploymentEnv]; + const poolIdsToExclude = AllNetworkConfigsKeyedOnChain[chain].data.sor?.poolIdsToExclude ?? []; const pools = await prisma.prismaPool.findMany({ where: { chain, From c30fcc90478ee0aef23f69bf34eccc30dc875a04 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 29 Feb 2024 10:59:57 +0100 Subject: [PATCH 13/14] Fix/staking model (#140) * wip * only include staked balance of active/preferred gauge --- modules/pool/lib/pool-gql-loader.service.ts | 109 ++++++++++++++------ 1 file changed, 78 insertions(+), 31 deletions(-) diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 8be100aba..7b44b663e 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -37,7 +37,14 @@ import { import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; import { prisma } from '../../../prisma/prisma-client'; -import { Chain, Prisma, PrismaPoolAprType, PrismaUserStakedBalance, PrismaUserWalletBalance } from '@prisma/client'; +import { + Chain, + Prisma, + PrismaPoolAprType, + PrismaPoolStaking, + 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'; @@ -48,6 +55,7 @@ import { BeethovenChainIds, chainToIdMap } from '../../network/network-config'; import { GithubContentService } from '../../content/github-content.service'; import { SanityContentService } from '../../content/sanity-content.service'; import { ElementData, FxData, GyroData, LinearData, StableData } from '../subgraph-mapper'; +import { PoolUsdDataService } from './pool-usd-data.service'; export class PoolGqlLoaderService { public async getPool(id: string, chain: Chain, userAddress?: string): Promise { @@ -55,8 +63,7 @@ export class PoolGqlLoaderService { pool = await prisma.prismaPool.findUnique({ where: { id_chain: { id, chain: chain } }, include: { - ...prismaPoolWithExpandedNesting.include, - ...this.getUserBalancesInclude(userAddress), + ...this.getPoolInclude(userAddress), }, }); @@ -68,7 +75,11 @@ export class PoolGqlLoaderService { throw new Error('Pool exists, but has an unknown type'); } - return this.mapPoolToGqlPool(pool, pool.userWalletBalances, pool.userStakedBalances); + return this.mapPoolToGqlPool( + pool, + pool.userWalletBalances, + pool.staking.map((staking) => staking.userStakedBalances).flat(), + ); } public async getPools(args: QueryPoolGetPoolsArgs): Promise { @@ -83,16 +94,20 @@ export class PoolGqlLoaderService { args.first = undefined; args.skip = undefined; } + // const includeQuery = args.where.userAddress ? prismaPoolMinimal.include.staking.include. const pools = await prisma.prismaPool.findMany({ ...this.mapQueryArgsToPoolQuery(args), include: { - ...prismaPoolMinimal.include, - ...this.getUserBalancesInclude(args.where.userAddress), + ...this.getPoolInclude(args.where.userAddress), }, }); const gqlPools = pools.map((pool) => - this.mapToMinimalGqlPool(pool, pool.userWalletBalances, pool.userStakedBalances), + this.mapToMinimalGqlPool( + pool, + pool.userWalletBalances, + pool.staking.map((staking) => staking.userStakedBalances).flat(), + ), ); if (args.orderBy === 'userbalanceUsd') { @@ -458,9 +473,11 @@ export class PoolGqlLoaderService { const bpt = pool.tokens.find((token) => token.address === pool.address); + const stakingData = this.getStakingData(pool); + const mappedData = { decimals: 18, - staking: this.getStakingData(pool), + staking: stakingData, dynamicData: this.getPoolDynamicData(pool), investConfig: this.getPoolInvestConfig(pool), withdrawConfig: this.getPoolWithdrawConfig(pool), @@ -647,7 +664,21 @@ export class PoolGqlLoaderService { } } - const sorted = _.sortBy(pool.staking, (staking) => { + const sorted = this.getSortedGauges(pool); + + return { + ...sorted[0], + gauge: { + ...sorted[0].gauge!, + otherGauges: sorted.slice(1).map((item) => item.gauge!), + }, + farm: null, + reliquary: null, + }; + } + + private getSortedGauges(pool: PrismaPoolMinimal) { + return _.sortBy(pool.staking, (staking) => { if (staking.gauge) { switch (staking.gauge.status) { case 'PREFERRED': @@ -661,16 +692,6 @@ export class PoolGqlLoaderService { return 100; }).filter((staking) => staking.gauge); - - return { - ...sorted[0], - gauge: { - ...sorted[0].gauge!, - otherGauges: sorted.slice(1).map((item) => item.gauge!), - }, - farm: null, - reliquary: null, - }; } private getUserBalance( @@ -683,14 +704,24 @@ export class PoolGqlLoaderService { bptPrice = pool.dynamicData.totalLiquidity / parseFloat(pool.dynamicData.totalShares); } + let activeStakingId = userStakedBalances.at(0)?.id; + if (pool.staking.length > 1) { + const sortedGauges = this.getSortedGauges(pool); + activeStakingId = sortedGauges[0].id; + } + + const activeUserStakedBalance = userStakedBalances.find( + (userStakedBalance) => userStakedBalance.id === activeStakingId, + ); + const walletBalance = parseUnits(userWalletBalances.at(0)?.balance || '0', 18); - const stakedBalance = parseUnits(userStakedBalances.at(0)?.balance || '0', 18); + const stakedBalance = parseUnits(activeUserStakedBalance?.balance || '0', 18); const walletBalanceNum = userWalletBalances.at(0)?.balanceNum || 0; - const stakedBalanceNum = userStakedBalances.at(0)?.balanceNum || 0; + const stakedBalanceNum = activeUserStakedBalance?.balanceNum || 0; return { walletBalance: userWalletBalances.at(0)?.balance || '0', - stakedBalance: userStakedBalances.at(0)?.balance || '0', + stakedBalance: activeUserStakedBalance?.balance || '0', totalBalance: formatFixed(stakedBalance.add(walletBalance), 18), walletBalanceUsd: walletBalanceNum * bptPrice, stakedBalanceUsd: stakedBalanceNum * bptPrice, @@ -1244,21 +1275,37 @@ export class PoolGqlLoaderService { }; } - private getUserBalancesInclude(userAddress?: string) { + private getPoolInclude(userAddress?: string) { if (!userAddress) { - return {}; + return { + ...prismaPoolWithExpandedNesting.include, + staking: { + include: { + ...prismaPoolWithExpandedNesting.include.staking.include, + userStakedBalances: false, + }, + }, + userWalletBalances: false, + }; } + return { - userWalletBalances: { - where: { - userAddress: { - equals: userAddress, - mode: 'insensitive' as const, + ...prismaPoolWithExpandedNesting.include, + staking: { + include: { + ...prismaPoolWithExpandedNesting.include.staking.include, + userStakedBalances: { + where: { + userAddress: { + equals: userAddress, + mode: 'insensitive' as const, + }, + balanceNum: { gt: 0 }, + }, }, - balanceNum: { gt: 0 }, }, }, - userStakedBalances: { + userWalletBalances: { where: { userAddress: { equals: userAddress, From 05eba5693a054a4272fa3d0f315d15b8a20d3598 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:42:06 +0100 Subject: [PATCH 14/14] not always override (#179) --- modules/content/github-content.service.ts | 24 +++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/content/github-content.service.ts b/modules/content/github-content.service.ts index 22fc3167c..c39023b10 100644 --- a/modules/content/github-content.service.ts +++ b/modules/content/github-content.service.ts @@ -91,6 +91,23 @@ export class GithubContentService implements ContentService { } } + // only override coingecko ID if it's present + let update = {}; + if (coingeckoTokenId) { + update = { + name: githubToken.name, + symbol: githubToken.symbol, + logoURI: { set: githubToken.logoURI || null }, + coingeckoTokenId: coingeckoTokenId, + }; + } else { + update = { + name: githubToken.name, + symbol: githubToken.symbol, + logoURI: { set: githubToken.logoURI || null }, + }; + } + await prisma.prismaToken.upsert({ where: { address_chain: { address: tokenAddress, chain: networkContext.chain }, @@ -111,12 +128,7 @@ export class GithubContentService implements ContentService { telegramUrl: null, twitterUsername: null, }, - update: { - name: githubToken.name, - symbol: githubToken.symbol, - logoURI: { set: githubToken.logoURI || null }, - coingeckoTokenId: coingeckoTokenId, - }, + update: update, }); }