[
  {
    "path": ".dockerignore",
    "content": "node_modules\ndist\nbuild\n\n**/node_modules\n**/build\n**/dist\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n    extends: [\n        'eslint:recommended',\n        'plugin:markdown/recommended',\n        'plugin:react/recommended',\n        'plugin:react/jsx-runtime',\n        'plugin:react-hooks/recommended',\n        'plugin:jsx-a11y/recommended',\n        'plugin:prettier/recommended'\n    ],\n    settings: {\n        react: {\n            version: 'detect'\n        }\n    },\n    parser: '@typescript-eslint/parser',\n    ignorePatterns: ['**/node_modules', '**/dist', '**/build', '**/package-lock.json'],\n    plugins: ['unused-imports'],\n    rules: {\n        '@typescript-eslint/explicit-module-boundary-types': 'off',\n        'no-unused-vars': 'off',\n        'unused-imports/no-unused-imports': 'warn',\n        'unused-imports/no-unused-vars': ['warn', { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' }],\n        'no-undef': 'off',\n        'no-console': [process.env.CI ? 'error' : 'warn', { allow: ['warn', 'error', 'info'] }],\n        'prettier/prettier': 'error'\n    }\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[BUG]\"\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Setup**\n - OS: [e.g. iOS, Windows, Linux]\n - Browser [e.g. chrome, safari]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[FEATURE]\"\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the feature you'd like**\nA clear and concise description of what you would like Outerbridge to have.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Node CI\n\non:\n    push:\n        branches:\n            - master\n\n    pull_request:\n        branches:\n            - '*'\n\npermissions:\n    contents: read\n\njobs:\n    build:\n        strategy:\n            matrix:\n                platform: [ubuntu-latest]\n                node-version: [14.x, 16.x]\n        runs-on: ${{ matrix.platform }}\n\n        steps:\n            - uses: actions/checkout@v3\n            - name: Use Node.js ${{ matrix.node-version }}\n              uses: actions/setup-node@v3\n              with:\n                  node-version: ${{ matrix.node-version }}\n\n            - run: npm i -g yarn\n\n            - run: yarn install --ignore-engines\n\n            - run: yarn lint\n\n            - run: yarn build\n"
  },
  {
    "path": ".gitignore",
    "content": "# editor\n.idea\n.vscode\n\n# dependencies\n**/node_modules\n**/package-lock.json\n**/yarn.lock\n\n## logs\n**/*.log\n\n## build\n**/dist\n**/build\n\n## temp\n**/tmp\n**/temp\n\n## test\n**/coverage\n\n# misc\n.DS_Store\n\n## env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n**/.env\n\n## turbo\n.turbo\n\n## secrets\n**/*.key\n**/api.json\n\n## compressed\n**/*.tgz"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn quick # prettify\nyarn lint-staged # eslint lint(also include prettify but prettify support more file extensions than eslint, so run prettify first)"
  },
  {
    "path": ".prettierignore",
    "content": "**/node_modules\n**/dist\n**/build"
  },
  {
    "path": ".prettierrc.js",
    "content": "module.exports = {\n    printWidth: 140,\n    singleQuote: true,\n    jsxSingleQuote: true,\n    trailingComma: 'none',\n    tabWidth: 4,\n    semi: false,\n    endOfLine: 'auto'\n}\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at hello@outerbridge.io. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "<!-- markdownlint-disable MD030 -->\n\n# Contributing to Outerbridge\n\nAs the old chinese saying goes: 礼轻情意重 (It's the thought that counts), we appreciate any form of contributions.\n\n## ⭐ Star\n\nStar and share the [Github Repo](https://github.com/Outerbridgeio/Outerbridge).\n\n## 🙋 Q&A\n\nSearch up for any questions in [Q&A section](https://github.com/Outerbridgeio/Outerbridge/discussions/categories/q-a), if you can't find one, don't hesitate to create one. It might helps others that have similar question.\n\n## 🙌 Share Workflow\n\nYes! Sharing how you use Outerbridge is a way of contribution. Export your workflow as JSON, attach a screenshot and share it in [Show and Tell section](https://github.com/Outerbridgeio/Outerbridge/discussions/categories/show-and-tell).\n\n## 💡 Ideas\n\nIdeas are welcome such as new feature, apps integration, and blockchain networks. Submit in [Ideas section](https://github.com/Outerbridgeio/Outerbridge/discussions/categories/ideas).\n\n## 🐞 Report Bugs\n\nFound an issue? [Report it](https://github.com/Outerbridgeio/Outerbridge/issues/new/choose).\n\n## 👨‍💻 Contribute to Code\n\nNot sure what to contribute? Some ideas:\n\n-   Create new node/credential component\n-   Update existing components such as extending functionality, fixing bugs\n-   Add new blockchain network to support\n-   Unit tests and E2E tests\n\n### Developers\n\nOuterbridge has 3 different modules in a single mono repository.\n\n-   `server`: Node backend to serve API logics\n-   `ui`: React frontend\n-   `components`: Nodes and Credentials of applications\n\n#### Prerequisite\n\n-   Install MongoDB [here](https://www.mongodb.com/try/download/community?tck=docs_server)\n-   Install Yarn\n    ```bash\n    npm i -g yarn\n    ```\n\n#### Step by step\n\n1. Fork the official [Outerbridge Github Repository](https://github.com/Outerbridgeio/Outerbridge).\n\n2. Clone your forked repository.\n\n3. Create a new branch, see [guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-and-deleting-branches-within-your-repository). Naming conventions:\n\n    - For feature branch: `feature/<Your New Feature>`\n    - For bug fix branch: `bugfix/<Your New Bugfix>`.\n\n4. Switch to the newly created branch.\n\n5. Go into repository folder\n\n    ```bash\n    cd Outerbridge\n    ```\n\n6. Install all dependencies of all modules:\n\n    ```bash\n    yarn install\n    ```\n\n7. Build all the code:\n\n    ```bash\n    yarn build\n    ```\n\n8. Start the app on [http://localhost:3000](http://localhost:3000)\n\n    ```bash\n    yarn start\n    ```\n\n9. For development, run\n\n    ```bash\n    yarn dev\n    ```\n\n    Any changes made in `packages/ui` or `packages/server` will be reflected on [http://localhost:8080](http://localhost:8080)\n\n    For changes made in `packages/components`, run `yarn build` again to pickup the changes.\n\n10. After making all the changes, run\n\n    ```bash\n    yarn build\n    ```\n\n    and\n\n    ```bash\n    yarn start\n    ```\n\n    to make sure everything works fine in production.\n\n11. Commit code and submit Pull Request from forked branch pointing to [Outerbridge master](https://github.com/Outerbridgeio/Outerbridge/tree/master). Example [PR](https://github.com/Outerbridgeio/Outerbridge/pull/50).\n\n## 📖 Contribute to Docs\n\nContribute to Outerbridge [docs](https://github.com/Outerbridgeio/outerbridge-docs) if there is any mistake, confusion or grap.\n\n## 🏷️ Pull Request process\n\nA member of the Outerbridge team will automatically be notified/assigned when you open a pull request. You can also reach out to us on [Discord](https://discord.gg/Y9VE4ykPDJ).\n\n## 📃 Contributor License Agreement\n\nBefore we can merge your contribution you have to sign our [Contributor License Agreement (CLA)](https://cla-assistant.io/OuterbridgeIO/Outerbridge). The CLA contains the terms and conditions under which the contribution is submitted. You need to do this only once for your first pull request. Keep in mind that without a signed CLA we cannot merge your contribution.\n\n## 📜 Code of Conduct\n\nThis project and everyone participating in it are governed by the Code of Conduct which can be found in the [file](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to henryheng@outerbridge.io or hello@outerbridge.io.\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Build local monorepo image\n# docker build --no-cache -t  outerbridge .\n# Run image\n# docker run -d -p 3000:3000 outerbridge\nFROM node:16\n\nWORKDIR /usr/src/packages\n\n# Copy root package.json and lockfile\nCOPY package.json ./\nCOPY yarn.lock ./\n\n# Copy components package.json\nCOPY packages/components/package.json ./packages/components/package.json\n\n# Copy ui package.json\nCOPY packages/ui/package.json ./packages/ui/package.json\n\n# Copy server package.json\nCOPY packages/server/package.json ./packages/server/package.json\n\nRUN yarn install\n\n# Copy app source\nCOPY . .\n\nRUN yarn build\n\nEXPOSE 3000\n\nCMD [ \"yarn\", \"start\" ]\n"
  },
  {
    "path": "LICENSE.md",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS"
  },
  {
    "path": "README.md",
    "content": "<!-- markdownlint-disable MD030 -->\n\n# Outerbridge - Automate Web3 and Web2 applications\n\nOuterbridge is a low code/no code workflow automation application, focusing on integrating both on-chain and off-chain applications. The project is licensed under [Apache License Version 2.0](LICENSE.md), source available and free to self-host.\n\n![Outerbridge](./assets/outerbridge_brand.png)\n\n![Outerbridge Screenshot](./assets/screenshot_outerbridge.jpg)\n\n## 💡Why another workflow automation tool?\n\nThere are many awesome automation tools out there, however there isn't one that has the built-in logic of interacting/consuming information from blockchains. Hence, Outerbridge is created to allow people building workflows involving on-chain and off-chain applications, with simple drag and drop interface.\n\n## ⚡Quick Start\n\nWatch [Outerbridge Quickstart Demo](https://www.youtube.com/watch?v=x-AfrkKvZ4M) on Youtube (4mins)\n\n1. Install MongoDB [locally](https://www.mongodb.com/docs/manual/administration/install-community/) OR follow the guide of using MongoDB Atlas [here](https://docs.outerbridge.io/get-started#mongodb-atlas)\n2. Install Outerbridge\n    ```bash\n    npm install -g outerbridge\n    ```\n3. Start Outerbridge\n\n    ```bash\n    npx outerbridge start\n    ```\n\n    If using MongoDB Atlas\n\n    ```bash\n    npx outerbridge start --mongourl=mongodb+srv://<user>:<password>@<your-cluster>.mongodb.net/outerbridge?retryWrites=true&w=majority\n    ```\n\n4. Open [http://localhost:3000](http://localhost:3000)\n\n## 🐳 Docker\n\n1. Go to `docker` folder at the root of the project\n2. `docker-compose up -d`\n3. This will automatically spins up mongodb and outerbridge containers\n4. Open [http://localhost:3000](http://localhost:3000)\n5. You can bring the containers down by `docker-compose stop`\n6. If using MongoDB Atlas, follow the guide [here](https://docs.outerbridge.io/get-started#-docker)\n\n## 👨‍💻 Developers\n\nOuterbridge has 3 different modules in a single mono repository.\n\n-   `server`: Node backend to serve API logics\n-   `ui`: React frontend\n-   `components`: Nodes and Credentials of applications\n\n### Prerequisite\n\n-   Install MongoDB [locally](https://www.mongodb.com/docs/manual/administration/install-community/) OR register a MongoDB Atlas [here](https://www.mongodb.com/atlas/database)\n-   Install Yarn\n    ```bash\n    npm i -g yarn\n    ```\n\n### Setup\n\n1. Clone the repository\n\n    ```bash\n    git clone https://github.com/Outerbridgeio/Outerbridge.git\n    ```\n\n2. Go into repository folder\n\n    ```bash\n    cd Outerbridge\n    ```\n\n3. Install all dependencies of all modules:\n\n    ```bash\n    yarn install\n    ```\n\n4. Build all the code:\n\n    ```bash\n    yarn build\n    ```\n\n5. Start the app:\n\n    ```bash\n    yarn start\n    ```\n\n    You can now access the app on [http://localhost:3000](http://localhost:3000)\n\n6. For development build:\n\n    ```bash\n    yarn dev\n    ```\n\n    Any code changes will reload the app automatically on [http://localhost:8080](http://localhost:8080)\n\n## 📖 Documentation\n\nOfficial Outerbridge docs can be found under: [https://docs.outerbridge.io](https://docs.outerbridge.io)\n\n## 💻 Cloud Hosted\n\n-   [Cloud Hosted](https://app.outerbridge.io) version of Outerbridge.\n\n## 🌐 Self Host\n\n-   Digital Ocean Droplet: [Setup guide](https://gist.github.com/HenryHengZJ/93210d43d655b4172ee50794ce473b62)\n-   AWS EC2: [Setup guide](https://gist.github.com/HenryHengZJ/627cec19671664a88754c7e383232dc8)\n\n## 🙋 Support\n\nFeel free to ask any questions, raise problems, and request new features in [discussion](https://github.com/Outerbridgeio/Outerbridge/discussions)\n\n## 🙌 Contributing\n\nSee [contributing guide](CONTRIBUTING.md). Reach out to us at [Discord](https://discord.gg/Y9VE4ykPDJ) if you have any questions or issues.\n\n## 📄 License\n\nSource code in this repository is made available under the [Apache License Version 2.0](LICENSE.md).\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n    presets: [\n        '@babel/preset-typescript',\n        [\n            '@babel/preset-env',\n            {\n                targets: {\n                    node: 'current'\n                }\n            }\n        ]\n    ]\n}\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM node:14.20.0-alpine\n\nUSER root\n\nRUN apk add --no-cache git\n\n# You can install a specific version like: outerbridge@1.0.5\nRUN npm install -g outerbridge\n\nWORKDIR /data\n\nCMD \"outerbridge\""
  },
  {
    "path": "docker/docker-compose.yml",
    "content": "version: '3.1'\n\nservices:\n\n  mongo:\n    image: mongo\n    ports:\n      - \"27017:27017\"\n    restart: always\n    environment:\n      - MONGO_INITDB_DATABASE=outerbridge\n  \n  outerbridge:\n    image: outerbridgeio/outerbridge\n    restart: always\n    environment:\n      - MONGO_HOST=${MONGO_HOST}\n      - PASSPHRASE=${PASSPHRASE}\n      - ENABLE_TUNNEL=${ENABLE_TUNNEL}\n      - PORT=${PORT}\n    ports:\n      - \"${PORT}:${PORT}\"\n    links:\n      - mongo\n    volumes:\n      - ~/.outerbridge:/root/.outerbridge\n    command: /bin/sh -c \"sleep 3; outerbridge start\""
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"outerbridge\",\n    \"version\": \"1.0.20\",\n    \"private\": true,\n    \"homepage\": \"https://outerbridge.io\",\n    \"workspaces\": [\n        \"packages/*\",\n        \"outerbridge\",\n        \"ui\",\n        \"components\"\n    ],\n    \"scripts\": {\n        \"build\": \"turbo run build\",\n        \"dev\": \"turbo run dev --parallel\",\n        \"start\": \"run-script-os\",\n        \"start:windows\": \"cd packages/server/bin && run start\",\n        \"start:default\": \"cd packages/server/bin && ./run start\",\n        \"clean\": \"npm exec -ws -- rimraf dist build\",\n        \"format\": \"prettier --write \\\"**/*.{ts,tsx,md}\\\"\",\n        \"test\": \"turbo run test\",\n        \"lint\": \"eslint \\\"**/*.{js,jsx,ts,tsx,json,md}\\\"\",\n        \"lint-fix\": \"yarn lint --fix\",\n        \"quick\": \"pretty-quick --staged\",\n        \"postinstall\": \"husky install\"\n    },\n    \"lint-staged\": {\n        \"*.{js,jsx,ts,tsx,json,md}\": \"eslint --fix\"\n    },\n    \"devDependencies\": {\n        \"turbo\": \"1.7.4\",\n        \"@babel/preset-env\": \"^7.19.4\",\n        \"@babel/preset-typescript\": \"7.18.6\",\n        \"@types/express\": \"^4.17.13\",\n        \"@typescript-eslint/typescript-estree\": \"^5.39.0\",\n        \"eslint\": \"^8.24.0\",\n        \"eslint-config-prettier\": \"^8.3.0\",\n        \"eslint-config-react-app\": \"^7.0.1\",\n        \"eslint-plugin-jsx-a11y\": \"^6.6.1\",\n        \"eslint-plugin-markdown\": \"^3.0.0\",\n        \"eslint-plugin-prettier\": \"^3.4.0\",\n        \"eslint-plugin-react\": \"^7.26.1\",\n        \"eslint-plugin-react-hooks\": \"^4.6.0\",\n        \"eslint-plugin-unused-imports\": \"^2.0.0\",\n        \"husky\": \"^8.0.1\",\n        \"lint-staged\": \"^13.0.3\",\n        \"prettier\": \"^2.7.1\",\n        \"pretty-quick\": \"^3.1.3\",\n        \"rimraf\": \"^3.0.2\",\n        \"run-script-os\": \"^1.1.6\",\n        \"typescript\": \"^4.8.4\"\n    },\n    \"engines\": {\n        \"node\": \">=14.7.0\"\n    }\n}\n"
  },
  {
    "path": "packages/components/README.md",
    "content": "<!-- markdownlint-disable MD030 -->\n\n# Outerbridge Components\n\nApps integration for Outerbridge. Contain Nodes and Credentials.\n\n![Outerbridge](https://raw.githubusercontent.com/Outerbridgeio/Outerbridge/master/assets/outerbridge_brand.png)\n\nInstall:\n\n```bash\nnpm i outerbridge-components\n```\n\n## License\n\nSource code in this repository is made available under the [Apache License Version 2.0](https://github.com/Outerbridgeio/Outerbridge/blob/master/LICENSE.md).\n"
  },
  {
    "path": "packages/components/credentials/Alchemy/AlchemyApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass AlchemyApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'alchemyApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: '',\n                description:\n                    'Navigate to the <a target=\"_blank\" href=\"https://dashboard.alchemyapi.io\">Dashboard page</a> to copy your \"API Key\".'\n            },\n            {\n                label: 'Webhook Auth Token',\n                name: 'authToken',\n                type: 'string',\n                default: '',\n                optional: true,\n                description:\n                    'Navigate to the top right corner of <a target=\"_blank\" href=\"https://dashboard.alchemyapi.io/notify\">Notify page</a> to copy your \"Auth Token\".'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: AlchemyApi }\n"
  },
  {
    "path": "packages/components/credentials/Arbiscan/ArbiscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass ArbiscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'arbiscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: ArbiscanApi }\n"
  },
  {
    "path": "packages/components/credentials/Binance/BinanceApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass BinanceApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'binanceApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Secret Key',\n                name: 'secretKey',\n                type: 'password',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: BinanceApi }\n"
  },
  {
    "path": "packages/components/credentials/Bscscan/BscscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass BscscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'bscscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: BscscanApi }\n"
  },
  {
    "path": "packages/components/credentials/Celoscan/CeloscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass CeloscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'celoscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: CeloscanApi }\n"
  },
  {
    "path": "packages/components/credentials/Cronosscan/CronosscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass CronosscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'cronosscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: CronosscanApi }\n"
  },
  {
    "path": "packages/components/credentials/EmailSend/EmailSendSmtp.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass EmailSendSmtp implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'emailSendSmtp'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'User',\n                name: 'user',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Password',\n                name: 'password',\n                type: 'password',\n                default: ''\n            },\n            {\n                label: 'Host',\n                name: 'host',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Port',\n                name: 'port',\n                type: 'number',\n                default: 465\n            },\n            {\n                label: 'SSL/TLS',\n                name: 'secure',\n                type: 'boolean',\n                default: true\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: EmailSendSmtp }\n"
  },
  {
    "path": "packages/components/credentials/Etherscan/EtherscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass EtherscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'etherscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: EtherscanApi }\n"
  },
  {
    "path": "packages/components/credentials/Fantomscan/FantomscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass FantomscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'fantomscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: FantomscanApi }\n"
  },
  {
    "path": "packages/components/credentials/FootprintAnalytics/FootprintAnalyticsApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass FootprintAnalyticsApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'footprintAnalyticsApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: FootprintAnalyticsApi }\n"
  },
  {
    "path": "packages/components/credentials/GitHub/GitHubApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass GitHubApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'gitHubApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Access Token',\n                name: 'accessToken',\n                type: 'string',\n                default: '',\n                description: '<a target=\"_blank\" href=\"https://github.com/settings/tokens\">Register GitHub and get your access token.</a>\"'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: GitHubApi }\n"
  },
  {
    "path": "packages/components/credentials/Gnosisscan/GnosisscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass GnosisscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'gnosisscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: GnosisscanApi }\n"
  },
  {
    "path": "packages/components/credentials/GoogleDocs/GoogleDocsOAuth2.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass GoogleDocsOAuth2 implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'googleDocsOAuth2Api'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Client ID',\n                name: 'clientID',\n                type: 'string',\n                default: '',\n                description: 'How to get Client ID & Secret: https://www.youtube.com/watch?v=VBwDwHbPaYQ'\n            },\n            {\n                label: 'Client Secret',\n                name: 'clientSecret',\n                type: 'password',\n                default: '',\n                description: 'How to get Client ID & Secret: https://www.youtube.com/watch?v=VBwDwHbPaYQ'\n            },\n            {\n                label: 'Authorization URL',\n                name: 'authUrl',\n                type: 'string',\n                default: 'https://accounts.google.com/o/oauth2/v2/auth'\n            },\n            {\n                label: 'Access Token URL',\n                name: 'accessTokenUrl',\n                type: 'string',\n                default: 'https://oauth2.googleapis.com/token'\n            },\n            {\n                label: 'Authorization URL Parameters',\n                name: 'authorizationURLParameters',\n                type: 'string',\n                default: 'access_type=offline&prompt=consent&response_type=code'\n            },\n            {\n                label: 'Scope',\n                name: 'scope',\n                type: 'json',\n                default: `[\"https://www.googleapis.com/auth/drive\", \"https://www.googleapis.com/auth/documents\"]`\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: GoogleDocsOAuth2 }\n"
  },
  {
    "path": "packages/components/credentials/GoogleSheet/GoogleSheetOAuth2.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass GoogleSheetOAuth2 implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'googleSheetsOAuth2Api'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Client ID',\n                name: 'clientID',\n                type: 'string',\n                default: '',\n                description: 'How to get Client ID & Secret: https://www.youtube.com/watch?v=VBwDwHbPaYQ'\n            },\n            {\n                label: 'Client Secret',\n                name: 'clientSecret',\n                type: 'password',\n                default: '',\n                description: 'How to get Client ID & Secret: https://www.youtube.com/watch?v=VBwDwHbPaYQ'\n            },\n            {\n                label: 'Authorization URL',\n                name: 'authUrl',\n                type: 'string',\n                default: 'https://accounts.google.com/o/oauth2/v2/auth'\n            },\n            {\n                label: 'Access Token URL',\n                name: 'accessTokenUrl',\n                type: 'string',\n                default: 'https://oauth2.googleapis.com/token'\n            },\n            {\n                label: 'Authorization URL Parameters',\n                name: 'authorizationURLParameters',\n                type: 'string',\n                default: 'access_type=offline&prompt=consent&response_type=code'\n            },\n            {\n                label: 'Scope',\n                name: 'scope',\n                type: 'json',\n                default: `[\"https://www.googleapis.com/auth/drive\", \"https://www.googleapis.com/auth/spreadsheets\"]`\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: GoogleSheetOAuth2 }\n"
  },
  {
    "path": "packages/components/credentials/HTTP/HTTPBasicAuth.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass HTTPBasicAuth implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'httpBasicAuth'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Username',\n                name: 'userName',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Password',\n                name: 'password',\n                type: 'password',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: HTTPBasicAuth }\n"
  },
  {
    "path": "packages/components/credentials/HTTP/HTTPBearerTokenAuth.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass HTTPBearerTokenAuth implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'httpBearerTokenAuth'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Token',\n                name: 'token',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: HTTPBearerTokenAuth }\n"
  },
  {
    "path": "packages/components/credentials/Helio/HelioApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass HelioApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'helioApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Secret Key',\n                name: 'secretKey',\n                type: 'password',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: HelioApi }\n"
  },
  {
    "path": "packages/components/credentials/Helius/HeliusApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass HeliusApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n    constructor() {\n        this.name = 'heliusApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\nmodule.exports = { credClass: HeliusApi }\n"
  },
  {
    "path": "packages/components/credentials/Hubspot/HubspotApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass HubspotApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'hubspotCredential'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Private App Access Token',\n                name: 'accessToken',\n                type: 'string',\n                description: `Private apps allow you to use HubSpot's APIs to access specific data from your HubSpot account. Learn how to create one <a target=\"_blank\" href=\"https://developers.hubspot.com/docs/api/private-apps\">here</a>`,\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: HubspotApi }\n"
  },
  {
    "path": "packages/components/credentials/HuggingFace/HuggingFaceAccessToken.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass HuggingFaceAccessToken implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'huggingFaceAccessToken'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Access Token',\n                name: 'accessToken',\n                type: 'string',\n                default: '',\n                description:\n                    'Navigate to the <a target=\"_blank\" href=\"https://huggingface.co/settings/tokens\">Access Token page</a> and copy your token'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: HuggingFaceAccessToken }\n"
  },
  {
    "path": "packages/components/credentials/Imap/Imap.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass Imap implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'imap'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'User Email',\n                name: 'userEmail',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Password',\n                name: 'password',\n                type: 'password',\n                default: ''\n            },\n            {\n                label: 'Host',\n                name: 'host',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Port',\n                name: 'port',\n                type: 'number',\n                default: 993\n            },\n            {\n                label: 'Enable TLS',\n                name: 'tls',\n                type: 'boolean',\n                default: true\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: Imap }\n"
  },
  {
    "path": "packages/components/credentials/Infura/InfuraApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass InfuraApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'infuraApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Secret Key',\n                name: 'secretKey',\n                type: 'password',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: InfuraApi }\n"
  },
  {
    "path": "packages/components/credentials/Mailchimp/MailchimpApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\nclass MailchimpApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n    constructor() {\n        this.name = 'mailChimpCredential'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Mailchimp API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\nmodule.exports = { credClass: MailchimpApi }\n"
  },
  {
    "path": "packages/components/credentials/MoonBeamScan/MoonBeamScanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass MoonBeamScanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'moonBeamScanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: MoonBeamScanApi }\n"
  },
  {
    "path": "packages/components/credentials/MoonRiverScan/MoonRiverScanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass MoonRiverScanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'moonRiverScanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: MoonRiverScanApi }\n"
  },
  {
    "path": "packages/components/credentials/Moralis/MoralisApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass MoralisApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'moralisApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: '',\n                description: 'How to get API key: https://docs.moralis.io/reference/getting-the-api-key'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: MoralisApi }\n"
  },
  {
    "path": "packages/components/credentials/Notion/NotionApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass NotionApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'notionApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Internal Integration Token',\n                name: 'integrationToken',\n                type: 'string',\n                default: '',\n                description:\n                    'Get your <a target=\"_blank\" href=\"https://www.notion.so/my-integrations\">Internal Integration Token</a> for your workspace'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: NotionApi }\n"
  },
  {
    "path": "packages/components/credentials/OpenAI/OpenAIApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass OpenAIApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'openAIApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: '',\n                description:\n                    'Navigate to the <a target=\"_blank\" href=\"https://platform.openai.com/account/api-keys\">API page</a> to copy your \"API Key\".'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: OpenAIApi }\n"
  },
  {
    "path": "packages/components/credentials/Opensea/OpenseaApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass OpenseaApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'openSeaApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: OpenseaApi }\n"
  },
  {
    "path": "packages/components/credentials/OptimisticEtherscan/OptimisticEtherscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass OptimisticEtherscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'optimisticEtherscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: OptimisticEtherscanApi }\n"
  },
  {
    "path": "packages/components/credentials/Pinata/PinataApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass PinataApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'pinataApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Secret Key',\n                name: 'secretKey',\n                type: 'password',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: PinataApi }\n"
  },
  {
    "path": "packages/components/credentials/Polygonscan/PolygonscanApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass PolygonscanApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'polygonscanApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: PolygonscanApi }\n"
  },
  {
    "path": "packages/components/credentials/QuickNode/QuickNodeEndpoints.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass QuickNodeEndpoints implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'quickNodeEndpoints'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'HTTP Provider',\n                name: 'httpProvider',\n                type: 'string',\n                default: '',\n                placeholder: 'https://sample-endpoint-name.network.discover.quiknode.pro/token-goes-here/'\n            },\n            {\n                label: 'WSS Provider',\n                name: 'wssProvider',\n                type: 'string',\n                default: '',\n                placeholder: 'wss://sample-endpoint-name.network.discover.quiknode.pro/token-goes-here/',\n                optional: true\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: QuickNodeEndpoints }\n"
  },
  {
    "path": "packages/components/credentials/RequestFinanceApi/RequestFinanceApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass RequestFinanceApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'requestFinanceApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: RequestFinanceApi }\n"
  },
  {
    "path": "packages/components/credentials/SnowTrace/SnowTraceApi.ts",
    "content": "import { INodeCredential, INodeParams } from '../../src/Interface'\n\nclass SnowTraceApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n    constructor() {\n        this.name = 'snowtraceApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\nmodule.exports = { credClass: SnowTraceApi }\n"
  },
  {
    "path": "packages/components/credentials/Telegram/TelegramApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass TelegramApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'telegramApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Bot Token',\n                name: 'botToken',\n                type: 'string',\n                placeholder: 'eg: 1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI',\n                default: '',\n                description:\n                    'Telegram bot token. <a target=\"_blank\" href=\"https://www.youtube.com/watch?v=MZixi8oIdaA\">Learn how to get it</a>'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: TelegramApi }\n"
  },
  {
    "path": "packages/components/credentials/Twitter/TwitterApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass TwitterApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'twitterApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Bearer Token',\n                name: 'bearerToken',\n                type: 'string',\n                default: '',\n                description:\n                    '<a target=\"_blank\" href=\"https://developer.twitter.com/en/docs/twitter-api/getting-started/getting-access-to-the-twitter-api\">Register Twitter Dev account and get your token.</a>\"'\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: TwitterApi }\n"
  },
  {
    "path": "packages/components/credentials/Typeform/TypeformApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\nclass TypeformApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n    constructor() {\n        this.name = 'typeformApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'API Key',\n                name: 'apiKey',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n}\nmodule.exports = { credClass: TypeformApi }\n"
  },
  {
    "path": "packages/components/credentials/TypeformWebhook/TypeformApi.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\nclass TypeformApi implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n    constructor() {\n        this.name = 'typeformApi'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Typeform Access Token',\n                name: 'accessToken',\n                type: 'string',\n                default: '',\n                description: 'Get your access token from typeform account section'\n            }\n        ]\n    }\n}\nmodule.exports = { credClass: TypeformApi }\n"
  },
  {
    "path": "packages/components/credentials/Xero/XeroOAuth2.ts",
    "content": "import { INodeParams, INodeCredential } from '../../src/Interface'\n\nclass XeroOAuth2 implements INodeCredential {\n    name: string\n    version: number\n    credentials: INodeParams[]\n\n    constructor() {\n        this.name = 'xeroOAuth2Api'\n        this.version = 1.0\n        this.credentials = [\n            {\n                label: 'Client ID',\n                name: 'clientID',\n                type: 'string',\n                default: '',\n                description: 'How to get Client ID & Secret: https://developer.xero.com/documentation/guides/oauth2/client-credentials/'\n            },\n            {\n                label: 'Client Secret',\n                name: 'clientSecret',\n                type: 'password',\n                default: '',\n                description: 'How to get Client ID & Secret: https://developer.xero.com/documentation/guides/oauth2/client-credentials/'\n            },\n            {\n                label: 'Authorization URL',\n                name: 'authUrl',\n                type: 'string',\n                default: 'https://login.xero.com/identity/connect/authorize'\n            },\n            {\n                label: 'Access Token Url',\n                name: 'accessTokenUrl',\n                type: 'string',\n                default: 'https://identity.xero.com/connect/token'\n            },\n            {\n                label: 'Authorization URL Parameters',\n                name: 'authorizationURLParameters',\n                type: 'string',\n                default: 'response_type=code'\n            },\n            {\n                label: 'Scope',\n                name: 'scope',\n                type: 'json',\n                default: `[\n                    \"openid\", \n                    \"profile\", \n                    \"email\", \n                    \"accounting.settings\", \n                    \"accounting.reports.read\", \n                    \"accounting.journals.read\", \n                    \"accounting.contacts\", \n                    \"accounting.attachments\", \n                    \"accounting.transactions\", \n                    \"offline_access\"\n                ]`\n            }\n        ]\n    }\n}\n\nmodule.exports = { credClass: XeroOAuth2 }\n"
  },
  {
    "path": "packages/components/gulpfile.ts",
    "content": "import gulp from 'gulp'\n\nconst { src, dest } = gulp\n\nfunction copyIcons() {\n    return src(['nodes/**/*.{jpg,png,svg}']).pipe(dest('dist/nodes'))\n}\n\nexports.default = copyIcons\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/Alchemy.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport { alchemyHTTPAPIs, NETWORK } from '../../src/ChainNetwork'\nimport {\n    alchemySupportedNetworks,\n    ethOperations,\n    IETHOperation,\n    operationCategoryMapping,\n    polygonOperations\n} from '../../src/ETHOperations'\nimport {\n    getContractsForOwnerProperties,\n    getNFTMetadataProperties,\n    getNFTsForCollectionProperties,\n    getNFTsProperties,\n    getOwnersForCollectionProperties,\n    getOwnersForTokenProperties,\n    isHolderOfCollectionProperties,\n    NFTOperationsOptions,\n    searchContractMetadataProperties,\n    tokenAPIOperations,\n    transactionReceiptsOperations\n} from './extendedOperation'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { solanaAPIOperations, solanaOperationsNetworks } from './solanaOperation'\nimport { AlchemySupportedNetworks } from './supportedNetwork'\n\nclass Alchemy implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Alchemy'\n        this.name = 'alchemy'\n        this.icon = 'alchemy.svg'\n        this.type = 'action'\n        this.category = 'Network Provider'\n        this.version = 1.1\n        this.description = 'Perform Alchemy on-chain operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...AlchemySupportedNetworks]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Alchemy API Key',\n                        name: 'alchemyApi'\n                    }\n                ],\n                default: 'alchemyApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'EVM Chain API',\n                        name: 'chainAPI',\n                        description: 'API for fetching standard EVM onchain data using Alchemy supported calls',\n                        show: {\n                            'networks.network': alchemySupportedNetworks\n                        }\n                    },\n                    {\n                        label: 'NFT API',\n                        name: 'nftAPI',\n                        description: 'API for fetching NFT data, including ownership, metadata attributes, and more.',\n                        show: {\n                            'networks.network': [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n                        }\n                    },\n                    {\n                        label: 'Transaction Receipts API',\n                        name: 'txReceiptsAPI',\n                        description: 'API that gets all transaction receipts for a given block by number or block hash.',\n                        show: {\n                            'networks.network': [\n                                NETWORK.MAINNET,\n                                NETWORK.GÖRLI,\n                                NETWORK.MATIC,\n                                NETWORK.MATIC_MUMBAI,\n                                NETWORK.ARBITRUM,\n                                NETWORK.ARBITRUM_GOERLI\n                            ]\n                        }\n                    },\n                    {\n                        label: 'Token API',\n                        name: 'tokenAPI',\n                        description:\n                            'The Token API allows you to easily get token information, minimizing the number of necessary requests.',\n                        show: {\n                            'networks.network': alchemySupportedNetworks\n                        }\n                    },\n                    {\n                        label: 'Solana API',\n                        name: 'solanaAPI',\n                        description: 'API for fetching Solana on-chain data using Alchemy supported calls',\n                        show: {\n                            'networks.network': solanaOperationsNetworks\n                        }\n                    }\n                ],\n                default: 'chainAPI'\n            },\n            {\n                label: 'Chain Category',\n                name: 'chainCategory',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Retrieving Blocks',\n                        name: 'retrievingBlocks',\n                        description: 'Retrieve onchain blocks data'\n                    },\n                    {\n                        label: 'EVM/Smart Contract Execution',\n                        name: 'evmExecution',\n                        description: 'Execute or submit transaction onto blockchain'\n                    },\n                    {\n                        label: 'Reading Transactions',\n                        name: 'readingTransactions',\n                        description: 'Read onchain transactions data'\n                    },\n                    {\n                        label: 'Account Information',\n                        name: 'accountInformation',\n                        description: 'Retrieve onchain account information'\n                    },\n                    {\n                        label: 'Event Logs',\n                        name: 'eventLogs',\n                        description: 'Fetch onchain logs'\n                    },\n                    {\n                        label: 'Chain Information',\n                        name: 'chainInformation',\n                        description: 'Get general selected blockchain information'\n                    },\n                    {\n                        label: 'Retrieving Uncles',\n                        name: 'retrievingUncles',\n                        description: 'Retrieve onchain uncles blocks data'\n                    },\n                    {\n                        label: 'Filters',\n                        name: 'filters',\n                        description: 'Get block filters and logs, or create new filter'\n                    }\n                ],\n                show: {\n                    'inputParameters.api': ['chainAPI']\n                }\n            },\n            {\n                label: 'Chain Category',\n                name: 'chainCategory',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Reading & Writing Transactions',\n                        name: 'readWriteTransactions',\n                        description: 'Read and Write transactins onto Solana chain'\n                    },\n                    {\n                        label: 'Getting Blocks',\n                        name: 'gettingBlocks',\n                        description: 'Get Solana blocks data'\n                    },\n                    {\n                        label: 'Account Information',\n                        name: 'accountInformation',\n                        description: 'Retrieve Solana onchain account information'\n                    },\n                    {\n                        label: 'Network Information',\n                        name: 'networkInformation',\n                        description: 'Get Solana network onchain information'\n                    },\n                    {\n                        label: 'Slot Information',\n                        name: 'slotInformation',\n                        description: 'Fetch Solana slot information'\n                    },\n                    {\n                        label: 'Node Information',\n                        name: 'nodeInformation',\n                        description: 'Retrieve Solana node onchain information'\n                    },\n                    {\n                        label: 'Token Information',\n                        name: 'tokenInformation',\n                        description: 'Fetch Solana onchain token information'\n                    },\n                    {\n                        label: 'Network Inflation',\n                        name: 'networkInflation',\n                        description: 'Retrieve Solana network inflation onchain data'\n                    }\n                ],\n                show: {\n                    'inputParameters.api': ['solanaAPI']\n                }\n            },\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'asyncOptions',\n                loadMethod: 'getOperations'\n            },\n            ...getNFTsProperties,\n            ...getNFTMetadataProperties,\n            ...getNFTsForCollectionProperties,\n            ...getOwnersForCollectionProperties,\n            ...getOwnersForTokenProperties,\n            ...searchContractMetadataProperties,\n            ...isHolderOfCollectionProperties,\n            ...getContractsForOwnerProperties,\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: '[\"param1\", \"param2\"]',\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]',\n                show: {\n                    'inputParameters.api': ['chainAPI', 'txReceiptsAPI', 'tokenAPI', 'solanaAPI']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const inputParametersData = nodeData.inputParameters\n            const networksData = nodeData.networks\n\n            if (inputParametersData === undefined || networksData === undefined) {\n                return returnData\n            }\n\n            const api = inputParametersData.api as string\n            const chainCategory = inputParametersData.chainCategory as string\n            const network = networksData.network as NETWORK\n\n            if (api === 'chainAPI' || api === 'txReceiptsAPI' || api === 'tokenAPI' || api === 'solanaAPI') {\n                const operations = getSelectedOperations(api, network).filter(\n                    (op: IETHOperation) =>\n                        Object.prototype.hasOwnProperty.call(op.providerNetworks, 'alchemy') &&\n                        op.providerNetworks['alchemy'].includes(network)\n                )\n\n                if (api === 'chainAPI' && !chainCategory) return returnData\n                if (api === 'solanaAPI' && !chainCategory) return returnData\n\n                let filteredOperations: IETHOperation[] = operations\n                if (api === 'chainAPI' || api === 'solanaAPI')\n                    filteredOperations = operations.filter(\n                        (op: IETHOperation) => op.parentGroup === operationCategoryMapping[chainCategory]\n                    )\n\n                for (const op of filteredOperations) {\n                    returnData.push({\n                        label: op.name,\n                        name: op.value,\n                        parentGroup: op.parentGroup,\n                        description: op.description,\n                        inputParameters: op.inputParameters,\n                        exampleParameters: op.exampleParameters,\n                        exampleResponse: op.exampleResponse\n                    })\n                }\n                return returnData\n            } else if (api === 'nftAPI') {\n                return NFTOperationsOptions\n            } else {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (inputParametersData === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        // GET api\n        const api = inputParametersData.api as string\n\n        // GET network\n        const network = networksData.network as NETWORK\n\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n\n        // GET operation\n        const operation = inputParametersData.operation as string\n\n        if (api === 'chainAPI' || api === 'txReceiptsAPI' || api === 'tokenAPI' || api === 'solanaAPI') {\n            const uri = `${alchemyHTTPAPIs[network]}${apiKey}`\n\n            let responseData: any // tslint:disable-line: no-any\n            let bodyParameters: any[] = [] // tslint:disable-line: no-any\n            const returnData: ICommonObject[] = []\n\n            const parameters = inputParametersData.parameters as string\n            if (parameters) {\n                try {\n                    bodyParameters = JSON.parse(parameters.replace(/\\s/g, ''))\n                } catch (error) {\n                    throw handleErrorMessage(error)\n                }\n            }\n\n            try {\n                let totalOperations: IETHOperation[] = []\n                totalOperations = getSelectedOperations(api, network)\n\n                const result = totalOperations.find((obj) => {\n                    return obj.value === operation\n                })\n\n                if (result === undefined) throw new Error('Invalid Operation')\n\n                const requestBody = JSON.parse(JSON.stringify(result.body))\n                const bodyParams = requestBody.params\n                requestBody.params = Array.isArray(bodyParameters) ? bodyParameters.concat(bodyParams) : bodyParameters\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: result.method as Method,\n                    url: uri,\n                    data: requestBody,\n                    headers: {\n                        'Content-Type': 'application/json'\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n\n            return returnNodeExecutionData(returnData)\n        }\n\n        //NFT API\n        else if (api === 'nftAPI') {\n            const uri = `${alchemyHTTPAPIs[network]}${apiKey}/${operation}/`\n\n            let responseData: any // tslint:disable-line: no-any\n            const queryParameters: ICommonObject = {}\n            const returnData: ICommonObject[] = []\n\n            if (operation === 'getNFTs') {\n                const owner = inputParametersData.owner as string\n                const pageKey = inputParametersData.pageKey as string\n                const withMetadata = inputParametersData.withMetadata as boolean\n\n                queryParameters['owner'] = owner\n                queryParameters['withMetadata'] = withMetadata\n                if (pageKey) queryParameters['pageKey'] = pageKey\n            } else if (operation === 'getNFTMetadata') {\n                const contractAddress = inputParametersData.contractAddress as string\n                const tokenId = inputParametersData.tokenId as string\n                const tokenType = inputParametersData.tokenType as string\n\n                queryParameters['contractAddress'] = contractAddress\n                queryParameters['tokenId'] = tokenId\n                if (tokenType) queryParameters['tokenType'] = tokenType\n            } else if (operation === 'getNFTsForCollection') {\n                const contractAddress = inputParametersData.contractAddress as string\n                const startToken = inputParametersData.startToken as string\n                const withMetadata = inputParametersData.withMetadata as boolean\n                const limit = inputParametersData.limit as number\n                const tokenUriTimeoutInMs = inputParametersData.tokenUriTimeoutInMs as number\n\n                queryParameters['contractAddress'] = contractAddress\n                if (startToken) queryParameters['startToken'] = startToken\n                if (withMetadata) queryParameters['withMetadata'] = withMetadata\n                if (limit) queryParameters['limit'] = limit\n                if (tokenUriTimeoutInMs) queryParameters['tokenUriTimeoutInMs'] = tokenUriTimeoutInMs\n            } else if (operation === 'getOwnersForCollection') {\n                const contractAddress = inputParametersData.contractAddress as string\n                const withTokenBalances = inputParametersData.withTokenBalances as boolean\n                const block = inputParametersData.block as string\n                const pageKey = inputParametersData.pageKey as string\n\n                queryParameters['contractAddress'] = contractAddress\n                if (withTokenBalances) queryParameters['withTokenBalances'] = withTokenBalances\n                if (block) queryParameters['block'] = block\n                if (pageKey) queryParameters['pageKey'] = pageKey\n            } else if (operation === 'getOwnersForToken' || operation === 'computeRarity') {\n                const contractAddress = inputParametersData.contractAddress as string\n                const tokenId = inputParametersData.tokenId as string\n\n                queryParameters['contractAddress'] = contractAddress\n                queryParameters['tokenId'] = tokenId\n            } else if (\n                operation === 'isSpamContract' ||\n                operation === 'reingestContract' ||\n                operation === 'getFloorPrice' ||\n                operation === 'summarizeNFTAttributes' ||\n                operation === 'reportSpamContract'\n            ) {\n                const contractAddress = inputParametersData.contractAddress as string\n                queryParameters['contractAddress'] = contractAddress\n            } else if (operation === 'searchContractMetadata') {\n                const query = inputParametersData.query as string\n                queryParameters['query'] = query\n            } else if (operation === 'isHolderOfCollection') {\n                const contractAddress = inputParametersData.contractAddress as string\n                const wallet = inputParametersData.wallet as string\n\n                queryParameters['contractAddress'] = contractAddress\n                queryParameters['wallet'] = wallet\n            } else if (operation === 'getNFTSales') {\n                const contractAddress = inputParametersData.contractAddress as string\n                const tokenId = inputParametersData.tokenId as string\n                const startBlock = inputParametersData.startBlock as string\n                const startLogIndex = inputParametersData.startLogIndex as number\n                const startBundleIndex = inputParametersData.startBundleIndex as number\n                const ascendingOrder = inputParametersData.ascendingOrder as boolean\n                const marketplace = inputParametersData.marketplace as string\n                const buyerAddress = inputParametersData.buyerAddress as string\n                const sellerAddress = inputParametersData.sellerAddress as string\n                const buyerIsMaker = inputParametersData.buyerIsMaker as boolean\n                const limit = inputParametersData.limit as number\n\n                queryParameters['contractAddress'] = contractAddress\n                queryParameters['tokenId'] = tokenId\n                if (startBlock) queryParameters['startBlock'] = startBlock\n                if (startLogIndex) queryParameters['startLogIndex'] = startLogIndex\n                if (startBundleIndex) queryParameters['startBundleIndex'] = startBundleIndex\n                if (ascendingOrder) queryParameters['ascendingOrder'] = ascendingOrder\n                if (marketplace) queryParameters['marketplace'] = marketplace\n                if (buyerAddress) queryParameters['buyerAddress'] = buyerAddress\n                if (sellerAddress) queryParameters['sellerAddress'] = sellerAddress\n                if (buyerIsMaker) queryParameters['buyerIsMaker'] = buyerIsMaker\n                if (limit) queryParameters['limit'] = limit\n            } else if (operation === 'getContractsForOwner') {\n                const owner = inputParametersData.owner as string\n                const pageKey = inputParametersData.pageKey as string\n\n                queryParameters['owner'] = owner\n                if (pageKey) queryParameters['pageKey'] = pageKey\n            }\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET',\n                    url: uri,\n                    params: queryParameters,\n                    paramsSerializer: (params) => serializeQueryParams(params),\n                    headers: {\n                        'Content-Type': 'application/json'\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n\n            return returnNodeExecutionData(returnData)\n        }\n        return returnNodeExecutionData([])\n    }\n}\n\nconst getSelectedOperations = (api: string, network: string) => {\n    switch (api) {\n        case 'chainAPI':\n            if (network === NETWORK.MATIC || network === NETWORK.MATIC_MUMBAI) return [...polygonOperations, ...ethOperations]\n            else return ethOperations\n        case 'txReceiptsAPI':\n            return transactionReceiptsOperations\n        case 'tokenAPI':\n            return tokenAPIOperations\n        case 'solanaAPI':\n            return solanaAPIOperations\n        default:\n            return ethOperations\n    }\n}\n\nmodule.exports = { nodeClass: Alchemy }\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/AlchemyTrigger.ts",
    "content": "import { INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    alchemyWSSAPIs,\n    ArbitrumNetworks,\n    ETHNetworks,\n    NETWORK,\n    OptimismNetworks,\n    PolygonNetworks,\n    SolanaNetworks\n} from '../../src/ChainNetwork'\nimport { subscribeOperations, unsubscribeOperations } from './subscribeOperation'\nimport { IETHOperation } from '../../src/ETHOperations'\nimport WebSocket from 'ws'\n\nclass AlchemyTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'Alchemy Trigger'\n        this.name = 'alchemyTrigger'\n        this.icon = 'alchemy.svg'\n        this.type = 'trigger'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Start workflow whenever subscribed event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks, ...SolanaNetworks]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Alchemy API Key',\n                        name: 'alchemyApi'\n                    }\n                ],\n                default: 'alchemyApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Subscribe Operation',\n                name: 'subscribeOperation',\n                type: 'asyncOptions',\n                loadMethod: 'getSubscribeOperations'\n            },\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: `[\n  \"param1\",\n  \"param2\"\n]`,\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]'\n            },\n            {\n                label: 'Unsubscribe Operation',\n                name: 'unsubscribeOperation',\n                type: 'asyncOptions',\n                loadMethod: 'getUnsubscribeOperations'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getSubscribeOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const networksData = nodeData.networks\n\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            const network = networksData.network as NETWORK\n\n            const totalOperations = subscribeOperations\n            const filteredOperations = totalOperations.filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'alchemy') && op.providerNetworks['alchemy'].includes(network)\n            )\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        },\n\n        async getUnsubscribeOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const networksData = nodeData.networks\n\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            const network = networksData.network as NETWORK\n\n            const totalOperations = unsubscribeOperations\n            const filteredOperations = totalOperations.filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'alchemy') && op.providerNetworks['alchemy'].includes(network)\n            )\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        // GET network\n        const network = networksData.network as NETWORK\n\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n        const wssProvider = `${alchemyWSSAPIs[network]}${apiKey}`\n\n        // GET subscribeOperation\n        const subscribeOperation = inputParametersData.subscribeOperation as string\n\n        // GET parameters\n        let bodyParameters: any\n        const parameters = inputParametersData.parameters as string\n        if (parameters) {\n            try {\n                bodyParameters = JSON.parse(parameters.replace(/\\s/g, ''))\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        const emitEventKey = nodeData.emitEventKey as string\n\n        const result = subscribeOperations.find((obj) => {\n            return obj.value === subscribeOperation\n        })\n\n        if (result === undefined) throw new Error('Invalid Operation')\n\n        const requestBody = result.body\n        requestBody.params = bodyParameters\n\n        const ws = new WebSocket(wssProvider)\n\n        ws.on('open', function open() {\n            ws.send(JSON.stringify(requestBody))\n        })\n\n        let subscriptionID = ''\n        ws.on('message', (data) => {\n            const messageData = JSON.parse(data as any)\n\n            if (messageData.method) {\n                this.emit(emitEventKey, returnNodeExecutionData(messageData))\n            } else {\n                subscriptionID = messageData.result\n                this.providers[emitEventKey] = { provider: ws, filter: subscriptionID }\n            }\n        })\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider: WebSocket = this.providers[emitEventKey].provider\n            const subscriptionID = this.providers[emitEventKey].filter\n\n            const result = unsubscribeOperations.find((obj) => {\n                return obj.value === (inputParametersData.unsubscribeOperation as string)\n            })\n\n            if (result === undefined) throw new Error('Invalid Operation')\n\n            const requestBody = result.body\n            requestBody.params = [subscriptionID]\n\n            provider.send(JSON.stringify(requestBody))\n            provider.close(1000)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: AlchemyTrigger }\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/AlchemyWebhook.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeParams, IWebhookNodeExecutionData, NodeType } from '../../src/Interface'\nimport { returnWebhookNodeExecutionData } from '../../src/utils'\n\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass AlchemyWebhook implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Alchemy Webhook'\n        this.name = 'AlchemyWebhook'\n        this.icon = 'alchemy.svg'\n        this.type = 'webhook'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Start workflow whenever Alchemy webhook event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Mainnet',\n                        name: 'ETH_MAINNET'\n                    },\n                    {\n                        label: 'Goerli',\n                        name: 'ETH_GOERLI'\n                    },\n                    {\n                        label: 'Polygon Mainnet',\n                        name: 'MATIC_MAINNET'\n                    },\n                    {\n                        label: 'Polygon Mumbai',\n                        name: 'MATIC_MUMBAI'\n                    },\n                    {\n                        label: 'Arbitrum Mainnet',\n                        name: 'ARB_MAINNET'\n                    },\n                    {\n                        label: 'Arbitrum Goerli',\n                        name: 'ARB_GOERLI'\n                    },\n                    {\n                        label: 'Optimism Mainnet',\n                        name: 'OPT_MAINNET'\n                    },\n                    {\n                        label: 'Optimism Goerli',\n                        name: 'OPT_GOERLI'\n                    }\n                ],\n                default: 'ETH_MAINNET'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Alchemy API Key',\n                        name: 'alchemyApi'\n                    }\n                ],\n                default: 'alchemyApi'\n            }\n        ] as INodeParams[]\n        this.actions = [\n            {\n                label: 'Event',\n                name: 'webhook_type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Mined Transactions',\n                        name: 'MINED_TRANSACTION',\n                        description: 'Triggered anytime a transaction sent through your API key gets successfully mined.'\n                    },\n                    {\n                        label: 'Dropped Transactions',\n                        name: 'DROPPED_TRANSACTION',\n                        description: `The Dropped Transactions Webhook is used to notify your app anytime a transaction send through your API key gets dropped.`\n                    },\n                    {\n                        label: 'Address Activity',\n                        name: 'ADDRESS_ACTIVITY',\n                        description: `The Address Activity Webhook allows you to track all ETH, ERC20 and ERC721 transfer events for as many Ethereum addresses as you'd like.`\n                    }\n                ],\n                default: 'MINED_TRANSACTION'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'App ID',\n                name: 'app_id',\n                type: 'string',\n                default: '',\n                description:\n                    'App ID can be found within the URL of your specific app. For example, given the URL https://dashboard.alchemyapi.io/apps/xfu8frt3wf94j7h5 your App ID would be xfu8frt3wf94j7h5',\n                show: {\n                    'actions.webhook_type': ['MINED_TRANSACTION', 'DROPPED_TRANSACTION']\n                }\n            },\n            {\n                label: 'Ethereum Addresses',\n                name: 'addresses',\n                type: 'string',\n                default: '',\n                description: 'Ethereum addresses to track the transfer events',\n                placeholder: '[\"<your-Ethereum-Address>\"]',\n                show: {\n                    'actions.webhook_type': ['ADDRESS_ACTIVITY']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    webhookMethods = {\n        async createWebhook(nodeData: INodeData, webhookFullUrl: string): Promise<string | undefined> {\n            // Check if webhook exists\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const networksData = nodeData.networks\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined || networksData === undefined) {\n                throw new Error('Required data missing')\n            }\n\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n\n            const authToken = credentials.authToken as string\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url: `https://dashboard.alchemyapi.io/api/team-webhooks`,\n                headers: { 'X-Alchemy-Token': authToken }\n            }\n\n            try {\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                const webhooks = responseData.data\n                const network = networksData.network as string\n                const webhook_type = actionsData.webhook_type as string\n                let webhookExist = false\n\n                for (const webhook of webhooks) {\n                    if (webhook.webhook_type === webhook_type && webhook.webhook_url === webhookFullUrl) {\n                        if (webhook_type !== 'ADDRESS_ACTIVITY') {\n                            const app_id = (inputParametersData.app_id as string) || ''\n                            if (webhook.app_id === app_id) {\n                                webhookExist = true\n                                break\n                            }\n                            continue\n                        }\n                        webhookExist = true\n                        break\n                    }\n                }\n\n                if (!webhookExist) {\n                    const data: ICommonObject = {\n                        webhook_type,\n                        network,\n                        webhook_url: webhookFullUrl\n                    }\n                    if (webhook_type === 'ADDRESS_ACTIVITY') {\n                        let addresses = (inputParametersData.addresses as string) || '[]'\n                        //Remove whitespaces\n                        addresses = addresses.replace(/\\s/g, '')\n                        if (addresses) data.addresses = JSON.parse(addresses)\n                    } else {\n                        const app_id = (inputParametersData.app_id as string) || ''\n                        data.app_id = app_id\n                    }\n\n                    const axiosCreateConfig: AxiosRequestConfig = {\n                        method: 'POST' as Method,\n                        url: `https://dashboard.alchemyapi.io/api/create-webhook`,\n                        data,\n                        headers: { 'X-Alchemy-Token': authToken }\n                    }\n                    let createResponseData = await axios(axiosCreateConfig)\n                    createResponseData = createResponseData.data\n                    if (createResponseData && createResponseData.data && createResponseData.data.id) {\n                        return createResponseData.data.id\n                    }\n                    return\n                }\n            } catch (error) {\n                return\n            }\n        },\n\n        async deleteWebhook(nodeData: INodeData, webhookId: string): Promise<boolean> {\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n\n            const authToken = credentials.authToken as string\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'DELETE' as Method,\n                url: `https://dashboard.alchemyapi.io/api/delete-webhook?webhook_id=${webhookId}`,\n                headers: { 'X-Alchemy-Token': authToken }\n            }\n\n            try {\n                await axios(axiosConfig)\n            } catch (error) {\n                return false\n            }\n\n            return true\n        }\n    }\n\n    async runWebhook(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const req = nodeData.req\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (req === undefined) {\n            throw new Error('Missing request')\n        }\n\n        //TODO: Verify webhook via signing key\n\n        const returnData: ICommonObject[] = []\n\n        returnData.push({\n            headers: req?.headers,\n            params: req?.params,\n            query: req?.query,\n            body: req?.body,\n            rawBody: (req as any).rawBody,\n            url: req?.url\n        })\n\n        return returnWebhookNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: AlchemyWebhook }\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/extendedOperation.ts",
    "content": "import { INodeOptionsValue, INodeParams, NETWORK } from '../../src'\nimport { IETHOperation } from '../../src/ETHOperations'\n\nexport const NFTOperationsOptions = [\n    {\n        label: 'Get NFTs',\n        name: 'getNFTs',\n        description: 'Gets all NFTs currently owned by a given address'\n    },\n    {\n        label: 'Get NFT Sales',\n        name: 'getNFTSales',\n        description: 'Gets NFT sales that have happened through on-chain marketplaces',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Get NFT Metadata',\n        name: 'getNFTMetadata',\n        description: 'Gets the metadata associated with a given NFT'\n    },\n    {\n        label: 'Get NFTs For Collection',\n        name: 'getNFTsForCollection',\n        description: 'Gets all NFTs for a given NFT contract'\n    },\n    {\n        label: 'Get Owners For Collection',\n        name: 'getOwnersForCollection',\n        description: 'Gets all owners for a given NFT contract.'\n    },\n    {\n        label: 'Get Owners For Token',\n        name: 'getOwnersForToken',\n        description: 'Get the owner(s) for a token.'\n    },\n    {\n        label: 'Get Contracts For Owner',\n        name: 'getContractsForOwner',\n        description: 'Gets all NFT contracts held by an owner address.'\n    },\n    {\n        label: 'Get Spam Contracts',\n        name: 'getSpamContracts',\n        description: 'Returns a list of all spam contracts marked by Alchemy.',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Is Spam Contracts',\n        name: 'isSpamContract',\n        description: 'Returns whether a contract is marked as spam or not by Alchemy.',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Reingest Contract',\n        name: 'reingestContract',\n        description:\n            'Triggers metadata refresh for an entire NFT collection and refreshes stale metadata after a collection reveal/collection changes.',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Get Floor Price',\n        name: 'getFloorPrice',\n        description: 'Returns the floor prices of a NFT collection by marketplace.',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Compute Rarity',\n        name: 'computeRarity',\n        description: 'Computes the rarity of each attribute of an NFT.',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Search Contract Metadata',\n        name: 'searchContractMetadata',\n        description: 'Search for a keyword across metadata of all ERC-721 and ERC-1155 smart contracts',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Summarize NFT Attributes',\n        name: 'summarizeNFTAttributes',\n        description: 'Generate a summary of attribute prevalence for an NFT collection.',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    },\n    {\n        label: 'Is Holder Of Collection',\n        name: 'isHolderOfCollection',\n        description: 'Checks whether a wallet holds a NFT in a given collection'\n    },\n    {\n        label: 'Report Spam Contract',\n        name: 'reportSpamContract',\n        description: 'Report a particular contract address to our APIs if you think it is spam',\n        show: {\n            'networks.network': [NETWORK.MAINNET]\n        }\n    }\n] as INodeOptionsValue[]\n\nexport const getNFTsProperties = [\n    {\n        label: 'Owner Address',\n        name: 'owner',\n        type: 'string',\n        description: 'Address for NFT owner (can be in ENS format!)',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTs']\n        }\n    },\n    {\n        label: 'PageKey',\n        name: 'pageKey',\n        type: 'string',\n        description:\n            'UUID for pagination. If more results are available, a UUID pageKey will be returned in the response. Pass that UUID into pageKey to fetch the next 100 NFTs.',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTs']\n        },\n        optional: true\n    },\n    {\n        label: 'Metadata',\n        name: 'withMetadata',\n        type: 'boolean',\n        description: 'If boolean is set to true the query will include metadata for each returned token.',\n        default: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTs']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTMetadataProperties = [\n    {\n        label: 'Contract Address',\n        name: 'contractAddress',\n        type: 'string',\n        description: 'Address of NFT contract',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTMetadata']\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'tokenId',\n        type: 'string',\n        description: 'Id for NFT',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTMetadata']\n        }\n    },\n    {\n        label: 'Token Type',\n        name: 'tokenType ',\n        type: 'options',\n        description: '\"ERC721\" or \"ERC1155\"; specifies type of token to query for',\n        options: [\n            {\n                label: 'ERC721',\n                name: 'ERC721'\n            },\n            {\n                label: 'ERC1155',\n                name: 'ERC1155'\n            },\n            {\n                label: '',\n                name: ''\n            }\n        ],\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTMetadata']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTsForCollectionProperties = [\n    {\n        label: 'Contract Address',\n        name: 'contractAddress',\n        type: 'string',\n        description: 'Contract address for the NFT collection',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTsForCollection']\n        }\n    },\n    {\n        label: 'Start Token',\n        name: 'startToken',\n        type: 'string',\n        description: 'An offset used for pagination. Can be a hex string, or a decimal.',\n        default: '',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTsForCollection']\n        }\n    },\n    {\n        label: 'Metadata',\n        name: 'withMetadata',\n        type: 'boolean',\n        description: 'If set to true, returns NFT metadata; otherwise will only return tokenIds',\n        default: true,\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTsForCollection']\n        }\n    },\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        description: 'Sets the total number of NFTs returned in the response. Defaults to 100.',\n        default: 100,\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTsForCollection']\n        }\n    },\n    {\n        label: 'Token Uri Timeout In Ms',\n        name: 'tokenUriTimeoutInMs',\n        type: 'number',\n        description:\n            'No set timeout by default - When metadata is requested, this parameter is the timeout (in milliseconds) for the website hosting the metadata to respond. If you want to only access the cache and not live fetch any metadata for cache misses then set this value to 0.',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTsForCollection']\n        }\n    }\n] as INodeParams[]\n\nexport const getOwnersForCollectionProperties = [\n    {\n        label: 'Contract Address',\n        name: 'contractAddress',\n        type: 'string',\n        description: 'Contract address for the NFT collection (ERC721 and ERC1155 supported).',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getOwnersForCollection']\n        }\n    },\n    {\n        label: 'With Token Balances',\n        name: 'withTokenBalances',\n        type: 'boolean',\n        description: 'If set to true the query will include the token balances per token id for each owner. false by default.',\n        default: false,\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getOwnersForCollection']\n        }\n    },\n    {\n        label: 'Block',\n        name: 'block',\n        type: 'string',\n        description: 'The point in time or block number (in hex or decimal) to fetch collection ownership information for.',\n        default: '',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getOwnersForCollection']\n        }\n    },\n    {\n        label: 'PageKey',\n        name: 'pageKey',\n        type: 'string',\n        description:\n            'used for collections with >50,000 owners. pageKey field can be passed back as request parameter to get the next page of results.',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getOwnersForCollection']\n        },\n        optional: true\n    }\n] as INodeParams[]\n\nexport const getOwnersForTokenProperties = [\n    {\n        label: 'Contract Address',\n        name: 'contractAddress',\n        type: 'string',\n        description: 'Contract address for the NFT collection (ERC721 and ERC1155 supported).',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': [\n                'getOwnersForToken',\n                'isSpamContract',\n                'reingestContract',\n                'getFloorPrice',\n                'computeRarity',\n                'summarizeNFTAttributes',\n                'reportSpamContract'\n            ]\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'tokenId',\n        type: 'string',\n        description: 'The ID of the token. Can be in hex or decimal format.',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getOwnersForToken', 'computeRarity']\n        }\n    }\n] as INodeParams[]\n\nexport const searchContractMetadataProperties = [\n    {\n        label: 'Query',\n        name: 'query',\n        type: 'string',\n        description: 'The search string that you want to search for in contract metadata',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['searchContractMetadata']\n        }\n    }\n] as INodeParams[]\n\nexport const isHolderOfCollectionProperties = [\n    {\n        label: 'Contract Address',\n        name: 'contractAddress',\n        type: 'string',\n        description: 'Contract address for the NFT collection (ERC721 and ERC1155 supported).',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['isHolderOfCollection']\n        }\n    },\n    {\n        label: 'Wallet',\n        name: 'wallet',\n        type: 'string',\n        description: 'Wallet address to check for collection ownership.',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['isHolderOfCollection']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTSalesProperties = [\n    {\n        label: 'Contract Address',\n        name: 'contractAddress',\n        type: 'string',\n        description: 'The contract address of a NFT collection to filter sales by. Defaults to returning all NFT contracts.',\n        default: '',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'tokenId',\n        type: 'string',\n        description:\n            'The token ID of an NFT within the collection specified by contractAddress to filter sales by. Defaults to returning all token IDs.',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Start Block',\n        name: 'startBlock',\n        type: 'string',\n        description:\n            'The block number to start fetching NFT sales data from. Allowed values are decimal integers and \"latest\". Defaults to \"latest\".',\n        optional: true,\n        default: 'latest',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Start Log Index',\n        name: 'startLogIndex',\n        type: 'number',\n        description: 'The log index within the startBlock to start fetching NFT sales data from. Defaults to 0.',\n        optional: true,\n        default: 0,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Start Bundle Index',\n        name: 'startBundleIndex',\n        type: 'number',\n        description: 'The index of an NFT within a sale bundle to start fetching NFT sales data from. Defaults to 0.',\n        optional: true,\n        default: 0,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Ascending Order',\n        name: 'ascendingOrder',\n        type: 'boolean',\n        description: 'Whether to return the results ascending from startBlock or descending from startBlock. Defaults to ascending (true).',\n        optional: true,\n        default: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Marketplace',\n        name: 'marketplace',\n        type: 'options',\n        description: 'The name of the NFT marketplace to filter sales by. Currently only \"seaport\" is supported.',\n        options: [\n            {\n                label: 'Seaport',\n                name: 'seaport'\n            }\n        ],\n        optional: true,\n        default: 'seaport',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Buyer Address',\n        name: 'buyerAddress',\n        type: 'string',\n        description: 'The address of the NFT buyer to filter sales by. Defaults to returning sales involving any buyer.',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Seller Address',\n        name: 'sellerAddress',\n        type: 'string',\n        description: 'The address of the NFT seller to filter sales by. Defaults to returning sales involving any seller.',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Buyer Is Maker',\n        name: 'buyerIsMaker',\n        type: 'boolean',\n        description:\n            'Filter by whether if the buyer was the maker in the trade, i.e. if the sale involved a buyer offering a bid and the seller then accepting the bid. Defaults to returning both maker and taker orders.',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    },\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        description: 'The maximum number of NFT sales to return. Defaults to 100.',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getNFTSales']\n        }\n    }\n] as INodeParams[]\n\nexport const getContractsForOwnerProperties = [\n    {\n        label: 'Owner',\n        name: 'owner',\n        type: 'string',\n        description: 'Address for NFT owner (can be in ENS format!).',\n        placeholder: 'vitalk.eth',\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getContractsForOwner']\n        }\n    },\n    {\n        label: 'PageKey',\n        name: 'pageKey',\n        type: 'string',\n        description:\n            'key for pagination. If more results are available, a pageKey will be returned in the response. Pass back the pageKey as a param to fetch the next page of results.',\n        optional: true,\n        show: {\n            'inputParameters.api': ['nftAPI'],\n            'inputParameters.operation': ['getContractsForOwner']\n        }\n    }\n] as INodeParams[]\n\nexport const transactionReceiptsOperations = [\n    {\n        name: 'alchemy_getTransactionReceipts',\n        value: 'alchemy_getTransactionReceipts',\n        parentGroup: 'Transaction Receipt Information',\n        description: 'Fetch all transaction receipts for a block number or a block hash in one API call ',\n        providerNetworks: {\n            alchemy: [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.MATIC, NETWORK.MATIC_MUMBAI, NETWORK.ARBITRUM, NETWORK.ARBITRUM_GOERLI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'alchemy_getTransactionReceipts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">blockNumber</code> - (hex) The block number you want to get transaction receipts for</li>\n            <li><code class=\"inline\">blockHash</code> - The block hash you want to get transaction receipts for</li>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"blockHash\": \"0xeb7214680220e50f1e9662d2f15569395f8e67455cc2f5cbf7db8b3b1567558c\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: {\n                receipts: [\n                    {\n                        transactionHash: '0x103c47bd1917f5b57c89d55bc9a664eca732fadcc7596670030ad27ac26259ae',\n                        blockHash: '0xbd6523808cf0a98c528b7e169b357c46a7cd0f602cec98f05bb5962553522647',\n                        blockNumber: '0xd63adc',\n                        contractAddress: null,\n                        cumulativeGasUsed: '0x26d93',\n                        effectiveGasPrice: '0x1e836343a7',\n                        from: '0xcc72f778eedd8e337e6cb58ca9ec8ba2912e71dc',\n                        gasUsed: '0x26d93',\n                        logs: [\n                            {\n                                address: '0xa5def515cfd373d17830e7c1de1639cb3530a112',\n                                topics: [\n                                    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                                    '0x000000000000000000000000cc72f778eedd8e337e6cb58ca9ec8ba2912e71dc',\n                                    '0x0000000000000000000000001ca1a5937d73f74f89764c3835d6796e4e1c8314'\n                                ],\n                                data: '0x000000000000000000000000000000000000000000000df7b2d4343e99d38475',\n                                blockNumber: '0xd63adc',\n                                transactionHash: '0x103c47bd1917f5b57c89d55bc9a664eca732fadcc7596670030ad27ac26259ae',\n                                transactionIndex: '0x0',\n                                blockHash: '0xbd6523808cf0a98c528b7e169b357c46a7cd0f602cec98f05bb5962553522647',\n                                logIndex: '0x0',\n                                removed: false\n                            }\n                        ],\n                        logsBloom:\n                            '0x00000000000000000000000000000000000000001400000020000000000000000000000000000000000000000000000008000000000000000000000008240000000000000000000000000408000000000000000000040000000000002000000000000000000000000000010000000004000000000000000000000810000000000000000000000000000000000000000000000000000020000000000000000000020000000000800000000004000000020000000000000000000000000000000000008002000000000000000000000000020000000000000000000000000040000010000000000000000000020000000000000000400000000000000000000000',\n                        status: '0x1',\n                        to: '0x1ca1a5937d73f74f89764c3835d6796e4e1c8314',\n                        transactionIndex: '0x0',\n                        type: '0x2'\n                    }\n                ]\n            }\n        }\n    }\n] as IETHOperation[]\n\nexport const tokenAPIOperations = [\n    {\n        name: 'alchemy_getTokenAllowance',\n        value: 'alchemy_getTokenAllowance',\n        parentGroup: 'Token Information',\n        description: 'Returns the amount which the spender is allowed to withdraw from the owner.',\n        providerNetworks: {\n            alchemy: [\n                NETWORK.MAINNET,\n                NETWORK.GÖRLI,\n                NETWORK.MATIC,\n                NETWORK.MATIC_MUMBAI,\n                NETWORK.ARBITRUM,\n                NETWORK.ARBITRUM_GOERLI,\n                NETWORK.OPTIMISM,\n                NETWORK.OPTIMISM_GOERLI\n            ]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'alchemy_getTokenAllowance',\n            params: [],\n            id: 83\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code> - An object with the following fields:</li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">contract</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address of the token contract.</li>\n\t\t\t\t<li><code class=\"inline\">owner</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address of the token owner.</li>\n                <li><code class=\"inline\">spender</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address of the token spender.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"contract\": \"0xE41d2489571d322189246DaFA5ebDe1F4699F498\",\n    \"owner\": \"0xe8095A54C83b069316521835408736269bfb389C\",\n    \"spender\": \"0x3Bcc5bD4abBc853395eBE5103b7DbA20411E38db\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 83,\n            result: '10963536149943846000'\n        }\n    },\n    {\n        name: 'alchemy_getTokenBalances',\n        value: 'alchemy_getTokenBalances',\n        parentGroup: 'Token Information',\n        description: 'Returns token balances for a specific address given a list of contracts.',\n        providerNetworks: {\n            alchemy: [\n                NETWORK.MAINNET,\n                NETWORK.GÖRLI,\n                NETWORK.MATIC,\n                NETWORK.MATIC_MUMBAI,\n                NETWORK.ARBITRUM,\n                NETWORK.ARBITRUM_GOERLI,\n                NETWORK.OPTIMISM,\n                NETWORK.OPTIMISM_GOERLI\n            ]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'alchemy_getTokenBalances',\n            params: [],\n            id: 42\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - The address for which token balances will be checked</li>\n            <li>One of:</li>\n            <ul>\n\t\t\t    <li><code class=\"inline\">Array</code> - A list of contract addresses</li>\n\t\t\t\t<li><code class=\"inline\">String</code> \"DEFAULT_TOKENS\" - denotes a query for the top 100 tokens by 24 hour volume</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be\",\n  [\n    \"0x607f4c5bb672230e8672085532f7e901544a7375\",\n    \"0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6\",\n    \"0x63b992e6246d88f07fc35a056d2c365e6d441a3d\",\n    \"0x6467882316dc6e206feef05fba6deaa69277f155\",\n    \"0x647f274b3a7248d6cf51b35f08e7e7fd6edfb271\"\n  ]\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 42,\n            result: {\n                address: '0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be',\n                tokenBalances: [\n                    {\n                        contractAddress: '0x607f4c5bb672230e8672085532f7e901544a7375',\n                        tokenBalance: '0x00000000000000000000000000000000000000000000000000003c005f81ab00',\n                        error: null\n                    }\n                ]\n            }\n        }\n    },\n    {\n        name: 'alchemy_getTokenMetadata',\n        value: 'alchemy_getTokenMetadata',\n        parentGroup: 'Token Information',\n        description: 'Returns metadata (name, symbol, decimals, logo) for a given token contract address.',\n        providerNetworks: {\n            alchemy: [\n                NETWORK.MAINNET,\n                NETWORK.GÖRLI,\n                NETWORK.MATIC,\n                NETWORK.MATIC_MUMBAI,\n                NETWORK.ARBITRUM,\n                NETWORK.ARBITRUM_GOERLI,\n                NETWORK.OPTIMISM,\n                NETWORK.OPTIMISM_GOERLI\n            ]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'alchemy_getTokenMetadata',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - The address of the token contract.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x1985365e9f78359a9B6AD760e32412f4a445E862\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: {\n                logo: 'https://static.alchemyapi.io/images/assets/1104.png',\n                symbol: 'REP',\n                decimals: 18,\n                name: 'Augur'\n            }\n        }\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/solanaOperation.ts",
    "content": "import { NETWORK, NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport { IETHOperation } from '../../src/ETHOperations'\n\nexport const solanaOperationsNetworks = [NETWORK.SOLANA, NETWORK.SOLANA_DEVNET]\n\nexport const solanaAPIOperations = [\n    {\n        name: 'getTransaction',\n        value: 'getTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Returns transaction details for a confirmed transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - transaction signature</li>\n            <li><code class=\"inline\">object</code> - (optional) Config object:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) encoding for tx data; Either \"json\", \"jsonParsed\", \"base58\" (slow), \"base64\". (default is \"json\")</li>\n                <li><code class=\"inline\">maxSupportedTransactionVersion</code>: <code class=\"inline\">number</code> - (optional) set the max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"FhGuWorGjyu1sAMvn53GWb96apbExf8HvX18MVwexMQxmo2sweuSfFpoApJbMT19ijDHRRUk6kDbvE1kgNfRkse\",\n  {\n    \"commitment\": \"confirmed\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                blockTime: 1655319302,\n                meta: {\n                    err: null,\n                    fee: 5000,\n                    innerInstructions: [\n                        {\n                            index: 1,\n                            instructions: [\n                                {\n                                    accounts: [26, 13, 19, 15, 16, 0],\n                                    data: '63S77LdPnZrhcJ2wGjQ7xuV',\n                                    programIdIndex: 21\n                                },\n                                {\n                                    accounts: [14, 16, 0],\n                                    data: '3QCBRJNuTemd',\n                                    programIdIndex: 26\n                                },\n                                {\n                                    accounts: [2, 12, 19],\n                                    data: '3KiXXdFFB5Km',\n                                    programIdIndex: 26\n                                }\n                            ]\n                        }\n                    ]\n                }\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'sendTransaction',\n        value: 'sendTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Submits a signed transaction to the cluster for processing.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">encoded string</code> - fully-signed Transaction</li>\n            <li><code class=\"inline\">object</code> - (optional) Config object:</li>\n            <ul>\n                <li><code class=\"inline\">skipPreflight</code>: <code class=\"inline\">bool</code> - if true, skip the preflight transaction checks (default: false)</li>\n                <li><code class=\"inline\">preflightCommitment</code>: <code class=\"inline\">string</code> - (optional) Commitment level to use for preflight (default: \"finalized\").</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) Encoding used for the transaction data. Either \"base58\" (slow, DEPRECATED), or \"base64\". (default: \"base58\").</li>\n                <li><code class=\"inline\">maxRetries</code>: <code class=\"inline\">usize</code> - (optional) Maximum number of times for the RPC node to retry sending the transaction to the leader. If this parameter is not provided, the RPC node will retry the transaction until it is finalized or until the blockhash expires.</li>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"AeF71jBTbqF33iO17K/WepKejY6ED8Db2t00aizbkvRpDUaX6mKZMInTbDgrOLTTPgOmYbE96Nlt2IwGmZKSpQ0BAAsZeGK9wdFlyWz2mdFDfZ3/65ZjurcmrptLNm5mN3f8p0ogsmSV/IVZRQW5a5ssHj+xTkSMSg6MvA28piZAJSLyxSNlSqXrkQU25XOnSMYzHuJNOeei2iMWq3lwa8TdxeqjJRFwcKkY21dentnVTDwl1cueQS/QUd9mUuo25ivPwYY3RX3yAyUj3Q6IaALYsHU/017gPZpDvHJJ7jrgSdN8dzeHLDAOEIF1ci79StKCB8hABC87tDUy+cyHrGjv7ceJbKfgtajeraDLc3Ndv8FX/Azg8HJ4eCu29Y+gxBJROld53yhEmAooyHHkcMEPF/qybgtUUE4DMxUftcYRcEeGtXsupOBa7USG+TC3fPVLShGfKH//XF/H9EyQut9RHKTrnofS6+89GaTLV8VZXwcr3giCPPZtOymEVDquZoAthk6pIbU9bjTY+O+NVm9AJVKtkUh02Is8rj1xlTRevQ65RutmBvIsMFSykgwwkEcthzgnj9RgOcNPV63bOnMwtnA25xGVrA5WPR3Cp/qLwsfk76yDUU1ksxe/AT+BbeZD6tzuifWhovJqdEpfnl+qwU5RSmm+bf9lGWui27urEkLY0SuJudyP3580cJpbEGtHLw85u2ypzgSw/X8ulxaI4uU7M7MexO/4+iia6oyVTAFjLi12SQjOVE1oZb3vERv/YSsBOP45MdZXb1/RCItG0H924+Qvzh1Vy52i1Sl3ULQWHFoPCOzzP1aH2CdIP9hLVSu8Mn+Lr2Nkvn22qA5HyDclu6nN/XmuJp8Db7aXm0uYp3KSexdu9rcZZXaHIfHx8uTmLfbItKhf4aZ9tE3BLeXbMw96xmty3GWK/t8PSkFbQ/9hSRqTERLd8b2BR80bZBN1959YJRJtZlSAh0Y0/QrOBfhDaTInzNg0xDUbatfDR7qLZN0mJVepe+fnfUZ28WcGm4uYWqtTKkUJDehVf83cvmy378c6CmWwb5IDXbc+7Aan1RcYx3TJKFZjmGkdXraLXrijm0ttXHNVWyEAAAAABt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKnVgl9CjQpAihYbPBN/CahLRcSHNFl1MS9n9YRISJ2GJAYWBAwTERcBAxYEAhQVFwEDFgQKFBUXAQMWBAYOEBcBAxYGCRcMAgoGAQcWDwQNAwYBDAsIBQcJDxIAGAkR+PwcVAIAAAA=\",\n  {\n    \"encoding\": \"base64\",\n    \"skipPreflight\": true,\n    \"preflightCommitment\": \"processed\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: '5WUPdF1zGiCbMX4dAGRrVJBvZuRjQatzsDJf8rcmLH8q67m8AoupcFsVNSo1CsPhLat4B3C2yZAtGp34yVgmcKNk'\n        }\n    },\n    {\n        name: 'getSignatureStatuses',\n        value: 'getSignatureStatuses',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Returns the statuses of a list of signatures.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSignatureStatuses',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">array of base-58 encoded string</code> - An array of transaction signatures to confirm</li>\n            <li><code class=\"inline\">object</code> - (optional) Config object:</li>\n            <ul>\n                <li><code class=\"inline\">searchTransactionHistory</code>: <code class=\"inline\">bool</code> - if true, a Solana node will search its ledger cache for any signatures not found in the recent status cache</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  [\n    \"28P1gdVq52uEbCHns4EL5DCMjU5PtcBo5M3Gju4FX8DLwjLPDchudttnQapAxYy5dkdVZ6sqa6pvtgC5mbKLqfQA\"\n  ],\n  {\n    \"searchTransactionHistory\": true\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 137569378\n                },\n                value: [\n                    {\n                        confirmationStatus: 'finalized',\n                        confirmations: null,\n                        err: null,\n                        slot: 137529522,\n                        status: {\n                            Ok: null\n                        }\n                    }\n                ]\n            }\n        }\n    },\n    {\n        name: 'getSignaturesForAddress',\n        value: 'getSignaturesForAddress',\n        parentGroup: 'Reading & Writing Transactions',\n        description:\n            'Returns signatures for confirmed transactions that include the given address in their accountKeys list. Returns signatures backwards in time from the provided signature or most recent confirmed block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSignaturesForAddress',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - account address</li>\n            <li><code class=\"inline\">object</code> - (optional) Config object:</li>\n            <ul>\n                <li><code class=\"inline\">limit</code>: <code class=\"inline\">number</code> - (optional) maximum transaction signatures to return (between 1 and 1,000, default: 1,000).</li>\n                <li><code class=\"inline\">before</code>: <code class=\"inline\">string</code> - (optional) start searching backwards from this transaction signature. If not provided the search starts from the top of the highest max confirmed block.</li>\n                <li><code class=\"inline\">until</code>: <code class=\"inline\">string</code> - (optional) search until this transaction signature, if found before limit reached.</li>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at.</li>\n                <li><code class=\"inline\">commitment</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"Vote111111111111111111111111111111111111111\",\n  {\n    \"limit\": 1\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: [\n                {\n                    blockTime: 1654173549,\n                    confirmationStatus: 'finalized',\n                    err: null,\n                    memo: null,\n                    signature: '67iWWgeXYSXxKmxMjAahr9ATXvv1SJoHedXYZxicFQtF4eFxCWJxUwEYczNbrua8pQAshmkf73gfAX5itutWTA7m',\n                    slot: 136105283\n                }\n            ],\n            id: 1\n        }\n    },\n    {\n        name: 'simulateTransaction',\n        value: 'simulateTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Simulate sending a transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'simulateTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">string</code> - Transaction, as an encoded string. The transaction must have a valid blockhash, but is not required to be signed.</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">sigVerify</code> -  if true the transaction signatures will be verified (default: false, conflicts with replaceRecentBlockhash).</li>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - (optional) Encoding used for the transaction data. Accepts one of the following strings: [\"base64\" (default), \"base64+zstd\" or \"jsonParsed\"]</li>\n                <li><code class=\"inline\">replaceRecentBlockhash</code> - (optional) if true the transaction recent blockhash will be replaced with the most recent blockhash. (default: false, conflicts with sigVerify).</li>\n                <li><code class=\"inline\">accounts </code>: <code class=\"inline\">object</code> - (optional) Accounts configuration object containing the following fields:</li>\n                <ul>\n                    <li><code class=\"inline\">encoding</code> - (optional) encoding for returned Account data, either \"base64\" (default), \"base64+zstd\" or \"jsonParsed\". \"jsonParsed\" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If \"jsonParsed\" is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the data field is type string.</li>\n                    <li><code class=\"inline\">addresses</code> - An array of accounts to return, as base-58 encoded strings.</li>\n                </ul>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCAgiFt0xk+FyHf76k7/dkz9vjFqSY8vhrizSEx2GWidkQ5ewAKY7OUUt6ZEIryxOHEx1w1mGioe0SGujvGtbbPvaSDCG6EGTb0+Q1B98oAxfD0GiOZwOLQW1IkeOC71yX0NQm+LOK6+h53IwvrgMD9JNZme6u7PeARqlqZzOD4iRLRnyGE+3cPqKW5IiU66yIZZOgOtO4B9TYReBdaWsp+Dx5lyxj0FXRm/6oiIDpU9abZ6qIYeIKRR96fuhXeHpmjwUMJxNk9vhCZs3zhxL/0CZLdm0EbWEwrD4A7KBrPOYP2gbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCph9Yfdu8NCN6d9KtOQeMPovh7jKgKo56yxXQO8eTA6XUBBgcAAgEDBQQH4gMobmZ0X2FjY291bnQ6ICI5enlMTjVDUXd4V3JFUGl0MXNGeTVEWjV4TXFKZk5RVExpcUJWU2Z2UHZXWSIsCiAgICBkYXRhX2FjY291bnQ6ICIzZDhTcG5abnRtR0t0dlVtaHVhazdLU3U2WFQ3QVVNaW51VURtYTVobWlCWCIsCiAgICBjb2luX3NyY19hY2N0OiAiQkI3cGN0UGVWQ1FQalhNR2RrRFg5QURUOTk1Z1hDVkx5SEpVeWdwSlBOMngiLAogICAgY29pbl9kZXN0X2FjY3Q6ICI5UDY4a2J2VzlLelBIc1pURnE0eVFyUlZrWXNmNUNRRmc5aFZoeUJaZHBQMiIsCiAgICB0cmFuc2Zlcl9hdXRob3JpdHk6ICJwTWludFRaNUNFa0hVaGdydXJQZ29pTW50b0xnRndic0tkYnFOclhzcmR5IiwKICAgIGFjdGl2aXR5OiAxLCAKICAgIHBheV9wZXJpb2Q6IDYwNDgwMC4wMDAwMDAsIAogICAgcGF5X3JhdGU6IDguMDAwMDAwLAogICAgdGltZXN0YW1wOiAxNjU1MTg0ODYwLAogICAgbXVsdGlwbGllcjogMS4wLAogICAgbWF4X3BheW91dDogMTYuMDAwMDAwKQ==\",\n  {\n    \"encoding\": \"base64\",\n    \"commitment\": \"recent\",\n    \"sigVerify\": false,\n    \"accounts\": {\n        \"addresses\": [\n            \"9zyLN5CQwxWrEPit1sFy5DZ5xMqJfNQTLiqBVSfvPvWY\",\n            \"GtFMtrW31RdCeSdW4ot3jNVuoLtFywJGGTiF1Q8Uopky\",\n            \"pMintTZ5CEkHUhgrurPgoiMntoLgFwbsKdbqNrXsrdy\",\n            \"3d8SpnZntmGKtvUmhuak7KSu6XT7AUMinuUDma5hmiBX\",\n            \"9P68kbvW9KzPHsZTFq4yQrRVkYsf5CQFg9hVhyBZdpP2\",\n            \"BB7pctPeVCQPjXMGdkDX9ADT995gXCVLyHJUygpJPN2x\",\n            \"pSTAkE7Z2guBmSheg6tHdHk3rT1wmF1Mfuf52tnWeAd\",\n            \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\"\n        ]\n    }\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 137569919\n                },\n                value: {\n                    accounts: [null, null, null, null, null, null, null, null],\n                    err: 'BlockhashNotFound',\n                    logs: [],\n                    unitsConsumed: 0\n                }\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getBlockProduction',\n        value: 'getBlockProduction',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns recent block production information from the current or previous epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlockProduction',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getBlock',\n        value: 'getBlock',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns identity and transaction information about a confirmed block in the ledger.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlock',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">u64</code> - a slot integer denoting the target block number.</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">transactionDetails</code>: <code class=\"inline\">string</code> - (optional) level of transaction detail to return. Accepts one of the following strings: [\"full\" (Default), \"signatures\", or \"none\"]</li>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) data encoding for each returned transaction. Accepts one of the following strings: [\"json\" (Default), \"jsonParsed\", \"base58\" (slow), \"base64\"]</li>\n                <li><code class=\"inline\">rewards</code>: <code class=\"inline\">bool</code> - (optional) whether to populate the rewards array. true m(Default)</li>\n                <li><code class=\"inline\">maxSupportedTransactionVersion</code>: <code class=\"inline\">number</code> - (optional) sets the maximum transaction version.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  430, \n  {\n    \"encoding\": \"json\",\n    \"transactionDetails\":\"full\",\n    \"rewards\":false\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                blockHeight: 428,\n                blockTime: null,\n                blockhash: '3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA',\n                parentSlot: 429,\n                previousBlockhash: 'mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B',\n                transactions: [\n                    {\n                        meta: {\n                            err: null,\n                            fee: 5000,\n                            innerInstructions: [],\n                            logMessages: [],\n                            postBalances: [499998932500, 26858640, 1, 1, 1],\n                            postTokenBalances: [],\n                            preBalances: [499998937500, 26858640, 1, 1, 1],\n                            preTokenBalances: [],\n                            status: {\n                                Ok: null\n                            }\n                        },\n                        transaction: {\n                            message: {\n                                accountKeys: [\n                                    '3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe',\n                                    'AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc',\n                                    'SysvarS1otHashes111111111111111111111111111',\n                                    'SysvarC1ock11111111111111111111111111111111',\n                                    'Vote111111111111111111111111111111111111111'\n                                ],\n                                header: {\n                                    numReadonlySignedAccounts: 0,\n                                    numReadonlyUnsignedAccounts: 3,\n                                    numRequiredSignatures: 1\n                                },\n                                instructions: [\n                                    {\n                                        accounts: [1, 2, 3, 0],\n                                        data: '37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1',\n                                        programIdIndex: 4\n                                    }\n                                ],\n                                recentBlockhash: 'mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B'\n                            },\n                            signatures: ['2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv']\n                        }\n                    }\n                ]\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getBlocks',\n        value: 'getBlocks',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns a list of confirmed blocks between two slots.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlocks',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">u64</code> - starting slot integer</li>\n            <li><code class=\"inline\">u64</code> - (optional) ending slot integer</li>\n            <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n            <ul>\n                <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[5, 10]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: [5, 6, 7, 8, 9, 10],\n            id: 1\n        }\n    },\n    {\n        name: 'getBlocksWithLimit',\n        value: 'getBlocksWithLimit',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns a list of confirmed blocks starting at the given slot.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlocksWithLimit',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">u64</code> - starting slot integer</li>\n            <li><code class=\"inline\">u64</code> - (optional) ending slot integer</li>\n            <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n            <ul>\n                <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[5, 3]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: [5, 6, 7],\n            id: 1\n        }\n    },\n    {\n        name: 'getBlockHeight',\n        value: 'getBlockHeight',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns the current block height of the node.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlockHeight',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'isBlockhashValid',\n        value: 'isBlockhashValid',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns whether a blockhash is still valid or not.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'isBlockhashValid',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">blockhash</code>: <code class=\"inline\">base-58 encoded string</code> - the blockhash of this block</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"J7rBdM6AecPDEZp8aPq5iPSNKVkU5Q76F3oAV4eW5wsW\",\n  {\n    \"commitment\": \"processed\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 136103237\n                },\n                value: false\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getTokenAccountsByOwner',\n        value: 'getTokenAccountsByOwner',\n        parentGroup: 'Token Information',\n        description: 'Returns all SPL Token accounts by token owner.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenAccountsByOwner',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - Pubkey of queried SPL token account owner</li>\n            <li><code class=\"inline\">object</code> - Either:</li>\n            <ul>\n                <li><code class=\"inline\">mint</code> - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string.</li>\n                <li><code class=\"inline\">programId</code> - Pubkey of the Token program ID that owns the accounts, as base-58 encoded string.</li>\n            </ul>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) data encoding for each returned transaction. Accepts one of the following strings: [\"json\" (Default), \"jsonParsed\", \"base58\" (slow), \"base64\"]</li>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n                <li><code class=\"inline\">dataSlice</code>: <code class=\"inline\">object</code> - (optional) limits the returned account data using the provided <code class=\"inline\">offset: usize</code> and <code class=\"inline\">length: usize</code> fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"J27ma1MPBRvmPJxLqBqQGNECMXDm9L6abFa4duKiPosa\",\n  {\n    \"mint\": \"2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk\"\n  },\n  {\n    \"encoding\": \"jsonParsed\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 137568828\n                },\n                value: [\n                    {\n                        account: {\n                            data: {\n                                parsed: {\n                                    info: {\n                                        isNative: false,\n                                        mint: '2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk',\n                                        owner: 'J27ma1MPBRvmPJxLqBqQGNECMXDm9L6abFa4duKiPosa',\n                                        state: 'initialized',\n                                        tokenAmount: {\n                                            amount: '821',\n                                            decimals: 6,\n                                            uiAmount: 8.21e-4,\n                                            uiAmountString: '0.000821'\n                                        }\n                                    },\n                                    type: 'account'\n                                },\n                                program: 'spl-token',\n                                space: 165\n                            },\n                            executable: false,\n                            lamports: 2039280,\n                            owner: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',\n                            rentEpoch: 318\n                        },\n                        pubkey: 'Exo9AH6fNchE43GaJB85FT7ToYiuKnKzYDyW5mFeTXRR'\n                    }\n                ]\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getTokenAccountBalance',\n        value: 'getTokenAccountBalance',\n        parentGroup: 'Token Information',\n        description: 'Returns the token balance of an SPL Token account.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenAccountBalance',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - Pubkey of queried token account</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n            \"3Lz6rCrXdLybFiuJGJnEjv6Z2XtCh5n4proPGP2aBkA1\"\n        ]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 137567036\n                },\n                value: {\n                    amount: '301922375078',\n                    decimals: 6,\n                    uiAmount: 301922.375078,\n                    uiAmountString: '301922.375078'\n                }\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getTokenSupply',\n        value: 'getTokenSupply',\n        parentGroup: 'Token Information',\n        description: 'Returns the total supply of an SPL Token type.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenSupply',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - Pubkey of token Mint to query</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n            \"HfYFjMKNZygfMC8LsQ8LtpPsPxEJoXJx4M6tqi75Hajo\"\n        ]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 137571639\n                },\n                value: {\n                    amount: '999999999997060679',\n                    decimals: 9,\n                    uiAmount: 9.999999999970608e8,\n                    uiAmountString: '999999999.997060679'\n                }\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getEpochSchedule',\n        value: 'getEpochSchedule',\n        parentGroup: 'Network Information',\n        description: `Returns epoch schedule information from this cluster's genesis config.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getEpochSchedule',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getEpochInfo',\n        value: 'getEpochInfo',\n        parentGroup: 'Network Information',\n        description: `Returns information about the current epoch.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getEpochInfo',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getFeeForMessage',\n        value: 'getFeeForMessage',\n        parentGroup: 'Network Information',\n        description: `Get the fee the network will charge for a particular message.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFeeForMessage',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - encoded Message</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\"AQABAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAA\"]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 135143215\n                },\n                value: null\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getHighestSnapshotSlot',\n        value: 'getHighestSnapshotSlot',\n        parentGroup: 'Network Information',\n        description: `Returns the highest slot information that the node has snapshots for.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getHighestSnapshotSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getGenesisHash',\n        value: 'getGenesisHash',\n        parentGroup: 'Network Information',\n        description: `Returns the genesis hash.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getGenesisHash',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getRecentPerformanceSamples',\n        value: 'getRecentPerformanceSamples',\n        parentGroup: 'Network Information',\n        description: `Returns a list of recent performance samples, in reverse slot order. Performance samples are taken every 60 seconds and include the number of transactions and slots that occur in a given time window.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getRecentPerformanceSamples',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getFirstAvailableBlock',\n        value: 'getFirstAvailableBlock',\n        parentGroup: 'Network Information',\n        description: `Get the fee the network will charge for a particular message.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFirstAvailableBlock',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getMinimumBalanceForRentExemption',\n        value: 'getMinimumBalanceForRentExemption',\n        parentGroup: 'Network Information',\n        description: `Returns minimum balance required to make account rent exempt.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMinimumBalanceForRentExemption',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">usize</code> - account data length</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n            50\n        ]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: 1238880,\n            id: 1\n        }\n    },\n    {\n        name: 'getClusterNodes',\n        value: 'getClusterNodes',\n        parentGroup: 'Node Information',\n        description: `Returns information about all the nodes participating in the cluster.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getClusterNodes',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getHealth',\n        value: 'getHealth',\n        parentGroup: 'Node Information',\n        description: `Returns the current health of the node.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getHealth',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getVersion',\n        value: 'getVersion',\n        parentGroup: 'Node Information',\n        description: `Returns the current solana versions running on the node.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getVersion',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getIdentity',\n        value: 'getIdentity',\n        parentGroup: 'Node Information',\n        description: `Returns the identity pubkey for the current node.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getIdentity',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getInflationGovernor',\n        value: 'getInflationGovernor',\n        parentGroup: 'Network Inflation',\n        description: `Returns the current inflation governor.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getInflationGovernor',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getInflationReward',\n        value: 'getInflationReward',\n        parentGroup: 'Network Inflation',\n        description: `Returns the inflation / staking reward for a list of addresses for an epoch.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getInflationReward',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">array of base-58 encoded strings</code> - An array of addresses to query</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">epoch</code>: <code class=\"inline\">u64</code> - (optional) An epoch for which the reward occurs. If omitted, the previous epoch will be used</li>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  [\"9zyLN5CQwxWrEPit1sFy5DZ5xMqJfNQTLiqBVSfvPvWY\"],\n  {\n    \"commitment\": \"confirmed\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: 1238880,\n            id: 1\n        }\n    },\n    {\n        name: 'getInflationRate',\n        value: 'getInflationRate',\n        parentGroup: 'Network Inflation',\n        description: `Returns the specific inflation values for the current epoch.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getInflationRate',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getSupply',\n        value: 'getSupply',\n        parentGroup: 'Network Inflation',\n        description: `Returns information about the current supply.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSupply',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getBalance',\n        value: 'getBalance',\n        parentGroup: 'Account Information',\n        description: `Returns the balance of the account of provided Pubkey.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBalance',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - Pubkey of account to query</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">commitment</code>: <code class=\"inline\">string</code> - (optional) Configures the commitment level of the blocks queried. Accepts one of the following strings:</li>\n                <ul>\n                    <li><code class=\"inline\">finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n            \"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri\"\n        ]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: { context: { slot: 1 }, value: 0 },\n            id: 1\n        }\n    },\n    {\n        name: 'getLargestAccounts',\n        value: 'getLargestAccounts',\n        parentGroup: 'Account Information',\n        description: `Returns the 20 largest accounts, by lamport balance (results may be cached up to two hours).`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getLargestAccounts',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getAccountInfo',\n        value: 'getAccountInfo',\n        parentGroup: 'Account Information',\n        description: `Returns all information associated with the account of provided Pubkey.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getAccountInfo',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">base-58 encoded string</code> - Pubkey of account to query</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) data encoding for each returned transaction. Accepts one of the following strings: [\"json\" (Default), \"jsonParsed\", \"base58\" (slow), \"base64\"]</li>\n                <li><code class=\"inline\">dataSlice</code>: <code class=\"inline\">object</code> - (optional) limits the returned account data using the provided <code class=\"inline\">offset: usize</code> and <code class=\"inline\">length: usize</code> fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg\",\n  {\n    \"encoding\": \"base58\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 134461197\n                },\n                value: {\n                    data: ['', 'base58'],\n                    executable: false,\n                    lamports: 410431055,\n                    owner: '11111111111111111111111111111111',\n                    rentEpoch: 311\n                }\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getVoteAccounts',\n        value: 'getVoteAccounts',\n        parentGroup: 'Account Information',\n        description: `Returns the account info and associated stake for all the voting accounts in the current bank.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getVoteAccounts',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getMultipleAccounts',\n        value: 'getMultipleAccounts',\n        parentGroup: 'Account Information',\n        description: `Returns the account information for a list of Pubkeys.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMultipleAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">array of base-58 encoded strings</code> - An array of addresses to query</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) data encoding for each returned transaction. Accepts one of the following strings: [\"json\" (Default), \"jsonParsed\", \"base58\" (slow), \"base64\"]</li>\n                <li><code class=\"inline\">dataSlice</code>: <code class=\"inline\">object</code> - (optional) limits the returned account data using the provided <code class=\"inline\">offset: usize</code> and <code class=\"inline\">length: usize</code> fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  [\n    \"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg\",\n    \"4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA\"\n  ],\n  {\n    \"dataSlice\": {\n        \"offset\": 0,\n        \"length\": 0\n    }\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: {\n                context: {\n                    slot: 136100846\n                },\n                value: [\n                    {\n                        data: ['', 'base64'],\n                        executable: false,\n                        lamports: 410426055,\n                        owner: '11111111111111111111111111111111',\n                        rentEpoch: 314\n                    },\n                    {\n                        data: ['', 'base64'],\n                        executable: false,\n                        lamports: 2000000,\n                        owner: '11111111111111111111111111111111',\n                        rentEpoch: 314\n                    }\n                ]\n            },\n            id: 1\n        }\n    },\n    {\n        name: 'getProgramAccounts',\n        value: 'getProgramAccounts',\n        parentGroup: 'Account Information',\n        description: `Returns all accounts owned by the provided program Pubkey.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getProgramAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">base-58 encoded string</code> - Pubkey of program</li>\n            <li><code class=\"inline\">object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">encoding</code>: <code class=\"inline\">string</code> - (optional) data encoding for each returned transaction. Accepts one of the following strings: [\"json\" (Default), \"jsonParsed\", \"base58\" (slow), \"base64\"]</li>\n                <li><code class=\"inline\">dataSlice</code>: <code class=\"inline\">object</code> - (optional) limits the returned account data using the provided <code class=\"inline\">offset: usize</code> and <code class=\"inline\">length: usize</code> fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n                <li><code class=\"inline\">minContextSlot</code>: <code class=\"inline\">number</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n                <li><code class=\"inline\">withContext</code>: <code class=\"inline\">bool</code> - (optional) wrap the result in an RpcResponse JSON object.</li>\n                <li><code class=\"inline\">filters</code>: <code class=\"inline\">array</code> - (optional) filter results using various filter objects; account must meet all filter criteria to be included in results</li>\n                <ul>\n                    <li><code class=\"inline\">memcmp</code>: <code class=\"inline\">object</code> - (optional) compares a provided series of bytes with program account data at a particular offset. Fields:</li>\n                    <ul>\n                        <li><code class=\"inline\">offset</code>: <code class=\"inline\">usize</code> - (optional) offset into program account data to start comparison</li>\n                        <li><code class=\"inline\">bytes</code>: <code class=\"inline\">string</code> - (optional) data to match, as base-58 encoded string and limited to less than 129 bytes</li>\n                    </ul>\n                    <li><code class=\"inline\">dataSize</code>: <code class=\"inline\">u64</code> - (optional) compares the program account data length with the provided data size</li>\n                </ul>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"Stake11111111111111111111111111111111111111\",\n  {\n    \"encoding\": \"jsonParsed\",\n    \"commitment\": \"recent\",\n    \"filters\": [\n        {\n            \"memcmp\": {\n                \"offset\": 44,\n                \"bytes\": \"65qFmhCmDgXjg1duFdcpYyPheWyWGyusZhy3Y8khMoYm\"\n            }\n        }\n    ]\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            result: [\n                {\n                    account: {\n                        data: '2R9jLfiAQ9bgdcw6h8s44439',\n                        executable: false,\n                        lamports: 15298080,\n                        owner: '4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T',\n                        rentEpoch: 28\n                    },\n                    pubkey: 'CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY'\n                }\n            ],\n            id: 1\n        }\n    },\n    {\n        name: 'minimumLedgerSlot',\n        value: 'minimumLedgerSlot',\n        parentGroup: 'Slot Information',\n        description: `Returns the lowest slot that the node has information about in its ledger. This value may increase over time if the node is configured to purge older ledger data.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'minimumLedgerSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getMaxShredInsertSlot',\n        value: 'getMaxShredInsertSlot',\n        parentGroup: 'Slot Information',\n        description: `Get the max slot seen from after shred insert`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMaxShredInsertSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getSlotLeader',\n        value: 'getSlotLeader',\n        parentGroup: 'Slot Information',\n        description: `Returns the current slot leader.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSlotLeader',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getSlotLeaders',\n        value: 'getSlotLeaders',\n        parentGroup: 'Slot Information',\n        description: `Returns the slot leaders for a given slot range.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSlotLeader',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">array</code> - array with first item as start slot, second item as limit:</li>\n            <ul>\n                <li><code class=\"inline\">u64</code> - Start slot</li>\n                <li><code class=\"inline\">u64 </code> - Limit of the number of slot leaders in the response payload.</li>\n            </ul>\n        </ul>\n        `,\n        exampleParameters: `[\n            5,\n            10\n        ]`\n    },\n    {\n        name: 'getSlot',\n        value: 'getSlot',\n        parentGroup: 'Slot Information',\n        description: `Returns the slot that has reached the given or default commitment level.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'getMaxRetransmitSlot',\n        value: 'getMaxRetransmitSlot',\n        parentGroup: 'Slot Information',\n        description: `Get the max slot seen from retransmit stage.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMaxRetransmitSlot',\n            params: [],\n            id: 1\n        }\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/subscribeOperation.ts",
    "content": "import { NETWORK, NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport { alchemySupportedNetworks, IETHOperation } from '../../src/ETHOperations'\n\nexport const subsOperationsNetworks = [...alchemySupportedNetworks]\n\nexport const solanaOperationsNetworks = [NETWORK.SOLANA, NETWORK.SOLANA_DEVNET, NETWORK.SOLANA_TESTNET]\n\nexport const subscribeOperations = [\n    {\n        name: 'Eth Subscribe (eth_subscribe)',\n        value: 'eth_subscribe',\n        parentGroup: 'Subscribe',\n        description: 'Starts a subscription to a specific event.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: subsOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'eth_subscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subscription name</code>: <code class=\"inline\">string</code> - The type of event you want to subscribe to (i.e., newHeads, logs, pendingTransactions, newPendingTransactions). This method supports the following subscription types:</li>\n            <ul>\n\t\t\t    <li><code class=\"inline\">alchemy_pendingTransactions</code> - Returns full transactions that are sent to the network, marked as pending, and are sent from or to a certain address. (NOT supported on Arbitrum and Optimism)</li>\n\t\t\t    <li><code class=\"inline\">newPendingTransactions</code> - Returns the hash for all transactions that are added to the pending state and are signed with a key that is available in the node. (NOT supported on Arbitrum and Optimism)</li>\n\t\t\t    <li><code class=\"inline\">newHeads</code> - Fires a notification each time a new header is appended to the chain, including chain reorganizations.</li>\n\t\t\t    <li><code class=\"inline\">logs</code> - Returns logs that are included in new imported blocks and match the given filter criteria.</li>\n            </ul>\n            <li><code class=\"inline\">data</code>: <code class=\"inline\">object</code> - (Optional) - Arguments such as an address, multiple addresses, and topics. Note, only logs that are created from these addresses or match the specified topics will return logs.</li>\n        </ul>`,\n        exampleParameters: `[\"newHeads\"]`\n    },\n    {\n        name: 'Log Subscribe (logSubscribe)',\n        value: 'logSubscribe',\n        parentGroup: 'Subscribe',\n        description: '(Subscription Websocket) Subscribe to transaction logging.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'logSubscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">filters</code>: <code class=\"inline\">Object</code> - filter criteria for the logs to receive results by account type; currently supported mentions:</li>\n            <ul>\n                <li><code class=\"inline\">mentions</code> - subscribe to all transactions that mention the provided Pubkey (as base-58 encoded string), only accepts one account address</li>\n            </ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"mentions\": [ \"11111111111111111111111111111111\" ]\n  },\n  {\n    \"commitment\": \"finalized\"\n  }\n]`\n    },\n    {\n        name: 'Signature Subscribe (signatureSubscribe)',\n        value: 'signatureSubscribe',\n        parentGroup: 'Subscribe',\n        description:\n            '(Subscription Websocket) Subscribe to a transaction signature to receive notification when the transaction is confirmed On signatureNotification, the subscription is automatically cancelled.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'signatureSubscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">String</code> - Transaction Signature, as base-58 encoded string</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"51y9Hf2cFzrUPDH24qvL6b6PtPMDGQSX3WwiHsSvkdfGiFTKdoJwGkvqS3gny6XNPLtUtRwGERAs45639EfR5XfT\",\n  {\n    \"commitment\": \"finalized\"\n  }\n]`\n    },\n    {\n        name: 'Slot Subscribe (slotSubscribe)',\n        value: 'slotSubscribe',\n        parentGroup: 'Subscribe',\n        description: '(Subscription Websocket) Subscribe to receive notification anytime a slot is processed by the validator.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'slotSubscribe',\n            params: []\n        }\n    }\n] as IETHOperation[]\n\nexport const unsubscribeOperations = [\n    {\n        name: 'Eth Unsubscribe (eth_unsubscribe)',\n        value: 'eth_unsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: 'Cancels an existing subscription so that no further events are sent.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: subsOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'eth_unsubscribe',\n            params: []\n        }\n    },\n\n    {\n        name: 'Log Unsubscribe (logUnsubscribe)',\n        value: 'logUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from transaction logging.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'logUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Signature Unsubscribe (signatureUnsubscribe)',\n        value: 'signatureUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from signature confirmation notification.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'signatureUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Slot Unsubscribe (slotUnsubscribe)',\n        value: 'slotUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from slot notifications.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'slotUnsubscribe',\n            params: []\n        }\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/Alchemy/supportedNetwork.ts",
    "content": "import { INodeOptionsValue } from '../../src'\nimport { NETWORK, NETWORK_LABEL } from '../../src/ChainNetwork'\n\nexport const AlchemySupportedNetworks = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: NETWORK.MAINNET\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: NETWORK.GÖRLI\n    },\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: NETWORK.MATIC\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: NETWORK.MATIC_MUMBAI\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM,\n        name: NETWORK.ARBITRUM\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n        name: NETWORK.ARBITRUM_GOERLI\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM,\n        name: NETWORK.OPTIMISM\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM_GOERLI,\n        name: NETWORK.OPTIMISM_GOERLI\n    },\n    {\n        label: NETWORK_LABEL.SOLANA,\n        name: NETWORK.SOLANA\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_DEVNET,\n        name: NETWORK.SOLANA_DEVNET\n    }\n] as INodeOptionsValue[]\n"
  },
  {
    "path": "packages/components/nodes/Arbiscan/Arbiscan.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport { NETWORK, NETWORK_LABEL, etherscanAPIs } from '../../src/ChainNetwork'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport {\n    SORT_BY,\n    OPERATIONS,\n    GET_ETHER_BALANCE,\n    GET_MULTI_ETHER_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    CHECK_CONTRACT_EXECUTION_STATUS,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_BALANCE,\n    GET_ETHER_PRICE,\n    GET_ETHER_SUPPLY\n} from './constants'\nclass Arbiscan implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Arbiscan'\n        this.name = 'arbiscan'\n        this.icon = 'arbiscan.png'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Perform Arbiscan operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Ether Balance for a Single Address',\n                        name: GET_ETHER_BALANCE.name,\n                        description: 'Returns the Ether balance of a given address.'\n                    },\n                    {\n                        label: 'Get Ether Balance for Multiple Addresses(separated by a comma)',\n                        name: GET_MULTI_ETHER_BALANCE.name,\n                        description: 'Returns the Ether balance of the addresses(each address separated by a comma) entered.'\n                    },\n                    {\n                        label: 'Get transactions',\n                        name: GET_NORMAL_TRANSACTIONS.name,\n                        description: 'Returns the list of transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions',\n                        name: GET_INTERNAL_TRANSACTIONS.name,\n                        description: 'Returns the list of internal transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions by hash',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_HASH.name,\n                        description: 'Returns the list of internal transactions performed within a transaction.'\n                    },\n                    {\n                        label: 'Get internal transactions by block',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        description: 'Returns the list of internal transactions performed within a block range, with optional pagination.'\n                    },\n                    {\n                        label: 'Get Contract ABI',\n                        name: GET_ABI.name,\n                        description: 'Returns the contract Application Binary Interface ( ABI ) of a verified smart contract.'\n                    },\n                    {\n                        label: 'Get Contract Source Code',\n                        name: GET_CONTRACT_SOURCE_CODE.name,\n                        description: 'Returns the Solidity source code of a verified smart contract.'\n                    },\n                    {\n                        label: 'Check Transaction Receipt Status',\n                        name: CHECK_TRANSACTION_RECEIPT_STATUS.name,\n                        description: 'Returns the status code of a transaction execution.'\n                    },\n                    {\n                        label: 'Check Contract Execution Status',\n                        name: CHECK_CONTRACT_EXECUTION_STATUS.name,\n                        description: 'Returns the status code of a contract execution.'\n                    },\n                    {\n                        label: 'Get ERC20 Token Supply',\n                        name: GET_ERC20_TOKEN_SUPPLY.name,\n                        description: `Returns the total supply of a ERC-20 token. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get ERC20 Token Balance',\n                        name: GET_ERC20_TOKEN_BALANCE.name,\n                        description: `Returns the current balance of a ERC-20 token of an address. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get ETHER Price',\n                        name: GET_ETHER_PRICE.name,\n                        description: 'Returns the latest price of 1 ETHER.'\n                    },\n                    {\n                        label: 'Get Total Supply of Ether',\n                        name: GET_ETHER_SUPPLY.name,\n                        description: 'Returns the current amount of Ether in circulation.'\n                    }\n                ],\n\n                default: 'getEtherBalance'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: NETWORK_LABEL.ARBITRUM,\n                        name: NETWORK.ARBITRUM\n                    },\n                    {\n                        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n                        name: NETWORK.ARBITRUM_GOERLI\n                    }\n                ],\n                default: 'homestead'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Arbiscan API Key',\n                        name: 'arbiscanApi'\n                    }\n                ],\n                default: 'arbiscanApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Address',\n                name: 'address',\n                type: 'string',\n                description: 'The address parameter(s) required',\n                show: {\n                    'actions.api': [\n                        GET_ETHER_BALANCE.name,\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_ABI.name,\n                        GET_CONTRACT_SOURCE_CODE.name,\n                        GET_ERC20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Start Block',\n                name: 'startBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to start searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 0\n            },\n            {\n                label: 'End Block',\n                name: 'endBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to stop searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                }\n            },\n            {\n                label: 'Page',\n                name: 'page',\n                type: 'number',\n                optional: true,\n                description: 'the page number, if pagination is enabled',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 1\n            },\n            {\n                label: 'Offset',\n                name: 'offset',\n                type: 'number',\n                optional: true,\n                description: 'the number of transactions displayed per page',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 10\n            },\n            {\n                label: 'Sort By',\n                name: 'sortBy',\n                type: 'options',\n                optional: true,\n                options: SORT_BY,\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 'desc'\n            },\n            {\n                label: 'Transaction Hash',\n                name: 'txhash',\n                type: 'string',\n                description: 'the string representing the transaction hash to check for internal transactions',\n                show: {\n                    'actions.api': [\n                        GET_INTERNAL_TRANSACTIONS_BY_HASH.name,\n                        CHECK_TRANSACTION_RECEIPT_STATUS.name,\n                        CHECK_CONTRACT_EXECUTION_STATUS.name\n                    ]\n                }\n            },\n            {\n                label: 'Contract Address',\n                name: 'contractAddress',\n                type: 'string',\n                description: 'the contract address of the ERC-20 token',\n                show: {\n                    'actions.api': [GET_ERC20_TOKEN_SUPPLY.name, GET_ERC20_TOKEN_BALANCE.name]\n                }\n            },\n            {\n                label: 'Tag',\n                name: 'tag',\n                type: 'options',\n                options: [{ label: 'latest', name: 'latest' }],\n                default: 'latest',\n                show: {\n                    'actions.api': [GET_ERC20_TOKEN_BALANCE.name]\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    getNetwork(network: NETWORK): string {\n        return `${etherscanAPIs[network]}`\n    }\n\n    getBaseParams(api: string) {\n        const operation = OPERATIONS.filter(({ name }) => name === api)[0]\n        return { module: operation.module, action: operation.action }\n    }\n\n    getISODate(date: Date) {\n        return date.toISOString().split('T')[0]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const networksData = nodeData.networks\n        const inputParameters = nodeData.inputParameters\n        const credentials = nodeData.credentials\n        if (actionData === undefined || inputParameters === undefined || credentials === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        // GET api\n        const api = actionData.api as string\n        // GET network\n        const network = networksData.network as NETWORK\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n        // GET address\n        const address = inputParameters.address as string\n\n        const startblock = inputParameters.startBlock as number\n        const endblock = inputParameters.endBlock as number\n        const page = inputParameters.page as number\n        const offset = inputParameters.offset as number\n        const sort = inputParameters.sortBy as string\n        const txhash = inputParameters.txhash as string\n        const blocktype = inputParameters.blockType as string\n        const contractaddress = inputParameters.contractAddress as string\n        const tag = inputParameters.tag as string\n        const startTime = inputParameters.startTime as string\n        const endTime = inputParameters.endTime as string\n\n        const startdate = startTime ? this.getISODate(new Date(startTime)) : undefined\n        const enddate = endTime ? this.getISODate(new Date(endTime)) : undefined\n\n        const url = this.getNetwork(network)\n        const { module, action } = this.getBaseParams(api)\n\n        const queryParameters = {\n            module,\n            action,\n            address,\n            apiKey,\n            startblock,\n            endblock,\n            page,\n            offset,\n            sort,\n            txhash,\n            blocktype,\n            contractaddress,\n            tag,\n            startdate,\n            enddate\n        }\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Arbiscan }\n"
  },
  {
    "path": "packages/components/nodes/Arbiscan/constants.ts",
    "content": "// Account\nexport const GET_ETHER_BALANCE = {\n    name: 'getEtherBalance',\n    module: 'account',\n    action: 'balance'\n}\n\nexport const GET_MULTI_ETHER_BALANCE = {\n    name: 'getEtherBalanceMulti',\n    module: 'account',\n    action: 'balancemulti'\n}\n\nexport const GET_NORMAL_TRANSACTIONS = {\n    name: 'getTransactions',\n    module: 'account',\n    action: 'txlist'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS = {\n    name: 'getInternalTransactions',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_HASH = {\n    name: 'getInternalTransactionsByHash',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_BLOCK = {\n    name: 'getInternalTransactionsByBlock',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\n// Contracts\nexport const GET_ABI = {\n    name: 'getAbi',\n    module: 'contract',\n    action: 'getabi'\n}\n\nexport const GET_CONTRACT_SOURCE_CODE = {\n    name: 'getContractSourceCode',\n    module: 'contract',\n    action: 'getsourcecode'\n}\n\n// Transactions\nexport const CHECK_CONTRACT_EXECUTION_STATUS = {\n    name: 'getContractExecutionStatus',\n    module: 'transaction',\n    action: 'getstatus'\n}\n\nexport const CHECK_TRANSACTION_RECEIPT_STATUS = {\n    name: 'getTransactionReceiptStatus',\n    module: 'transaction',\n    action: 'gettxreceiptstatus'\n}\n\n// Tokens\nexport const GET_ERC20_TOKEN_SUPPLY = {\n    name: 'getErc20TokenSupply',\n    module: 'stats',\n    action: 'tokensupply'\n}\n\nexport const GET_ERC20_TOKEN_BALANCE = {\n    name: 'getErc20TokenBalance',\n    module: 'account',\n    action: 'tokenbalance'\n}\n\n// Stats\nexport const GET_ETHER_PRICE = {\n    name: 'getEtherPrice',\n    module: 'stats',\n    action: 'ethprice'\n}\nexport const GET_ETHER_SUPPLY = {\n    name: 'getEtherSupply',\n    module: 'stats',\n    action: 'ethsupply'\n}\n\nexport const OPERATIONS = [\n    GET_ETHER_BALANCE,\n    GET_MULTI_ETHER_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    CHECK_CONTRACT_EXECUTION_STATUS,\n\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_BALANCE,\n\n    GET_ETHER_PRICE,\n    GET_ETHER_SUPPLY\n] as const\n\nexport const SORT_BY = [\n    { label: 'Desc', name: 'desc' },\n    { label: 'Asc', name: 'asc' }\n]\n"
  },
  {
    "path": "packages/components/nodes/BEP20Function/BEP20Function.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    BSCNetworks,\n    chainIdLookup,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER\n} from '../../src'\nimport { ContractInterface, ethers } from 'ethers'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { IToken } from '../Uniswap/nativeTokens'\nimport {\n    allowanceParameters,\n    approveParameters,\n    balanceOfParameters,\n    BEP20Functions,\n    transferFromParameters,\n    withdrawParameters\n} from './helperFunctions'\nimport IBEP20 from '../../src/abis/WBNB.json'\nimport IWETH from '@uniswap/v2-periphery/build/IWETH.json'\nimport { depositParameters } from '../ERC20Function/helperFunctions'\n\nclass BEP20Function implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'BEP20 Function'\n        this.name = 'BEP20Function'\n        this.icon = 'bep20.png'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Execute BEP20 function such as deposit, withdraw, get balance, etc'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Function',\n                name: 'function',\n                type: 'options',\n                options: [...BEP20Functions]\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...BSCNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'BEP20 Token',\n                name: 'bep20Token',\n                type: 'asyncOptions',\n                description: 'BEP20 Token to send/transfer',\n                loadMethod: 'getTokens'\n            },\n            {\n                label: 'Custom BEP20 Address',\n                name: 'customBEP20TokenAddress',\n                type: 'string',\n                description: 'BEP20 Token Address',\n                show: {\n                    'inputParameters.bep20Token': ['customBEP20Address']\n                }\n            },\n            ...approveParameters,\n            ...allowanceParameters,\n            ...balanceOfParameters,\n            ...transferFromParameters,\n            ...depositParameters,\n            ...withdrawParameters,\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction',\n                show: {\n                    'actions.function': ['approve', 'transferFrom', 'deposit']\n                }\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>',\n                show: {\n                    'actions.function': ['approve', 'transferFrom', 'deposit']\n                }\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>',\n                show: {\n                    'actions.function': ['approve', 'transferFrom', 'deposit']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        },\n\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.pancakeswap.finance/pancakeswap-extended.json`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n\n                // Add custom token\n                const data = {\n                    label: `- Custom BEP20 Address -`,\n                    name: `customBEP20Address`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const actionsData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const BEP20Function = actionsData.function as string\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            const owner = inputParametersData.owner as string\n            const spender = inputParametersData.spender as string\n            const amount = inputParametersData.amount as string\n            const account = inputParametersData.account as string\n            const from = inputParametersData.from as string\n            const to = inputParametersData.to as string\n\n            const bep20Token = inputParametersData.bep20Token as string\n            const customBEP20TokenAddress = inputParametersData.customBEP20TokenAddress as string\n\n            const contractAddress = bep20Token === 'customBEP20Address' ? customBEP20TokenAddress : bep20Token.split(';')[0]\n\n            const contractInstance = new ethers.Contract(contractAddress, IBEP20, provider)\n            const decimals =\n                bep20Token === 'customBEP20Address'\n                    ? parseInt(await contractInstance.decimals(), 10)\n                    : parseInt(bep20Token.split(';').pop() || '0', 10)\n\n            let returnItem = { function: BEP20Function, link: `${networkExplorers[network]}/address/${contractAddress}` } as any\n\n            if (BEP20Function === 'allowance') {\n                // allowance(address owner, address spender) → uint256\n                returnItem.result = await contractInstance.allowance(owner, spender)\n            } else if (BEP20Function === 'approve') {\n                // approve(address spender, uint256 amount) → bool\n                const { txOption, wallet } = await getWalletSigner(inputParametersData, provider)\n                const functionApproveAbi = ['function approve(address spender, uint256 amount) external returns (boolean)']\n                const contractInstance = new ethers.Contract(contractAddress, functionApproveAbi, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await contractInstance.approve(spender, numberOfTokens, txOption)\n                const txReceipt = await tx.wait()\n                returnItem = {\n                    function: BEP20Function,\n                    spender,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            } else if (BEP20Function === 'balanceOf') {\n                // balanceOf(address account) → uint256\n                returnItem.result = ethers.utils.formatEther(await contractInstance.balanceOf(account))\n            } else if (BEP20Function === 'decimals') {\n                // decimals() → uint8\n                returnItem.result = await contractInstance.decimals()\n            } else if (BEP20Function === 'name') {\n                // name() → string\n                returnItem.result = await contractInstance.name()\n            } else if (BEP20Function === 'symbol') {\n                // symbol() → string\n                returnItem.result = await contractInstance.symbol()\n            } else if (BEP20Function === 'totalSupply') {\n                // totalSupply() → uint256\n                returnItem.result = ethers.utils.formatEther(await contractInstance.totalSupply())\n            } else if (BEP20Function === 'transferFrom') {\n                // transferFrom(address sender, address recipient, uint256 amount) → bool\n                const { txOption, wallet } = await getWalletSigner(inputParametersData, provider)\n                const functionTransferFromAbi = [\n                    'function transferFrom(address sender, address recipient, uint256 amount) external returns (boolean)'\n                ]\n                const contractInstance = new ethers.Contract(contractAddress, functionTransferFromAbi, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await contractInstance.transferFrom(from, to, numberOfTokens, txOption)\n                const txReceipt = await tx.wait()\n                returnItem = {\n                    function: BEP20Function,\n                    transferFrom: from,\n                    transferTo: to,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            } else if (BEP20Function === 'deposit') {\n                const { txOption, wallet } = await getWalletSigner(inputParametersData, provider)\n                const wrapEthContract = new ethers.Contract(contractAddress, IWETH['abi'] as ContractInterface, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await wrapEthContract.deposit({ ...txOption, value: numberOfTokens })\n                const txReceipt = await tx.wait()\n                if (txReceipt.status === 0) throw new Error(`Failed to deposit BNB to ${contractAddress}`)\n                returnItem = {\n                    function: BEP20Function,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            } else if (BEP20Function === 'withdraw') {\n                const { wallet } = await getWalletSigner(inputParametersData, provider)\n                const wrapEthContract = new ethers.Contract(contractAddress, IWETH['abi'] as ContractInterface, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await wrapEthContract.withdraw(numberOfTokens)\n                const txReceipt = await tx.wait()\n                if (txReceipt.status === 0) throw new Error(`Failed to withdraw BNB from ${contractAddress}`)\n                returnItem = {\n                    function: BEP20Function,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nconst getWalletSigner = async (\n    inputParametersData: ICommonObject,\n    provider: ethers.providers.JsonRpcProvider | ethers.providers.FallbackProvider\n) => {\n    const walletString = inputParametersData.wallet as string\n    const walletDetails: IWallet = JSON.parse(walletString)\n    const walletCredential = JSON.parse(walletDetails.walletCredential)\n    const gasLimit = inputParametersData.gasLimit as number\n    const maxFeePerGas = inputParametersData.maxFeePerGas as number\n    const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n    const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n    const txOption = {} as any\n    txOption.nonce = await provider.getTransactionCount(walletDetails.address)\n    if (gasLimit) txOption.gasLimit = gasLimit\n    if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n    if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n    return { txOption, wallet }\n}\n\nmodule.exports = { nodeClass: BEP20Function }\n"
  },
  {
    "path": "packages/components/nodes/BEP20Function/helperFunctions.ts",
    "content": "import { INodeOptionsValue, INodeParams } from '../../src'\n\nexport const BEP20Functions = [\n    {\n        name: 'deposit',\n        label: 'Deposit',\n        description: 'Deposit BNB into BEP20 token'\n    },\n    {\n        name: 'withdraw',\n        label: 'Withdraw',\n        description: 'Withdraw BNB from BEP20 token'\n    },\n    {\n        name: 'allowance',\n        label: 'Get Allowance',\n        description:\n            'Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom. This is zero by default.'\n    },\n    {\n        name: 'approve',\n        label: 'Approve',\n        description: 'Sets amount as the allowance of spender over the caller’s tokens.'\n    },\n    {\n        name: 'balanceOf',\n        label: 'Get Balance',\n        description: 'Returns the amount of tokens owned by account'\n    },\n    {\n        name: 'decimals',\n        label: 'Get BEP20 Decimals',\n        description: 'Returns the decimals of BEP20'\n    },\n    {\n        name: 'name',\n        label: 'Get BEP20 Name',\n        description: 'Returns the name of BEP20'\n    },\n    {\n        name: 'symbol',\n        label: 'Get BEP20 Symbol',\n        description: 'Returns the symbol of BEP20'\n    },\n    {\n        name: 'totalSupply',\n        label: 'Get BEP20 Total Supply',\n        description: 'Returns the total supply of BEP20'\n    },\n    {\n        name: 'transferFrom',\n        label: 'Transfer From',\n        description:\n            'Moves amount tokens from sender to recipient using the allowance mechanism. Amount is then deducted from the caller’s allowance.'\n    }\n] as INodeOptionsValue[]\n\nexport const allowanceParameters = [\n    {\n        label: 'Owner Address',\n        name: 'owner',\n        type: 'string',\n        show: {\n            'actions.function': ['allowance']\n        }\n    },\n    {\n        label: 'Spender Address',\n        name: 'spender',\n        type: 'string',\n        show: {\n            'actions.function': ['allowance']\n        }\n    }\n] as INodeParams[]\n\nexport const approveParameters = [\n    {\n        label: 'Spender Address',\n        name: 'spender',\n        type: 'string',\n        show: {\n            'actions.function': ['approve']\n        }\n    },\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        show: {\n            'actions.function': ['approve']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Wallet to execute approve function',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['approve']\n        }\n    }\n] as INodeParams[]\n\nexport const balanceOfParameters = [\n    {\n        label: 'Account Address',\n        name: 'account',\n        type: 'string',\n        description: 'Account address to check for remaining amount',\n        show: {\n            'actions.function': ['balanceOf']\n        }\n    }\n] as INodeParams[]\n\nexport const transferFromParameters = [\n    {\n        label: 'From Address',\n        name: 'from',\n        type: 'string',\n        description: 'Account address to transfer the token',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    },\n    {\n        label: 'To Address',\n        name: 'to',\n        type: 'string',\n        description: 'Account address to receive the token',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    },\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        description: 'Amount of token transfer',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Wallet to move amount tokens from sender to recipient using the allowance mechanism',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    }\n] as INodeParams[]\n\nexport const depositParameters = [\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        description: 'Amount of token to deposit',\n        show: {\n            'actions.function': ['deposit']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Wallet to deposit ETH into ERC20 Token',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['deposit']\n        }\n    }\n] as INodeParams[]\n\nexport const withdrawParameters = [\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        description: 'Amount of token to withdraw',\n        show: {\n            'actions.function': ['withdraw']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Withdraw ERC20 Token to this wallet',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['withdraw']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/BEP20Transfer/BEP20Transfer.ts",
    "content": "import { IDbCollection, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, IWallet, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    BSCNetworks,\n    chainIdLookup,\n    functionTransferAbi,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER\n} from '../../src'\nimport { ethers } from 'ethers'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { IToken } from '../Uniswap/nativeTokens'\nimport IBEP20 from '../../src/abis/WBNB.json'\n\nclass BEP20Transfer implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'BEP20 Transfer'\n        this.name = 'BEP20Transfer'\n        this.icon = 'bep20.png'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Send/Transfer BEP20 to an address'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...BSCNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'BEP20 Token',\n                name: 'bep20Token',\n                type: 'asyncOptions',\n                description: 'BEP20 Token to send/transfer',\n                loadMethod: 'getTokens'\n            },\n            {\n                label: 'Custom BEP20 Address',\n                name: 'customBEP20TokenAddress',\n                type: 'string',\n                description: 'BEP20 Token Address',\n                show: {\n                    'inputParameters.bep20Token': ['customBEP20Address']\n                }\n            },\n            {\n                label: 'Wallet To Transfer',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to send/transfer BEP20',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Address To Receive',\n                name: 'address',\n                type: 'string',\n                default: '',\n                description: 'Address to receive BEP20'\n            },\n            {\n                label: 'Amount',\n                name: 'amount',\n                type: 'number',\n                description: 'Amount of BEP20 to transfer'\n            },\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction'\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        },\n\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.pancakeswap.finance/pancakeswap-extended.json`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n\n                // Add custom token\n                const data = {\n                    label: `- Custom BEP20 Address -`,\n                    name: `customBEP20Address`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n            const address = inputParametersData.address as string\n            const amount = inputParametersData.amount as string\n            const bep20Token = inputParametersData.bep20Token as string\n            const customBEP20TokenAddress = inputParametersData.customBEP20TokenAddress as string\n            const gasLimit = inputParametersData.gasLimit as number\n            const maxFeePerGas = inputParametersData.maxFeePerGas as number\n            const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n            const contractAddress = bep20Token === 'customBEP20Address' ? customBEP20TokenAddress : bep20Token.split(';')[0]\n            const contractInstance = new ethers.Contract(contractAddress, IBEP20, wallet)\n            const decimals =\n                bep20Token === 'customBEP20Address'\n                    ? parseInt(await contractInstance.decimals(), 10)\n                    : parseInt(bep20Token.split(';').pop() || '0', 10)\n            const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n\n            // Send token\n            const nonce = await provider.getTransactionCount(walletDetails.address)\n            const txOption = {\n                nonce\n            } as any\n\n            if (gasLimit) txOption.gasLimit = gasLimit\n            if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n            if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n            const contractInstanceForTransfer = new ethers.Contract(contractAddress, functionTransferAbi, wallet)\n            const tx = await contractInstanceForTransfer.transfer(address, numberOfTokens, txOption)\n            const txReceipt = await tx.wait()\n\n            const returnItem = {\n                transferFrom: wallet.address,\n                transferTo: address,\n                amount,\n                transactionHash: tx.hash,\n                transactionReceipt: txReceipt as any,\n                link: `${networkExplorers[network]}/tx/${tx.hash}`\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: BEP20Transfer }\n"
  },
  {
    "path": "packages/components/nodes/BEP20TransferTrigger/BEP20TransferTrigger.ts",
    "content": "import { ethers, utils } from 'ethers'\nimport { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    networkExplorers,\n    BSCNetworks,\n    binanceNetworkProviders,\n    NETWORK_PROVIDER,\n    NETWORK,\n    getNetworkProvider,\n    networkProviderCredentials,\n    chainIdLookup\n} from '../../src/ChainNetwork'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { IToken } from '../PancakeSwap/extendedTokens'\nimport IBEP20 from '../../src/abis/WBNB.json'\n\nclass BEP20TransferTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'BEP20 Transfer Trigger'\n        this.name = 'BEP20TransferTrigger'\n        this.icon = 'bep20.png'\n        this.type = 'trigger'\n        this.category = 'Cryptocurrency'\n        this.version = 1.1\n        this.description = 'Triggers whenever a BEP20 transfer event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...BSCNetworks],\n                default: 'bsc'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'options',\n                options: [...binanceNetworkProviders],\n                default: 'binance'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'BEP20 Token',\n                name: 'bep20Token',\n                type: 'asyncOptions',\n                description: 'BEP20 Token to send/transfer',\n                loadMethod: 'getTokens',\n                default: 'anyBEP20Address'\n            },\n            {\n                label: 'Custom BEP20 Address',\n                name: 'customBEP20TokenAddress',\n                type: 'string',\n                description: 'BEP20 Token Address',\n                show: {\n                    'inputParameters.bep20Token': ['customBEP20Address']\n                }\n            },\n            {\n                label: 'Direction',\n                name: 'direction',\n                type: 'options',\n                options: [\n                    {\n                        label: 'From',\n                        name: 'from',\n                        description: 'Transfer from wallet address'\n                    },\n                    {\n                        label: 'To',\n                        name: 'to',\n                        description: 'Transfer to wallet address'\n                    },\n                    {\n                        label: 'Both From and To',\n                        name: 'fromTo',\n                        description: 'Transfer from a wallet address to another wallet address'\n                    }\n                ],\n                default: ''\n            },\n            {\n                label: 'From Wallet Address',\n                name: 'fromAddress',\n                type: 'string',\n                default: '',\n                show: {\n                    'inputParameters.direction': ['from', 'fromTo']\n                }\n            },\n            {\n                label: 'To Wallet Address',\n                name: 'toAddress',\n                type: 'string',\n                default: '',\n                show: {\n                    'inputParameters.direction': ['to', 'fromTo']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.pancakeswap.finance/pancakeswap-extended.json`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n\n                // Add any token\n                const anyData = {\n                    label: `- Any BEP20 Token -`,\n                    name: `anyBEP20Address`\n                } as INodeOptionsValue\n                returnData.push(anyData)\n\n                // Add custom token\n                const data = {\n                    label: `- Custom BEP20 Address -`,\n                    name: `customBEP20Address`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const fromAddress = (inputParametersData.fromAddress as string) || null\n        const toAddress = (inputParametersData.toAddress as string) || null\n        const bep20Token = inputParametersData.bep20Token as string\n        const customBEP20TokenAddress = inputParametersData.customBEP20TokenAddress as string\n\n        const filter = {\n            topics: [\n                utils.id('Transfer(address,address,uint256)'),\n                fromAddress ? utils.hexZeroPad(fromAddress, 32) : null,\n                toAddress ? utils.hexZeroPad(toAddress, 32) : null\n            ]\n        } as any\n\n        if (bep20Token !== 'anyBEP20Address') {\n            filter['address'] = bep20Token === 'customBEP20Address' ? customBEP20TokenAddress : bep20Token.split(';')[0]\n        }\n\n        provider.on(filter, async (log: any) => {\n            const txHash = log.transactionHash\n            const contractInstance = new ethers.Contract(log.address, IBEP20, provider)\n\n            /* events are empty at the moment\n\t\t\tconst iface = new ethers.utils.Interface(eventTransferAbi);\n\t\t\tconst logs = await provider.getLogs(filter);\n\t\t\tconst events = logs.map((log) => iface.parseLog(log));\n            const fromWallet = events.length ? events[0].args[0] : '';\n\t\t\tconst toWallet = events.length ? events[0].args[1] : '';\n\t\t\tconst value: BigNumber = events.length ? events[0].args[2] : '';\n\t\t\t*/\n\n            //BEP20 has 3 topics length\n            if (log.topics.length === 3) {\n                const returnItem = {} as ICommonObject\n\n                const name = await contractInstance.name()\n                const symbol = await contractInstance.symbol()\n                //const decimals = await contractInstance.decimals();\n                //const amount = utils.formatUnits(value.toString(), decimals);\n\n                returnItem['Token Name'] = name\n                returnItem['Token Symbol'] = symbol\n                returnItem['Token Address'] = log.address\n                if (fromAddress) returnItem['From Wallet'] = fromAddress\n                if (toAddress) returnItem['To Wallet'] = toAddress\n                //returnItem['Amount Transfered'] = parseFloat(amount);\n                returnItem['txHash'] = txHash\n                returnItem['explorerLink'] = `${networkExplorers[network]}/tx/${txHash}`\n\n                this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n            }\n        })\n\n        this.providers[emitEventKey] = { provider, filter }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            const filter = this.providers[emitEventKey].filter\n            provider.off(filter)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: BEP20TransferTrigger }\n"
  },
  {
    "path": "packages/components/nodes/BNBBalanceTrigger/BNBBalanceTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { BigNumber, utils } from 'ethers'\nimport { ICronJobs, INode, INodeData, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    binanceNetworkProviders,\n    BSCNetworks,\n    getNetworkProvider,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER\n} from '../../src/ChainNetwork'\n\nclass BNBBalanceTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'BNB Balance Trigger'\n        this.name = 'BNBBalanceTrigger'\n        this.icon = 'bnb.svg'\n        this.type = 'trigger'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Triggers whenever BNB balance in wallet changes'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...BSCNetworks],\n                default: 'bsc'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'options',\n                options: [...binanceNetworkProviders],\n                default: 'binance'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet Address',\n                name: 'address',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Trigger Condition',\n                name: 'triggerCondition',\n                type: 'options',\n                options: [\n                    {\n                        label: 'When balance increased',\n                        name: 'increase'\n                    },\n                    {\n                        label: 'When balance decreased',\n                        name: 'decrease'\n                    }\n                ],\n                default: 'increase'\n            },\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const address = (inputParametersData.address as string) || ''\n        const pollTime = (inputParametersData.pollTime as string) || '30s'\n        const triggerCondition = (inputParametersData.triggerCondition as string) || 'increase'\n\n        const cronTimes: string[] = []\n\n        if (pollTime === '15s') {\n            cronTimes.push(`*/15 * * * * *`)\n        } else if (pollTime === '30s') {\n            cronTimes.push(`*/30 * * * * *`)\n        } else if (pollTime === '1min') {\n            cronTimes.push(`*/1 * * * *`)\n        } else if (pollTime === '5min') {\n            cronTimes.push(`*/5 * * * *`)\n        } else if (pollTime === '10min') {\n            cronTimes.push(`*/10 * * * *`)\n        }\n\n        let lastBalance: BigNumber = await provider.getBalance(address)\n\n        const executeTrigger = async () => {\n            const newBalance: BigNumber = await provider.getBalance(address)\n\n            if (!newBalance.eq(lastBalance)) {\n                if (triggerCondition === 'increase' && newBalance.gt(lastBalance)) {\n                    const balanceInBNB = utils.formatEther(BigNumber.from(newBalance.toString()))\n                    const diffInBNB = newBalance.sub(lastBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInBNB} BNB`,\n                        lastBalance: `${utils.formatEther(BigNumber.from(lastBalance.toString()))} BNB`,\n                        difference: `${utils.formatEther(BigNumber.from(diffInBNB.toString()))} BNB`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'BNB balance increase'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else if (triggerCondition === 'decrease' && newBalance.lt(lastBalance)) {\n                    const balanceInBNB = utils.formatEther(BigNumber.from(newBalance.toString()))\n                    const diffInBNB = lastBalance.sub(newBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInBNB} BNB`,\n                        lastBalance: `${utils.formatEther(BigNumber.from(lastBalance.toString()))} BNB`,\n                        difference: `${utils.formatEther(BigNumber.from(diffInBNB.toString()))} BNB`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'BNB balance decrease'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else {\n                    lastBalance = newBalance\n                }\n            }\n        }\n\n        // Start the cron-jobs\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n            }\n        } else {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n            }\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: BNBBalanceTrigger }\n"
  },
  {
    "path": "packages/components/nodes/BNBTransfer/BNBTransfer.ts",
    "content": "import { IDbCollection, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, IWallet, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    BSCNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER\n} from '../../src'\nimport { ethers } from 'ethers'\n\nclass BNBTransfer implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'BNB Transfer'\n        this.name = 'BNBTransfer'\n        this.icon = 'bnb.svg'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Send/Transfer BNB to an address'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...BSCNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet To Transfer',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to send/transfer BNB',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Address To Receive',\n                name: 'address',\n                type: 'string',\n                default: '',\n                description: 'Address to receive BNB'\n            },\n            {\n                label: 'Amount',\n                name: 'amount',\n                type: 'number',\n                description: 'Amount of BNB to transfer'\n            },\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction'\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n            const address = inputParametersData.address as string\n            const amount = inputParametersData.amount as string\n            const gasLimit = inputParametersData.gasLimit as number\n            const maxFeePerGas = inputParametersData.maxFeePerGas as number\n            const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n            // Send token\n            const nonce = await provider.getTransactionCount(walletDetails.address)\n            const txOption = {\n                nonce\n            } as any\n\n            if (gasLimit) txOption.gasLimit = gasLimit\n            if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n            if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n            const tx = await wallet.sendTransaction({\n                to: address,\n                value: ethers.utils.parseEther(amount),\n                ...txOption\n            })\n\n            const txReceipt = await tx.wait()\n\n            const returnItem = {\n                transferFrom: wallet.address,\n                transferTo: address,\n                amount,\n                transactionHash: tx.hash,\n                transactionReceipt: txReceipt as any,\n                link: `${networkExplorers[network]}/tx/${tx.hash}`\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: BNBTransfer }\n"
  },
  {
    "path": "packages/components/nodes/Binance/BinancePrivate.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { notEmptyRegex, handleErrorMessage, numberOrExpressionRegex, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\n\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport { createHmac } from 'crypto'\n\nclass BinancePrivate implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Binance Private'\n        this.name = 'binancePrivate'\n        this.icon = 'binance-logo.svg'\n        this.type = 'action'\n        this.category = 'Centralized Exchange'\n        this.version = 1.0\n        this.description = 'Binance Private APIs that require API and Secret keys'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                description: 'Network to execute API: Test or Live',\n                options: [\n                    {\n                        label: 'TEST',\n                        name: 'test',\n                        description: 'Test network: https://testnet.binance.vision/'\n                    },\n                    {\n                        label: 'LIVE',\n                        name: 'live',\n                        description: 'Live network: https://api.binance.com/'\n                    }\n                ],\n                default: 'test'\n            },\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Send New Order',\n                        name: 'postNewOrder',\n                        description: 'Send in a new order.'\n                    },\n                    {\n                        label: 'Test New Order',\n                        name: 'postTestNewOrder',\n                        description:\n                            'Test new order creation. Creates and validates a new order but does not send it into the matching engine.'\n                    },\n                    {\n                        label: 'Query Order',\n                        name: 'getOrder',\n                        description: `Check an order's status.`\n                    },\n                    {\n                        label: 'Cancel Order',\n                        name: 'delOrder',\n                        description: `Cancel an active order.`\n                    },\n                    {\n                        label: 'Cancel All Open Orders on a Symbol',\n                        name: 'delOpenOrders',\n                        description: `Cancels all active orders on a symbol. This includes OCO orders.`\n                    },\n                    {\n                        label: 'Query Current Open Orders',\n                        name: 'getOpenOrders',\n                        description: `Get all open orders on a symbol.`\n                    },\n                    {\n                        label: 'Query All Orders',\n                        name: 'getAllOrders',\n                        description: `Get all account orders; active, canceled, or filled.`\n                    },\n                    {\n                        label: 'Query Account Information',\n                        name: 'getAccountInformation',\n                        description: `Get current account information.`\n                    },\n                    {\n                        label: 'Query Account Trade List',\n                        name: 'getMyTrades',\n                        description: `Get trades for a specific account and symbol.`\n                    },\n                    {\n                        label: 'Query Current Order Count Usage',\n                        name: 'getOrderCountUsage',\n                        description: `Displays the user's current order count usage for all intervals.`\n                    }\n                ],\n                default: 'getAccountInformation'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Binance API Key',\n                        name: 'binanceApi'\n                    }\n                ],\n                default: 'binanceApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Symbol',\n                name: 'symbol',\n                type: 'asyncOptions',\n                loadMethod: 'getSupportedSymbols',\n                show: {\n                    'actions.operation': [\n                        'postNewOrder',\n                        'postTestNewOrder',\n                        'getOrder',\n                        'delOrder',\n                        'delOpenOrders',\n                        'getOpenOrders',\n                        'getAllOrders',\n                        'getMyTrades'\n                    ]\n                }\n            },\n            {\n                label: 'Order Side',\n                name: 'side',\n                type: 'options',\n                options: [\n                    {\n                        label: 'BUY',\n                        name: 'BUY'\n                    },\n                    {\n                        label: 'SELL',\n                        name: 'SELL'\n                    }\n                ],\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Order Type',\n                name: 'type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'LIMIT',\n                        name: 'LIMIT'\n                    },\n                    {\n                        label: 'MARKET',\n                        name: 'MARKET'\n                    },\n                    {\n                        label: 'STOP_LOSS',\n                        name: 'STOP_LOSS'\n                    },\n                    {\n                        label: 'STOP_LOSS_LIMIT',\n                        name: 'STOP_LOSS_LIMIT'\n                    },\n                    {\n                        label: 'TAKE_PROFIT',\n                        name: 'TAKE_PROFIT'\n                    },\n                    {\n                        label: 'TAKE_PROFIT_LIMIT',\n                        name: 'TAKE_PROFIT_LIMIT'\n                    },\n                    {\n                        label: 'LIMIT_MAKER',\n                        name: 'LIMIT_MAKER'\n                    }\n                ],\n                default: 'LIMIT',\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Time in Force',\n                name: 'timeInForce',\n                type: 'options',\n                description: 'This sets how long an order will be active before expiration.',\n                options: [\n                    {\n                        label: 'Good Til Canceled',\n                        name: 'GTC',\n                        description: 'An order will be on the book unless the order is canceled.'\n                    },\n                    {\n                        label: 'Immediate Or Cancel',\n                        name: 'IOC',\n                        description: 'An order will try to fill the order as much as it can before the order expires.'\n                    },\n                    {\n                        label: 'Fill or Kill',\n                        name: 'FOK',\n                        description: 'An order will expire if the full order cannot be filled upon execution.'\n                    }\n                ],\n                optional: {\n                    'inputParameters.type': ['MARKET', 'STOP_LOSS', 'TAKE_PROFIT', 'LIMIT_MAKER']\n                },\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Quantity',\n                name: 'quantity',\n                type: 'number',\n                description: 'For MARKET order type, Quantity or Quote Order Quantity is mandatory.',\n                optional: {\n                    'inputParameters.type': ['MARKET'],\n                    'inputParameters.quoteOrderQty': numberOrExpressionRegex\n                },\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Quote Order Quantity',\n                name: 'quoteOrderQty',\n                type: 'number',\n                description:\n                    'Specifies the amount the user wants to spend (when buying) or receive (when selling). For MARKET order type, Quantity or Quote Order Quantity is mandatory.',\n                optional: {\n                    'inputParameters.quantity': numberOrExpressionRegex\n                },\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder'],\n                    'inputParameters.type': ['MARKET']\n                }\n            },\n            {\n                label: 'Price',\n                name: 'price',\n                type: 'number',\n                optional: {\n                    'inputParameters.type': ['MARKET', 'STOP_LOSS', 'TAKE_PROFIT']\n                },\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'New Client Order Id',\n                name: 'newClientOrderId',\n                description:\n                    'A unique id among open orders. Automatically generated if not sent. Orders with the same newClientOrderID can be accepted only when the previous one is filled, otherwise the order will be rejected.',\n                type: 'string',\n                optional: true,\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Stop Price',\n                name: 'stopPrice',\n                type: 'number',\n                description:\n                    'For [STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT] order type, Stop Price or Trailing Delta is mandatory.',\n                optional: {\n                    'inputParameters.trailingDelta': numberOrExpressionRegex\n                },\n                show: {\n                    'inputParameters.type': ['STOP_LOSS', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT', 'TAKE_PROFIT_LIMIT'],\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Trailing Delta',\n                name: 'trailingDelta',\n                type: 'number',\n                description:\n                    'For [STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT] order type, Stop Price or Trailing Delta is mandatory.',\n                optional: {\n                    'inputParameters.stopPrice': numberOrExpressionRegex\n                },\n                show: {\n                    'inputParameters.type': ['STOP_LOSS', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT', 'TAKE_PROFIT_LIMIT'],\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Iceberg Quantity',\n                name: 'icebergQty',\n                type: 'number',\n                optional: true,\n                show: {\n                    'inputParameters.type': ['LIMIT', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT_LIMIT'],\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'New Order Response Type',\n                name: 'newOrderRespType',\n                type: 'options',\n                description: 'Set the response JSON. MARKET and LIMIT order types default to FULL, all other orders default to ACK.',\n                options: [\n                    {\n                        label: 'ACK',\n                        name: 'ACK'\n                    },\n                    {\n                        label: 'RESULT',\n                        name: 'RESULT'\n                    },\n                    {\n                        label: 'FULL',\n                        name: 'FULL'\n                    }\n                ],\n                optional: true,\n                show: {\n                    'actions.operation': ['postNewOrder', 'postTestNewOrder']\n                }\n            },\n            {\n                label: 'Order Id',\n                name: 'orderId',\n                type: 'number',\n                description: 'Order Id or Orig Client Order Id is mandatory.',\n                optional: {\n                    'inputParameters.origClientOrderId': notEmptyRegex\n                },\n                show: {\n                    'actions.operation': ['getOrder', 'delOrder']\n                }\n            },\n            {\n                label: 'Orig Client Order Id',\n                name: 'origClientOrderId',\n                type: 'string',\n                description: 'Order Id or Orig Client Order Id is mandatory.',\n                optional: {\n                    'inputParameters.orderId': numberOrExpressionRegex\n                },\n                show: {\n                    'actions.operation': ['getOrder', 'delOrder']\n                }\n            },\n            {\n                label: 'Order Id',\n                name: 'orderId',\n                type: 'number',\n                description:\n                    'Get orders >= Order Id. Otherwise most recent orders are returned. Not needed if Start Time and/or End Time provided.',\n                optional: true,\n                show: {\n                    'actions.operation': ['getAllOrders', 'getMyTrades']\n                }\n            },\n            {\n                label: 'Start Time',\n                name: 'startTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.operation': ['getAllOrders', 'getMyTrades']\n                }\n            },\n            {\n                label: 'End Time',\n                name: 'endTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.operation': ['getAllOrders', 'getMyTrades']\n                }\n            },\n            {\n                label: 'From Id',\n                name: 'fromId',\n                type: 'number',\n                optional: true,\n                description: 'TradeId to fetch from. Default gets most recent trades.',\n                show: {\n                    'actions.operation': ['getMyTrades']\n                }\n            },\n            {\n                label: 'Limit',\n                name: 'limit',\n                type: 'number',\n                default: 500,\n                optional: true,\n                show: {\n                    'actions.operation': ['getAllOrders', 'getMyTrades']\n                }\n            }\n        ]\n    }\n\n    loadMethods = {\n        async getSupportedSymbols(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionData = nodeData.actions\n\n            let apiUrl = ''\n            if (actionData !== undefined && (actionData.network as string) === 'test') {\n                apiUrl = 'https://testnet.binance.vision/api'\n            } else {\n                apiUrl = 'https://api.binance.com/api'\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `${apiUrl}/v3/exchangeInfo`,\n                headers: {\n                    'Content-Type': 'application/json'\n                }\n            }\n\n            const response = await axios(axiosConfig)\n            const responseData = response.data\n            for (const s of responseData['symbols']) {\n                returnData.push({\n                    label: s.symbol,\n                    name: s.symbol\n                })\n            }\n\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = actionData.network as string\n        const operation = actionData.operation as string\n        const apiKey = credentials.apiKey as string\n        const secretKey = credentials.secretKey as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any // tslint:disable-line: no-any\n\n        let apiUrl = ''\n        if (network === 'test') {\n            apiUrl = 'https://testnet.binance.vision/api'\n        } else if (network === 'live') {\n            apiUrl = 'https://api.binance.com/api'\n        }\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        const queryBody: ICommonObject = {}\n        let method: Method = 'GET'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json'\n        }\n\n        try {\n            if (operation === 'postNewOrder' || operation === 'postTestNewOrder') {\n                const symbol = inputParametersData.symbol as string\n                const side = inputParametersData.side as string\n                const type = inputParametersData.type as string\n                const timeInForce = inputParametersData.timeInForce as string\n                const quantity = inputParametersData.quantity as number\n                const quoteOrderQty = inputParametersData.quoteOrderQty as number\n                const price = inputParametersData.price as number\n                const newClientOrderId = inputParametersData.newClientOrderId as string\n                const stopPrice = inputParametersData.stopPrice as number\n                const trailingDelta = inputParametersData.trailingDelta as number\n                const icebergQty = inputParametersData.icebergQty as number\n                const newOrderRespType = inputParametersData.newOrderRespType as string\n\n                const timestamp = Date.now()\n\n                queryParameters['symbol'] = symbol\n                queryParameters['side'] = side\n                queryParameters['type'] = type\n                if (timeInForce) queryParameters['timeInForce'] = timeInForce\n                if (quantity) queryParameters['quantity'] = quantity\n                if (quoteOrderQty) queryParameters['quoteOrderQty'] = quoteOrderQty\n                if (price) queryParameters['price'] = price\n                if (newClientOrderId) queryParameters['newClientOrderId'] = newClientOrderId\n                if (stopPrice) queryParameters['stopPrice'] = stopPrice\n                if (trailingDelta) queryParameters['trailingDelta'] = trailingDelta\n                if (icebergQty) queryParameters['icebergQty'] = icebergQty\n                if (newOrderRespType) queryParameters['newOrderRespType'] = newOrderRespType\n                queryParameters['recvWindow'] = 5000\n                queryParameters['timestamp'] = timestamp\n\n                const serializedQueryParams = serializeQueryParams(queryParameters)\n\n                const signature = createHmac('sha256', secretKey).update(serializedQueryParams).digest('hex')\n                queryParameters['signature'] = signature\n\n                method = 'POST'\n                headers['X-MBX-APIKEY'] = apiKey\n                url = `${apiUrl}/v3/order`\n                if (operation === 'postTestNewOrder') url += '/test'\n            } else if (operation === 'getOrder' || operation === 'delOrder') {\n                const symbol = inputParametersData.symbol as string\n                const orderId = inputParametersData.orderId as number\n                const origClientOrderId = inputParametersData.origClientOrderId as string\n\n                const timestamp = Date.now()\n\n                queryParameters['symbol'] = symbol\n                if (orderId) queryParameters['orderId'] = orderId\n                if (origClientOrderId) queryParameters['origClientOrderId'] = origClientOrderId\n                queryParameters['recvWindow'] = 5000\n                queryParameters['timestamp'] = timestamp\n\n                const serializedQueryParams = serializeQueryParams(queryParameters)\n\n                const signature = createHmac('sha256', secretKey).update(serializedQueryParams).digest('hex')\n                queryParameters['signature'] = signature\n\n                method = operation === 'delOrder' ? 'DELETE' : 'GET'\n                headers['X-MBX-APIKEY'] = apiKey\n                url = `${apiUrl}/v3/order`\n            } else if (operation === 'getOpenOrders' || operation === 'delOpenOrders') {\n                const symbol = inputParametersData.symbol as string\n\n                const timestamp = Date.now()\n\n                queryParameters['symbol'] = symbol\n                queryParameters['recvWindow'] = 5000\n                queryParameters['timestamp'] = timestamp\n\n                const serializedQueryParams = serializeQueryParams(queryParameters)\n\n                const signature = createHmac('sha256', secretKey).update(serializedQueryParams).digest('hex')\n                queryParameters['signature'] = signature\n\n                method = operation === 'delOpenOrders' ? 'DELETE' : 'GET'\n                headers['X-MBX-APIKEY'] = apiKey\n                url = `${apiUrl}/v3/openOrders`\n            } else if (operation === 'getAllOrders' || operation === 'getMyTrades') {\n                const symbol = inputParametersData.symbol as string\n                const orderId = inputParametersData.orderId as number\n                const startTime = Date.parse(inputParametersData.startTime as string)\n                const endTime = Date.parse(inputParametersData.endTime as string)\n                const limit = inputParametersData.limit as number\n                const fromId = inputParametersData.fromId as number\n\n                const timestamp = Date.now()\n\n                queryParameters['symbol'] = symbol\n                if (orderId) queryParameters['orderId'] = orderId\n                if (startTime) queryParameters['startTime'] = startTime\n                if (endTime) queryParameters['endTime'] = endTime\n                if (limit) queryParameters['limit'] = limit\n                if (fromId) queryParameters['fromId'] = fromId\n                queryParameters['recvWindow'] = 5000\n                queryParameters['timestamp'] = timestamp\n\n                const serializedQueryParams = serializeQueryParams(queryParameters)\n\n                const signature = createHmac('sha256', secretKey).update(serializedQueryParams).digest('hex')\n                queryParameters['signature'] = signature\n\n                method = 'GET'\n                headers['X-MBX-APIKEY'] = apiKey\n                const endpoint = operation === 'getMyTrades' ? 'myTrades' : 'allOrders'\n                url = `${apiUrl}/v3/${endpoint}`\n            } else if (operation === 'getAccountInformation' || operation === 'getOrderCountUsage') {\n                const timestamp = Date.now()\n\n                queryParameters['recvWindow'] = 5000\n                queryParameters['timestamp'] = timestamp\n\n                const serializedQueryParams = serializeQueryParams(queryParameters)\n\n                const signature = createHmac('sha256', secretKey).update(serializedQueryParams).digest('hex')\n                queryParameters['signature'] = signature\n\n                method = 'GET'\n                headers['X-MBX-APIKEY'] = apiKey\n                const endpoint = operation === 'getAccountInformation' ? 'account' : 'rateLimit/order'\n                url = `${apiUrl}/v3/${endpoint}`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers\n            }\n\n            if (Object.keys(queryBody).length > 0) {\n                axiosConfig.data = queryBody\n            }\n\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: BinancePrivate }\n"
  },
  {
    "path": "packages/components/nodes/Binance/BinancePublic.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass BinancePublic implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Binance Public'\n        this.name = 'binancePublic'\n        this.type = 'action'\n        this.icon = 'binance-logo.svg'\n        this.category = 'Centralized Exchange'\n        this.description = 'Binance Public API'\n        this.version = 1.0\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                description: 'Network to execute API: Test or Real',\n                options: [\n                    {\n                        label: 'TEST',\n                        name: 'test',\n                        description: 'Test network: https://testnet.binance.vision/'\n                    },\n                    {\n                        label: 'LIVE',\n                        name: 'live',\n                        description: 'Live network: https://api.binance.com/'\n                    }\n                ],\n                default: 'test'\n            },\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Order Book',\n                        name: 'getOrderBook',\n                        description: 'Get order book.'\n                    },\n                    {\n                        label: 'Get Exchange Information',\n                        name: 'getExchangeInfo',\n                        description: 'Get current exchange trading rules and symbol information.'\n                    },\n                    {\n                        label: 'Get Recent Trades List',\n                        name: 'getRecentTradesList',\n                        description: 'Get recent trades.'\n                    },\n                    {\n                        label: 'Get Compressed, Aggregate Trades',\n                        name: 'getAggTrades',\n                        description:\n                            'Get compressed, aggregate trades. Trades that fill at the time, from the same taker order, with the same price will have the quantity aggregated.'\n                    },\n                    {\n                        label: 'Get Kline/Candlestick data',\n                        name: 'getKlines',\n                        description: 'Kline/candlestick bars for a symbol. Klines are uniquely identified by their open time.'\n                    },\n                    {\n                        label: 'Get Current Average Price',\n                        name: 'getAvgPrice',\n                        description: 'Current average price for a symbol.'\n                    },\n                    {\n                        label: 'Get 24hr Ticker Price Change Statistics',\n                        name: 'get24hrTickerPrice',\n                        description: '24 hour rolling window price change statistics.'\n                    },\n                    {\n                        label: 'Get Symbol Price Ticker',\n                        name: 'getTickerPrice',\n                        description: 'Latest price for a symbol or symbols.'\n                    },\n                    {\n                        label: 'Get Symbol Order Book Ticker',\n                        name: 'getBookTicker',\n                        description: 'Best price/qty on the order book for a symbol or symbols.'\n                    }\n                ],\n                default: 'getOrderBook'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Symbol',\n                name: 'symbol',\n                type: 'asyncOptions',\n                optional: {\n                    'actions.operation': ['getExchangeInfo']\n                },\n                loadMethod: 'getSupportedSymbols',\n                show: {\n                    'actions.operation': [\n                        'getOrderBook',\n                        'getRecentTradesList',\n                        'getAggTrades',\n                        'getKlines',\n                        'getAvgPrice',\n                        'get24hrTickerPrice',\n                        'getTickerPrice',\n                        'getBookTicker',\n                        'getExchangeInfo'\n                    ]\n                }\n            },\n            {\n                label: 'Limit',\n                name: 'limit',\n                type: 'number',\n                default: 100,\n                optional: true,\n                show: {\n                    'actions.operation': ['getOrderBook', 'getRecentTradesList', 'getAggTrades', 'getKlines']\n                }\n            },\n            {\n                label: 'From Id',\n                name: 'fromId',\n                type: 'number',\n                optional: true,\n                description: 'ID to get aggregate trades from INCLUSIVE.',\n                show: {\n                    'actions.operation': ['getAggTrades']\n                }\n            },\n            {\n                label: 'Start Time',\n                name: 'startTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.operation': ['getAggTrades', 'getKlines']\n                }\n            },\n            {\n                label: 'End Time',\n                name: 'endTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.operation': ['getAggTrades', 'getKlines']\n                }\n            },\n            {\n                label: 'Interval',\n                name: 'interval',\n                type: 'options',\n                options: [\n                    {\n                        label: '1 minute',\n                        name: '1m'\n                    },\n                    {\n                        label: '3 minutes',\n                        name: '3m'\n                    },\n                    {\n                        label: '5 minutes',\n                        name: '5m'\n                    },\n                    {\n                        label: '15 minutes',\n                        name: '15m'\n                    },\n                    {\n                        label: '30 minutes',\n                        name: '30m'\n                    },\n                    {\n                        label: '1 hour',\n                        name: '1h'\n                    },\n                    {\n                        label: '2 hours',\n                        name: '2h'\n                    },\n                    {\n                        label: '4 hours',\n                        name: '4h'\n                    },\n                    {\n                        label: '6 hours',\n                        name: '6h'\n                    },\n                    {\n                        label: '8 hours',\n                        name: '8h'\n                    },\n                    {\n                        label: '12 hours',\n                        name: '12h'\n                    },\n                    {\n                        label: '1 day',\n                        name: '1d'\n                    },\n                    {\n                        label: '3 day',\n                        name: '3d'\n                    },\n                    {\n                        label: '1 week',\n                        name: '1w'\n                    },\n                    {\n                        label: '1 month',\n                        name: '1M'\n                    }\n                ],\n                default: '5m',\n                show: {\n                    'actions.operation': ['getKlines']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getSupportedSymbols(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionData = nodeData.actions\n\n            let apiUrl = ''\n            if (actionData !== undefined && (actionData.network as string) === 'test') {\n                apiUrl = 'https://testnet.binance.vision/api'\n            } else {\n                apiUrl = 'https://api.binance.com/api'\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `${apiUrl}/v3/exchangeInfo`,\n                headers: {\n                    'Content-Type': 'application/json'\n                }\n            }\n\n            const response = await axios(axiosConfig)\n            const responseData = response.data\n            for (const s of responseData['symbols']) {\n                returnData.push({\n                    label: s.symbol,\n                    name: s.symbol\n                })\n            }\n\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = actionData.network as string\n        const operation = actionData.operation as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any // tslint:disable-line: no-any\n\n        let apiUrl = ''\n        if (network === 'test') {\n            apiUrl = 'https://testnet.binance.vision/api'\n        } else if (network === 'live') {\n            apiUrl = 'https://api.binance.com/api'\n        }\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        const queryBody: ICommonObject = {}\n        let method: Method = 'GET'\n\n        try {\n            if (operation === 'getOrderBook') {\n                const symbol = inputParametersData.symbol as string\n                const limit = inputParametersData.limit as number\n\n                url = `${apiUrl}/v3/depth`\n                queryParameters['symbol'] = symbol\n                queryParameters['limit'] = limit\n                method = 'GET'\n            } else if (operation === 'getExchangeInfo') {\n                const symbol = inputParametersData.symbol as string\n                if (symbol) queryParameters['symbol'] = symbol\n                url = `${apiUrl}/v3/exchangeInfo`\n                method = 'GET'\n            } else if (operation === 'getRecentTradesList') {\n                const symbol = inputParametersData.symbol as string\n                const limit = inputParametersData.limit as number\n\n                url = `${apiUrl}/v3/trades`\n                queryParameters['symbol'] = symbol\n                queryParameters['limit'] = limit\n                method = 'GET'\n            } else if (operation === 'getAggTrades') {\n                const symbol = inputParametersData.symbol as string\n                const limit = inputParametersData.limit as number\n                const fromId = inputParametersData.fromId as number\n                const startTime = Date.parse(inputParametersData.startTime as string)\n                const endTime = Date.parse(inputParametersData.endTime as string)\n\n                url = `${apiUrl}/v3/aggTrades`\n                queryParameters['symbol'] = symbol\n                queryParameters['limit'] = limit\n                if (fromId) queryParameters['fromId'] = fromId\n                if (startTime) queryParameters['startTime'] = startTime\n                if (endTime) queryParameters['endTime'] = endTime\n                method = 'GET'\n            } else if (operation === 'getKlines') {\n                const symbol = inputParametersData.symbol as string\n                const limit = inputParametersData.limit as number\n                const interval = inputParametersData.interval as string\n                const startTime = Date.parse(inputParametersData.startTime as string)\n                const endTime = Date.parse(inputParametersData.endTime as string)\n\n                url = `${apiUrl}/v3/klines`\n                queryParameters['symbol'] = symbol\n                queryParameters['limit'] = limit\n                queryParameters['interval'] = interval\n                if (startTime) queryParameters['startTime'] = startTime\n                if (endTime) queryParameters['endTime'] = endTime\n                method = 'GET'\n            } else if (operation === 'getAvgPrice') {\n                const symbol = inputParametersData.symbol as string\n\n                url = `${apiUrl}/v3/avgPrice`\n                queryParameters['symbol'] = symbol\n                method = 'GET'\n            } else if (operation === 'get24hrTickerPrice') {\n                const symbol = inputParametersData.symbol as string\n\n                url = `${apiUrl}/v3/ticker/24hr`\n                queryParameters['symbol'] = symbol\n                method = 'GET'\n            } else if (operation === 'getTickerPrice') {\n                const symbol = inputParametersData.symbol as string\n\n                url = `${apiUrl}/v3/ticker/price`\n                queryParameters['symbol'] = symbol\n                method = 'GET'\n            } else if (operation === 'getBookTicker') {\n                const symbol = inputParametersData.symbol as string\n\n                url = `${apiUrl}/v3/ticker/bookTicker`\n                queryParameters['symbol'] = symbol\n                method = 'GET'\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: {\n                    'Content-Type': 'application/json'\n                }\n            }\n\n            if (Object.keys(queryBody).length > 0) {\n                axiosConfig.data = queryBody\n            }\n\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) returnData.push(...responseData)\n        else returnData.push(responseData)\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: BinancePublic }\n"
  },
  {
    "path": "packages/components/nodes/BlockchainEvent/BlockchainEvent.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    ArbitrumNetworks,\n    ETHNetworks,\n    OptimismNetworks,\n    PolygonNetworks,\n    getNetworkProvidersList,\n    NETWORK,\n    getNetworkProvider,\n    NETWORK_PROVIDER,\n    networkProviderCredentials,\n    BSCNetworks,\n    AvalancheNetworks,\n    SolanaNetworks,\n    FantomNetworks,\n    GnosisNetworks,\n    HecoNetworks,\n    HarmonyNetworks,\n    MoonRiverNetworks,\n    MoonBeamNetworks,\n    MetisNetworks,\n    KlatynNetworks\n} from '../../src/ChainNetwork'\nimport { ethers } from 'ethers'\n\nclass BlockchainEvent extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'Blockchain Event Trigger'\n        this.name = 'blockchainEventTrigger'\n        this.icon = 'blockchainevent.svg'\n        this.type = 'trigger'\n        this.category = 'Blockchain Events'\n        this.version = 1.0\n        this.description = 'Start workflow whenever a specified event happened on chain'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.actions = [\n            {\n                label: 'Event Name',\n                name: 'event',\n                type: 'options',\n                options: [\n                    {\n                        label: 'New Block',\n                        name: 'block',\n                        description: 'Emitted when a new block is mined'\n                    },\n                    {\n                        label: 'Error',\n                        name: 'error',\n                        description: 'emitted on any error'\n                    },\n                    {\n                        label: 'New Transaction',\n                        name: 'pending',\n                        description:\n                            'Emitted when a new transaction enters the memory pool. Only certain providers offer this event and may require running your own node for reliable results'\n                    },\n                    {\n                        label: 'Transaction Hash',\n                        name: 'txHash',\n                        description: 'Emitted when the transaction has been mined'\n                    }\n                ],\n                default: 'block'\n            },\n            {\n                label: 'Transaction Hash',\n                name: 'txHash',\n                type: 'string',\n                default: '',\n                show: {\n                    'actions.event': ['txHash']\n                }\n            }\n        ]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    ...ETHNetworks,\n                    ...PolygonNetworks,\n                    ...ArbitrumNetworks,\n                    ...OptimismNetworks,\n                    ...BSCNetworks,\n                    ...AvalancheNetworks,\n                    ...SolanaNetworks,\n                    ...FantomNetworks,\n                    ...GnosisNetworks,\n                    ...HecoNetworks,\n                    ...HarmonyNetworks,\n                    ...MoonRiverNetworks,\n                    ...MoonBeamNetworks,\n                    ...MetisNetworks,\n                    ...KlatynNetworks\n                ],\n                default: 'homestead'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            const actionsData = nodeData.actions\n            if (networksData === undefined || actionsData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            if (actionsData.event === 'pending') {\n                return [\n                    {\n                        label: 'Custom Websocket Endpoint',\n                        name: NETWORK_PROVIDER.CUSTOMWSS,\n                        description: 'WSS Endpoint',\n                        parentGroup: 'Custom Nodes'\n                    }\n                ]\n            }\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const actionsData = nodeData.actions\n\n        if (networksData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string,\n            true\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        let event = actionsData.event as string\n\n        if (event === 'txHash') event = actionsData.txHash as string\n\n        try {\n            provider.on(event, async (result: any) => {\n                const returnData = await getOutputResponse(event, result, provider)\n                this.emit(emitEventKey, returnNodeExecutionData(returnData))\n            })\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        this.providers[emitEventKey] = { provider, filter: event }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            const filter = this.providers[emitEventKey].filter\n            provider.off(filter)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nconst getOutputResponse = async (\n    event: string,\n    result: any,\n    provider: ethers.providers.JsonRpcProvider | ethers.providers.FallbackProvider | ethers.providers.WebSocketProvider\n) => {\n    let returnItem = {} as ICommonObject\n\n    switch (event) {\n        case 'block':\n            returnItem = { blockNumber: result }\n            break\n        case 'error':\n            returnItem = { error: result }\n            break\n        case 'pending':\n            returnItem = {\n                pendingTransactionHash: result,\n                pendingTransaction: ((await provider.getTransaction(result)) as any) || {}\n            }\n            break\n        case 'txHash':\n            returnItem = { transaction: result }\n            break\n    }\n\n    return returnItem\n}\n\nmodule.exports = { nodeClass: BlockchainEvent }\n"
  },
  {
    "path": "packages/components/nodes/BscScan/Bscscan.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport { NETWORK, NETWORK_LABEL, etherscanAPIs } from '../../src/ChainNetwork'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport {\n    SORT_BY,\n    OPERATIONS,\n    GET_BNB_BALANCE,\n    GET_HISTORICAL_BNB_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_BLOCKS_VALIDATED,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    GET_BEP20_TOKEN_SUPPLY,\n    GET_BEP20_TOKEN_BALANCE,\n    GET_HISTORICAL_BEP20_TOKEN_SUPPLY,\n    GET_HISTORICAL_BEP20_TOKEN_BALANCE,\n    GET_TOKEN_INFO,\n    GET_BNB_PRICE,\n    GET_HISTORICAL_BNB_PRICE,\n    GET_MULTI_BNB_BALANCE,\n    GET_BEP20_CIRCULATION_TOKEN_SUPPLY\n} from './constants'\nclass Bscscan implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'BscScan'\n        this.name = 'BscScan'\n        this.icon = 'bscscan.svg'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Perform Bscscan operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Bnb Balance for a Single Address',\n                        name: GET_BNB_BALANCE.name,\n                        description: 'Returns the Bnb balance of a given address.'\n                    },\n                    {\n                        label: 'Get Bnb Balance for Multiple Addresses(separated by a comma)',\n                        name: GET_MULTI_BNB_BALANCE.name,\n                        description: 'Returns the Bnb balance of the addresses(each address separated by a comma) entered.'\n                    },\n                    {\n                        label: 'Get transactions',\n                        name: GET_NORMAL_TRANSACTIONS.name,\n                        description: 'Returns the list of transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions',\n                        name: GET_INTERNAL_TRANSACTIONS.name,\n                        description: 'Returns the list of internal transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions by hash',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_HASH.name,\n                        description: 'Returns the list of internal transactions performed within a transaction.'\n                    },\n                    {\n                        label: 'Get internal transactions by block',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        description: 'Returns the list of internal transactions performed within a block range, with optional pagination.'\n                    },\n                    {\n                        label: 'Get list of Blocks Validated by Address',\n                        name: GET_BLOCKS_VALIDATED.name,\n                        description: 'Returns the list of blocks validated by an address.'\n                    },\n                    {\n                        label: 'Get Contract ABI',\n                        name: GET_ABI.name,\n                        description: 'Returns the contract Application Binary Interface ( ABI ) of a verified smart contract.'\n                    },\n                    {\n                        label: 'Get Contract Source Code',\n                        name: GET_CONTRACT_SOURCE_CODE.name,\n                        description: 'Returns the Solidity source code of a verified smart contract.'\n                    },\n                    {\n                        label: 'Check Transaction Receipt Status',\n                        name: CHECK_TRANSACTION_RECEIPT_STATUS.name,\n                        description: 'Returns the status code of a transaction execution.'\n                    },\n                    {\n                        label: 'Get BEP-20 Token Circulating Supply by ContractAddress',\n                        name: GET_BEP20_CIRCULATION_TOKEN_SUPPLY.name,\n                        description: `Returns the current circulating supply of a BEP-20 token. \n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get BEP20 Token Supply',\n                        name: GET_BEP20_TOKEN_SUPPLY.name,\n                        description: `Returns the total supply of a BEP-20 token. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get BEP20 Token Balance',\n                        name: GET_BEP20_TOKEN_BALANCE.name,\n                        description: `Returns the current balance of a BEP-20 token of an address. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Historical BEP-20 Token TotalSupply by ContractAddress & BlockNo [PRO]',\n                        name: GET_HISTORICAL_BEP20_TOKEN_SUPPLY.name,\n                        description: `Returns the historical amount of a BEP-20 token in circulation at a certain block height. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Historical BEP-20 Token Account Balance by ContractAddress & BlockNo [PRO]',\n                        name: GET_HISTORICAL_BEP20_TOKEN_BALANCE.name,\n                        description: `Returns the balance of a BEP-20 token of an address at a certain block height. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Token Info [PRO]',\n                        name: GET_TOKEN_INFO.name,\n                        description: 'Returns project information and social media links of an BEP-20/ERC-721 token.'\n                    },\n                    {\n                        label: 'Get BNB Price',\n                        name: GET_BNB_PRICE.name,\n                        description: 'Returns the latest price of 1 BNB.'\n                    },\n                    {\n                        label: 'Get Historical BNB Price [PRO]',\n                        name: GET_HISTORICAL_BNB_PRICE.name,\n                        description: 'Returns the historical price of BNB.'\n                    }\n                ],\n\n                default: 'getBnbBalance'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: NETWORK_LABEL.BSC,\n                        name: NETWORK.BSC\n                    },\n                    {\n                        label: NETWORK_LABEL.BSC_TESTNET,\n                        name: NETWORK.BSC_TESTNET\n                    }\n                ],\n                default: 'homestead'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'BscScan API Key',\n                        name: 'bscscanApi'\n                    }\n                ],\n                default: 'bscscanApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Address',\n                name: 'address',\n                type: 'string',\n                description: 'The address parameter(s) required',\n                show: {\n                    'actions.api': [\n                        GET_BNB_BALANCE.name,\n                        GET_HISTORICAL_BNB_BALANCE.name,\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_BLOCKS_VALIDATED.name,\n                        GET_ABI.name,\n                        GET_CONTRACT_SOURCE_CODE.name,\n                        GET_BEP20_TOKEN_BALANCE.name,\n                        GET_HISTORICAL_BEP20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Block Number',\n                name: 'blockno',\n                type: 'number',\n                description: 'the block number to check balance for eg. 2000000',\n                show: {\n                    'actions.api': [\n                        GET_HISTORICAL_BNB_BALANCE.name,\n                        GET_HISTORICAL_BEP20_TOKEN_SUPPLY.name,\n                        GET_HISTORICAL_BEP20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Start Block',\n                name: 'startBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to start searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 0\n            },\n            {\n                label: 'End Block',\n                name: 'endBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to stop searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                }\n            },\n            {\n                label: 'Page',\n                name: 'page',\n                type: 'number',\n                optional: true,\n                description: 'the page number, if pagination is enabled',\n                show: {\n                    'actions.api': [\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        GET_BLOCKS_VALIDATED.name\n                    ]\n                },\n                default: 1\n            },\n            {\n                label: 'Offset',\n                name: 'offset',\n                type: 'number',\n                optional: true,\n                description: 'the number of transactions displayed per page',\n                show: {\n                    'actions.api': [\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        GET_BLOCKS_VALIDATED.name\n                    ]\n                },\n                default: 10\n            },\n            {\n                label: 'Sort By',\n                name: 'sortBy',\n                type: 'options',\n                optional: true,\n                options: SORT_BY,\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 'desc'\n            },\n            {\n                label: 'Transaction Hash',\n                name: 'txhash',\n                type: 'string',\n                description: 'the string representing the transaction hash to check for internal transactions',\n                show: {\n                    'actions.api': [GET_INTERNAL_TRANSACTIONS_BY_HASH.name, CHECK_TRANSACTION_RECEIPT_STATUS.name]\n                }\n            },\n            {\n                label: 'Block Type',\n                name: 'blockType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'blocks',\n                        name: 'blocks'\n                    }\n                ],\n                default: 'blocks',\n                show: {\n                    'actions.api': [GET_BLOCKS_VALIDATED.name]\n                }\n            },\n            {\n                label: 'Contract Address',\n                name: 'contractAddress',\n                type: 'string',\n                description: 'the contract address of the BEP-20 token',\n                show: {\n                    'actions.api': [\n                        GET_BEP20_TOKEN_SUPPLY.name,\n                        GET_BEP20_TOKEN_BALANCE.name,\n                        GET_HISTORICAL_BEP20_TOKEN_SUPPLY.name,\n                        GET_HISTORICAL_BEP20_TOKEN_BALANCE.name,\n                        GET_TOKEN_INFO.name,\n                        GET_BEP20_CIRCULATION_TOKEN_SUPPLY.name\n                    ]\n                }\n            },\n            {\n                label: 'Tag',\n                name: 'tag',\n                type: 'options',\n                options: [{ label: 'latest', name: 'latest' }],\n                default: 'latest',\n                show: {\n                    'actions.api': [GET_BEP20_TOKEN_BALANCE.name]\n                }\n            },\n            {\n                label: 'Start Time',\n                name: 'startTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.api': [GET_HISTORICAL_BNB_PRICE.name]\n                }\n            },\n            {\n                label: 'End Time',\n                name: 'endTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.api': [GET_HISTORICAL_BNB_PRICE.name]\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    getNetwork(network: NETWORK): string {\n        return `${etherscanAPIs[network]}`\n    }\n\n    getBaseParams(api: string) {\n        const operation = OPERATIONS.filter(({ name }) => name === api)[0]\n        return { module: operation.module, action: operation.action }\n    }\n\n    getISODate(date: Date) {\n        return date.toISOString().split('T')[0]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const networksData = nodeData.networks\n        const inputParameters = nodeData.inputParameters\n        const credentials = nodeData.credentials\n        if (actionData === undefined || inputParameters === undefined || credentials === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n        // GET api\n        const api = actionData.api as string\n\n        // GET network\n        const network = networksData.network as NETWORK\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n\n        // GET address\n        const address = inputParameters.address as string\n\n        const startblock = inputParameters.startBlock as number\n        const endblock = inputParameters.endBlock as number\n        const page = inputParameters.page as number\n        const offset = inputParameters.offset as number\n        const sort = inputParameters.sortBy as string\n        const txhash = inputParameters.txhash as string\n        const blocktype = inputParameters.blockType as string\n        const contractaddress = inputParameters.contractAddress as string\n        const tag = inputParameters.tag as string\n        const startTime = inputParameters.startTime as string\n        const endTime = inputParameters.endTime as string\n\n        const startdate = startTime ? this.getISODate(new Date(startTime)) : undefined\n        const enddate = endTime ? this.getISODate(new Date(endTime)) : undefined\n\n        const url = this.getNetwork(network)\n        const { module, action } = this.getBaseParams(api)\n\n        const queryParameters = {\n            module,\n            action,\n            address,\n            apiKey,\n            startblock,\n            endblock,\n            page,\n            offset,\n            sort,\n            txhash,\n            blocktype,\n            contractaddress,\n            tag,\n            startdate,\n            enddate\n        }\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Bscscan }\n"
  },
  {
    "path": "packages/components/nodes/BscScan/constants.ts",
    "content": "// Account\nexport const GET_BNB_BALANCE = {\n    name: 'getBnbBalance',\n    module: 'account',\n    action: 'balance'\n}\n\nexport const GET_MULTI_BNB_BALANCE = {\n    name: 'getBnbBalanceMulti',\n    module: 'account',\n    action: 'balancemulti'\n}\n\nexport const GET_HISTORICAL_BNB_BALANCE = {\n    name: 'getHistoricalBnbBalance',\n    module: 'account',\n    action: 'balancehistory'\n}\n\nexport const GET_NORMAL_TRANSACTIONS = {\n    name: 'getTransactions',\n    module: 'account',\n    action: 'txlist'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS = {\n    name: 'getInternalTransactions',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_HASH = {\n    name: 'getInternalTransactionsByHash',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_BLOCK = {\n    name: 'getInternalTransactionsByBlock',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_BLOCKS_VALIDATED = {\n    name: 'getBlocksValidated',\n    module: 'account',\n    action: 'getminedblocks'\n}\n\n// Contracts\nexport const GET_ABI = {\n    name: 'getAbi',\n    module: 'contract',\n    action: 'getabi'\n}\n\nexport const GET_CONTRACT_SOURCE_CODE = {\n    name: 'getContractSourceCode',\n    module: 'contract',\n    action: 'getsourcecode'\n}\nexport const GET_CONTRACT_CREATION = {\n    name: 'getContractCreation',\n    module: 'contract',\n    action: 'getcontractcreation'\n}\n\n// Transactions\nexport const CHECK_TRANSACTION_RECEIPT_STATUS = {\n    name: 'getTransactionReceiptStatus',\n    module: 'transaction',\n    action: 'gettxreceiptstatus'\n}\n\n// Tokens\nexport const GET_BEP20_CIRCULATION_TOKEN_SUPPLY = {\n    name: 'getBep20TokenCirculatingSupply',\n    module: 'stats',\n    action: 'tokenCsupply'\n}\nexport const GET_BEP20_TOKEN_SUPPLY = {\n    name: 'getBep20TokenSupply',\n    module: 'stats',\n    action: 'tokensupply'\n}\n\nexport const GET_BEP20_TOKEN_BALANCE = {\n    name: 'getBep20TokenBalance',\n    module: 'account',\n    action: 'tokenbalance'\n}\n\nexport const GET_HISTORICAL_BEP20_TOKEN_SUPPLY = {\n    name: 'getHistoricalBep20TokenSupply',\n    module: 'stats',\n    action: 'tokensupplyhistory'\n}\n\nexport const GET_HISTORICAL_BEP20_TOKEN_BALANCE = {\n    name: 'getHistoricalBep20TokenBalance',\n    module: 'account',\n    action: 'tokenbalancehistory'\n}\n\nexport const GET_TOKEN_INFO = {\n    name: 'getTokenInfo',\n    module: 'token',\n    action: 'tokeninfo'\n}\n\n// Stats\nexport const GET_BNB_SUPPLY = {\n    name: 'getBep20TokenSupply',\n    module: 'stats',\n    action: 'tokensupply'\n}\n\nexport const GET_BNB_PRICE = {\n    name: 'getBnbPrice',\n    module: 'stats',\n    action: 'bnbprice'\n}\n\nexport const GET_HISTORICAL_BNB_PRICE = {\n    name: 'getHistoricalBnbPrice',\n    module: 'stats',\n    action: 'bnbdailyprice'\n}\n\nexport const OPERATIONS = [\n    GET_BNB_BALANCE,\n    GET_MULTI_BNB_BALANCE,\n    GET_HISTORICAL_BNB_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_BLOCKS_VALIDATED,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    GET_CONTRACT_CREATION,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    GET_BEP20_TOKEN_SUPPLY,\n    GET_BEP20_TOKEN_BALANCE,\n    GET_HISTORICAL_BEP20_TOKEN_SUPPLY,\n    GET_HISTORICAL_BEP20_TOKEN_BALANCE,\n    GET_TOKEN_INFO,\n    GET_BNB_PRICE,\n    GET_HISTORICAL_BNB_PRICE,\n    GET_BNB_SUPPLY,\n    GET_BEP20_CIRCULATION_TOKEN_SUPPLY\n] as const\n\nexport const SORT_BY = [\n    { label: 'Desc', name: 'desc' },\n    { label: 'Asc', name: 'asc' }\n]\n"
  },
  {
    "path": "packages/components/nodes/ChainLink/ChainLink.ts",
    "content": "import { ethers } from 'ethers'\nimport { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { getNetworkProvidersList, NETWORK_PROVIDER, getNetworkProvider, NETWORK, networkProviderCredentials } from '../../src/ChainNetwork'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { chainLinkNetworks, chainLinkNetworkMapping } from './supportedNetwork'\n\nclass ChainLink implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    actions?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'ChainLink'\n        this.name = 'chainLink'\n        this.icon = 'chainlink.svg'\n        this.type = 'action'\n        this.category = 'Decentralized Oracle Network'\n        this.version = 1.0\n        this.description = 'Execute ChainLink operations such as Data Feeds, Randomness, Oracles.'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Price Feeds',\n                        name: 'getPriceFeeds',\n                        description: 'Get real-world market prices of assets using ChainLink Oracle'\n                    },\n                    {\n                        label: 'Get Proof of Reserve',\n                        name: 'getProofReserve',\n                        description: 'Provide the status of the reserves for several assets'\n                    },\n                    {\n                        label: 'Get NFT Floor Pricing',\n                        name: 'getNFTFloorPricing',\n                        description: 'Get the lowest price of an NFT in a collection using ChainLink Oracle, ONLY available on Goerli'\n                    }\n                ],\n                default: 'getPriceFeeds'\n            }\n        ]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...chainLinkNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Pair',\n                name: 'pair',\n                type: 'asyncOptions',\n                loadMethod: 'getPairAddress',\n                show: {\n                    'actions.operation': ['getPriceFeeds']\n                }\n            },\n            {\n                label: 'Reserve',\n                name: 'reserve',\n                type: 'asyncOptions',\n                loadMethod: 'getReserveAddress',\n                show: {\n                    'actions.operation': ['getProofReserve']\n                }\n            },\n            {\n                label: 'NFT Collection',\n                name: 'nftCollection',\n                type: 'asyncOptions',\n                loadMethod: 'getNftCollection',\n                show: {\n                    'actions.operation': ['getNFTFloorPricing']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        },\n\n        async getPairAddress(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const networksData = nodeData.networks\n            if (networksData === undefined) return []\n\n            const network = networksData.network as NETWORK\n\n            return await getInputParametersData(network, 'default')\n        },\n\n        async getNftCollection(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const networksData = nodeData.networks\n            if (networksData === undefined) return []\n\n            const network = networksData.network as NETWORK\n\n            return await getInputParametersData(network, 'nftFloor')\n        },\n\n        async getReserveAddress(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const networksData = nodeData.networks\n            if (networksData === undefined) return []\n\n            const network = networksData.network as NETWORK\n\n            return await getInputParametersData(network, 'por')\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (networksData === undefined || actionsData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) {\n                throw new Error('Invalid Network Provider')\n            }\n\n            const operation = actionsData.operation as string\n\n            if (operation === 'getPriceFeeds' || operation === 'getProofReserve' || operation === 'getNFTFloorPricing') {\n                const pair = inputParametersData.pair as string\n                const parsedPair = JSON.parse(pair.replace(/\\s/g, ''))\n                const address = parsedPair.proxy\n\n                const aggregatorV3InterfaceABI = [\n                    {\n                        inputs: [],\n                        name: 'decimals',\n                        outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }],\n                        stateMutability: 'view',\n                        type: 'function'\n                    },\n                    {\n                        inputs: [],\n                        name: 'description',\n                        outputs: [{ internalType: 'string', name: '', type: 'string' }],\n                        stateMutability: 'view',\n                        type: 'function'\n                    },\n                    {\n                        inputs: [{ internalType: 'uint80', name: '_roundId', type: 'uint80' }],\n                        name: 'getRoundData',\n                        outputs: [\n                            { internalType: 'uint80', name: 'roundId', type: 'uint80' },\n                            { internalType: 'int256', name: 'answer', type: 'int256' },\n                            { internalType: 'uint256', name: 'startedAt', type: 'uint256' },\n                            { internalType: 'uint256', name: 'updatedAt', type: 'uint256' },\n                            { internalType: 'uint80', name: 'answeredInRound', type: 'uint80' }\n                        ],\n                        stateMutability: 'view',\n                        type: 'function'\n                    },\n                    {\n                        inputs: [],\n                        name: 'latestRoundData',\n                        outputs: [\n                            { internalType: 'uint80', name: 'roundId', type: 'uint80' },\n                            { internalType: 'int256', name: 'answer', type: 'int256' },\n                            { internalType: 'uint256', name: 'startedAt', type: 'uint256' },\n                            { internalType: 'uint256', name: 'updatedAt', type: 'uint256' },\n                            { internalType: 'uint80', name: 'answeredInRound', type: 'uint80' }\n                        ],\n                        stateMutability: 'view',\n                        type: 'function'\n                    },\n                    {\n                        inputs: [],\n                        name: 'version',\n                        outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n                        stateMutability: 'view',\n                        type: 'function'\n                    }\n                ]\n                const priceFeed = new ethers.Contract(address, aggregatorV3InterfaceABI, provider)\n\n                const roundData = await priceFeed.latestRoundData()\n\n                const returnItem: ICommonObject = {\n                    roundData,\n                    ...parsedPair,\n                    network\n                }\n                return returnNodeExecutionData(returnItem)\n            }\n\n            return returnNodeExecutionData([])\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nconst getInputParametersData = async (network: NETWORK, dataType: string) => {\n    const returnData: INodeOptionsValue[] = []\n\n    try {\n        const axiosConfig: AxiosRequestConfig = {\n            method: 'GET' as Method,\n            url: `https://cl-docs-addresses.web.app/addresses.json`\n        }\n\n        const response = await axios(axiosConfig)\n        const responseData = response.data\n\n        for (const parentNetwork in responseData) {\n            const availableNetworks = responseData[parentNetwork].networks\n            const selectedNetwork = availableNetworks.find(\n                (ntk: any) => ntk.name === chainLinkNetworkMapping[network] && ntk.dataType === dataType\n            )\n\n            let availableProxies = []\n            if (selectedNetwork) {\n                availableProxies = selectedNetwork.proxies\n            }\n\n            for (const proxy of availableProxies) {\n                const data = {\n                    label: proxy.pair,\n                    name: JSON.stringify(proxy)\n                } as INodeOptionsValue\n                returnData.push(data)\n            }\n        }\n        return returnData\n    } catch (e) {\n        return returnData\n    }\n}\n\nmodule.exports = { nodeClass: ChainLink }\n"
  },
  {
    "path": "packages/components/nodes/ChainLink/ChainLinkFunctionWebhook.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeOptionsValue,\n    INodeParams,\n    IWebhookNodeExecutionData,\n    NodeType\n} from '../../src/Interface'\nimport { compareKeys, returnWebhookNodeExecutionData } from '../../src/utils'\n\nclass ChainLinkFunctionWebhook implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Chainlink Function Webhook'\n        this.icon = 'chainlink.svg'\n        this.name = 'chainLinkFunctionWebhook'\n        this.type = 'webhook'\n        this.category = 'Decentralized Oracle Network'\n        this.version = 1.0\n        this.description = 'Start workflow whenever chainlink function webhook is called'\n        this.incoming = 0\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'HTTP Method',\n                name: 'httpMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'GET',\n                        name: 'GET'\n                    },\n                    {\n                        label: 'POST',\n                        name: 'POST'\n                    }\n                ],\n                default: 'GET',\n                description: 'The HTTP method to listen to.'\n            },\n            {\n                label: 'Authorization',\n                name: 'authorization',\n                type: 'options',\n                options: [\n                    {\n                        label: 'API',\n                        name: 'headerAuth',\n                        description: 'Webhook header must contains \"X-API-KEY\" with matching key'\n                    },\n                    {\n                        label: 'None',\n                        name: 'none'\n                    }\n                ],\n                default: 'none',\n                description: 'The way to authorize incoming webhook.'\n            },\n            {\n                label: 'API key',\n                name: 'apiKey',\n                type: 'asyncOptions',\n                description:\n                    'Incoming call must consists header \"x-api-key\" with matching API key. You can create new key from the dashboard',\n                loadMethod: 'getAPIKeys',\n                show: {\n                    'inputParameters.authorization': ['headerAuth']\n                }\n            }\n        ]\n    }\n\n    loadMethods = {\n        async getAPIKeys(nodeData: INodeData, dbCollection?: IDbCollection, apiKeys?: ICommonObject[]): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            if (!apiKeys || !apiKeys.length) return returnData\n\n            for (let i = 0; i < apiKeys.length; i += 1) {\n                const key = apiKeys[i]\n                const data = {\n                    label: key.keyName,\n                    description: key.apiKey,\n                    name: key.apiSecret\n                } as INodeOptionsValue\n                returnData.push(data)\n            }\n            return returnData\n        }\n    }\n\n    async runWebhook(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const req = nodeData.req\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (req === undefined) {\n            throw new Error('Missing request')\n        }\n\n        const authorization = inputParametersData.authorization as string\n        const apiSecret = inputParametersData.apiKey as string\n\n        const returnData: ICommonObject[] = []\n\n        if (authorization === 'headerAuth') {\n            let suppliedKey = ''\n            if (req.headers['X-API-KEY']) suppliedKey = req.headers['X-API-KEY'] as string\n            if (req.headers['x-api-key']) suppliedKey = req.headers['x-api-key'] as string\n            if (!suppliedKey) throw new Error('401: Missing API Key')\n            const isKeyValid = compareKeys(apiSecret, suppliedKey)\n            if (!isKeyValid) throw new Error('403: Unauthorized API Key')\n        }\n\n        returnData.push({\n            headers: req?.headers,\n            params: req?.params,\n            query: req?.query,\n            body: req?.body,\n            rawBody: (req as any).rawBody,\n            url: req?.url\n        })\n\n        return returnWebhookNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: ChainLinkFunctionWebhook }\n"
  },
  {
    "path": "packages/components/nodes/ChainLink/supportedNetwork.ts",
    "content": "import { INodeOptionsValue } from '../../src'\nimport { NETWORK, NETWORK_LABEL } from '../../src/ChainNetwork'\n\nexport type IChainLinkNetworkMapping = {\n    [key in NETWORK]: string\n}\n\nexport const chainLinkNetworkMapping = {\n    [NETWORK.MAINNET]: 'Ethereum Mainnet',\n    [NETWORK.GÖRLI]: 'Goerli Testnet',\n    [NETWORK.ARBITRUM]: 'Arbitrum Mainnet',\n    [NETWORK.ARBITRUM_GOERLI]: 'Arbitrum Goerli',\n    [NETWORK.OPTIMISM]: 'Optimism Mainnet',\n    [NETWORK.OPTIMISM_GOERLI]: 'Optimism Goerli',\n    [NETWORK.MATIC]: 'Polygon Mainnet',\n    [NETWORK.MATIC_MUMBAI]: 'Mumbai Testnet',\n    [NETWORK.BSC]: 'BNB Chain Mainnet',\n    [NETWORK.BSC_TESTNET]: 'BNB Chain Mainnet',\n    [NETWORK.GNOSIS]: 'Gnosis Chain Mainnet',\n    [NETWORK.HECO]: 'HECO Mainnet',\n    [NETWORK.FANTOM]: 'Fantom Mainnet',\n    [NETWORK.FANTOM_TESTNET]: 'Fantom Testnet',\n    [NETWORK.AVALANCHE]: 'Avalanche Mainnet',\n    [NETWORK.AVALANCHE_TESTNET]: 'Avalanche Testnet',\n    [NETWORK.SOLANA]: 'Solana Mainnet',\n    [NETWORK.SOLANA_DEVNET]: 'Solana Devnet',\n    [NETWORK.HARMONY]: 'Harmony Mainnet',\n    [NETWORK.MOONRIVER]: 'Moonriver Mainnet',\n    [NETWORK.MOONBEAM]: 'Moonbeam Mainnet',\n    [NETWORK.METIS]: 'Metis Mainnet',\n    [NETWORK.KLATYN_TESTNET]: 'Klaytn Baobab testnet'\n} as IChainLinkNetworkMapping\n\nexport const chainLinkNetworks: INodeOptionsValue[] = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: NETWORK.MAINNET,\n        parentGroup: 'Ethereum',\n        hide: {\n            'actions.operation': ['getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: NETWORK.GÖRLI,\n        parentGroup: 'Ethereum',\n        hide: {\n            'actions.operation': ['getProofReserve']\n        }\n    },\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: NETWORK.MATIC,\n        parentGroup: 'Polygon',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: NETWORK.MATIC_MUMBAI,\n        parentGroup: 'Polygon',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM,\n        name: NETWORK.ARBITRUM,\n        parentGroup: 'Arbitrum',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n        name: NETWORK.ARBITRUM_GOERLI,\n        parentGroup: 'Arbitrum',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE,\n        name: NETWORK.AVALANCHE,\n        parentGroup: 'Avalanche',\n        hide: {\n            'actions.operation': ['getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE_TESTNET,\n        name: NETWORK.AVALANCHE_TESTNET,\n        parentGroup: 'Avalanche',\n        hide: {\n            'actions.operation': ['getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM,\n        name: NETWORK.OPTIMISM,\n        parentGroup: 'Optimism',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM_GOERLI,\n        name: NETWORK.OPTIMISM_GOERLI,\n        parentGroup: 'Optimism',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.BSC,\n        name: NETWORK.BSC,\n        parentGroup: 'Binance Smart Chain',\n        hide: {\n            'actions.operation': ['getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.BSC_TESTNET,\n        name: NETWORK.BSC_TESTNET,\n        parentGroup: 'Binance Smart Chain',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.GNOSIS,\n        name: NETWORK.GNOSIS,\n        parentGroup: 'Gnosis',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.HECO,\n        name: NETWORK.HECO,\n        parentGroup: 'Heco',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.FANTOM,\n        name: NETWORK.FANTOM,\n        parentGroup: 'Fantom',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.FANTOM_TESTNET,\n        name: NETWORK.FANTOM_TESTNET,\n        parentGroup: 'Fantom',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.SOLANA,\n        name: NETWORK.SOLANA,\n        parentGroup: 'Solana',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_DEVNET,\n        name: NETWORK.SOLANA_DEVNET,\n        parentGroup: 'Solana',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.HARMONY,\n        name: NETWORK.HARMONY,\n        parentGroup: 'Harmony',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.MOONRIVER,\n        name: NETWORK.MOONRIVER,\n        parentGroup: 'Moonriver',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.MOONBEAM,\n        name: NETWORK.MOONBEAM,\n        parentGroup: 'Moonbeam',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.METIS,\n        name: NETWORK.METIS,\n        parentGroup: 'Metis',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    },\n    {\n        label: NETWORK_LABEL.KLATYN_TESTNET,\n        name: NETWORK.KLATYN_TESTNET,\n        parentGroup: 'Klaytn Baobab testnet',\n        hide: {\n            'actions.operation': ['getProofReserve', 'getNFTFloorPricing']\n        }\n    }\n]\n"
  },
  {
    "path": "packages/components/nodes/ContractEventTrigger/ContractEventTrigger.ts",
    "content": "import { ethers, utils } from 'ethers'\nimport { IContract, IDbCollection, INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    networkExplorers,\n    getNetworkProvidersList,\n    NETWORK,\n    NETWORK_PROVIDER,\n    getNetworkProvider,\n    networkProviderCredentials\n} from '../../src/ChainNetwork'\n\nclass ContractEventTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    actions?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'Contract Event Trigger'\n        this.name = 'ContractEventTrigger'\n        this.icon = 'contract-event-trigger.svg'\n        this.type = 'trigger'\n        this.category = 'Smart Contract'\n        this.version = 1.0\n        this.description = 'Start workflow whenever the specified contract event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.actions = [\n            {\n                label: 'Select Contract',\n                name: 'contract',\n                type: 'asyncOptions',\n                loadFromDbCollections: ['Contract'],\n                loadMethod: 'getContracts'\n            },\n            {\n                label: 'Event',\n                name: 'event',\n                type: 'asyncOptions',\n                loadMethod: 'getEvents'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getContracts(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            if (dbCollection === undefined || !dbCollection || !dbCollection.Contract) {\n                return returnData\n            }\n\n            const contracts: IContract[] = dbCollection.Contract\n\n            for (let i = 0; i < contracts.length; i += 1) {\n                const contract = contracts[i]\n                const data = {\n                    label: `${contract.name} (${contract.network})`,\n                    name: JSON.stringify(contract),\n                    description: contract.address\n                } as INodeOptionsValue\n                returnData.push(data)\n            }\n\n            return returnData\n        },\n\n        async getEvents(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionsData = nodeData.actions\n            if (actionsData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionsData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.abi || !contractDetails.address) return returnData\n\n                const abiString = contractDetails.abi\n\n                const abi = JSON.parse(abiString)\n\n                for (const item of abi) {\n                    if (!item.name) continue\n                    if (item.type === 'event') {\n                        const eventName = item.name\n                        const eventInputs = item.inputs\n                        let inputTypes = ''\n                        let value = ''\n                        for (let i = 0; i < eventInputs.length; i++) {\n                            const input = eventInputs[i]\n                            value += input.type\n                            inputTypes += `${input.type} ${input.name}`\n                            if (i !== eventInputs.length - 1) {\n                                inputTypes += ', '\n                                value += ','\n                            }\n                        }\n                        returnData.push({\n                            label: eventName,\n                            name: `${eventName}(${value})`,\n                            description: inputTypes\n                        })\n                    }\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionData = nodeData.actions\n            if (actionData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.network) return returnData\n\n                const network = contractDetails.network\n                return getNetworkProvidersList(network)\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n\n        if (networksData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const contractString = (actionsData.contract as string) || ''\n            const contractDetails: IContract = JSON.parse(contractString)\n            const network = contractDetails.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            const address = contractDetails.address\n            let abi = contractDetails.abi\n            abi = JSON.parse(abi)\n            const event = (actionsData.event as string) || ''\n            const emitEventKey = nodeData.emitEventKey as string\n\n            const filter = {\n                address,\n                topics: [utils.id(event)]\n            }\n\n            provider.on(filter, async (log: any) => {\n                const txHash = log.transactionHash\n                const iface = new ethers.utils.Interface(abi)\n                const logs = await provider.getLogs(filter)\n                const events = logs.map((log) => iface.parseLog(log))\n\n                log['logs'] = events\n                log['explorerLink'] = `${networkExplorers[network]}/tx/${txHash}`\n                this.emit(emitEventKey, returnNodeExecutionData(log))\n            })\n\n            this.providers[emitEventKey] = { provider, filter }\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            const filter = this.providers[emitEventKey].filter\n            provider.off(filter)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ContractEventTrigger }\n"
  },
  {
    "path": "packages/components/nodes/ContractFunctionTrigger/ContractFunctionTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { ethers } from 'ethers'\nimport { IContract, ICronJobs, IDbCollection, INode, INodeData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport { getNetworkProvidersList, NETWORK, NETWORK_PROVIDER, getNetworkProvider, networkProviderCredentials } from '../../src/ChainNetwork'\n\nclass ContractFunctionTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    actions?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'Contract Function Trigger'\n        this.name = 'ContractFunctionTrigger'\n        this.icon = 'contract-function-trigger.svg'\n        this.type = 'trigger'\n        this.category = 'Smart Contract'\n        this.version = 1.0\n        this.description = 'Triggers whenever the specified view function return value changes'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.actions = [\n            {\n                label: 'Select Contract',\n                name: 'contract',\n                type: 'asyncOptions',\n                loadFromDbCollections: ['Contract'],\n                loadMethod: 'getContracts'\n            },\n            {\n                label: 'View Function',\n                name: 'function',\n                type: 'asyncOptions',\n                loadMethod: 'getViewFunctions'\n            },\n            {\n                label: 'Function Parameters',\n                name: 'funcParameters',\n                type: 'json',\n                placeholder: '[\"param1\", \"param2\"]',\n                description: 'Function parameters in array. Ex: [\"param1\", \"param2\"]',\n                optional: true\n            },\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getContracts(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            if (dbCollection === undefined || !dbCollection || !dbCollection.Contract) {\n                return returnData\n            }\n\n            const contracts: IContract[] = dbCollection.Contract\n\n            for (let i = 0; i < contracts.length; i += 1) {\n                const contract = contracts[i]\n                const data = {\n                    label: `${contract.name} (${contract.network})`,\n                    name: JSON.stringify(contract),\n                    description: contract.address\n                } as INodeOptionsValue\n                returnData.push(data)\n            }\n\n            return returnData\n        },\n\n        async getViewFunctions(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionsData = nodeData.actions\n            if (actionsData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionsData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.abi || !contractDetails.address) return returnData\n\n                const abiString = contractDetails.abi\n\n                const abi = JSON.parse(abiString)\n\n                for (const item of abi) {\n                    if (!item.name) continue\n                    if (item.type === 'function' && item.stateMutability === 'view') {\n                        const funcName = item.name\n                        const funcInputs = item.inputs\n                        let inputParameters = ''\n                        let inputTypes = ''\n                        for (let i = 0; i < funcInputs.length; i++) {\n                            const input = funcInputs[i]\n                            inputTypes += `${input.type} ${input.name}`\n                            if (i !== funcInputs.length - 1) inputTypes += ', '\n                            inputParameters += `<li><code class=\"inline\">${input.type}</code> ${input.name}</li>`\n                        }\n                        if (inputParameters) {\n                            inputParameters = '<ul>' + inputParameters + '</ul>'\n                        } else {\n                            inputParameters = '<ul>' + 'none' + '</ul>'\n                        }\n                        returnData.push({\n                            label: funcName,\n                            name: funcName,\n                            description: inputTypes,\n                            inputParameters\n                        })\n                    }\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionData = nodeData.actions\n            if (actionData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.network) return returnData\n\n                const network = contractDetails.network\n                return getNetworkProvidersList(network)\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n\n        if (networksData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const contractString = (actionsData.contract as string) || ''\n            const contractDetails: IContract = JSON.parse(contractString)\n            const network = contractDetails.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            const abiString = contractDetails.abi\n            const address = contractDetails.address\n            const abi = JSON.parse(abiString)\n\n            const emitEventKey = nodeData.emitEventKey as string\n            const functionName = (actionsData.function as string) || ''\n\n            let contractParameters: any[] = []\n\n            const funcParameters = actionsData.funcParameters as string\n            if (funcParameters) {\n                try {\n                    contractParameters = JSON.parse(funcParameters.replace(/\\s/g, ''))\n                } catch (error) {\n                    throw handleErrorMessage(error)\n                }\n            }\n\n            const contract = new ethers.Contract(address, abi, provider)\n\n            const pollTime = (actionsData.pollTime as string) || '30s'\n\n            const cronTimes: string[] = []\n\n            if (pollTime === '15s') {\n                cronTimes.push(`*/15 * * * * *`)\n            } else if (pollTime === '30s') {\n                cronTimes.push(`*/30 * * * * *`)\n            } else if (pollTime === '1min') {\n                cronTimes.push(`*/1 * * * *`)\n            } else if (pollTime === '5min') {\n                cronTimes.push(`*/5 * * * *`)\n            } else if (pollTime === '10min') {\n                cronTimes.push(`*/10 * * * *`)\n            }\n\n            const lastResult = await contract[functionName].apply(null, contractParameters.length > 1 ? contractParameters : null)\n\n            const executeTrigger = async () => {\n                const newResult = await contract[functionName].apply(null, contractParameters.length > 1 ? contractParameters : null)\n\n                if (JSON.stringify(newResult) !== JSON.stringify(lastResult)) {\n                    const returnItem = {\n                        function: functionName,\n                        oldValue: lastResult,\n                        newValue: newResult\n                    }\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                }\n            }\n\n            /// Start the cron-jobs\n            if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n                for (const cronTime of cronTimes) {\n                    // Automatically start the cron job\n                    this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n                }\n            } else {\n                for (const cronTime of cronTimes) {\n                    // Automatically start the cron job\n                    this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n                }\n            }\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ContractFunctionTrigger }\n"
  },
  {
    "path": "packages/components/nodes/CreateERC20Token/CreateERC20Token.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { getNodeModulesPackagePath, handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { ethers } from 'ethers'\nimport * as fs from 'fs'\nimport {\n    ArbitrumNetworks,\n    BSCNetworks,\n    ETHNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    OptimismNetworks,\n    PolygonNetworks\n} from '../../src/ChainNetwork'\n\n// @ts-expect-error no type definition\nimport solc from 'solc'\n\nfunction findImports(_path: string) {\n    const filepath = getNodeModulesPackagePath(_path)\n    const contents = fs.readFileSync(filepath).toString()\n    return { contents }\n}\n\nclass CreateERC20Token implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Create Token'\n        this.name = 'createToken'\n        this.icon = 'erc20.svg'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Create new cryptocurrency token (ERC20)'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks, ...BSCNetworks],\n                default: 'goerli'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Select Wallet',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to create ERC20 Token.',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Token Name',\n                name: 'tokenName',\n                type: 'string',\n                default: '',\n                placeholder: 'MyToken'\n            },\n            {\n                label: 'Token Symbol',\n                name: 'tokenSymbol',\n                type: 'string',\n                default: '',\n                placeholder: 'MYT'\n            },\n            {\n                label: 'Token Supply',\n                name: 'tokenSupply',\n                type: 'number',\n                default: 1000,\n                description: 'Initialy supply of the token'\n            },\n            {\n                label: 'Solidity Version',\n                name: 'solidityVersion',\n                type: 'options',\n                description: 'Soldity version to compile code for token creation',\n                options: [\n                    {\n                        label: '0.8.10',\n                        name: '0.8.10'\n                    },\n                    {\n                        label: '0.8.11',\n                        name: '0.8.11'\n                    },\n                    {\n                        label: '0.8.12',\n                        name: '0.8.12'\n                    },\n                    {\n                        label: '0.8.13',\n                        name: '0.8.13'\n                    },\n                    {\n                        label: '0.8.14',\n                        name: '0.8.14'\n                    },\n                    {\n                        label: '0.8.15',\n                        name: '0.8.15'\n                    }\n                ],\n                default: '0.8.15'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n            const tokenName = inputParametersData.tokenName as string\n            const tokenSupply = inputParametersData.tokenSupply as number\n            const tokenSymbol = inputParametersData.tokenSymbol as string\n            const solidityVersion = inputParametersData.solidityVersion as string\n\n            const input = {\n                language: 'Solidity',\n                sources: {},\n                settings: {\n                    outputSelection: {\n                        '*': {\n                            '*': ['*']\n                        }\n                    }\n                }\n            } as any\n\n            const contractCode = `// SPDX-License-Identifier: MIT\n            pragma solidity ^${solidityVersion};\n            import \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n            \n            contract ${tokenName.trim()} is ERC20 {\n                constructor(uint256 initialSupply) ERC20(\"${tokenName} Token\", \"${tokenSymbol}\"){\n                    _mint(msg.sender, initialSupply);\n                }\n            }`\n\n            input.sources[tokenName + '.sol'] = { content: contractCode }\n            const output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports }))\n\n            const contractOutput = output.contracts[tokenName + '.sol']\n\n            const contractName = Object.keys(contractOutput)[0]\n\n            const bytecode = contractOutput[contractName].evm.bytecode.object\n            const abi = contractOutput[contractName].abi\n\n            const factory = new ethers.ContractFactory(abi, bytecode, wallet)\n\n            const deployedContract = await factory.deploy(ethers.BigNumber.from(`${tokenSupply}000000000000000000`))\n\n            // The contract is NOT deployed yet; we must wait until it is mined\n            await deployedContract.deployed()\n            const returnItem: ICommonObject = {\n                link: `${networkExplorers[network]}/address/${deployedContract.address}`,\n                address: deployedContract.address,\n                transactionHash: deployedContract.deployTransaction.hash\n            }\n\n            return returnNodeExecutionData(returnItem)\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: CreateERC20Token }\n"
  },
  {
    "path": "packages/components/nodes/CreateNFT/CreateNFT.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { getNodeModulesPackagePath, handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { ethers } from 'ethers'\nimport * as fs from 'fs'\nimport {\n    ArbitrumNetworks,\n    BSCNetworks,\n    ETHNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    openseaExplorers,\n    OptimismNetworks,\n    PolygonNetworks\n} from '../../src/ChainNetwork'\n\n// @ts-expect-error no type definition\nimport solc from 'solc'\n\nfunction findImports(_path: string) {\n    const filepath = getNodeModulesPackagePath(_path)\n    const contents = fs.readFileSync(filepath).toString()\n    return { contents }\n}\n\nclass CreateNFT implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Create NFT'\n        this.name = 'createNFT'\n        this.icon = 'createNFT.png'\n        this.type = 'action'\n        this.category = 'NFT'\n        this.version = 1.0\n        this.description = 'Create new NFT (ERC1155)'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks, ...BSCNetworks],\n                default: 'goerli'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Select Wallet',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to create NFT.',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'NFT Metadata',\n                name: 'nftMetadata',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Ipfs Hash/Pin',\n                        name: 'ipfsHash',\n                        description: 'Ipfs hash/pin of the folder that contains the json metadata files'\n                    },\n                    {\n                        label: 'URL',\n                        name: 'url',\n                        description:\n                            'URL of the folder that contains the json metadata files. Ex: https://ipfs.io/ipfs/QmSPiKckfBDhw1pXdjHvU4jndN5pn4ZbKHeA9Nnn622C7U'\n                    }\n                ],\n                description: 'Fetch metadata from a url OR using Ipfs hash/pin'\n            },\n            {\n                label: 'NFT Metadata URL',\n                name: 'nftMetadataJsonUrl',\n                type: 'string',\n                placeholder: 'https://ipfs.io/ipfs/QmSPiKckfBDhw1pXdjHvU4jndN5pn4ZbKHeA9Nnn622C7U',\n                description: 'URL of the folder that contains the json metadata files',\n                show: {\n                    'inputParameters.nftMetadata': ['url']\n                }\n            },\n            {\n                label: 'NFT Metadata Ipfs Hash/Pin',\n                name: 'nftMetadataHash',\n                type: 'string',\n                placeholder: 'QmexuwvmmtwsazQ7LK93SyVdFeYRnDbjET414y2xXiToM4',\n                description: 'Ipfs hash/pin of the folder that contains the json metadata files',\n                show: {\n                    'inputParameters.nftMetadata': ['ipfsHash']\n                }\n            },\n            {\n                label: 'Contract Name',\n                name: 'contractName',\n                type: 'string',\n                default: '',\n                placeholder: 'MyContract',\n                optional: true\n            },\n            {\n                label: 'Collection Name',\n                name: 'collectionName',\n                type: 'string',\n                default: '',\n                placeholder: 'MyCollection',\n                optional: true\n            },\n            {\n                label: 'Solidity Version',\n                name: 'solidityVersion',\n                type: 'options',\n                description: 'Soldity version to compile code for NFT creation',\n                options: [\n                    {\n                        label: '0.8.10',\n                        name: '0.8.10'\n                    },\n                    {\n                        label: '0.8.11',\n                        name: '0.8.11'\n                    },\n                    {\n                        label: '0.8.12',\n                        name: '0.8.12'\n                    },\n                    {\n                        label: '0.8.13',\n                        name: '0.8.13'\n                    },\n                    {\n                        label: '0.8.14',\n                        name: '0.8.14'\n                    },\n                    {\n                        label: '0.8.15',\n                        name: '0.8.15'\n                    }\n                ],\n                default: '0.8.15'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        function getRandomInt(max: number) {\n            return Math.floor(Math.random() * max)\n        }\n\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n            let nftContractName = (inputParametersData.contractName as string) || `ERC1155Contract${getRandomInt(10000)}`\n            const collectionName = (inputParametersData.collectionName as string) || `Untilted Collection #${getRandomInt(10000)}`\n            const nftMetadataJsonUrl = inputParametersData.nftMetadataJsonUrl as string\n            const nftMetadataHash = inputParametersData.nftMetadataHash as string\n            const nftSupply = 1\n            const solidityVersion = inputParametersData.solidityVersion as string\n\n            const input = {\n                language: 'Solidity',\n                sources: {},\n                settings: {\n                    outputSelection: {\n                        '*': {\n                            '*': ['*']\n                        }\n                    }\n                }\n            } as any\n\n            let metadata = ''\n            if (nftMetadataJsonUrl) {\n                metadata = `${nftMetadataJsonUrl}/{id}.json`\n            } else if (nftMetadataHash) {\n                metadata = `ipfs://${nftMetadataHash}/{id}.json`\n            }\n\n            let encodePacked = ''\n            if (metadata) {\n                encodePacked = metadata.substring(0, metadata.lastIndexOf('/') + 1)\n            }\n\n            nftContractName = nftContractName.replace(/\\s/g, '')\n            const tokenId = 0\n\n            const contractCode = `// SPDX-License-Identifier: MIT\n            pragma solidity ^${solidityVersion};\n            \n            import \"@openzeppelin/contracts/token/ERC1155/ERC1155.sol\";\n            import \"@openzeppelin/contracts/access/Ownable.sol\";\n            import \"@openzeppelin/contracts/security/Pausable.sol\";\n            import \"@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol\";\n            import \"@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol\";\n            import \"@openzeppelin/contracts/utils/Strings.sol\";\n            \n            contract ${nftContractName} is ERC1155, Ownable, Pausable, ERC1155Burnable, ERC1155Supply {\n\n                string public name;\n\n                constructor()\n                    ERC1155(\"${metadata}\")\n                {\n                    name = \"${collectionName}\"; //collection name\n                    _mint(msg.sender, ${tokenId}, ${nftSupply}, \"\");\n                }\n\n                // to Put NFT to Opensea\n                function uri(uint256 _tokenId) override public view returns (string memory) {\n                    return string(\n                        abi.encodePacked(\n                            \"${encodePacked}\",\n                            Strings.toString(_tokenId),\n                            \".json\"\n                        )\n                    );\n                }\n\n                function setURI(string memory newuri) public onlyOwner {\n                    _setURI(newuri);\n                }\n\n                function pause() public onlyOwner {\n                    _pause();\n                }\n\n                function unpause() public onlyOwner {\n                    _unpause();\n                }\n\n                function mint(address account, uint256 id, uint256 amount, bytes memory data)\n                    public\n                    onlyOwner\n                {\n                    _mint(account, id, amount, data);\n                }\n\n                function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)\n                    public\n                    onlyOwner\n                {\n                    _mintBatch(to, ids, amounts, data);\n                }\n\n                function _beforeTokenTransfer(address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)\n                    internal\n                    whenNotPaused\n                    override(ERC1155, ERC1155Supply)\n                {\n                    super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n                }\n            }`\n\n            input.sources[nftContractName + '.sol'] = { content: contractCode }\n            const output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports }))\n\n            const contractOutput = output.contracts[nftContractName + '.sol']\n\n            const contractName = Object.keys(contractOutput)[0]\n\n            const bytecode = contractOutput[contractName].evm.bytecode.object\n            const abi = contractOutput[contractName].abi\n\n            const factory = new ethers.ContractFactory(abi, bytecode, wallet)\n\n            const deployedContract = await factory.deploy()\n\n            // The contract is NOT deployed yet; we must wait until it is mined\n            await deployedContract.deployed()\n            const returnItem: ICommonObject = {\n                explorerLink: `${networkExplorers[network]}/address/${deployedContract.address}`,\n                openseaLink: `${openseaExplorers[network]}/assets/${deployedContract.address}/${tokenId}`,\n                address: deployedContract.address,\n                transactionHash: deployedContract.deployTransaction.hash\n            }\n\n            return returnNodeExecutionData(returnItem)\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: CreateNFT }\n"
  },
  {
    "path": "packages/components/nodes/Discord/Discord.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\ninterface IDiscordWebhook {\n    content?: string\n    username?: string\n    avatar_url?: string\n    tts?: boolean\n}\n\nclass Discord implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Discord'\n        this.name = 'discord'\n        this.icon = 'discord.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Post message in Discord channel'\n        this.incoming = 1\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'Webhook URL',\n                name: 'webhookUrl',\n                type: 'string',\n                default: '',\n                description: 'Webhook URL for the channel. Learn how to get: https://www.youtube.com/watch?v=K8vgRWZnSZw'\n            },\n            {\n                label: 'Content',\n                description: 'Message contents (up to 2000 characters)',\n                name: 'content',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Username',\n                name: 'username',\n                type: 'string',\n                default: '',\n                description: 'Override the default username of the webhook',\n                optional: true\n            },\n            {\n                label: 'Avatar URL',\n                name: 'avatarUrl',\n                type: 'string',\n                default: '',\n                description: 'Override the default avatar of the webhook',\n                optional: true\n            },\n            {\n                label: 'TTS',\n                name: 'tts',\n                type: 'boolean',\n                default: false,\n                description: 'Send as Text To Speech message',\n                optional: true\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const returnData: ICommonObject[] = []\n        const body: IDiscordWebhook = {}\n\n        const webhookUrl = inputParametersData.webhookUrl as string\n        const content = inputParametersData.content as string\n        body.content = content\n\n        if (inputParametersData.username) body.username = inputParametersData.username as string\n        if (inputParametersData.avatarUrl) body.avatar_url = inputParametersData.avatarUrl as string\n        if (inputParametersData.tts) body.tts = inputParametersData.tts as boolean\n\n        let responseData: any\n        let maxRetries = 5\n\n        do {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `${webhookUrl}?wait=true`,\n                    data: body,\n                    headers: {\n                        'Content-Type': 'application/json; charset=utf-8'\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                // Rate limit exceeded\n                if (error.response && error.response.status === 429) {\n                    const retryAfter = error.response?.headers['retry-after'] || 60\n                    await new Promise<void>((resolve, _) => {\n                        setTimeout(() => {\n                            resolve()\n                        }, retryAfter * 1000)\n                    })\n                    continue\n                }\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error posting message to discord channel. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Discord }\n"
  },
  {
    "path": "packages/components/nodes/ERC20Function/ERC20Function.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    ArbitrumNetworks,\n    chainIdLookup,\n    ETHNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    OptimismNetworks,\n    PolygonNetworks\n} from '../../src'\nimport { ContractInterface, ethers } from 'ethers'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { IToken } from '../Uniswap/nativeTokens'\nimport {\n    allowanceParameters,\n    approveParameters,\n    balanceOfParameters,\n    depositParameters,\n    ERC20Functions,\n    transferFromParameters,\n    withdrawParameters\n} from './helperFunctions'\nimport IERC20 from '../../src/abis/WETH.json'\nimport IWETH from '@uniswap/v2-periphery/build/IWETH.json'\n\nclass ERC20Function implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'ERC20 Function'\n        this.name = 'ERC20Function'\n        this.icon = 'erc20.svg'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Execute ERC20 function such as deposit, withdraw, get balance, etc'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Function',\n                name: 'function',\n                type: 'options',\n                options: [...ERC20Functions]\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...OptimismNetworks, ...ArbitrumNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'ERC20 Token',\n                name: 'erc20Token',\n                type: 'asyncOptions',\n                description: 'ERC20 Token to send/transfer',\n                loadMethod: 'getTokens'\n            },\n            {\n                label: 'Custom ERC20 Address',\n                name: 'customERC20TokenAddress',\n                type: 'string',\n                description: 'ERC20 Token Address',\n                show: {\n                    'inputParameters.erc20Token': ['customERC20Address']\n                }\n            },\n            ...approveParameters,\n            ...allowanceParameters,\n            ...balanceOfParameters,\n            ...transferFromParameters,\n            ...depositParameters,\n            ...withdrawParameters,\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction',\n                show: {\n                    'actions.function': ['approve', 'transferFrom', 'deposit']\n                }\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>',\n                show: {\n                    'actions.function': ['approve', 'transferFrom', 'deposit']\n                }\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>',\n                show: {\n                    'actions.function': ['approve', 'transferFrom', 'deposit']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        },\n\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.uniswap.org`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n\n                // Add custom token\n                const data = {\n                    label: `- Custom ERC20 Address -`,\n                    name: `customERC20Address`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const actionsData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const erc20Function = actionsData.function as string\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            const owner = inputParametersData.owner as string\n            const spender = inputParametersData.spender as string\n            const amount = inputParametersData.amount as string\n            const account = inputParametersData.account as string\n            const from = inputParametersData.from as string\n            const to = inputParametersData.to as string\n\n            const erc20Token = inputParametersData.erc20Token as string\n            const customERC20TokenAddress = inputParametersData.customERC20TokenAddress as string\n\n            const contractAddress = erc20Token === 'customERC20Address' ? customERC20TokenAddress : erc20Token.split(';')[0]\n\n            const contractInstance = new ethers.Contract(contractAddress, IERC20, provider)\n            const decimals =\n                erc20Token === 'customERC20Address'\n                    ? parseInt(await contractInstance.decimals(), 10)\n                    : parseInt(erc20Token.split(';').pop() || '0', 10)\n\n            let returnItem = { function: erc20Function, link: `${networkExplorers[network]}/address/${contractAddress}` } as any\n\n            if (erc20Function === 'allowance') {\n                // allowance(address owner, address spender) → uint256\n                returnItem.result = await contractInstance.allowance(owner, spender)\n            } else if (erc20Function === 'approve') {\n                // approve(address spender, uint256 amount) → bool\n                const { txOption, wallet } = await getWalletSigner(inputParametersData, provider)\n                const functionApproveAbi = ['function approve(address spender, uint256 amount) external returns (boolean)']\n                const contractInstance = new ethers.Contract(contractAddress, functionApproveAbi, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await contractInstance.approve(spender, numberOfTokens, txOption)\n                const txReceipt = await tx.wait()\n                returnItem = {\n                    function: erc20Function,\n                    spender,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            } else if (erc20Function === 'balanceOf') {\n                // balanceOf(address account) → uint256\n                returnItem.result = ethers.utils.formatEther(await contractInstance.balanceOf(account))\n            } else if (erc20Function === 'decimals') {\n                // decimals() → uint8\n                returnItem.result = await contractInstance.decimals()\n            } else if (erc20Function === 'name') {\n                // name() → string\n                returnItem.result = await contractInstance.name()\n            } else if (erc20Function === 'symbol') {\n                // symbol() → string\n                returnItem.result = await contractInstance.symbol()\n            } else if (erc20Function === 'totalSupply') {\n                // totalSupply() → uint256\n                returnItem.result = ethers.utils.formatEther(await contractInstance.totalSupply())\n            } else if (erc20Function === 'transferFrom') {\n                // transferFrom(address sender, address recipient, uint256 amount) → bool\n                const { txOption, wallet } = await getWalletSigner(inputParametersData, provider)\n                const functionTransferFromAbi = [\n                    'function transferFrom(address sender, address recipient, uint256 amount) external returns (boolean)'\n                ]\n                const contractInstance = new ethers.Contract(contractAddress, functionTransferFromAbi, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await contractInstance.transferFrom(from, to, numberOfTokens, txOption)\n                const txReceipt = await tx.wait()\n                returnItem = {\n                    function: erc20Function,\n                    transferFrom: from,\n                    transferTo: to,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            } else if (erc20Function === 'deposit') {\n                const { txOption, wallet } = await getWalletSigner(inputParametersData, provider)\n                const wrapEthContract = new ethers.Contract(contractAddress, IWETH['abi'] as ContractInterface, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await wrapEthContract.deposit({ ...txOption, value: numberOfTokens })\n                const txReceipt = await tx.wait()\n                if (txReceipt.status === 0) throw new Error(`Failed to deposit ETH to ${contractAddress}`)\n                returnItem = {\n                    function: erc20Function,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            } else if (erc20Function === 'withdraw') {\n                const { wallet } = await getWalletSigner(inputParametersData, provider)\n                const wrapEthContract = new ethers.Contract(contractAddress, IWETH['abi'] as ContractInterface, wallet)\n                const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n                const tx = await wrapEthContract.withdraw(numberOfTokens)\n                const txReceipt = await tx.wait()\n                if (txReceipt.status === 0) throw new Error(`Failed to withdraw ETH from ${contractAddress}`)\n                returnItem = {\n                    function: erc20Function,\n                    amount,\n                    transactionHash: tx.hash,\n                    transactionReceipt: txReceipt as any,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nconst getWalletSigner = async (\n    inputParametersData: ICommonObject,\n    provider: ethers.providers.JsonRpcProvider | ethers.providers.FallbackProvider\n) => {\n    const walletString = inputParametersData.wallet as string\n    const walletDetails: IWallet = JSON.parse(walletString)\n    const walletCredential = JSON.parse(walletDetails.walletCredential)\n    const gasLimit = inputParametersData.gasLimit as number\n    const maxFeePerGas = inputParametersData.maxFeePerGas as number\n    const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n    const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n    const txOption = {} as any\n    txOption.nonce = await provider.getTransactionCount(walletDetails.address)\n    if (gasLimit) txOption.gasLimit = gasLimit\n    if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n    if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n    return { txOption, wallet }\n}\n\nmodule.exports = { nodeClass: ERC20Function }\n"
  },
  {
    "path": "packages/components/nodes/ERC20Function/helperFunctions.ts",
    "content": "import { INodeOptionsValue, INodeParams } from '../../src'\n\nexport const ERC20Functions = [\n    {\n        name: 'deposit',\n        label: 'Deposit',\n        description: 'Deposit ETH into ERC20 token'\n    },\n    {\n        name: 'withdraw',\n        label: 'Withdraw',\n        description: 'Withdraw ETH from ERC20 token'\n    },\n    {\n        name: 'allowance',\n        label: 'Get Allowance',\n        description:\n            'Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom. This is zero by default.'\n    },\n    {\n        name: 'approve',\n        label: 'Approve',\n        description: 'Sets amount as the allowance of spender over the caller’s tokens.'\n    },\n    {\n        name: 'balanceOf',\n        label: 'Get Balance',\n        description: 'Returns the amount of tokens owned by account'\n    },\n    {\n        name: 'decimals',\n        label: 'Get ERC20 Decimals',\n        description: 'Returns the decimals of ERC20'\n    },\n    {\n        name: 'name',\n        label: 'Get ERC20 Name',\n        description: 'Returns the name of ERC20'\n    },\n    {\n        name: 'symbol',\n        label: 'Get ERC20 Symbol',\n        description: 'Returns the symbol of ERC20'\n    },\n    {\n        name: 'totalSupply',\n        label: 'Get ERC20 Total Supply',\n        description: 'Returns the total supply of ERC20'\n    },\n    {\n        name: 'transferFrom',\n        label: 'Transfer From',\n        description:\n            'Moves amount tokens from sender to recipient using the allowance mechanism. Amount is then deducted from the caller’s allowance.'\n    }\n] as INodeOptionsValue[]\n\nexport const allowanceParameters = [\n    {\n        label: 'Owner Address',\n        name: 'owner',\n        type: 'string',\n        show: {\n            'actions.function': ['allowance']\n        }\n    },\n    {\n        label: 'Spender Address',\n        name: 'spender',\n        type: 'string',\n        show: {\n            'actions.function': ['allowance']\n        }\n    }\n] as INodeParams[]\n\nexport const approveParameters = [\n    {\n        label: 'Spender Address',\n        name: 'spender',\n        type: 'string',\n        show: {\n            'actions.function': ['approve']\n        }\n    },\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        show: {\n            'actions.function': ['approve']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Wallet to execute approve function',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['approve']\n        }\n    }\n] as INodeParams[]\n\nexport const balanceOfParameters = [\n    {\n        label: 'Account Address',\n        name: 'account',\n        type: 'string',\n        description: 'Account address to check for remaining amount',\n        show: {\n            'actions.function': ['balanceOf']\n        }\n    }\n] as INodeParams[]\n\nexport const transferFromParameters = [\n    {\n        label: 'From Address',\n        name: 'from',\n        type: 'string',\n        description: 'Account address to transfer the token',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    },\n    {\n        label: 'To Address',\n        name: 'to',\n        type: 'string',\n        description: 'Account address to receive the token',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    },\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        description: 'Amount of token transfer',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Wallet to move amount tokens from sender to recipient using the allowance mechanism',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['transferFrom']\n        }\n    }\n] as INodeParams[]\n\nexport const depositParameters = [\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        description: 'Amount of token to deposit',\n        show: {\n            'actions.function': ['deposit']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Wallet to deposit ETH into ERC20 Token',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['deposit']\n        }\n    }\n] as INodeParams[]\n\nexport const withdrawParameters = [\n    {\n        label: 'Amount',\n        name: 'amount',\n        type: 'number',\n        description: 'Amount of token to withdraw',\n        show: {\n            'actions.function': ['withdraw']\n        }\n    },\n    {\n        label: 'Select Wallet',\n        name: 'wallet',\n        type: 'asyncOptions',\n        description: 'Withdraw ERC20 Token to this wallet',\n        loadFromDbCollections: ['Wallet'],\n        loadMethod: 'getWallets',\n        show: {\n            'actions.function': ['withdraw']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/ERC20Transfer/ERC20Transfer.ts",
    "content": "import { IDbCollection, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, IWallet, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    ArbitrumNetworks,\n    chainIdLookup,\n    ETHNetworks,\n    functionTransferAbi,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    OptimismNetworks,\n    PolygonNetworks\n} from '../../src'\nimport { ethers } from 'ethers'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { IToken } from '../Uniswap/nativeTokens'\nimport IERC20 from '../../src/abis/WETH.json'\n\nclass ERC20Transfer implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'ERC20 Transfer'\n        this.name = 'ERC20Transfer'\n        this.icon = 'erc20.svg'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Send/Transfer ERC20 to an address'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...OptimismNetworks, ...ArbitrumNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'ERC20 Token',\n                name: 'erc20Token',\n                type: 'asyncOptions',\n                description: 'ERC20 Token to send/transfer',\n                loadMethod: 'getTokens'\n            },\n            {\n                label: 'Custom ERC20 Address',\n                name: 'customERC20TokenAddress',\n                type: 'string',\n                description: 'ERC20 Token Address',\n                show: {\n                    'inputParameters.erc20Token': ['customERC20Address']\n                }\n            },\n            {\n                label: 'Wallet To Transfer',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to send/transfer ERC20',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Address To Receive',\n                name: 'address',\n                type: 'string',\n                default: '',\n                description: 'Address to receive ERC20'\n            },\n            {\n                label: 'Amount',\n                name: 'amount',\n                type: 'number',\n                description: 'Amount of ERC20 to transfer'\n            },\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction'\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        },\n\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.uniswap.org`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n\n                // Add custom token\n                const data = {\n                    label: `- Custom ERC20 Address -`,\n                    name: `customERC20Address`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n            const address = inputParametersData.address as string\n            const amount = inputParametersData.amount as string\n            const erc20Token = inputParametersData.erc20Token as string\n            const customERC20TokenAddress = inputParametersData.customERC20TokenAddress as string\n            const gasLimit = inputParametersData.gasLimit as number\n            const maxFeePerGas = inputParametersData.maxFeePerGas as number\n            const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n            const contractAddress = erc20Token === 'customERC20Address' ? customERC20TokenAddress : erc20Token.split(';')[0]\n            const contractInstance = new ethers.Contract(contractAddress, IERC20, wallet)\n            const decimals =\n                erc20Token === 'customERC20Address'\n                    ? parseInt(await contractInstance.decimals(), 10)\n                    : parseInt(erc20Token.split(';').pop() || '0', 10)\n            const numberOfTokens = ethers.utils.parseUnits(amount, decimals)\n\n            // Send token\n            const nonce = await provider.getTransactionCount(walletDetails.address)\n            const txOption = {\n                nonce\n            } as any\n\n            if (gasLimit) txOption.gasLimit = gasLimit\n            if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n            if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n            const contractInstanceForTransfer = new ethers.Contract(contractAddress, functionTransferAbi, wallet)\n            const tx = await contractInstanceForTransfer.transfer(address, numberOfTokens, txOption)\n            const txReceipt = await tx.wait()\n\n            const returnItem = {\n                transferFrom: wallet.address,\n                transferTo: address,\n                amount,\n                transactionHash: tx.hash,\n                transactionReceipt: txReceipt as any,\n                link: `${networkExplorers[network]}/tx/${tx.hash}`\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ERC20Transfer }\n"
  },
  {
    "path": "packages/components/nodes/ERC20TransferTrigger/ERC20TransferTrigger.ts",
    "content": "import { BigNumber, ethers, utils } from 'ethers'\nimport { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    ArbitrumNetworks,\n    ETHNetworks,\n    OptimismNetworks,\n    PolygonNetworks,\n    networkExplorers,\n    getNetworkProvidersList,\n    NETWORK,\n    getNetworkProvider,\n    NETWORK_PROVIDER,\n    eventTransferAbi,\n    networkProviderCredentials,\n    chainIdLookup\n} from '../../src/ChainNetwork'\nimport IERC20 from '../../src/abis/WETH.json'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { IToken } from '../PancakeSwap/extendedTokens'\n\nclass ERC20TransferTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'ERC20 Transfer Trigger'\n        this.name = 'ERC20TransferTrigger'\n        this.icon = 'erc20.svg'\n        this.type = 'trigger'\n        this.category = 'Cryptocurrency'\n        this.version = 1.1\n        this.description = 'Start workflow whenever an ERC20 transfer event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks],\n                default: 'homestead'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'ERC20 Token',\n                name: 'erc20Token',\n                type: 'asyncOptions',\n                description: 'ERC20 Token to send/transfer',\n                loadMethod: 'getTokens',\n                default: 'anyERC20Address'\n            },\n            {\n                label: 'Custom ERC20 Address',\n                name: 'customERC20TokenAddress',\n                type: 'string',\n                description: 'ERC20 Token Address',\n                show: {\n                    'inputParameters.erc20Token': ['customERC20Address']\n                }\n            },\n            {\n                label: 'Direction',\n                name: 'direction',\n                type: 'options',\n                options: [\n                    {\n                        label: 'From',\n                        name: 'from',\n                        description: 'Transfer from wallet address'\n                    },\n                    {\n                        label: 'To',\n                        name: 'to',\n                        description: 'Transfer to wallet address'\n                    },\n                    {\n                        label: 'Both From and To',\n                        name: 'fromTo',\n                        description: 'Transfer from a wallet address to another wallet address'\n                    }\n                ],\n                default: ''\n            },\n            {\n                label: 'From Wallet Address',\n                name: 'fromAddress',\n                type: 'string',\n                default: '',\n                show: {\n                    'inputParameters.direction': ['from', 'fromTo']\n                }\n            },\n            {\n                label: 'To Wallet Address',\n                name: 'toAddress',\n                type: 'string',\n                default: '',\n                show: {\n                    'inputParameters.direction': ['to', 'fromTo']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        },\n\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.uniswap.org`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n\n                // Add any token\n                const anyData = {\n                    label: `- Any ERC20 Token -`,\n                    name: `anyERC20Address`\n                } as INodeOptionsValue\n                returnData.push(anyData)\n\n                // Add custom token\n                const data = {\n                    label: `- Custom ERC20 Address -`,\n                    name: `customERC20Address`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const fromAddress = (inputParametersData.fromAddress as string) || null\n        const toAddress = (inputParametersData.toAddress as string) || null\n        const erc20Token = inputParametersData.erc20Token as string\n        const customERC20TokenAddress = inputParametersData.customERC20TokenAddress as string\n\n        const filter = {\n            topics: [\n                utils.id('Transfer(address,address,uint256)'),\n                fromAddress ? utils.hexZeroPad(fromAddress, 32) : null,\n                toAddress ? utils.hexZeroPad(toAddress, 32) : null\n            ]\n        } as any\n\n        if (erc20Token !== 'anyERC20Address') {\n            filter['address'] = erc20Token === 'customERC20Address' ? customERC20TokenAddress : erc20Token.split(';')[0]\n        }\n\n        provider.on(filter, async (log: any) => {\n            const txHash = log.transactionHash\n            const contractInstance = new ethers.Contract(log.address, IERC20, provider)\n            const iface = new ethers.utils.Interface(eventTransferAbi)\n            const logs = await provider.getLogs(filter)\n            const events = logs.map((log) => iface.parseLog(log))\n            const fromWallet = events.length ? events[0].args[0] : ''\n            const toWallet = events.length ? events[0].args[1] : ''\n            const value: BigNumber = events.length ? events[0].args[2] : ''\n\n            //ERC20 has 3 topics length\n            if (log.topics.length === 3) {\n                const returnItem = {} as ICommonObject\n\n                const name = await contractInstance.name()\n                const symbol = await contractInstance.symbol()\n                const decimals = await contractInstance.decimals()\n                const amount = utils.formatUnits(value.toString(), decimals)\n\n                returnItem['Token Name'] = name\n                returnItem['Token Symbol'] = symbol\n                returnItem['Token Address'] = log.address\n                returnItem['From Wallet'] = fromWallet\n                returnItem['To Wallet'] = toWallet\n                returnItem['Amount Transfered'] = parseFloat(amount)\n                returnItem['txHash'] = txHash\n                returnItem['explorerLink'] = `${networkExplorers[network]}/tx/${txHash}`\n\n                this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n            }\n        })\n\n        this.providers[emitEventKey] = { provider, filter }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            const filter = this.providers[emitEventKey].filter\n            provider.off(filter)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ERC20TransferTrigger }\n"
  },
  {
    "path": "packages/components/nodes/ETHBalanceTrigger/ETHBalanceTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { BigNumber, utils } from 'ethers'\nimport { ICronJobs, INode, INodeData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    ArbitrumNetworks,\n    ETHNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    OptimismNetworks\n} from '../../src/ChainNetwork'\n\nclass ETHBalanceTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'ETH Balance Trigger'\n        this.name = 'ETHBalanceTrigger'\n        this.icon = 'ethereum.svg'\n        this.type = 'trigger'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Start workflow whenever ETH balance in wallet changes'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...ArbitrumNetworks, ...OptimismNetworks],\n                default: 'homestead'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet Address',\n                name: 'address',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Trigger Condition',\n                name: 'triggerCondition',\n                type: 'options',\n                options: [\n                    {\n                        label: 'When balance increased',\n                        name: 'increase'\n                    },\n                    {\n                        label: 'When balance decreased',\n                        name: 'decrease'\n                    }\n                ],\n                default: 'increase'\n            },\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const address = (inputParametersData.address as string) || ''\n        const pollTime = (inputParametersData.pollTime as string) || '30s'\n        const triggerCondition = (inputParametersData.triggerCondition as string) || 'increase'\n\n        const cronTimes: string[] = []\n\n        if (pollTime === '15s') {\n            cronTimes.push(`*/15 * * * * *`)\n        } else if (pollTime === '30s') {\n            cronTimes.push(`*/30 * * * * *`)\n        } else if (pollTime === '1min') {\n            cronTimes.push(`*/1 * * * *`)\n        } else if (pollTime === '5min') {\n            cronTimes.push(`*/5 * * * *`)\n        } else if (pollTime === '10min') {\n            cronTimes.push(`*/10 * * * *`)\n        }\n\n        let lastBalance: BigNumber = await provider.getBalance(address)\n\n        const executeTrigger = async () => {\n            const newBalance: BigNumber = await provider.getBalance(address)\n            if (!newBalance.eq(lastBalance)) {\n                if (triggerCondition === 'increase' && newBalance.gt(lastBalance)) {\n                    const balanceInEth = utils.formatEther(BigNumber.from(newBalance.toString()))\n                    const diffInEth = newBalance.sub(lastBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInEth} ETH`,\n                        lastBalance: `${utils.formatEther(BigNumber.from(lastBalance.toString()))} ETH`,\n                        difference: `${utils.formatEther(BigNumber.from(diffInEth.toString()))} ETH`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'ETH balance increase'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else if (triggerCondition === 'decrease' && newBalance.lt(lastBalance)) {\n                    const balanceInEth = utils.formatEther(BigNumber.from(newBalance.toString()))\n                    const diffInEth = lastBalance.sub(newBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInEth} ETH`,\n                        lastBalance: `${utils.formatEther(BigNumber.from(lastBalance.toString()))} ETH`,\n                        difference: `${utils.formatEther(BigNumber.from(diffInEth.toString()))} ETH`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'ETH balance decrease'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else {\n                    lastBalance = newBalance\n                }\n            }\n        }\n\n        // Start the cron-jobs\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n            }\n        } else {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n            }\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ETHBalanceTrigger }\n"
  },
  {
    "path": "packages/components/nodes/ETHTransfer/ETHTransfer.ts",
    "content": "import { IDbCollection, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, IWallet, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    ArbitrumNetworks,\n    ETHNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    OptimismNetworks\n} from '../../src'\nimport { ethers } from 'ethers'\n\nclass ETHTransfer implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'ETH Transfer'\n        this.name = 'ETHTransfer'\n        this.icon = 'ethereum.svg'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Send/Transfer ETH to an address'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...OptimismNetworks, ...ArbitrumNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet To Transfer',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to send/transfer ETH',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Address To Receive',\n                name: 'address',\n                type: 'string',\n                default: '',\n                description: 'Address to receive ETH. Can be an ens name: username.eth'\n            },\n            {\n                label: 'Amount',\n                name: 'amount',\n                type: 'number',\n                description: 'Amount of ETH to transfer'\n            },\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction'\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n            const address = inputParametersData.address as string\n            const amount = inputParametersData.amount as string\n            const gasLimit = inputParametersData.gasLimit as number\n            const maxFeePerGas = inputParametersData.maxFeePerGas as number\n            const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n            // Send token\n            const nonce = await provider.getTransactionCount(walletDetails.address)\n            const txOption = {\n                nonce\n            } as any\n\n            if (gasLimit) txOption.gasLimit = gasLimit\n            if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n            if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n            const tx = await wallet.sendTransaction({\n                to: address,\n                value: ethers.utils.parseEther(amount),\n                ...txOption\n            })\n\n            const txReceipt = await tx.wait()\n\n            const returnItem = {\n                transferFrom: wallet.address,\n                transferTo: address,\n                amount,\n                transactionHash: tx.hash,\n                transactionReceipt: txReceipt as any,\n                link: `${networkExplorers[network]}/tx/${tx.hash}`\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ETHTransfer }\n"
  },
  {
    "path": "packages/components/nodes/EmailSend/EmailSend.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport { createTransport } from 'nodemailer'\nimport SMTPTransport = require('nodemailer/lib/smtp-transport')\n\nclass EmailSend implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Send Email'\n        this.name = 'emailSend'\n        this.icon = 'emailsend.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Send email to single or multiple receipients'\n        this.incoming = 1\n        this.outgoing = 1\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Email Send Smtp',\n                        name: 'emailSendSmtp'\n                    }\n                ],\n                default: 'emailSendSmtp'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'From Email',\n                name: 'fromEmail',\n                type: 'string',\n                default: '',\n                description: 'Email address of the sender.'\n            },\n            {\n                label: 'To Email',\n                name: 'toEmail',\n                type: 'string',\n                default: '',\n                description: 'Email address of the recipient. Multiple emails can be comma-separated.',\n                optional: true\n            },\n            {\n                label: 'CC Email',\n                name: 'ccEmail',\n                type: 'string',\n                default: '',\n                description: 'Email address of CC recipient. Multiple emails can be comma-separated.',\n                optional: true\n            },\n            {\n                label: 'Subject',\n                name: 'subject',\n                type: 'string',\n                default: '',\n                description: 'Subject line of the email.'\n            },\n            {\n                label: 'Body - Plain Text',\n                name: 'text',\n                type: 'string',\n                rows: 5,\n                default: '',\n                description: 'Plain text message of email.',\n                optional: true\n            },\n            {\n                label: 'Body - HTML',\n                name: 'html',\n                type: 'string',\n                rows: 5,\n                default: '',\n                description: 'HTML text message of email.',\n                optional: true\n            },\n            {\n                label: 'Ignore SSL',\n                name: 'ignoreSSL',\n                type: 'boolean',\n                default: false,\n                description: 'Send email regardless of SSL validation.',\n                optional: true\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        const fromEmail = inputParametersData.fromEmail as string\n        const toEmail = inputParametersData.toEmail as string\n        const ccEmail = inputParametersData.ccEmail as string\n        const subject = inputParametersData.subject as string\n        const text = inputParametersData.text as string\n        const html = inputParametersData.html as string\n        const ignoreSSL = inputParametersData.ignoreSSL as boolean\n\n        const connectionOptions: SMTPTransport.Options = {\n            host: credentials.host as string,\n            port: credentials.port as number,\n            secure: credentials.secure as boolean\n        }\n\n        if (credentials.user || credentials.password) {\n            connectionOptions.auth = {\n                user: credentials.user as string,\n                pass: credentials.password as string\n            }\n        }\n\n        if (ignoreSSL) {\n            connectionOptions.tls = {\n                rejectUnauthorized: false\n            }\n        }\n\n        const transporter = createTransport(connectionOptions)\n\n        const mailOptions = {\n            from: fromEmail,\n            to: toEmail,\n            cc: ccEmail,\n            subject,\n            text,\n            html\n        }\n\n        const info = await transporter.sendMail(mailOptions)\n\n        const returnData: ICommonObject[] = []\n\n        if (Array.isArray(info)) {\n            returnData.push(...info)\n        } else {\n            returnData.push(info as unknown as ICommonObject)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: EmailSend }\n"
  },
  {
    "path": "packages/components/nodes/EmailTrigger/EmailTrigger.ts",
    "content": "import { INode, INodeData, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport Imap from 'imap'\nimport moment from 'moment'\nimport { simpleParser } from 'mailparser'\n\nclass EmailTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    credentials?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'Email Trigger'\n        this.name = 'emailTrigger'\n        this.icon = 'email-trigger.svg'\n        this.type = 'trigger'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Start workflow whenever a new email is received'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Imap',\n                        name: 'imap'\n                    }\n                ],\n                default: 'imap'\n            }\n        ] as INodeParams[]\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const credentials = nodeData.credentials\n\n        if (credentials === undefined) {\n            throw new Error('Imap credentials missing')\n        }\n\n        let timeNow = new Date().getTime()\n\n        const imap = new Imap({\n            user: credentials.userEmail as string,\n            password: credentials.password as string,\n            host: credentials.host as string,\n            port: credentials.port as number,\n            tls: credentials.tls as boolean\n        })\n\n        const openInbox = (cb: any) => {\n            imap.openBox('INBOX', true, cb)\n        }\n\n        imap.once('ready', () => {\n            openInbox((err: any) => {\n                if (err) throw handleErrorMessage(err)\n\n                imap.on('mail', () => {\n                    try {\n                        imap.search(['NEW', ['SINCE', moment().format('MMMM D, YYYY')]], (err, results) => {\n                            if (err) throw handleErrorMessage(err)\n\n                            if (!results || !results.length) {\n                                this.emit(emitEventKey, returnNodeExecutionData({ message: 'No new unread emails' }))\n                            }\n\n                            const f = imap.fetch(results, { bodies: '', markSeen: true })\n\n                            f.on('message', (msg: Imap.ImapMessage) => {\n                                msg.on('body', (stream) => {\n                                    simpleParser(stream, (err, mail) => {\n                                        if (err) throw handleErrorMessage(err)\n                                        const returnData = {\n                                            from: mail.headers.get('from'),\n                                            to: mail.headers.get('to'),\n                                            subject: mail.subject,\n                                            date: mail.date,\n                                            text: mail.text,\n                                            htmlText: mail.textAsHtml,\n                                            html: mail.html\n                                        } as any\n\n                                        if (mail.headers.has('cc')) returnData.cc = mail.headers.get('cc')\n                                        if (mail.headers.has('bcc')) returnData.bcc = mail.headers.get('bcc')\n\n                                        // Convert Buffer to base64 string\n                                        if (mail.attachments && mail.attachments.length) {\n                                            for (let i = 0; i < mail.attachments.length; i += 1) {\n                                                ;(mail.attachments[i] as any).content = `data:${\n                                                    mail.attachments[i].contentType\n                                                };base64,${mail.attachments[i].content.toString('base64')}`\n                                            }\n                                            returnData.attachments = mail.attachments\n                                        }\n\n                                        const emailDate = mail.date?.getTime()\n\n                                        if (emailDate && timeNow < emailDate) {\n                                            timeNow = emailDate\n                                            this.emit(emitEventKey, returnNodeExecutionData(returnData))\n                                        }\n                                    })\n                                })\n                            })\n\n                            f.once('error', (err) => {\n                                console.error('on mail error: ', err)\n                                throw new Error('Email Trigger Error: ' + err)\n                            })\n                        })\n                    } catch (err) {\n                        console.error(err)\n                        throw new Error('Email Trigger Error: ' + err)\n                    }\n                })\n            })\n        })\n\n        imap.once('error', (err: any) => {\n            console.error('imap error = ', err)\n            throw new Error('Email Trigger Error: ' + err)\n        })\n\n        imap.connect()\n\n        const emitEventKey = nodeData.emitEventKey as string\n\n        this.providers[emitEventKey] = { provider: imap }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            if (provider) provider.end()\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: EmailTrigger }\n"
  },
  {
    "path": "packages/components/nodes/Etherscan/Etherscan.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport { NETWORK, NETWORK_LABEL, etherscanAPIs } from '../../src/ChainNetwork'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport {\n    SORT_BY,\n    OPERATIONS,\n    GET_ETHER_BALANCE,\n    GET_HISTORICAL_ETHER_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_BLOCKS_VALIDATED,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_BALANCE,\n    GET_HISTORICAL_ERC20_TOKEN_SUPPLY,\n    GET_HISTORICAL_ERC20_TOKEN_BALANCE,\n    GET_TOKEN_INFO,\n    GET_ETHER_PRICE,\n    GET_HISTORICAL_ETHER_PRICE,\n    GET_MULTI_ETHER_BALANCE\n} from './constants'\nclass Etherscan implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Etherscan'\n        this.name = 'etherscan'\n        this.icon = 'etherscan.svg'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Perform Etherscan operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Ether Balance for a Single Address',\n                        name: GET_ETHER_BALANCE.name,\n                        description: 'Returns the Ether balance of a given address.'\n                    },\n                    {\n                        label: 'Get Ether Balance for Multiple Addresses(separated by a comma)',\n                        name: GET_MULTI_ETHER_BALANCE.name,\n                        description: 'Returns the Ether balance of the addresses(each address separated by a comma) entered.'\n                    },\n                    {\n                        label: 'Get transactions',\n                        name: GET_NORMAL_TRANSACTIONS.name,\n                        description: 'Returns the list of transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions',\n                        name: GET_INTERNAL_TRANSACTIONS.name,\n                        description: 'Returns the list of internal transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions by hash',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_HASH.name,\n                        description: 'Returns the list of internal transactions performed within a transaction.'\n                    },\n                    {\n                        label: 'Get internal transactions by block',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        description: 'Returns the list of internal transactions performed within a block range, with optional pagination.'\n                    },\n                    {\n                        label: 'Get list of Blocks Validated by Address',\n                        name: GET_BLOCKS_VALIDATED.name,\n                        description: 'Returns the list of blocks validated by an address.'\n                    },\n                    {\n                        label: 'Get Contract ABI',\n                        name: GET_ABI.name,\n                        description: 'Returns the contract Application Binary Interface ( ABI ) of a verified smart contract.'\n                    },\n                    {\n                        label: 'Get Contract Source Code',\n                        name: GET_CONTRACT_SOURCE_CODE.name,\n                        description: 'Returns the Solidity source code of a verified smart contract.'\n                    },\n                    {\n                        label: 'Check Transaction Receipt Status',\n                        name: CHECK_TRANSACTION_RECEIPT_STATUS.name,\n                        description: 'Returns the status code of a transaction execution.'\n                    },\n                    {\n                        label: 'Get ERC20 Token Supply',\n                        name: GET_ERC20_TOKEN_SUPPLY.name,\n                        description: `Returns the total supply of a ERC-20 token. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get ERC20 Token Balance',\n                        name: GET_ERC20_TOKEN_BALANCE.name,\n                        description: `Returns the current balance of a ERC-20 token of an address. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Historical ERC-20 Token TotalSupply by ContractAddress & BlockNo [PRO]',\n                        name: GET_HISTORICAL_ERC20_TOKEN_SUPPLY.name,\n                        description: `Returns the historical amount of a ERC-20 token in circulation at a certain block height. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Historical ERC-20 Token Account Balance by ContractAddress & BlockNo [PRO]',\n                        name: GET_HISTORICAL_ERC20_TOKEN_BALANCE.name,\n                        description: `Returns the balance of a ERC-20 token of an address at a certain block height. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Token Info [PRO]',\n                        name: GET_TOKEN_INFO.name,\n                        description: 'Returns project information and social media links of an ERC-20/ERC-721 token.'\n                    },\n                    {\n                        label: 'Get ETHER Price',\n                        name: GET_ETHER_PRICE.name,\n                        description: 'Returns the latest price of 1 ETHER.'\n                    },\n                    {\n                        label: 'Get Historical ETHER Price [PRO]',\n                        name: GET_HISTORICAL_ETHER_PRICE.name,\n                        description: 'Returns the historical price of ETHER.'\n                    }\n                ],\n\n                default: 'getEtherBalance'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: NETWORK_LABEL.MAINNET,\n                        name: NETWORK.MAINNET\n                    },\n                    {\n                        label: NETWORK_LABEL.GÖRLI,\n                        name: NETWORK.GÖRLI\n                    }\n                ],\n                default: 'homestead'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Etherscan API Key',\n                        name: 'etherscanApi'\n                    }\n                ],\n                default: 'etherscanApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Address',\n                name: 'address',\n                type: 'string',\n                description: 'The address parameter(s) required',\n                show: {\n                    'actions.api': [\n                        GET_ETHER_BALANCE.name,\n                        GET_HISTORICAL_ETHER_BALANCE.name,\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_BLOCKS_VALIDATED.name,\n                        GET_ABI.name,\n                        GET_CONTRACT_SOURCE_CODE.name,\n                        GET_ERC20_TOKEN_BALANCE.name,\n                        GET_HISTORICAL_ERC20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Block Number',\n                name: 'blockno',\n                type: 'number',\n                description: 'the block number to check balance for eg. 2000000',\n                show: {\n                    'actions.api': [\n                        GET_HISTORICAL_ETHER_BALANCE.name,\n                        GET_HISTORICAL_ERC20_TOKEN_SUPPLY.name,\n                        GET_HISTORICAL_ERC20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Start Block',\n                name: 'startBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to start searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 0\n            },\n            {\n                label: 'End Block',\n                name: 'endBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to stop searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                }\n            },\n            {\n                label: 'Page',\n                name: 'page',\n                type: 'number',\n                optional: true,\n                description: 'the page number, if pagination is enabled',\n                show: {\n                    'actions.api': [\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        GET_BLOCKS_VALIDATED.name\n                    ]\n                },\n                default: 1\n            },\n            {\n                label: 'Offset',\n                name: 'offset',\n                type: 'number',\n                optional: true,\n                description: 'the number of transactions displayed per page',\n                show: {\n                    'actions.api': [\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        GET_BLOCKS_VALIDATED.name\n                    ]\n                },\n                default: 10\n            },\n            {\n                label: 'Sort By',\n                name: 'sortBy',\n                type: 'options',\n                optional: true,\n                options: SORT_BY,\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 'desc'\n            },\n            {\n                label: 'Transaction Hash',\n                name: 'txhash',\n                type: 'string',\n                description: 'the string representing the transaction hash to check for internal transactions',\n                show: {\n                    'actions.api': [GET_INTERNAL_TRANSACTIONS_BY_HASH.name, CHECK_TRANSACTION_RECEIPT_STATUS.name]\n                }\n            },\n            {\n                label: 'Block Type',\n                name: 'blockType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'blocks',\n                        name: 'blocks'\n                    }\n                ],\n                default: 'blocks',\n                show: {\n                    'actions.api': [GET_BLOCKS_VALIDATED.name]\n                }\n            },\n            {\n                label: 'Contract Address',\n                name: 'contractAddress',\n                type: 'string',\n                description: 'the contract address of the ERC-20 token',\n                show: {\n                    'actions.api': [\n                        GET_ERC20_TOKEN_SUPPLY.name,\n                        GET_ERC20_TOKEN_BALANCE.name,\n                        GET_HISTORICAL_ERC20_TOKEN_SUPPLY.name,\n                        GET_HISTORICAL_ERC20_TOKEN_BALANCE.name,\n                        GET_TOKEN_INFO.name\n                    ]\n                }\n            },\n            {\n                label: 'Tag',\n                name: 'tag',\n                type: 'options',\n                options: [{ label: 'latest', name: 'latest' }],\n                default: 'latest',\n                show: {\n                    'actions.api': [GET_ERC20_TOKEN_BALANCE.name]\n                }\n            },\n            {\n                label: 'Start Time',\n                name: 'startTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.api': [GET_HISTORICAL_ETHER_PRICE.name]\n                }\n            },\n            {\n                label: 'End Time',\n                name: 'endTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.api': [GET_HISTORICAL_ETHER_PRICE.name]\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    getNetwork(network: NETWORK): string {\n        return `${etherscanAPIs[network]}`\n    }\n\n    getBaseParams(api: string) {\n        const operation = OPERATIONS.filter(({ name }) => name === api)[0]\n        return { module: operation.module, action: operation.action }\n    }\n\n    getISODate(date: Date) {\n        return date.toISOString().split('T')[0]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const networksData = nodeData.networks\n        const inputParameters = nodeData.inputParameters\n        const credentials = nodeData.credentials\n        if (actionData === undefined || inputParameters === undefined || credentials === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n        // GET api\n        const api = actionData.api as string\n\n        // GET network\n        const network = networksData.network as NETWORK\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n\n        // GET address\n        const address = inputParameters.address as string\n\n        const startblock = inputParameters.startBlock as number\n        const endblock = inputParameters.endBlock as number\n        const page = inputParameters.page as number\n        const offset = inputParameters.offset as number\n        const sort = inputParameters.sortBy as string\n        const txhash = inputParameters.txhash as string\n        const blocktype = inputParameters.blockType as string\n        const contractaddress = inputParameters.contractAddress as string\n        const tag = inputParameters.tag as string\n        const startTime = inputParameters.startTime as string\n        const endTime = inputParameters.endTime as string\n\n        const startdate = startTime ? this.getISODate(new Date(startTime)) : undefined\n        const enddate = endTime ? this.getISODate(new Date(endTime)) : undefined\n\n        const url = this.getNetwork(network)\n        const { module, action } = this.getBaseParams(api)\n\n        const queryParameters = {\n            module,\n            action,\n            address,\n            apiKey,\n            startblock,\n            endblock,\n            page,\n            offset,\n            sort,\n            txhash,\n            blocktype,\n            contractaddress,\n            tag,\n            startdate,\n            enddate\n        }\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Etherscan }\n"
  },
  {
    "path": "packages/components/nodes/Etherscan/constants.ts",
    "content": "// Account\nexport const GET_ETHER_BALANCE = {\n    name: 'getEtherBalance',\n    module: 'account',\n    action: 'balance'\n}\n\nexport const GET_MULTI_ETHER_BALANCE = {\n    name: 'getEtherBalanceMulti',\n    module: 'account',\n    action: 'balancemulti'\n}\n\nexport const GET_HISTORICAL_ETHER_BALANCE = {\n    name: 'getHistoricalEtherBalance',\n    module: 'account',\n    action: 'balancehistory'\n}\n\nexport const GET_NORMAL_TRANSACTIONS = {\n    name: 'getTransactions',\n    module: 'account',\n    action: 'txlist'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS = {\n    name: 'getInternalTransactions',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_HASH = {\n    name: 'getInternalTransactionsByHash',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_BLOCK = {\n    name: 'getInternalTransactionsByBlock',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_BLOCKS_VALIDATED = {\n    name: 'getBlocksValidated',\n    module: 'account',\n    action: 'getminedblocks'\n}\n\n// Contracts\nexport const GET_ABI = {\n    name: 'getAbi',\n    module: 'contract',\n    action: 'getabi'\n}\n\nexport const GET_CONTRACT_SOURCE_CODE = {\n    name: 'getContractSourceCode',\n    module: 'contract',\n    action: 'getsourcecode'\n}\n\n// Transactions\nexport const CHECK_TRANSACTION_RECEIPT_STATUS = {\n    name: 'getTransactionReceiptStatus',\n    module: 'transaction',\n    action: 'gettxreceiptstatus'\n}\n\n// Tokens\nexport const GET_ERC20_TOKEN_SUPPLY = {\n    name: 'getErc20TokenSupply',\n    module: 'stats',\n    action: 'tokensupply'\n}\n\nexport const GET_ERC20_TOKEN_BALANCE = {\n    name: 'getErc20TokenBalance',\n    module: 'account',\n    action: 'tokenbalance'\n}\n\nexport const GET_HISTORICAL_ERC20_TOKEN_SUPPLY = {\n    name: 'getHistoricalErc20TokenSupply',\n    module: 'stats',\n    action: 'tokensupplyhistory'\n}\n\nexport const GET_HISTORICAL_ERC20_TOKEN_BALANCE = {\n    name: 'getHistoricalErc20TokenBalance',\n    module: 'account',\n    action: 'tokenbalancehistory'\n}\n\nexport const GET_TOKEN_INFO = {\n    name: 'getTokenInfo',\n    module: 'token',\n    action: 'tokeninfo'\n}\n\n// Stats\nexport const GET_ETHER_PRICE = {\n    name: 'getEtherPrice',\n    module: 'stats',\n    action: 'ethprice'\n}\n\nexport const GET_HISTORICAL_ETHER_PRICE = {\n    name: 'getHistoricalEtherPrice',\n    module: 'stats',\n    action: 'ethdailyprice'\n}\n\nexport const OPERATIONS = [\n    GET_ETHER_BALANCE,\n    GET_MULTI_ETHER_BALANCE,\n    GET_HISTORICAL_ETHER_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_BLOCKS_VALIDATED,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_BALANCE,\n    GET_HISTORICAL_ERC20_TOKEN_SUPPLY,\n    GET_HISTORICAL_ERC20_TOKEN_BALANCE,\n    GET_TOKEN_INFO,\n    GET_ETHER_PRICE,\n    GET_HISTORICAL_ETHER_PRICE\n] as const\n\nexport const SORT_BY = [\n    { label: 'Desc', name: 'desc' },\n    { label: 'Asc', name: 'asc' }\n]\n"
  },
  {
    "path": "packages/components/nodes/ExecuteContractFunction/ExecuteContractFunction.ts",
    "content": "import { ethers } from 'ethers'\nimport {\n    IContract,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    networkExplorers,\n    getNetworkProvidersList,\n    NETWORK_PROVIDER,\n    getNetworkProvider,\n    NETWORK,\n    networkProviderCredentials\n} from '../../src/ChainNetwork'\n\nclass ExecuteContractFunction implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    actions?: INodeParams[]\n\n    constructor() {\n        this.label = 'Execute Contract Function'\n        this.name = 'executeContractFunction'\n        this.icon = 'execute-contract-function.svg'\n        this.type = 'action'\n        this.category = 'Smart Contract'\n        this.version = 1.1\n        this.description = 'Execute smart contract function.'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Select Contract',\n                name: 'contract',\n                type: 'asyncOptions',\n                loadFromDbCollections: ['Contract'],\n                loadMethod: 'getContracts'\n            },\n            {\n                label: 'Function',\n                name: 'function',\n                type: 'asyncOptions',\n                loadMethod: 'getFunctions'\n            },\n            {\n                label: 'Function Parameters',\n                name: 'funcParameters',\n                type: 'json',\n                placeholder: '[\"param1\", \"param2\"]',\n                description: 'Function parameters in array. Ex: [\"param1\", \"param2\"]',\n                optional: true\n            },\n            {\n                label: 'Select Wallet',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description:\n                    'Connect wallet to sign transactions for functions that require changing states on blockchain, i.e: nonpayable or payable.',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets',\n                show: {\n                    'actions.function': '(\\\\(payable\\\\)|\\\\(nonpayable\\\\))'\n                }\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction'\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getContracts(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            if (dbCollection === undefined || !dbCollection || !dbCollection.Contract) {\n                return returnData\n            }\n\n            const contracts: IContract[] = dbCollection.Contract\n\n            for (let i = 0; i < contracts.length; i += 1) {\n                const contract = contracts[i]\n                const data = {\n                    label: `${contract.name} (${contract.network})`,\n                    name: JSON.stringify(contract),\n                    description: contract.address\n                } as INodeOptionsValue\n                returnData.push(data)\n            }\n\n            return returnData\n        },\n\n        async getFunctions(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionsData = nodeData.actions\n            if (actionsData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionsData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.abi || !contractDetails.address) return returnData\n\n                const abiString = contractDetails.abi\n\n                const abi = JSON.parse(abiString)\n\n                for (const item of abi) {\n                    if (!item.name) continue\n                    if (item.type === 'function') {\n                        const funcName = `${item.name} (${item.stateMutability})`\n                        const funcInputs = item.inputs\n                        let inputParameters = ''\n                        let inputTypes = ''\n                        for (let i = 0; i < funcInputs.length; i++) {\n                            const input = funcInputs[i]\n                            inputTypes += `${input.type} ${input.name}`\n                            if (i !== funcInputs.length - 1) inputTypes += ', '\n                            inputParameters += `<li><code class=\"inline\">${input.type}</code> ${input.name}</li>`\n                        }\n                        if (inputParameters) {\n                            inputParameters = '<ul>' + inputParameters + '</ul>'\n                        } else {\n                            inputParameters = '<ul>' + 'none' + '</ul>'\n                        }\n                        returnData.push({\n                            label: funcName,\n                            name: funcName,\n                            description: inputTypes,\n                            inputParameters\n                        })\n                    }\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionsData = nodeData.actions\n            if (actionsData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionsData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.network) return returnData\n\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const actionData = nodeData.actions\n            if (actionData === undefined) {\n                return returnData\n            }\n\n            const contractString = (actionData.contract as string) || ''\n            if (!contractString) return returnData\n\n            try {\n                const contractDetails = JSON.parse(contractString)\n\n                if (!contractDetails.network) return returnData\n\n                const network = contractDetails.network\n                return getNetworkProvidersList(network)\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const contractString = (actionsData.contract as string) || ''\n            const contractDetails: IContract = JSON.parse(contractString)\n            const network = contractDetails.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get contract details\n            const abiString = contractDetails.abi\n            const address = contractDetails.address\n            const abi = JSON.parse(abiString)\n\n            let functionName = (actionsData.function as string) || ''\n\n            let contractParameters: any[] = []\n            const funcParameters = actionsData.funcParameters as string\n            if (funcParameters) {\n                try {\n                    contractParameters = JSON.parse(funcParameters.replace(/\\s/g, ''))\n                } catch (error) {\n                    throw handleErrorMessage(error)\n                }\n            }\n\n            let contractInstance: ethers.Contract\n            if (new RegExp('(\\\\(payable\\\\)|\\\\(nonpayable\\\\))').test(functionName)) {\n                // Get wallet instance\n                const walletString = actionsData.wallet as string\n                const walletDetails: IWallet = JSON.parse(walletString)\n                const walletCredential = JSON.parse(walletDetails.walletCredential)\n                const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n                contractInstance = new ethers.Contract(address, abi, wallet)\n\n                const gasLimit = (inputParametersData?.gasLimit as number) || 3000000\n                const maxFeePerGas = (inputParametersData?.maxFeePerGas as number) || undefined\n                const maxPriorityFeePerGas = (inputParametersData?.maxPriorityFeePerGas as number) || undefined\n                const gasPrice = await provider.getGasPrice()\n                const nonce = await provider.getTransactionCount(walletDetails.address)\n\n                const txOption = {\n                    gasPrice,\n                    gasLimit,\n                    nonce\n                } as any\n\n                if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n                if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n                functionName = functionName.split(' ')[0]\n                const tx = await contractInstance[functionName].apply(null, contractParameters.length ? contractParameters : null, txOption)\n\n                const approveReceipt = await tx.wait()\n\n                if (approveReceipt.status === 0) throw new Error(`Function ${functionName} failed to send transaction`)\n                const returnItem = {\n                    function: functionName,\n                    transactionHash: tx.hash,\n                    transactionReceipt: approveReceipt,\n                    link: `${networkExplorers[network]}/tx/${tx.hash}`\n                }\n                return returnNodeExecutionData(returnItem)\n            } else {\n                contractInstance = new ethers.Contract(address, abi, provider)\n                functionName = functionName.split(' ')[0]\n                const result = await contractInstance[functionName].apply(null, contractParameters.length ? contractParameters : null)\n                const returnItem = {\n                    function: functionName,\n                    result: result\n                }\n                return returnNodeExecutionData(returnItem)\n            }\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ExecuteContractFunction }\n"
  },
  {
    "path": "packages/components/nodes/FlowBalanceTrigger/FlowBalanceTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { BigNumber, BigNumberish } from 'ethers'\nimport { ICronJobs, INode, INodeData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport axios from 'axios'\nimport { FLOWNetworks, getNetworkProvidersList, NETWORK, networkExplorers } from '../../src/ChainNetwork'\nimport { formatUnits, parseUnits } from 'ethers/lib/utils'\n\nclass FlowBalanceTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'Flow Balance Trigger'\n        this.name = 'FlowBalanceTrigger'\n        this.icon = 'flow.png'\n        this.type = 'trigger'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Start workflow whenever FLOW balance in wallet changes'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...FLOWNetworks],\n                default: 'homestead'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet Address',\n                name: 'address',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Trigger Condition',\n                name: 'triggerCondition',\n                type: 'options',\n                options: [\n                    {\n                        label: 'When balance increased',\n                        name: 'increase'\n                    },\n                    {\n                        label: 'When balance decreased',\n                        name: 'decrease'\n                    }\n                ],\n                default: 'increase'\n            },\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n        const baseUrl =\n            network === NETWORK.FLOW_TESTNET\n                ? `https://rest-testnet.onflow.org/v1/accounts/`\n                : `https://rest-mainnet.onflow.org/v1/accounts/`\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const address = (inputParametersData.address as string) || ''\n        const pollTime = (inputParametersData.pollTime as string) || '30s'\n        const triggerCondition = (inputParametersData.triggerCondition as string) || 'increase'\n\n        const cronTimes: string[] = []\n\n        if (pollTime === '15s') {\n            cronTimes.push(`*/15 * * * * *`)\n        } else if (pollTime === '30s') {\n            cronTimes.push(`*/30 * * * * *`)\n        } else if (pollTime === '1min') {\n            cronTimes.push(`*/1 * * * *`)\n        } else if (pollTime === '5min') {\n            cronTimes.push(`*/5 * * * *`)\n        } else if (pollTime === '10min') {\n            cronTimes.push(`*/10 * * * *`)\n        }\n\n        const responseFlow: any = await axios.get(`${baseUrl}${address}`)\n        let lastBalance: BigNumber = BigNumber.from(responseFlow.data.balance)\n\n        const executeTrigger = async () => {\n            const responseFlow: any = await axios.get(`${baseUrl}${address}`)\n            const newBalance: BigNumber = BigNumber.from(responseFlow.data.balance)\n\n            if (!newBalance.eq(lastBalance)) {\n                if (triggerCondition === 'increase' && newBalance.gt(lastBalance)) {\n                    const balanceInFlow = this.formatFlow(BigNumber.from(newBalance.toString()))\n                    const diffInFlow = newBalance.sub(lastBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInFlow} FLOW`,\n                        lastBalance: `${this.formatFlow(BigNumber.from(lastBalance.toString()))} FLOW`,\n                        difference: `${this.formatFlow(BigNumber.from(diffInFlow.toString()))} FLOW`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'FLOW balance increase'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else if (triggerCondition === 'decrease' && newBalance.lt(lastBalance)) {\n                    const balanceInFlow = this.formatFlow(BigNumber.from(newBalance.toString()))\n                    const diffInFlow = lastBalance.sub(newBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInFlow} FLOW`,\n                        lastBalance: `${this.formatFlow(BigNumber.from(lastBalance.toString()))} FLOW`,\n                        difference: `${this.formatFlow(BigNumber.from(diffInFlow.toString()))} FLOW`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'FLOW balance decrease'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else {\n                    lastBalance = newBalance\n                }\n            }\n        }\n\n        // Start the cron-jobs\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n            }\n        } else {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n            }\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n\n    formatFlow(balance: BigNumberish): string {\n        return formatUnits(balance, 8)\n    }\n\n    parseFlow(flow: string): BigNumber {\n        return parseUnits(flow, 8)\n    }\n}\n\nmodule.exports = { nodeClass: FlowBalanceTrigger }\n"
  },
  {
    "path": "packages/components/nodes/FootprintAnalytics/FootprintAnalytics.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport {\n    ALL_OPERATIONS,\n    chainInputParameters,\n    CHAIN_OPERATIONS,\n    gamefiParams,\n    GAMEFI_OPERATIONS,\n    limitOffetParams,\n    nftBalanceParams,\n    nftParams,\n    nftWashTradeCheckerParams,\n    NFT_OPERATIONS,\n    timeRangeParams,\n    tokenBalanceParams,\n    tokenParams,\n    TOKEN_OPERATIONS\n} from './constants'\n\nclass FootprintAnalytics implements INode {\n    // properties\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    // parameter\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        // properties\n        this.label = 'Footprint Analytics'\n        this.name = 'footprintAnalytics'\n        this.icon = 'footprint-analytics.png'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Execute Footprint Analytics APIs and SQL query'\n        this.incoming = 1\n        this.outgoing = 1\n\n        // parameter\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Rest API (V2)',\n                        name: 'restAPI'\n                    },\n                    {\n                        label: 'SQL API (Synchronous)',\n                        name: 'sqlAPISynchronous',\n                        description: 'Suitable for simple table lookup query'\n                    },\n                    {\n                        label: 'SQL API (Asynchronous)',\n                        name: 'sqlAPIAsynchronous',\n                        description: 'For complex analysis table lookup query'\n                    }\n                ]\n            },\n            {\n                label: 'Category',\n                name: 'category',\n                type: 'options',\n                options: [\n                    {\n                        label: 'NFT',\n                        name: 'nft'\n                    },\n                    {\n                        label: 'Token',\n                        name: 'token'\n                    },\n                    {\n                        label: 'GameFi',\n                        name: 'gamefi'\n                    },\n                    {\n                        label: 'Chain',\n                        name: 'chain'\n                    }\n                ],\n                show: {\n                    'actions.api': ['restAPI']\n                }\n            },\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'asyncOptions',\n                loadMethod: 'getOperations',\n                show: {\n                    'actions.api': ['restAPI']\n                }\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Footprint Analytics API Key',\n                        name: 'footprintAnalyticsApi'\n                    }\n                ],\n                default: 'footprintAnalyticsApi'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            ...chainInputParameters,\n            ...nftParams,\n            ...nftWashTradeCheckerParams,\n            ...nftBalanceParams,\n            ...tokenParams,\n            ...tokenBalanceParams,\n            ...gamefiParams,\n            ...timeRangeParams,\n            ...limitOffetParams,\n            {\n                label: 'SQL query',\n                name: 'sqlQuery',\n                type: 'string',\n                rows: 5,\n                placeholder: `select * from ethereum_token_transfers where block_timestamp >= date_add('day',-1,current_date) limit 10`,\n                description: 'SQL query to execute. Must be Scale or Enterprise plan',\n                show: {\n                    'actions.api': ['sqlAPISynchronous', 'sqlAPIAsynchronous']\n                }\n            }\n        ]\n    }\n\n    getFormattedDate(date: Date, operation: string) {\n        const hours = date.toISOString().split('T')[1].split(':')[0]\n        const minutes = date.toISOString().split('T')[1].split(':')[1]\n        const seconds = date.toISOString().split('T')[1].split(':')[2]\n        return operation === 'nftCollectionStatistics'\n            ? date.getTime()\n            : `${date.toISOString().split('T')[0]} ${hours}:${minutes}:${seconds}`\n    }\n\n    loadMethods = {\n        async getOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const actionsData = nodeData.actions\n\n            if (actionsData === undefined) {\n                return returnData\n            }\n\n            const category = actionsData.category as string\n            let operations: any[] = []\n\n            switch (category) {\n                case 'nft':\n                    operations = NFT_OPERATIONS\n                    break\n                case 'token':\n                    operations = TOKEN_OPERATIONS\n                    break\n                case 'gamefi':\n                    operations = GAMEFI_OPERATIONS\n                    break\n                case 'chain':\n                    operations = CHAIN_OPERATIONS\n                    break\n            }\n\n            for (const op of operations) {\n                returnData.push({\n                    label: op.label,\n                    name: op.name,\n                    description: op.description\n                })\n            }\n\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const { actions, inputParameters, credentials } = nodeData\n\n        if (actions === undefined || inputParameters === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actions.api as string\n        const apiKey = credentials.apiKey as string\n        const operation = actions.operation as string\n\n        const chain = inputParameters.chain as string\n        const collection_contract_address = inputParameters.collection_contract_address as string\n        const type = inputParameters.type as string\n        const status = inputParameters.status as string\n        const statistics_metrics = inputParameters.statistics_metrics as string\n        const statistics_time_model = inputParameters.statistics_time_model as string\n        const wallet_address = inputParameters.wallet_address as string\n        const nft_type = inputParameters.nft_type as string\n        const nft_token_id = inputParameters.nft_token_id as string\n        const token_address = inputParameters.token_address as string\n        const from_address = inputParameters.from_address as string\n        const to_address = inputParameters.to_address as string\n        const protocol_slug = inputParameters.protocol_slug as string\n        const address_type = inputParameters.address_type as string\n        const statistics_frequency_model = inputParameters.statistics_frequency_model as string\n        const contract_address = inputParameters.contract_address as string\n\n        const startTime = inputParameters.start_time as string\n        const endTime = inputParameters.end_time as string\n        const offset = inputParameters.offset as number\n        const limit = inputParameters.limit as number\n\n        const start_time = startTime ? this.getFormattedDate(new Date(startTime), operation) : undefined\n        const end_time = endTime ? this.getFormattedDate(new Date(endTime), operation) : undefined\n\n        const sqlQuery = inputParameters.sqlQuery as string\n\n        const queryParameters = {\n            chain,\n            collection_contract_address,\n            type,\n            status,\n            statistics_metrics,\n            statistics_time_model,\n            nft_type,\n            protocol_slug,\n            address_type\n        } as any\n\n        if (nft_token_id) queryParameters.nft_token_id = nft_token_id\n        if (start_time) queryParameters.start_time = start_time\n        if (end_time) queryParameters.end_time = end_time\n        if (wallet_address) queryParameters.wallet_address = wallet_address\n        if (token_address) queryParameters.token_address = token_address\n        if (from_address) queryParameters.from_address = from_address\n        if (to_address) queryParameters.to_address = to_address\n        if (statistics_frequency_model) queryParameters.statistics_frequency_model = statistics_frequency_model\n        if (contract_address) queryParameters.contract_address = contract_address\n        if (offset) queryParameters.offset = offset\n        if (limit) queryParameters.limit = limit\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        if (api === 'sqlAPISynchronous') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: 'https://api.footprint.network/api/v1/native',\n                    data: JSON.stringify({\n                        query: sqlQuery\n                    }),\n                    headers: { 'Content-Type': 'application/json', 'API-KEY': apiKey }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                console.error(error)\n                throw handleErrorMessage(error)\n            }\n        } else if (api === 'sqlAPIAsynchronous') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: 'https://api.footprint.network/api/v1/native/async',\n                    data: JSON.stringify({\n                        query: sqlQuery\n                    }),\n                    headers: { 'Content-Type': 'application/json', 'API-KEY': apiKey }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n\n                const promise = (execution_id: string) => {\n                    let data: ICommonObject = {}\n                    return new Promise((resolve, reject) => {\n                        const timeout = setInterval(async () => {\n                            const axiosConfig: AxiosRequestConfig = {\n                                method: 'GET' as Method,\n                                url: `https://api.footprint.network/api/v1/native/${execution_id}/results`,\n                                headers: { 'Content-Type': 'application/json', 'API-KEY': apiKey }\n                            }\n                            const response = await axios(axiosConfig)\n                            data = response.data\n                            const state = response.data.data.state as 'INIT' | 'PENDING' | 'SUCCESS' | 'FAIL' | 'EXPIRED'\n\n                            if (state === 'SUCCESS') {\n                                clearInterval(timeout)\n                                resolve(data)\n                            } else if (state === 'FAIL' || state === 'EXPIRED') {\n                                clearInterval(timeout)\n                                reject(new Error(`Error querying async SQL request: ${state}`))\n                            }\n                        }, 1500)\n                    })\n                }\n\n                responseData = await promise(responseData.data.execution_id || '')\n            } catch (error) {\n                console.error(error)\n                throw handleErrorMessage(error)\n            }\n        } else if (api === 'restAPI') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: ALL_OPERATIONS.find((op) => op.name === operation)?.endpoint,\n                    params: queryParameters,\n                    paramsSerializer: (params) => serializeQueryParams(params),\n                    headers: { 'Content-Type': 'application/json', 'API-KEY': apiKey }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                console.error(error)\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: FootprintAnalytics }\n"
  },
  {
    "path": "packages/components/nodes/FootprintAnalytics/constants.ts",
    "content": "import { INodeParams } from '../../src'\n\nexport const chainInputParameters = [\n    {\n        label: 'Chain',\n        name: 'chain',\n        type: 'options',\n        options: [\n            {\n                label: 'All',\n                name: 'all',\n                show: {\n                    'actions.operation': ['protocolTransactions', 'protocolAddresses', 'protocolUserStatistics', 'protocolsByChain']\n                }\n            },\n            {\n                label: 'Arbitrum',\n                name: 'Arbitrum',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftCollectionsByChain',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Avalanche',\n                name: 'Avalanche',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftCollectionsByChain',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'BNB Chain',\n                name: 'BNB%20Chain',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftTransactions',\n                        'nftTransfers',\n                        'nftCollectionsByChain',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Boba',\n                name: 'Boba',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Bitcoin',\n                name: 'Bitcoin',\n                show: {\n                    'actions.operation': ['chainTransactions']\n                }\n            },\n            {\n                label: 'Cardano',\n                name: 'Cardano',\n                show: {\n                    'actions.operation': ['nftCollectionsByChain']\n                }\n            },\n            {\n                label: 'Celo',\n                name: 'Celo',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'DFK',\n                name: 'DFK',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Doge',\n                name: 'Doge',\n                show: {\n                    'actions.operation': ['chainTransactions']\n                }\n            },\n            {\n                label: 'EOS',\n                name: 'EOS',\n                show: {\n                    'actions.operation': ['tokenTransfers', 'chainTransactions']\n                }\n            },\n            {\n                label: 'Ethereum',\n                name: 'Ethereum',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'allTokenBalance',\n                        'nftTransactions',\n                        'nftTransfers',\n                        'nftOrders',\n                        'nftWashTradeChecker',\n                        'nftCollectionStatistics',\n                        'nftStatistics',\n                        'nftInfo',\n                        'nftAttributes',\n                        'nftCollectionsByChain',\n                        'nftBalance',\n                        'nftBalanceByWallet',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Fantom',\n                name: 'Fantom',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftCollectionsByChain',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Harmony',\n                name: 'Harmony',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftCollectionsByChain',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Hive',\n                name: 'Hive',\n                show: {\n                    'actions.operation': [\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Heco',\n                name: 'Heco',\n                show: {\n                    'actions.operation': ['tokenTransfers', 'tokenBalance', 'nftBalance', 'chainTransactions']\n                }\n            },\n            {\n                label: 'ImmutableX',\n                name: 'ImmutableX',\n                show: {\n                    'actions.operation': ['nftCollectionsByChain']\n                }\n            },\n            {\n                label: 'IoTeX',\n                name: 'IoTeX',\n                show: {\n                    'actions.operation': [\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Moonbeam',\n                name: 'Moonbeam',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Moonriver',\n                name: 'Moonriver',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Oasys',\n                name: 'Oasys',\n                show: {\n                    'actions.operation': ['tokenTransfers', 'tokenBalance', 'nftBalance', 'chainTransactions']\n                }\n            },\n            {\n                label: 'Optimism',\n                name: 'Optimism',\n                show: {\n                    'actions.operation': ['tokenTransfers', 'tokenBalance', 'nftBalance', 'chainTransactions']\n                }\n            },\n            {\n                label: 'Polygon',\n                name: 'Polygon',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftTransactions',\n                        'nftTransfers',\n                        'nftCollectionStatistics',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Solana',\n                name: 'Solana',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'nftTransactions',\n                        'nftTransfers',\n                        'nftCollectionStatistics',\n                        'nftStatistics',\n                        'nftInfo',\n                        'nftAttributes',\n                        'nftCollectionsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'ThunderCore',\n                name: 'ThunderCore',\n                show: {\n                    'actions.operation': [\n                        'tokenTransfers',\n                        'tokenBalance',\n                        'nftBalance',\n                        'protocolTransactions',\n                        'protocolAddresses',\n                        'protocolUserStatistics',\n                        'protocolsByChain',\n                        'chainTransactions'\n                    ]\n                }\n            },\n            {\n                label: 'Terra',\n                name: 'Terra',\n                show: {\n                    'actions.operation': ['nftCollectionsByChain']\n                }\n            },\n            {\n                label: 'Wax',\n                name: 'Wax',\n                show: {\n                    'actions.operation': ['tokenTransfers', 'chainTransactions']\n                }\n            }\n        ],\n        show: {\n            'actions.api': ['restAPI']\n        },\n        hide: {\n            'actions.operation': ['chainInfo']\n        }\n    }\n] as INodeParams[]\n\n// NFT\nexport const NFT_OPERATIONS = [\n    {\n        label: 'NFT Transactions',\n        name: 'nftTransactions',\n        description: 'Returns the sales record of the NFT collection in the marketplace.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/collection/transactions'\n    },\n    {\n        label: 'NFT Transfers',\n        name: 'nftTransfers',\n        description: 'Returns the transfers record of the NFT collection.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/collection/transfers'\n    },\n    {\n        label: 'NFT Orders',\n        name: 'nftOrders',\n        description: 'Returns the sales listing of the NFT in the marketplace.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/collection/orderbook'\n    },\n    {\n        label: 'NFT Wash Trade Checker',\n        name: 'nftWashTradeChecker',\n        description: 'Returns whether the transaction hash is wash trading.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/collection/transactions/is-washtrade'\n    },\n    {\n        label: 'NFT Collection Statistics',\n        name: 'nftCollectionStatistics',\n        description:\n            'Returns the statistics metrics (eg:market_cap,volume,floor_price) timeseries data or all metrics data of the NFT collection.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/collection/statistics'\n    },\n    {\n        label: 'NFT Statistics',\n        name: 'nftStatistics',\n        description:\n            'Returns all metrics of NFT different time models statistics by collection and nft_token_id.Currently only the latest state of NFT statistics.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/statistics'\n    },\n    {\n        label: 'NFT Info',\n        name: 'nftInfo',\n        description: 'Returns the basic information of the NFT.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/info'\n    },\n    {\n        label: 'NFT Attributes',\n        name: 'nftAttributes',\n        description: 'Returns the attributes of the NFT.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/attributes'\n    },\n    {\n        label: 'NFT Collections by Chain',\n        name: 'nftCollectionsByChain',\n        description: 'Returns all NFT collections by chain.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/collection/info'\n    },\n    {\n        label: 'NFT Balance',\n        name: 'nftBalance',\n        description: 'Returns the NFT balance of the wallet address.',\n        endpoint: 'https://api.footprint.network/api/v2/nft/balance'\n    },\n    {\n        label: 'NFT Balance by Wallet',\n        name: 'nftBalanceByWallet',\n        description: 'Get the list of NFTs balance by the specified wallet',\n        endpoint: 'https://api.footprint.network/api/v2/wallet/nfts'\n    }\n]\n\nexport const TOKEN_OPERATIONS = [\n    {\n        label: 'Token Transfers',\n        name: 'tokenTransfers',\n        description: 'Returns token transfers over a period of time.',\n        endpoint: 'https://api.footprint.network/api/v2/token/transfers'\n    },\n    {\n        label: 'Token Balance',\n        name: 'tokenBalance',\n        description: 'Returns a token balance for the specified wallet (ERC20)',\n        endpoint: 'https://api.footprint.network/api/v2/token/balance'\n    },\n    {\n        label: 'All Token Balance',\n        name: 'allTokenBalance',\n        description: 'Retrieve all token balances for the specified wallet (ERC20)',\n        endpoint: 'https://api.footprint.network/api/v2/wallet/tokens'\n    }\n]\n\nexport const GAMEFI_OPERATIONS = [\n    {\n        label: 'Protocol Transactions',\n        name: 'protocolTransactions',\n        description: 'Returns protocol transactions over a period of time.',\n        endpoint: 'https://api.footprint.network/api/v2/protocol/transactions'\n    },\n    {\n        label: 'Protocol Addresses',\n        name: 'protocolAddresses',\n        description: 'Returns protocol addresses over a period of time.',\n        endpoint: 'https://api.footprint.network/api/v2/protocol/address'\n    },\n    {\n        label: 'Protocol User Statistics',\n        name: 'protocolUserStatistics',\n        description: 'Returns protocol user statistics over a period of time.',\n        endpoint: 'https://api.footprint.network/api/v2/protocol/statistics'\n    },\n    {\n        label: 'Protocols by Chain',\n        name: 'protocolsByChain',\n        description: 'Returns protocols by chain.',\n        endpoint: 'https://api.footprint.network/api/v2/protocol/info'\n    }\n]\n\nexport const CHAIN_OPERATIONS = [\n    {\n        label: 'Chain Transactions',\n        name: 'chainTransactions',\n        description: 'Returns chain transactions over a period of time.',\n        endpoint: 'https://api.footprint.network/api/v2/chain/transactions'\n    },\n    {\n        label: 'Chain Info',\n        name: 'chainInfo',\n        description: 'Returns chains info',\n        endpoint: 'https://api.footprint.network/api/v2/chain/info'\n    }\n]\n\nexport const ALL_OPERATIONS = [...NFT_OPERATIONS, ...TOKEN_OPERATIONS, ...GAMEFI_OPERATIONS, ...CHAIN_OPERATIONS]\n\nexport const nftWashTradeCheckerParams = [\n    {\n        label: 'Transaction Hash',\n        name: 'transaction_hash',\n        type: 'string',\n        placeholder: '0x03a7615cd2bbe69a028debf6d76b7be99ba5c4d9246a639226889c5618a648fe',\n        show: { 'actions.operation': ['nftWashTradeChecker'] }\n    }\n] as INodeParams[]\n\nexport const nftBalanceParams = [\n    {\n        label: 'Wallet Address',\n        name: 'wallet_address',\n        type: 'string',\n        placeholder: '0x3cd3a9a9c66a6fbb7221a30e761bda3caaea197b',\n        show: { 'actions.operation': ['nftBalance', 'nftBalanceByWallet'] }\n    },\n    {\n        label: 'NFT Token Id',\n        name: 'nft_token_id',\n        type: 'string',\n        placeholder: '7237005577332285910032237647762877720934446650543256038857901852224856784896',\n        show: { 'actions.operation': ['nftBalance'] }\n    },\n    {\n        label: 'NFT Type',\n        name: 'nft_type',\n        type: 'options',\n        options: [\n            {\n                label: 'ERC1155',\n                name: 'ERC1155'\n            },\n            {\n                label: 'ERC721',\n                name: 'ERC721'\n            }\n        ],\n        default: 'ERC1155',\n        show: { 'actions.operation': ['nftBalance'] }\n    }\n] as INodeParams[]\n\nexport const nftParams = [\n    {\n        label: 'Collection Contract Address',\n        name: 'collection_contract_address',\n        type: 'string',\n        placeholder: '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d',\n        show: {\n            'actions.operation': [\n                'nftTransactions',\n                'nftTransfers',\n                'nftOrders',\n                'nftCollectionStatistics',\n                'nftStatistics',\n                'nftInfo',\n                'nftAttributes',\n                'nftCollectionsByChain',\n                'nftBalance'\n            ]\n        }\n    },\n    {\n        label: 'Type',\n        name: 'type',\n        type: 'options',\n        options: [\n            {\n                label: 'Listing',\n                name: 'listing'\n            },\n            {\n                label: 'Bidding',\n                name: 'bidding'\n            }\n        ],\n        show: { 'actions.operation': ['nftOrders'] }\n    },\n    {\n        label: 'Status',\n        name: 'status',\n        type: 'options',\n        options: [\n            {\n                label: 'Active',\n                name: 'active'\n            },\n            {\n                label: 'Expired',\n                name: 'expired'\n            },\n            {\n                label: 'Cancelled',\n                name: 'cancelled'\n            },\n            {\n                label: 'Filled',\n                name: 'filled'\n            },\n            {\n                label: 'Inactive',\n                name: 'inactive'\n            }\n        ],\n        show: { 'actions.operation': ['nftOrders'] }\n    },\n    {\n        label: 'Statistics Metrics',\n        name: 'statistics_metrics',\n        type: 'options',\n        options: [\n            {\n                label: 'Market Cap',\n                name: 'market_cap'\n            },\n            {\n                label: 'Volume',\n                name: 'volume'\n            },\n            {\n                label: 'Floor Price',\n                name: 'floor_price'\n            }\n        ],\n        show: { 'actions.operation': ['nftCollectionStatistics'] }\n    },\n    {\n        label: 'Statistics Time Model',\n        name: 'statistics_time_model',\n        type: 'options',\n        options: [\n            {\n                label: 'Latest',\n                name: 'latest'\n            }\n        ],\n        default: 'latest',\n        show: { 'actions.operation': ['nftStatistics'] }\n    },\n    {\n        label: 'NFT Token Id',\n        name: 'nft_token_id',\n        type: 'string',\n        placeholder: '1',\n        optional: true,\n        show: { 'actions.operation': ['nftStatistics', 'nftInfo', 'nftAttributes'] }\n    }\n] as INodeParams[]\n\nexport const timeRangeParams = [\n    {\n        label: 'Start Time',\n        name: 'start_time',\n        type: 'date',\n        description:\n            'The query supports time period query, when start_time is specified, but end_time is not specified, the default query is the data of 24 hours after start_time (including start_time); if you do not specify start_time and end_time, the default query is yesterday,the maximum span of one inquiry is 30 days , eg: 2022-02-02 00:00:00',\n        optional: true,\n        show: {\n            'actions.operation': [\n                'nftTransactions',\n                'nftTransfers',\n                'nftOrders',\n                'nftCollectionStatistics',\n                'nftCollectionsByChain',\n                'tokenTransfers',\n                'protocolTransactions',\n                'protocolAddresses',\n                'protocolUserStatistics',\n                'chainTransactions'\n            ]\n        }\n    },\n    {\n        label: 'End Time',\n        name: 'end_time',\n        type: 'date',\n        description:\n            'When end_time is specified, but no start_time is specified, the default query is 24 hours before end_time (not including end_time) , eg: 2022-02-03 00:00:00',\n        optional: true,\n        show: {\n            'actions.operation': [\n                'nftTransactions',\n                'nftTransfers',\n                'nftOrders',\n                'nftCollectionStatistics',\n                'nftCollectionsByChain',\n                'tokenTransfers',\n                'protocolTransactions',\n                'protocolAddresses',\n                'protocolUserStatistics',\n                'chainTransactions'\n            ]\n        }\n    }\n] as INodeParams[]\n\nexport const limitOffetParams = [\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        description: 'Page size, Defaults to 100, capped at 100, eg: 100',\n        optional: true,\n        show: {\n            'actions.operation': [\n                'nftTransactions',\n                'nftTransfers',\n                'nftOrders',\n                'nftCollectionStatistics',\n                'nftStatistics',\n                'nftInfo',\n                'nftAttributes',\n                'nftCollectionsByChain',\n                'nftBalanceByWallet',\n                'tokenTransfers',\n                'allTokenBalance',\n                'protocolTransactions',\n                'protocolAddresses',\n                'protocolUserStatistics',\n                'protocolsByChain',\n                'chainTransactions'\n            ]\n        }\n    },\n    {\n        label: 'Offset',\n        name: 'offset',\n        type: 'number',\n        description: 'The offset for pagination, capped at 80000 eg:120',\n        optional: true,\n        show: {\n            'actions.operation': [\n                'nftTransactions',\n                'nftTransfers',\n                'nftOrders',\n                'nftCollectionStatistics',\n                'nftStatistics',\n                'nftInfo',\n                'nftAttributes',\n                'nftCollectionsByChain',\n                'nftBalanceByWallet',\n                'tokenTransfers',\n                'allTokenBalance',\n                'protocolTransactions',\n                'protocolAddresses',\n                'protocolUserStatistics',\n                'protocolsByChain',\n                'chainTransactions'\n            ]\n        }\n    }\n] as INodeParams[]\n\nexport const tokenParams = [\n    {\n        label: 'Token Address',\n        name: 'token_address',\n        type: 'string',\n        description: 'Token contract address',\n        placeholder: '0xcc079ca45b62c5bc0064e0b40a2dd3d409503119',\n        optional: true,\n        show: { 'actions.operation': ['tokenTransfers'] }\n    },\n    {\n        label: 'From Address',\n        name: 'from_address',\n        type: 'string',\n        description: 'Address of the sender',\n        placeholder: '0x21787ae502b65d52071daa6519d7b6258758da5e',\n        optional: true,\n        show: { 'actions.operation': ['tokenTransfers', 'chainTransactions'] }\n    },\n    {\n        label: 'To Address',\n        name: 'to_address',\n        type: 'string',\n        description: 'Address of the receiver',\n        placeholder: '0x4ba50c4469f4c1388745739d9d73010781617688',\n        optional: true,\n        show: { 'actions.operation': ['tokenTransfers', 'chainTransactions'] }\n    }\n] as INodeParams[]\n\nexport const tokenBalanceParams = [\n    {\n        label: 'Token Address',\n        name: 'token_address',\n        type: 'string',\n        description: 'Token contract address',\n        placeholder: '0x4fabb145d64652a948d72533023f6e7a623c7c53',\n        show: { 'actions.operation': ['tokenBalance'] }\n    },\n    {\n        label: 'Wallet Address',\n        name: 'wallet_address',\n        type: 'string',\n        description: 'Wallet address',\n        placeholder: '0xb8001c3ec9aa1985f6c747e25c28324e4a361ec1',\n        show: { 'actions.operation': ['tokenBalance', 'allTokenBalance'] }\n    }\n] as INodeParams[]\n\nexport const gamefiParams = [\n    {\n        label: 'Protocol Slug',\n        name: 'protocol_slug',\n        type: 'string',\n        description: 'The protocol slug of the transactions, eg: aave',\n        placeholder: 'aave',\n        show: { 'actions.operation': ['protocolTransactions', 'protocolAddresses', 'protocolUserStatistics', 'protocolsByChain'] }\n    },\n    {\n        label: 'Address Type',\n        name: 'address_type',\n        type: 'options',\n        options: [\n            {\n                label: 'Active',\n                name: 'active'\n            }\n        ],\n        default: 'active',\n        show: { 'actions.operation': ['protocolAddresses'] }\n    },\n    {\n        label: 'Statistics Frequency Model',\n        name: 'statistics_frequency_model',\n        type: 'options',\n        options: [\n            {\n                label: 'Daily',\n                name: 'daily'\n            }\n        ],\n        optional: true,\n        show: { 'actions.operation': ['protocolUserStatistics'] }\n    },\n    {\n        label: 'Contract Address',\n        name: 'contract_address',\n        type: 'string',\n        description: 'The contract address of the transactions',\n        placeholder: '0x794a61358d6845594f94dc1db02a252b5b4814ad',\n        optional: true,\n        show: { 'actions.operation': ['protocolTransactions'] }\n    },\n    {\n        label: 'Wallet Address',\n        name: 'wallet_address',\n        type: 'string',\n        description: 'The wallet address of the transactions',\n        placeholder: '0xbd90be3937744e2cd0ee680807901b1ab9479ffc',\n        optional: true,\n        show: { 'actions.operation': ['protocolTransactions'] }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/GitHub/GitHub.ts",
    "content": "import axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\n\nclass GitHub implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'GitHub'\n        this.name = 'GitHub'\n        this.icon = 'GitHub-Logo.png'\n        this.type = 'action'\n        this.category = 'Development'\n        this.version = 1.0\n        this.description = 'GitHub API'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'List User Repositories',\n                        name: 'listUserRepositories',\n                        description: 'Lists public repositories for the specified user.'\n                    },\n                    {\n                        label: 'List Organization Repositories',\n                        name: 'listOrganizationRepositories',\n                        description: 'Lists repositories for the specified organization.'\n                    },\n                    {\n                        label: 'List repository issues',\n                        name: 'listRepositoryIssues',\n                        description: 'List issues in a repository.'\n                    },\n                    {\n                        label: 'Create an issue',\n                        name: 'createIssue',\n                        description: 'Any user with pull access to a repository can create an issue.'\n                    },\n                    {\n                        label: 'Get an issue',\n                        name: 'getIssue',\n                        description: 'Get an issues in a repository.'\n                    },\n                    {\n                        label: 'Update an issue',\n                        name: 'updateIssue',\n                        description: 'Update an issues in a repository.'\n                    },\n                    {\n                        label: 'Lock an issue',\n                        name: 'lockIssue',\n                        description: \"Users with push access can lock an issue or pull request's conversation.\"\n                    },\n                    {\n                        label: 'Unlock an issue',\n                        name: 'unlockIssue',\n                        description: \"Users with push access can unlock an issue's conversation.\"\n                    }\n                ]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'GitHub API',\n                        name: 'gitHubApi'\n                    }\n                ],\n                default: 'gitHubApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Owner/Organization',\n                name: 'owner',\n                type: 'string',\n                show: {\n                    'actions.operation': [\n                        'listRepositoryIssues',\n                        'createIssue',\n                        'getIssue',\n                        'updateIssue',\n                        'lockIssue',\n                        'unlockIssue',\n                        'listUserRepositories',\n                        'listOrganizationRepositories'\n                    ]\n                },\n                description: 'The account owner of the repository. The name is not case sensitive.'\n            },\n            {\n                label: 'Type',\n                name: 'orgType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'All',\n                        name: 'all'\n                    },\n                    {\n                        label: 'Public',\n                        name: 'public'\n                    },\n                    {\n                        label: 'Private',\n                        name: 'private'\n                    },\n                    {\n                        label: 'Forks',\n                        name: 'forks'\n                    },\n                    {\n                        label: 'Sources',\n                        name: 'sources'\n                    },\n                    {\n                        label: 'Member',\n                        name: 'member'\n                    },\n                    {\n                        label: 'Internal',\n                        name: 'internal'\n                    }\n                ],\n                show: {\n                    'actions.operation': ['listOrganizationRepositories']\n                },\n                description: 'Specifies the types of repositories you want returned.',\n                optional: true,\n                default: 'all'\n            },\n            {\n                label: 'Type',\n                name: 'userType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'All',\n                        name: 'all'\n                    },\n                    {\n                        label: 'Owner',\n                        name: 'owner'\n                    },\n                    {\n                        label: 'Member',\n                        name: 'member'\n                    }\n                ],\n                show: {\n                    'actions.operation': ['listUserRepositories']\n                },\n                description: 'Specifies the types of repositories you want returned.',\n                optional: true,\n                default: 'owner'\n            },\n            {\n                label: 'Sort',\n                name: 'sort',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Created',\n                        name: 'created'\n                    },\n                    {\n                        label: 'Updated',\n                        name: 'updated'\n                    },\n                    {\n                        label: 'Pushed',\n                        name: 'pushed'\n                    },\n                    {\n                        label: 'Full Name',\n                        name: 'full_name'\n                    }\n                ],\n                show: {\n                    'actions.operation': ['listUserRepositories', 'listOrganizationRepositories']\n                },\n                description: 'The property to sort the results by.',\n                optional: true,\n                default: 'created'\n            },\n            {\n                label: 'Direction of sort',\n                name: 'direction',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Ascending',\n                        name: 'asc'\n                    },\n                    {\n                        label: 'Descending',\n                        name: 'desc'\n                    }\n                ],\n                show: {\n                    'actions.operation': ['listUserRepositories', 'listOrganizationRepositories']\n                },\n                description: 'The order to sort by.',\n                optional: true,\n                default: 'desc'\n            },\n            {\n                label: 'Repository',\n                name: 'repo',\n                type: 'string',\n                show: {\n                    'actions.operation': ['listRepositoryIssues', 'createIssue', 'getIssue', 'updateIssue', 'lockIssue', 'unlockIssue']\n                },\n                description: 'The name of the repository. The name is not case sensitive.'\n            },\n            {\n                label: 'Title',\n                name: 'title',\n                type: 'string',\n                show: { 'actions.operation': ['createIssue'] },\n                description: 'The title of the issue.'\n            },\n            {\n                label: 'Title',\n                name: 'titleOptional',\n                type: 'string',\n                show: { 'actions.operation': ['updateIssue'] },\n                description: 'The title of the issue.',\n                optional: true\n            },\n            {\n                label: 'Body',\n                name: 'body',\n                type: 'string',\n                show: { 'actions.operation': ['createIssue', 'updateIssue'] },\n                description: 'The contents of the issue.',\n                optional: true\n            },\n            {\n                label: 'Issue Number',\n                name: 'issueNumber',\n                type: 'number',\n                show: {\n                    'actions.operation': ['getIssue', 'updateIssue', 'lockIssue', 'unlockIssue']\n                },\n                description: 'The number that identifies the issue.'\n            },\n            {\n                label: 'Lock Reason',\n                name: 'lockReason',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Off Topic',\n                        name: 'off-topic'\n                    },\n                    {\n                        label: 'Too Heated',\n                        name: 'too heated'\n                    },\n                    {\n                        label: 'Resolved',\n                        name: 'resolved'\n                    },\n                    {\n                        label: 'Spam',\n                        name: 'spam'\n                    }\n                ],\n                show: { 'actions.operation': ['lockIssue'] },\n                description: 'The reason for locking the issue or pull request conversation.',\n                optional: true\n            }\n        ] as INodeParams[]\n    }\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const returnData: ICommonObject[] = []\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const operation = actionData.operation as string\n        const accessToken = credentials.accessToken as string\n        let responseData: any\n\n        try {\n            let method: Method = 'GET'\n            let url = ''\n            const queryParameters: ICommonObject = {}\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                Authorization: 'Bearer ' + accessToken\n            }\n            let dataString: any = {}\n            const queryBody: ICommonObject = {}\n            if (operation === 'listUserRepositories' || operation === 'listOrganizationRepositories') {\n                const owner = inputParametersData.owner as string\n                const type = operation === 'listUserRepositories' ? inputParametersData.userType : inputParametersData.orgType\n                const path = operation === 'listUserRepositories' ? 'users' : 'orgs'\n                const sort = inputParametersData.sort as string\n                const direction = inputParametersData.direction as string\n                method = 'GET'\n                url = `https://api.github.com/${path}/${owner}/repos`\n                if (type) url += url.includes('?') ? `&type=${type};` : `?type=${type}`\n                if (sort) url += url.includes('?') ? `&sort=${sort};` : `?sort=${sort}`\n                if (direction) url += url.includes('?') ? `&direction=${direction};` : `?direction=${direction}`\n            } else if (operation === 'listRepositoryIssues') {\n                const owner = inputParametersData.owner as string\n                const repo = inputParametersData.repo as string\n                method = 'GET'\n                url = 'https://api.github.com/repos/' + owner + '/' + repo + '/issues'\n            } else if (operation === 'createIssue') {\n                const owner = inputParametersData.owner as string\n                const repo = inputParametersData.repo as string\n                const title = inputParametersData.title as string\n                const body = inputParametersData.body as string\n                method = 'POST'\n                url = 'https://api.github.com/repos/' + owner + '/' + repo + '/issues'\n                if (title) dataString['title'] = title\n                if (body) dataString['body'] = body\n            } else if (operation === 'getIssue') {\n                const owner = inputParametersData.owner as string\n                const repo = inputParametersData.repo as string\n                const issueNumber = inputParametersData.issueNumber as string\n                method = 'GET'\n                url = 'https://api.github.com/repos/' + owner + '/' + repo + '/issues/' + issueNumber\n            } else if (operation === 'updateIssue') {\n                const owner = inputParametersData.owner as string\n                const repo = inputParametersData.repo as string\n                const issueNumber = inputParametersData.issueNumber as string\n                const title = inputParametersData.titleOptional as string\n                const body = inputParametersData.body as string\n                method = 'PATCH'\n                url = 'https://api.github.com/repos/' + owner + '/' + repo + '/issues/' + issueNumber\n                if (title) dataString['title'] = title\n                if (body) dataString['body'] = body\n            } else if (operation === 'lockIssue') {\n                const owner = inputParametersData.owner as string\n                const repo = inputParametersData.repo as string\n                const issueNumber = inputParametersData.issueNumber as string\n                const lockReason = inputParametersData.lockReason as string\n                method = 'PUT'\n                url = 'https://api.github.com/repos/' + owner + '/' + repo + '/issues/' + issueNumber + '/lock'\n                if (lockReason) dataString['lock_reason'] = lockReason\n            } else if (operation === 'unlockIssue') {\n                const owner = inputParametersData.owner as string\n                const repo = inputParametersData.repo as string\n                const issueNumber = inputParametersData.issueNumber as string\n                method = 'DELETE'\n                url = 'https://api.github.com/repos/' + owner + '/' + repo + '/issues/' + issueNumber + '/lock'\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers,\n                data: dataString\n            }\n            if (Object.keys(queryBody).length > 0) {\n                axiosConfig.data = queryBody\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: GitHub }\n"
  },
  {
    "path": "packages/components/nodes/GitHub/GitHubWebhook.ts",
    "content": "import axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport { ICommonObject, INode, INodeData, INodeParams, IWebhookNodeExecutionData, NodeType } from '../../src/Interface'\nimport { returnWebhookNodeExecutionData } from '../../src/utils'\n\nclass GitHubWebhook implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'GitHub Webhook'\n        this.name = 'GitHubWebhook'\n        this.icon = 'GitHub-Logo.png'\n        this.type = 'webhook'\n        this.category = 'Development'\n        this.version = 1.0\n        this.description = 'Start workflow whenever GitHub webhook event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Events',\n                name: 'events',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Branch Protection Rule',\n                        name: 'branch_protection_rule',\n                        description: 'Activity related to a branch protection rule.'\n                    },\n                    {\n                        label: 'Check Run',\n                        name: 'check_run',\n                        description: 'Check run activity has occurred.'\n                    },\n                    {\n                        label: 'Check Suite',\n                        name: 'check_suite',\n                        description: 'Check suite activity has occurred.'\n                    },\n                    {\n                        label: 'Code Scanning Alert',\n                        name: 'code_scanning_alert',\n                        description: 'Activity related to code scanning alerts in a repository.'\n                    },\n                    {\n                        label: 'Commit Comment',\n                        name: 'commit_comment',\n                        description: 'A commit comment is created.'\n                    },\n                    {\n                        label: 'Create',\n                        name: 'create',\n                        description: 'A Git branch or tag is created.'\n                    },\n                    {\n                        label: 'Delete',\n                        name: 'delete',\n                        description: 'A Git branch or tag is deleted.'\n                    },\n                    {\n                        label: 'Dependabot Alert',\n                        name: 'dependabot_alert',\n                        description: 'Activity related to Dependabot alerts.'\n                    },\n                    {\n                        label: 'Deploy Key',\n                        name: 'deploy_key',\n                        description: 'A deploy key is added or removed from a repository.'\n                    },\n                    {\n                        label: 'Deployment',\n                        name: 'deployment',\n                        description: 'A deployment is created.'\n                    },\n                    {\n                        label: 'Deployment Status',\n                        name: 'deployment_status',\n                        description: 'A deployment is created.'\n                    },\n                    {\n                        label: 'Discussion',\n                        name: 'discussion',\n                        description: 'Activity related to a discussion.'\n                    },\n                    {\n                        label: 'Discussion Comment',\n                        name: 'discussion_comment',\n                        description: 'Activity related to a comment in a discussion.'\n                    },\n                    {\n                        label: 'Fork',\n                        name: 'fork',\n                        description: 'A user forks a repository.'\n                    },\n                    {\n                        label: 'Github App Authorization',\n                        name: 'github_app_authorization',\n                        description:\n                            'When someone revokes their authorization of a GitHub App, this event occurs. A GitHub App receives this webhook by default and cannot unsubscribe from this event.'\n                    },\n                    {\n                        label: 'Gollum',\n                        name: 'gollum',\n                        description: 'A wiki page is created or updated.'\n                    },\n                    {\n                        label: 'Installation',\n                        name: 'installation',\n                        description: 'Activity related to a GitHub App installation.'\n                    },\n                    {\n                        label: 'Installation Repositories',\n                        name: 'installation_repositories',\n                        description: 'Activity related to repositories being added to a GitHub App installation.'\n                    },\n                    {\n                        label: 'Issue Comment',\n                        name: 'issue_comment',\n                        description: 'Activity related to an issue or pull request comment. '\n                    },\n                    {\n                        label: 'Issues',\n                        name: 'issues',\n                        description: 'Activity related to an issue.'\n                    },\n                    {\n                        label: 'Label',\n                        name: 'label',\n                        description: 'Activity related to a label.'\n                    },\n                    {\n                        label: 'Marketplace Purchase',\n                        name: 'marketplace_purchase',\n                        description: 'Activity related to a GitHub Marketplace purchase.'\n                    },\n                    {\n                        label: 'Member',\n                        name: 'member',\n                        description: 'Activity related to repository collaborators.'\n                    },\n                    {\n                        label: 'Membership',\n                        name: 'membership',\n                        description: 'Activity related to team membership.'\n                    },\n                    {\n                        label: 'Merge Group',\n                        name: 'merge_group',\n                        description: 'Activity related to merge groups in a merge queue.'\n                    },\n                    {\n                        label: 'Meta',\n                        name: 'meta',\n                        description: 'The webhook this event is configured on was deleted.'\n                    },\n                    {\n                        label: 'Milestone',\n                        name: 'milestone',\n                        description: 'Activity related to milestones.'\n                    },\n                    {\n                        label: 'Organization',\n                        name: 'organization',\n                        description: 'Activity related to an organization and its members.'\n                    },\n                    {\n                        label: 'Org Block',\n                        name: 'org_block',\n                        description: 'Activity related to people being blocked in an organization. '\n                    },\n                    {\n                        label: 'Package',\n                        name: 'package',\n                        description: 'Activity related to GitHub Packages.'\n                    },\n                    {\n                        label: 'Page Build',\n                        name: 'page_build',\n                        description: 'Represents an attempted build of a GitHub Pages site,'\n                    },\n                    {\n                        label: 'Ping',\n                        name: 'ping',\n                        description:\n                            'When you create a new webhook, we send you a simple ping event to let you know you have set up the webhook correctly.'\n                    },\n                    {\n                        label: 'Project',\n                        name: 'project',\n                        description: 'Activity related to classic projects.'\n                    },\n                    {\n                        label: 'Project Card',\n                        name: 'project_card',\n                        description: 'Activity related to cards in a classic project.'\n                    },\n                    {\n                        label: 'Project Column',\n                        name: 'project_column',\n                        description: 'Activity related to columns in a classic project. '\n                    },\n                    {\n                        label: 'Projects V2 Item',\n                        name: 'projects_v2_item',\n                        description: 'Activity related to items in a project.'\n                    },\n                    {\n                        label: 'Public',\n                        name: 'public',\n                        description: 'When a private repository is made public.'\n                    },\n                    {\n                        label: 'Pull Request',\n                        name: 'pull_request',\n                        description: 'Activity related to pull requests.'\n                    },\n                    {\n                        label: 'Pull Request Review',\n                        name: 'pull_request_review',\n                        description: 'Activity related to pull request reviews.'\n                    },\n                    {\n                        label: 'Pull Request Review Comment',\n                        name: 'pull_request_review_comment',\n                        description: 'Activity related to pull request review comments in the pull request unified diff.'\n                    },\n                    {\n                        label: 'Pull Request Review Thread',\n                        name: 'pull_request_review_thread',\n                        description: 'Activity related to a comment thread on a pull request being marked as resolved or unresolved.'\n                    },\n                    {\n                        label: 'Push',\n                        name: 'push',\n                        description: 'Activity related to one or more commits pushed to a repository branch or tag.'\n                    },\n                    {\n                        label: 'Release',\n                        name: 'release',\n                        description: 'Activity related to a release.'\n                    },\n                    {\n                        label: 'Repository Dispatch',\n                        name: 'repository_dispatch',\n                        description:\n                            'This event occurs when a GitHub App sends a POST request to the \"Create a repository dispatch event\" endpoint.'\n                    },\n                    {\n                        label: 'Repository',\n                        name: 'repository',\n                        description: 'Activity related to a repository.'\n                    },\n                    {\n                        label: 'Repository Import',\n                        name: 'repository_import',\n                        description: 'Activity related to a repository being imported to GitHub.'\n                    },\n                    {\n                        label: 'Repository Vulnerability Alert',\n                        name: 'repository_vulnerability_alert',\n                        description: 'Activity related to security vulnerability alerts in a repository.'\n                    },\n                    {\n                        label: 'Security Advisory',\n                        name: 'security_advisory',\n                        description:\n                            'Activity related to a security advisory that has been reviewed by GitHub. A GitHub-reviewed security advisory provides information about security-related vulnerabilities in software on GitHub.'\n                    },\n                    {\n                        label: 'Sponsorship',\n                        name: 'sponsorship',\n                        description:\n                            'Activity related to a sponsorship listing. The type of activity is specified in the action property of the payload object.'\n                    },\n                    {\n                        label: 'Star',\n                        name: 'star',\n                        description: 'Activity related to a repository being starred.'\n                    },\n                    {\n                        label: 'Status',\n                        name: 'status',\n                        description: 'When the status of a Git commit changes.'\n                    },\n                    {\n                        label: 'Team',\n                        name: 'team',\n                        description: 'Activity related to an organization team.'\n                    },\n                    {\n                        label: 'Team Add',\n                        name: 'team_add',\n                        description: 'When a repository is added to a team.'\n                    },\n                    {\n                        label: 'Watch',\n                        name: 'watch',\n                        description: 'When someone stars a repository.'\n                    },\n                    {\n                        label: 'Workflow Dispatch',\n                        name: 'workflow_dispatch',\n                        description:\n                            'This event occurs when someone triggers a workflow run on GitHub or sends a POST request to the \"Create a workflow dispatch event\" endpoint.'\n                    },\n                    {\n                        label: 'Workflow Job',\n                        name: 'workflow_job',\n                        description: 'A GitHub Actions workflow job has been queued, is in progress, or has been completed on a repository.'\n                    },\n                    {\n                        label: 'Workflow Run',\n                        name: 'workflow_run',\n                        description: 'When a GitHub Actions workflow run is requested or completed.'\n                    }\n                ],\n                default: 'push'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'GitHub API',\n                        name: 'gitHubApi'\n                    }\n                ],\n                default: 'gitHubApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Owner/Organization',\n                name: 'owner',\n                type: 'string',\n                description: 'The account owner of the repository. The name is not case sensitive.'\n            },\n            {\n                label: 'Repository',\n                name: 'repo',\n                type: 'string',\n                description: 'The name of the repository. The name is not case sensitive.'\n            }\n        ] as INodeParams[]\n    }\n\n    webhookMethods = {\n        async createWebhook(nodeData: INodeData, webhookFullUrl: string): Promise<string | undefined> {\n            // Check if webhook exists\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined) {\n                throw new Error('Required data missing')\n            }\n\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n\n            const owner = inputParametersData.owner as string\n            const repo = inputParametersData.repo as string\n\n            const baseUrl = `https://api.github.com/repos/${owner}/${repo}/hooks`\n            const accessToken = credentials.accessToken as string\n            const headers: AxiosRequestHeaders = {\n                Authorization: 'Bearer ' + accessToken\n            }\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url: `${baseUrl}?per_page=100`,\n                headers\n            }\n\n            let webhookExist = false\n            const webhook_events = []\n            try {\n                const responseListWebhooks = await axios(axiosConfig)\n                const responseWebhooks = responseListWebhooks.data\n                webhook_events.push(actionsData.events as string)\n\n                for (const webhook of responseWebhooks) {\n                    if (webhook.events === webhook_events && webhook.config.url === webhookFullUrl) {\n                        webhookExist = true\n                        break\n                    }\n                }\n            } catch (err) {\n                if (err.response.status !== 404) throw new Error(err)\n            }\n\n            if (!webhookExist) {\n                try {\n                    const data: ICommonObject = {\n                        name: 'web',\n                        config: {\n                            content_type: 'json',\n                            insecure_ssl: '0',\n                            url: webhookFullUrl\n                        },\n                        events: webhook_events,\n                        active: true\n                    }\n\n                    const axiosCreateConfig: AxiosRequestConfig = {\n                        method: 'POST' as Method,\n                        url: baseUrl,\n                        data,\n                        headers\n                    }\n                    const createResponse = await axios(axiosCreateConfig)\n                    const createResponseData = createResponse.data\n                    if (createResponseData && createResponseData.id) {\n                        return createResponseData.id\n                    }\n                    return\n                } catch (error) {\n                    return\n                }\n            }\n        },\n\n        async deleteWebhook(nodeData: INodeData, webhookId: string): Promise<boolean> {\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined) {\n                throw new Error('Required data missing')\n            }\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n\n            const owner = inputParametersData.owner as string\n            const repo = inputParametersData.repo as string\n\n            const baseUrl = `https://api.github.com/repos/${owner}/${repo}/hooks`\n            const accessToken = credentials.accessToken as string\n            const headers: AxiosRequestHeaders = {\n                Authorization: 'Bearer ' + accessToken\n            }\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'DELETE' as Method,\n                url: `${baseUrl}/${webhookId}`,\n                headers\n            }\n\n            try {\n                await axios(axiosConfig)\n            } catch (error) {\n                return false\n            }\n            return true\n        }\n    }\n\n    async runWebhook(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const req = nodeData.req\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (req === undefined) {\n            throw new Error('Missing request')\n        }\n\n        // Check if it is a ping event, if yes return null\n        if (req.headers && req.headers['x-github-event'] === 'ping') return null\n\n        const returnData: ICommonObject[] = []\n\n        returnData.push({\n            headers: req?.headers,\n            params: req?.params,\n            query: req?.query,\n            body: req?.body,\n            rawBody: (req as any).rawBody,\n            url: req?.url\n        })\n        return returnWebhookNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: GitHubWebhook }\n"
  },
  {
    "path": "packages/components/nodes/GoogleDocs/GoogleDocs.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, refreshOAuth2Token, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\n\nclass GoogleDocs implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'GoogleDocs'\n        this.name = 'googleDocs'\n        this.icon = 'gdocs.svg'\n        this.type = 'action'\n        this.category = 'Productivity'\n        this.version = 1.0\n        this.description = 'Execute GoogleDocs operations'\n        this.incoming = 1\n        this.outgoing = 1\n\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Create New Document',\n                        name: 'create',\n                        description: 'Create a new document'\n                    },\n                    {\n                        label: 'Get All Values',\n                        name: 'getAll',\n                        description: 'Get all values from a document'\n                    },\n                    {\n                        label: 'Update a Document',\n                        name: 'update',\n                        description: 'Update a document'\n                    }\n                ],\n                default: 'create'\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Google Docs OAuth2',\n                        name: 'googleDocsOAuth2Api'\n                    }\n                ],\n                default: 'googleDocsOAuth2Api'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Document Name',\n                name: 'documentName',\n                type: 'string',\n                optional: true,\n                description: 'Name of the document to create. Default to Untitled document.',\n                show: {\n                    'actions.operation': ['create']\n                }\n            },\n            {\n                label: 'Document',\n                name: 'documentId',\n                type: 'asyncOptions',\n                loadMethod: 'getAllDocsFromDrive',\n                hide: {\n                    'actions.operation': ['create']\n                }\n            },\n\n            /**\n             *  batch update\n             */\n\n            {\n                label: 'Requests',\n                name: 'requests',\n                description:\n                    \"update a document. You can simply add one reequest data or add multiple request data. If request format is invalid, document won't be updated. The details about how to write a request data can be found at https://developers.google.com/docs/api/reference/rest/v1/documents/batchUpdate\",\n                type: 'json',\n                placeholder: `[\n                    {\n                        \"insertText\": {\n                          \"text\": \"new text\",\n                            \"location\": {\n                                \"index\": 1 \n                            }\n                        }\n                    },\n                    {\n                        \"insertTable\": {\n                            \"rows\": 3,\n                            \"columns\": 4,\n                            \"endOfSegmentLocation\":{\n                            }\n                        }\n                    }\n]`,\n                show: {\n                    'actions.operation': ['update']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getAllDocsFromDrive(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            // Get credentials\n            const token_type = credentials!.token_type as string\n            const access_token = credentials!.access_token as string\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                Authorization: `${token_type} ${access_token}`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `https://www.googleapis.com/drive/v3/files?q=mimeType='application/vnd.google-apps.document'`,\n                headers\n            }\n\n            let maxRetries = 5\n            do {\n                try {\n                    const response = await axios(axiosConfig)\n                    const responseData = response.data\n                    for (const file of responseData.files || []) {\n                        returnData.push({\n                            label: file.name as string,\n                            name: file.id as string\n                        })\n                    }\n                    return returnData\n                } catch (e) {\n                    // Access_token expired\n                    if (e.response && e.response.status === 401) {\n                        const { access_token } = await refreshOAuth2Token(credentials)\n                        headers['Authorization'] = `${token_type} ${access_token}`\n                        continue\n                    }\n                    return returnData\n                }\n            } while (--maxRetries)\n\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionsData === undefined) {\n            throw new Error('Required data missing!')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credential!')\n        }\n\n        // Get operation\n        const operation = actionsData.operation as string\n\n        // Get credentials\n        const token_type = credentials!.token_type as string\n        const access_token = credentials!.access_token as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        let queryBody: any = {}\n        let method: Method = 'POST'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json',\n            Authorization: `${token_type} ${access_token}`\n        }\n\n        const documentId = inputParametersData?.documentId as string\n\n        let maxRetries = 5\n        let oAuth2RefreshedData: any = {}\n\n        do {\n            try {\n                if (operation === 'create') {\n                    url = 'https://docs.googleapis.com/v1/documents'\n                    const documentName = inputParametersData?.documentName as string\n                    if (documentName) {\n                        queryBody['title'] = documentName\n                    }\n                } else if (operation === 'getAll') {\n                    method = 'GET'\n                    url = `https://docs.googleapis.com/v1/documents/${documentId}`\n                } else if (operation === 'update') {\n                    // batch update\n                    url = `https://docs.googleapis.com/v1/documents/${documentId}:batchUpdate`\n\n                    const requestsString = inputParametersData?.requests as string\n                    queryBody['requests'] = JSON.parse(requestsString)\n                }\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method,\n                    url,\n                    headers\n                }\n\n                if (Object.keys(queryParameters).length > 0) {\n                    axiosConfig.params = queryParameters\n                    axiosConfig.paramsSerializer = (params) => serializeQueryParams(params)\n                }\n\n                if (Object.keys(queryBody).length > 0) {\n                    axiosConfig.data = queryBody\n                }\n\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                // Access_token expired\n                if (error.response && error.response.status === 401) {\n                    const { access_token, expires_in } = await refreshOAuth2Token(credentials)\n                    headers['Authorization'] = `${token_type} ${access_token}`\n                    oAuth2RefreshedData = { access_token, expires_in }\n                    continue\n                }\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error executing GoogleDocs node. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData, oAuth2RefreshedData)\n    }\n}\n\nmodule.exports = { nodeClass: GoogleDocs }\n"
  },
  {
    "path": "packages/components/nodes/GoogleSheet/GoogleSheet.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, refreshOAuth2Token, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\n\nclass GoogleSheet implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'GoogleSheet'\n        this.name = 'googleSheet'\n        this.icon = 'gsheet.svg'\n        this.type = 'action'\n        this.category = 'Productivity'\n        this.version = 1.0\n        this.description = 'Execute GoogleSheet operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Create New SpreadSheet',\n                        name: 'create',\n                        description: 'Create a new spreadsheet'\n                    },\n                    {\n                        label: 'Add Rows',\n                        name: 'addRows',\n                        description: 'Add multiple rows to a spreadsheet'\n                    },\n                    {\n                        label: 'Get All Values',\n                        name: 'getAll',\n                        description: 'Get all values from a spreadsheet'\n                    },\n                    {\n                        label: 'Get Values By Range',\n                        name: 'getRange',\n                        description: 'Get values from cells from specified range'\n                    },\n                    {\n                        label: 'Update Cell',\n                        name: 'updateCell',\n                        description: 'Update single cell in a spreadsheet'\n                    },\n                    {\n                        label: 'Update Rows',\n                        name: 'updateRows',\n                        description: 'Update multiple rows in a spreadsheet'\n                    },\n                    {\n                        label: 'Clear Row',\n                        name: 'clearRow',\n                        description: 'Clear the values of a row in a spreadsheet'\n                    },\n                    {\n                        label: 'Clear Column',\n                        name: 'clearCol',\n                        description: 'Clear the values of a column in a spreadsheet'\n                    },\n                    {\n                        label: 'Clear By Range',\n                        name: 'clearRange',\n                        description: 'Clear the values in a spreadsheet from specified range'\n                    },\n                    {\n                        label: 'Clear All Values',\n                        name: 'clearAll',\n                        description: 'Clear all the values in a spreadsheet'\n                    }\n                ],\n                default: 'create'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Google Sheet OAuth2',\n                        name: 'googleSheetsOAuth2Api'\n                    }\n                ],\n                default: 'googleSheetsOAuth2Api'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Spreadsheet Name',\n                name: 'spreadsheetName',\n                type: 'string',\n                optional: true,\n                description: 'Name of the spreadsheet to create. Default to Untitled spreadsheet.',\n                show: {\n                    'actions.operation': ['create']\n                }\n            },\n            {\n                label: 'Spreadsheet',\n                name: 'spreadsheetId',\n                type: 'asyncOptions',\n                loadMethod: 'getAllSheetsFromDrive',\n                hide: {\n                    'actions.operation': ['create']\n                }\n            },\n            {\n                label: 'Sheet Name',\n                name: 'sheetName',\n                type: 'asyncOptions',\n                loadMethod: 'getSheets',\n                hide: {\n                    'actions.operation': ['create']\n                }\n            },\n            /***\n             * Get Values By Range\n             */\n            {\n                label: 'Range',\n                name: 'range',\n                type: 'string',\n                placeholder: 'A1:B2',\n                description:\n                    'For example, if the spreadsheet data is: A1=1, B1=2, A2=3, B2=4, then requesting range=A1:B2, returns [[1, 2],[3, 4]]',\n                show: {\n                    'actions.operation': ['getRange']\n                }\n            },\n            /***\n             * Add Rows\n             */\n            {\n                label: 'Row Values',\n                name: 'rowValues',\n                type: 'json',\n                placeholder: '[[1, 2], [3, 4]]',\n                description: 'For instance: [[1, 2], [3, 4]] will insert 2 new rows. FIRST row: A1=1, B1=2, and SECOND row: A2=3, B2=4',\n                show: {\n                    'actions.operation': ['addRows']\n                }\n            },\n            /***\n             * Update Rows\n             */\n            {\n                label: 'Range',\n                name: 'range',\n                type: 'string',\n                placeholder: 'A1:B4',\n                description: 'A1 notation of the rows to update.',\n                show: {\n                    'actions.operation': ['updateRows']\n                }\n            },\n            {\n                label: 'Row Values',\n                name: 'rowValues',\n                type: 'json',\n                placeholder: '[[1, 2], [3, 4]]',\n                description: 'For instance: [[1, 2], [3, 4]] will update FIRST row: A1=1, B1=2, and SECOND row: A2=3, B2=4',\n                show: {\n                    'actions.operation': ['updateRows']\n                }\n            },\n            /***\n             * Update Cell\n             */\n            {\n                label: 'Cell',\n                name: 'range',\n                type: 'string',\n                placeholder: 'A1',\n                description: 'A1 notation of the cell to update.',\n                show: {\n                    'actions.operation': ['updateCell']\n                }\n            },\n            {\n                label: 'Cell Value',\n                name: 'cellValue',\n                type: 'string',\n                placeholder: 'New Update Value',\n                default: '',\n                description: 'New value of the cell',\n                show: {\n                    'actions.operation': ['updateCell']\n                }\n            },\n            /***\n             * Clear\n             */\n            {\n                label: 'Row Number',\n                name: 'clearRowNumber',\n                type: 'number',\n                placeholder: '1',\n                description: 'Row (number) to clear the values',\n                show: {\n                    'actions.operation': ['clearRow']\n                }\n            },\n            {\n                label: 'Column Number',\n                name: 'clearColNumber',\n                type: 'string',\n                placeholder: 'A',\n                description: 'Column (alphabet) to clear the values',\n                show: {\n                    'actions.operation': ['clearCol']\n                }\n            },\n            {\n                label: 'Range',\n                name: 'range',\n                type: 'string',\n                placeholder: 'A1:B4',\n                description: 'A1 notation of the range to clear.',\n                show: {\n                    'actions.operation': ['clearRange']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getAllSheetsFromDrive(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            // Get credentials\n            const token_type = credentials!.token_type as string\n            const access_token = credentials!.access_token as string\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                Authorization: `${token_type} ${access_token}`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `https://www.googleapis.com/drive/v3/files?q=mimeType='application/vnd.google-apps.spreadsheet'`,\n                headers\n            }\n\n            let maxRetries = 5\n            do {\n                try {\n                    const response = await axios(axiosConfig)\n                    const responseData = response.data\n                    for (const file of responseData.files || []) {\n                        returnData.push({\n                            label: file.name as string,\n                            name: file.id as string\n                        })\n                    }\n                    return returnData\n                } catch (e) {\n                    // Access_token expired\n                    if (e.response && e.response.status === 401) {\n                        const { access_token } = await refreshOAuth2Token(credentials)\n                        headers['Authorization'] = `${token_type} ${access_token}`\n                        continue\n                    }\n                    return returnData\n                }\n            } while (--maxRetries)\n\n            return returnData\n        },\n\n        async getSheets(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const credentials = nodeData.credentials\n            const inputParameters = nodeData.inputParameters\n\n            if (credentials === undefined || inputParameters === undefined) {\n                return returnData\n            }\n\n            const spreadsheetId = inputParameters.spreadsheetId as string\n            const token_type = credentials!.token_type as string\n            const access_token = credentials!.access_token as string\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                Authorization: `${token_type} ${access_token}`\n            }\n\n            if (spreadsheetId === undefined || token_type === undefined || access_token === undefined) {\n                return returnData\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}`,\n                headers\n            }\n\n            let maxRetries = 5\n            do {\n                try {\n                    const response = await axios(axiosConfig)\n                    const responseData = response.data\n                    for (const sheet of responseData.sheets!) {\n                        if (sheet.properties!.sheetType !== 'GRID') continue\n                        returnData.push({\n                            label: sheet.properties!.title as string,\n                            name: sheet.properties!.title as string\n                        })\n                    }\n                    return returnData\n                } catch (e) {\n                    // Access_token expired\n                    if (e.response && e.response.status === 401) {\n                        const { access_token } = await refreshOAuth2Token(credentials)\n                        headers['Authorization'] = `${token_type} ${access_token}`\n                        continue\n                    }\n                    return returnData\n                }\n            } while (--maxRetries)\n\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionsData === undefined) {\n            throw new Error('Required data missing!')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credential!')\n        }\n\n        // Get operation\n        const operation = actionsData.operation as string\n\n        // Get credentials\n        const token_type = credentials!.token_type as string\n        const access_token = credentials!.access_token as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        let queryBody: any = {}\n        let method: Method = 'POST'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json',\n            Authorization: `${token_type} ${access_token}`\n        }\n\n        const spreadsheetId = inputParametersData?.spreadsheetId as string\n        const sheetName = inputParametersData?.sheetName as string\n        const rowValues = inputParametersData?.rowValues as string\n\n        let maxRetries = 5\n        let oAuth2RefreshedData: any = {}\n        do {\n            try {\n                if (operation === 'create') {\n                    url = 'https://sheets.googleapis.com/v4/spreadsheets'\n                    const spreadsheetName = inputParametersData?.spreadsheetName as string\n                    if (spreadsheetName) {\n                        queryBody['properties'] = {\n                            title: spreadsheetName\n                        }\n                    }\n                } else if (operation === 'addRows') {\n                    const rows = JSON.parse(rowValues.replace(/\\s/g, ''))\n                    const range = sheetName\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}:append`\n                    queryParameters['valueInputOption'] = 'USER_ENTERED'\n                    queryParameters['insertDataOption'] = 'INSERT_ROWS'\n                    queryBody['values'] = rows\n                } else if (operation === 'getAll') {\n                    method = 'GET'\n                    const range = sheetName\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`\n                } else if (operation === 'getRange') {\n                    method = 'GET'\n                    const range = inputParametersData?.range as string\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`\n                } else if (operation === 'updateCell') {\n                    method = 'PUT'\n                    const range = inputParametersData?.range as string\n                    const cellValue = inputParametersData?.cellValue as string\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`\n                    queryParameters['valueInputOption'] = 'USER_ENTERED'\n                    queryBody['values'] = [[cellValue]]\n                } else if (operation === 'updateRows') {\n                    method = 'PUT'\n                    const rows = JSON.parse(rowValues.replace(/\\s/g, ''))\n                    const range = inputParametersData?.range as string\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`\n                    queryParameters['valueInputOption'] = 'USER_ENTERED'\n                    queryBody['values'] = rows\n                } else if (operation === 'clearRow') {\n                    const clearRowNumber = inputParametersData?.clearRowNumber as number\n                    const range = `${sheetName}!${clearRowNumber}:${clearRowNumber}`\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}:clear`\n                } else if (operation === 'clearCol') {\n                    const clearColNumber = inputParametersData?.clearColNumber as string\n                    const range = `${sheetName}!${clearColNumber}:${clearColNumber}`\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}:clear`\n                } else if (operation === 'clearRange') {\n                    const range = inputParametersData?.range as string\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}:clear`\n                } else if (operation === 'clearAll') {\n                    const range = sheetName\n                    url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}:clear`\n                }\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method,\n                    url,\n                    headers\n                }\n\n                if (Object.keys(queryParameters).length > 0) {\n                    axiosConfig.params = queryParameters\n                    axiosConfig.paramsSerializer = (params) => serializeQueryParams(params)\n                }\n\n                if (Object.keys(queryBody).length > 0) {\n                    axiosConfig.data = queryBody\n                }\n\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                // Access_token expired\n                if (error.response && error.response.status === 401) {\n                    const { access_token, expires_in } = await refreshOAuth2Token(credentials)\n                    headers['Authorization'] = `${token_type} ${access_token}`\n                    oAuth2RefreshedData = { access_token, expires_in }\n                    continue\n                }\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error executing GoogleSheet node. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData, oAuth2RefreshedData)\n    }\n}\n\nmodule.exports = { nodeClass: GoogleSheet }\n"
  },
  {
    "path": "packages/components/nodes/GraphQL/GraphQL.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport isLocalhost from 'is-localhost-ip'\n\nclass GraphQL implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'GraphQL'\n        this.name = 'graphQL'\n        this.icon = 'graphql.svg'\n        this.type = 'action'\n        this.category = 'Development'\n        this.version = 1.0\n        this.description = 'Execute GraphQL request'\n        this.incoming = 1\n        this.outgoing = 1\n        this.credentials = [\n            {\n                label: 'Authorization',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Basic Auth',\n                        name: 'httpBasicAuth'\n                    },\n                    {\n                        label: 'Bearer Token Auth',\n                        name: 'httpBearerTokenAuth'\n                    },\n                    {\n                        label: 'No Auth',\n                        name: 'noAuth',\n                        hideRegisteredCredential: true\n                    }\n                ],\n                default: 'noAuth'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'URL',\n                name: 'url',\n                type: 'string',\n                default: '',\n                placeholder: 'http://<your-url>.com/'\n            },\n            {\n                label: 'Headers',\n                name: 'headers',\n                type: 'array',\n                array: [\n                    {\n                        label: 'Key',\n                        name: 'key',\n                        type: 'string',\n                        default: ''\n                    },\n                    {\n                        label: 'Value',\n                        name: 'value',\n                        type: 'string',\n                        default: ''\n                    }\n                ],\n                optional: true\n            },\n            {\n                label: 'GraphQL Body',\n                name: 'body',\n                type: 'json',\n                placeholder: `{\n  me {\n    name\n  }\n}`,\n                optional: true\n            },\n            {\n                label: 'Variables',\n                name: 'variables',\n                type: 'json',\n                placeholder: '{\"var1\": \"value1\"}',\n                optional: true\n            }\n        ]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const credentialMethod = credentials.credentialMethod as string\n\n        const url = inputParametersData.url as string\n        const headers = (inputParametersData.headers as ICommonObject[]) || []\n        const body = inputParametersData.body as string\n        const variables = inputParametersData.variables as string\n\n        const returnData: ICommonObject = {}\n\n        const urlHost = new URL(url).hostname\n        if ((await isLocalhost(urlHost)) || urlHost === '169.254.169.254' || urlHost === '[fd00:ec2::254]') {\n            throw new Error('URL not allowed')\n        }\n\n        try {\n            const queryHeaders: AxiosRequestHeaders = {}\n            let data: any = {}\n\n            for (const header of headers) {\n                const key = header.key as string\n                const value = header.value as string\n                if (key) queryHeaders[key] = value\n            }\n\n            if (body) {\n                data = { query: body.replace(/\\s/g, ' ') }\n            }\n\n            if (variables) {\n                const variablesJSON = JSON.parse(variables.replace(/\\s/g, ''))\n                if (Object.keys(variablesJSON).length) {\n                    data.variables = variablesJSON\n                }\n            }\n\n            if (credentialMethod === 'httpBearerTokenAuth') {\n                queryHeaders['Authorization'] = `Bearer ${credentials!.token}`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'POST' as Method,\n                url\n            }\n\n            if (Object.keys(data).length) {\n                axiosConfig.data = data\n            }\n\n            if (Object.keys(queryHeaders).length) {\n                axiosConfig.headers = queryHeaders\n            }\n\n            if (credentialMethod === 'httpBasicAuth') {\n                axiosConfig.auth = {\n                    username: credentials!.userName as string,\n                    password: credentials!.password as string\n                }\n            }\n\n            const response = await axios(axiosConfig)\n            returnData['data'] = response.data\n            returnData['status'] = response.status\n            returnData['statusText'] = response.statusText\n            returnData['headers'] = response.headers\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: GraphQL }\n"
  },
  {
    "path": "packages/components/nodes/HTTP/HTTP.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method, ResponseType } from 'axios'\nimport isLocalhost from 'is-localhost-ip'\n\nclass HTTP implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'HTTP'\n        this.name = 'http'\n        this.icon = 'http.svg'\n        this.type = 'action'\n        this.category = 'Development'\n        this.version = 1.0\n        this.description = 'Execute HTTP request'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Method',\n                name: 'method',\n                type: 'options',\n                options: [\n                    {\n                        label: 'GET',\n                        name: 'GET'\n                    },\n                    {\n                        label: 'POST',\n                        name: 'POST'\n                    },\n                    {\n                        label: 'PUT',\n                        name: 'PUT'\n                    },\n                    {\n                        label: 'DELETE',\n                        name: 'DELETE'\n                    },\n                    {\n                        label: 'HEAD',\n                        name: 'HEAD'\n                    }\n                ],\n                default: 'GET',\n                description: 'HTTP method'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Authorization',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Basic Auth',\n                        name: 'httpBasicAuth'\n                    },\n                    {\n                        label: 'Bearer Token Auth',\n                        name: 'httpBearerTokenAuth'\n                    },\n                    {\n                        label: 'No Auth',\n                        name: 'noAuth',\n                        hideRegisteredCredential: true\n                    }\n                ],\n                default: 'noAuth'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'URL',\n                name: 'url',\n                type: 'string',\n                default: '',\n                placeholder: 'http://<your-url>.com/'\n            },\n            {\n                label: 'Headers',\n                name: 'headers',\n                type: 'array',\n                array: [\n                    {\n                        label: 'Key',\n                        name: 'key',\n                        type: 'string',\n                        default: ''\n                    },\n                    {\n                        label: 'Value',\n                        name: 'value',\n                        type: 'string',\n                        default: ''\n                    }\n                ],\n                optional: true\n            },\n            {\n                label: 'Query Params',\n                name: 'queryParams',\n                type: 'array',\n                array: [\n                    {\n                        label: 'Key',\n                        name: 'key',\n                        type: 'string',\n                        default: ''\n                    },\n                    {\n                        label: 'Value',\n                        name: 'value',\n                        type: 'string',\n                        default: ''\n                    }\n                ],\n                optional: true\n            },\n            {\n                label: 'Body Type',\n                name: 'bodyType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'JSON',\n                        name: 'json'\n                    },\n                    {\n                        label: 'Text',\n                        name: 'text'\n                    }\n                ],\n                default: 'json',\n                optional: true\n            },\n            {\n                label: 'Body',\n                name: 'body',\n                type: 'json',\n                show: {\n                    'inputParameters.bodyType': ['json']\n                },\n                placeholder: '{\"var1\": \"value1\"}',\n                optional: true\n            },\n            {\n                label: 'Body',\n                name: 'body',\n                type: 'string',\n                show: {\n                    'inputParameters.bodyType': ['text']\n                },\n                default: '',\n                optional: true\n            },\n            {\n                label: 'Response Type',\n                name: 'responseType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'JSON',\n                        name: 'json'\n                    },\n                    {\n                        label: 'Text',\n                        name: 'text'\n                    },\n                    {\n                        label: 'Array Buffer',\n                        name: 'arraybuffer'\n                    },\n                    {\n                        label: 'Raw (Base64)',\n                        name: 'base64'\n                    }\n                ],\n                optional: true\n            }\n        ]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const method = actionData.method as string\n\n        const credentialMethod = credentials.credentialMethod as string\n\n        const url = inputParametersData.url as string\n        const headers = (inputParametersData.headers as ICommonObject[]) || []\n        const queryParams = (inputParametersData.queryParams as ICommonObject[]) || []\n        const bodyType = inputParametersData.bodyType as string\n        const body = inputParametersData.body as string\n        const responseType = inputParametersData.responseType as string\n\n        const returnData: ICommonObject = {}\n\n        const urlHost = new URL(url).hostname\n        if ((await isLocalhost(urlHost)) || urlHost === '169.254.169.254' || urlHost === '[fd00:ec2::254]') {\n            throw new Error('URL not allowed')\n        }\n\n        try {\n            const queryParameters: ICommonObject = {}\n            const queryHeaders: AxiosRequestHeaders = {}\n            let data: any = {}\n\n            for (const params of queryParams) {\n                const key = params.key as string\n                const value = params.value as string\n                if (key) queryParameters[key] = value\n            }\n\n            for (const header of headers) {\n                const key = header.key as string\n                const value = header.value as string\n                if (key) queryHeaders[key] = value\n            }\n\n            if (bodyType && bodyType === 'json' && body) {\n                data = JSON.parse(body.replace(/\\s/g, ' '))\n            } else if (bodyType && bodyType === 'text' && body) {\n                data = body\n            }\n\n            if (credentialMethod === 'httpBearerTokenAuth') {\n                queryHeaders['Authorization'] = `Bearer ${credentials!.token}`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: method as Method,\n                url: url\n            }\n\n            if (Object.keys(data).length) {\n                axiosConfig.data = data\n            }\n\n            if (Object.keys(queryParameters).length) {\n                axiosConfig.params = queryParameters\n                axiosConfig.paramsSerializer = (params) => serializeQueryParams(params)\n            }\n\n            if (Object.keys(queryHeaders).length) {\n                axiosConfig.headers = queryHeaders\n            }\n\n            if (responseType) {\n                axiosConfig.responseType = responseType as ResponseType\n                if (responseType === 'base64') axiosConfig.responseType = 'arraybuffer'\n            }\n\n            if (credentialMethod === 'httpBasicAuth') {\n                axiosConfig.auth = {\n                    username: credentials!.userName as string,\n                    password: credentials!.password as string\n                }\n            }\n\n            const response = await axios(axiosConfig)\n\n            if (responseType && responseType === 'base64') {\n                const content = `data:${response.headers['content-type']};base64,${response.data.toString('base64')}`\n                const attachment = {\n                    contentType: response.headers['content-type'],\n                    size: response.headers['content-length'],\n                    content\n                }\n                returnData['data'] = content\n                returnData.attachments = [attachment]\n            } else {\n                returnData['data'] = response.data\n            }\n\n            returnData['status'] = response.status\n            returnData['statusText'] = response.statusText\n            returnData['headers'] = response.headers\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: HTTP }\n"
  },
  {
    "path": "packages/components/nodes/Helio/Helio.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass Helio implements INode {\n    // properties\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    // parameters\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        // properties\n        this.label = 'Helio'\n        this.name = 'helio'\n        this.icon = 'helio.png'\n        this.type = 'action'\n        this.category = 'Payment'\n        this.version = 1.0\n        this.description = 'Execute Helio API integration'\n        this.incoming = 1\n        this.outgoing = 1\n\n        // parameter\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get All Transactions',\n                        name: 'listTransactions'\n                    }\n                ],\n                default: 'listTransactions'\n            }\n        ] as INodeParams[]\n\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                description: 'Network to execute API: Test or Prod',\n                options: [\n                    {\n                        label: 'TEST',\n                        name: 'test',\n                        description: 'Test network: https://dev.hel.io/'\n                    },\n                    {\n                        label: 'PROD',\n                        name: 'prod',\n                        description: 'Prod network: https://hel.io/'\n                    }\n                ],\n                default: 'test'\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Helio API Key',\n                        name: 'helioApi'\n                    }\n                ],\n                default: 'helioApi'\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const { actions, networks, credentials } = nodeData\n\n        if (actions === undefined || networks === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        const network = networks.network as string\n        const operation = actions.operation as string\n        const apiKey = credentials.apiKey as string\n        const secretKey = credentials.secretKey as string\n        const baseUrl = network === 'test' ? 'https://dev.api.hel.io/v1' : 'https://api.hel.io/v1'\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n        let method: Method = 'GET'\n        let url = ''\n\n        if (operation === 'listTransactions') {\n            url = `${baseUrl}/export/payments?apiKey=${apiKey}`\n        }\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${secretKey}` }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Helio }\n"
  },
  {
    "path": "packages/components/nodes/Helio/HelioWebhook.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeParams, IWebhookNodeExecutionData, NodeType } from '../../src/Interface'\nimport { returnWebhookNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass HelioWebhook implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Helio Webhook'\n        this.name = 'helioWebhook'\n        this.icon = 'helio.png'\n        this.type = 'webhook'\n        this.category = 'Payment'\n        this.version = 1.0\n        this.description = 'Start workflow whenever Helio webhook event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                description: 'Network to execute API: Test or Prod',\n                options: [\n                    {\n                        label: 'TEST',\n                        name: 'test',\n                        description: 'Test network: https://dev.hel.io/'\n                    },\n                    {\n                        label: 'PROD',\n                        name: 'prod',\n                        description: 'Prod network: https://hel.io/'\n                    }\n                ],\n                default: 'test'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Helio API Key',\n                        name: 'helioApi'\n                    }\n                ],\n                default: 'helioApi'\n            }\n        ] as INodeParams[]\n        this.actions = [\n            {\n                label: 'Event',\n                name: 'event',\n                type: 'options',\n                options: [\n                    {\n                        label: 'New payment on Pay Link',\n                        name: 'CREATED',\n                        description: 'Triggered upon new payment on the Pay Link'\n                    },\n                    {\n                        label: 'New subscription on Pay Stream',\n                        name: 'STARTED',\n                        description: `Triggered upon new subscription/stream started on the Pay Stream`\n                    },\n                    {\n                        label: 'Cancellation of subscription on Pay Stream',\n                        name: 'ENDED',\n                        description: `Triggered when a subscription/stream was stopped/ended on the Pay Stream`\n                    }\n                ]\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Pay Link Id',\n                name: 'paylinkId',\n                type: 'string',\n                placeholder: '63ea24cc1ea62a8e0d272444',\n                description: 'For example, pay link id of https://hel.io/pay/63ea24cc1ea62a8e0d272444 is 63ea24cc1ea62a8e0d272444',\n                show: {\n                    'actions.event': ['CREATED']\n                }\n            },\n            {\n                label: 'Pay Stream Id',\n                name: 'streamId',\n                type: 'string',\n                placeholder: '63ea543143507a1df4f6fccf',\n                description: 'For example, pay stream id of https://hel.io/pay/63ea543143507a1df4f6fccf is 63ea543143507a1df4f6fccf',\n                show: {\n                    'actions.event': ['STARTED', 'ENDED']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    webhookMethods = {\n        async createWebhook(nodeData: INodeData, webhookFullUrl: string): Promise<string | undefined> {\n            // Check if webhook exists\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const networksData = nodeData.networks\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined || networksData === undefined) {\n                throw new Error('Required data missing')\n            }\n\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n\n            const apiKey = credentials.apiKey as string\n            const secretKey = credentials.secretKey as string\n            const network = networksData.network as string\n            const baseUrl = network === 'test' ? 'https://dev.api.hel.io/v1' : 'https://api.hel.io/v1'\n            const paylinkId = inputParametersData.paylinkId as string\n            const streamId = inputParametersData.streamId as string\n            const event = actionsData.event as string\n            const payType = paylinkId ? 'paylink' : 'stream'\n            const payTypeId = paylinkId ? 'paylinkId' : 'streamId'\n            const payId = paylinkId ? paylinkId : streamId\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url: `${baseUrl}/webhook/${payType}/transaction?apiKey=${apiKey}&${payTypeId}=${payId}`,\n                headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${secretKey}` }\n            }\n\n            try {\n                const response = await axios(axiosConfig)\n                const webhooks = response.data\n                let webhookExist = false\n\n                for (const webhook of webhooks) {\n                    if (webhook.events.includes(event) && webhook.targetUrl === webhookFullUrl) {\n                        // Check match pay link id\n                        if (paylinkId) {\n                            if (webhook.paylink === paylinkId) {\n                                webhookExist = true\n                                break\n                            }\n                        }\n\n                        // Check match pay stream id\n                        if (streamId) {\n                            if (webhook.stream === streamId) {\n                                webhookExist = true\n                                break\n                            }\n                        }\n                    }\n                }\n\n                if (!webhookExist) {\n                    const data: ICommonObject = {\n                        events: [event],\n                        targetUrl: webhookFullUrl\n                    }\n                    if (paylinkId) data.paylinkId = paylinkId\n                    if (streamId) data.streamId = streamId\n\n                    const axiosCreateConfig: AxiosRequestConfig = {\n                        method: 'POST' as Method,\n                        url: `${baseUrl}/webhook/${payType}/transaction?apiKey=${apiKey}`,\n                        data,\n                        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${secretKey}` }\n                    }\n                    const response = await axios(axiosCreateConfig)\n\n                    const createResponseData = response.data\n                    if (createResponseData && createResponseData.id) {\n                        return createResponseData.id\n                    }\n                    return\n                }\n            } catch (error) {\n                return\n            }\n        },\n\n        async deleteWebhook(nodeData: INodeData, webhookId: string): Promise<boolean> {\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const networksData = nodeData.networks\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined || networksData === undefined) {\n                throw new Error('Required data missing')\n            }\n\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n\n            const apiKey = credentials.apiKey as string\n            const secretKey = credentials.secretKey as string\n            const network = networksData.network as string\n            const baseUrl = network === 'test' ? 'https://dev.api.hel.io/v1' : 'https://api.hel.io/v1'\n            const paylinkId = inputParametersData.paylinkId as string\n            const payType = paylinkId ? 'paylink' : 'stream'\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'DELETE' as Method,\n                url: `${baseUrl}/webhook/${payType}/transaction/${webhookId}?apiKey=${apiKey}`,\n                headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${secretKey}` }\n            }\n\n            try {\n                await axios(axiosConfig)\n            } catch (error) {\n                return false\n            }\n\n            return true\n        }\n    }\n\n    async runWebhook(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const req = nodeData.req\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (req === undefined) {\n            throw new Error('Missing request')\n        }\n\n        //TODO: Verify webhook via signing key\n\n        const returnData: ICommonObject[] = []\n\n        returnData.push({\n            headers: req?.headers,\n            params: req?.params,\n            query: req?.query,\n            body: req?.body,\n            rawBody: (req as any).rawBody,\n            url: req?.url\n        })\n\n        return returnWebhookNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: HelioWebhook }\n"
  },
  {
    "path": "packages/components/nodes/Helius/Helius.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass Helius implements INode {\n    // properties\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    // parameters\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        // properties\n        this.label = 'Helius'\n        this.name = 'helius'\n        this.icon = 'helius.png'\n        this.type = 'action'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Perform Helius on-chain operations on Solana'\n        this.incoming = 1\n        this.outgoing = 1\n\n        // parameter\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get NFT events',\n                        name: 'nft-events',\n                        description: 'Returns all NFT events given an address.'\n                    },\n                    {\n                        label: 'Get NFT Portfolio',\n                        name: 'nfts',\n                        description: 'Returns all the NFTs that the given address currently holds.'\n                    },\n                    {\n                        label: 'Name Lookup',\n                        name: 'names',\n                        description: 'Does a reverse lookup with the given address for Solana Naming Service domains.'\n                    },\n                    {\n                        label: 'Get Token Balances',\n                        name: 'balances',\n                        description: 'Returns the native Solana balance (in lamports) and all token balances for a given address.'\n                    }\n                ],\n                default: 'balances'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            // credentialMethod is mandatory field\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Helius API Key',\n                        name: 'heliusApi'\n                    }\n                ],\n                default: 'heliusApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Address',\n                name: 'address',\n                type: 'string',\n                optional: false\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const { actions, inputParameters, credentials } = nodeData\n\n        if (actions === undefined || inputParameters === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actions.api as string\n        const apiKey = credentials.apiKey as string\n\n        const address = inputParameters.address as string\n\n        const apiURL = 'https://api.helius.xyz/v0/addresses'\n        const resource = `${api}`\n        const options = `api-key=${apiKey}`\n\n        const url = `${apiURL}/${address}/${resource}?${options}`\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Helius }\n"
  },
  {
    "path": "packages/components/nodes/Hubspot/Hubspot.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass Hubspot implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Hubspot'\n        this.name = 'hubspot'\n        this.icon = 'hubspot.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 2.0\n        this.description = 'Execute Hubspot API integration'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Create new contact',\n                        name: 'addContact',\n                        description: 'Create a new contact'\n                    },\n                    {\n                        label: 'Get contact',\n                        name: 'getContact',\n                        description: 'Get a contact details'\n                    },\n                    {\n                        label: 'Delete contact',\n                        name: 'deleteContact',\n                        description: 'Delete a contact'\n                    },\n                    {\n                        label: 'Get list of contacts',\n                        name: 'listContacts',\n                        description: 'Get a list of contact details'\n                    }\n                ]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            // credentialMethod is mandatory field\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Hubspot Credentials',\n                        name: 'hubspotCredential'\n                    }\n                ],\n                default: 'hubspotCredential'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Customer Email',\n                name: 'email',\n                type: 'string',\n                show: {\n                    'actions.operation': ['addContact']\n                }\n            },\n            {\n                label: 'Customer First Name',\n                name: 'firstname',\n                type: 'string',\n                optional: true,\n                show: {\n                    'actions.operation': ['addContact']\n                }\n            },\n            {\n                label: 'Customer Last Name',\n                name: 'lastname',\n                type: 'string',\n                optional: true,\n                show: {\n                    'actions.operation': ['addContact']\n                }\n            },\n            {\n                label: 'Customer Company',\n                name: 'company',\n                type: 'string',\n                optional: true,\n                show: {\n                    'actions.operation': ['addContact']\n                }\n            },\n            {\n                label: 'Contact',\n                name: 'contactId',\n                type: 'asyncOptions',\n                loadMethod: 'getContacts',\n                show: {\n                    'actions.operation': ['getContact', 'deleteContact']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getContacts(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n            const accessToken = credentials!.accessToken as string\n\n            if (!accessToken) return returnData\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.hubapi.com/crm/v3/objects/contacts`,\n                    headers: {\n                        Authorization: `Bearer ${accessToken}`,\n                        'Content-Type': 'application/json'\n                    }\n                }\n                const response = await axios(axiosConfig)\n                const contacts = response.data?.results\n\n                contacts.forEach((contact: any) => {\n                    const data = {\n                        label: contact.properties.email,\n                        name: contact.id\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                })\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionData === undefined) {\n            throw handleErrorMessage({ message: 'Required data missing' })\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credential!')\n        }\n\n        const accessToken = credentials.accessToken as string\n        const operation = actionData.operation as string\n\n        async function makeApiCall(method: string, url: string, operation: string, body?: ICommonObject): Promise<any[]> {\n            const axiosConfig: AxiosRequestConfig = {\n                method: method as Method,\n                url,\n                headers: {\n                    Authorization: `Bearer ${accessToken}`,\n                    'Content-Type': 'application/json'\n                }\n            }\n            if (method === 'post' && body) axiosConfig.data = body\n            let responseData: any[] = []\n            try {\n                const response = await axios(axiosConfig)\n                if (response?.data) {\n                    responseData.push(response?.data)\n                }\n            } catch (err) {\n                if (operation === 'addContact' && err.response.data.message.includes('Contact already exists')) {\n                    responseData.push(err.response.data)\n                    // dont throw error\n                } else throw handleErrorMessage(err)\n            }\n            return responseData\n        }\n\n        let returnData: ICommonObject[] = []\n        let url = `https://api.hubapi.com/crm/v3/objects`\n        const contactId = inputParametersData?.contactId as string\n\n        if (operation === 'listContacts') {\n            returnData = await makeApiCall('get', `${url}/contacts`, operation)\n        } else if (operation === 'getContact') {\n            returnData = await makeApiCall('get', `${url}/contacts/${contactId}`, operation)\n        } else if (operation === 'deleteContact') {\n            returnData = await makeApiCall('delete', `${url}/contacts/${contactId}`, operation)\n        } else if (operation === 'addContact') {\n            const email = inputParametersData?.email as string\n            const firstname = inputParametersData?.firstname as string\n            const lastname = inputParametersData?.lastname as string\n            const company = inputParametersData?.company as string\n\n            const body = {\n                properties: {\n                    email\n                }\n            } as any\n\n            if (firstname) body.properties.firstname = firstname\n            if (lastname) body.properties.lastname = lastname\n            if (company) body.properties.company = company\n\n            const createContactResponse = await makeApiCall('post', `${url}/contacts`, operation, body)\n            if (createContactResponse[0].message && createContactResponse[0].message.includes('Contact already exists')) {\n                const listContactsResponse = await makeApiCall('get', `${url}/contacts`, operation)\n                const contacts = listContactsResponse[0].results || []\n                const findContact = contacts.find((contact: any) => contact.properties.email === email)\n                if (findContact) return returnNodeExecutionData(findContact)\n            }\n            returnData = createContactResponse\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Hubspot }\n"
  },
  {
    "path": "packages/components/nodes/HuggingFace/HuggingFace.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport FormData from 'form-data'\n\nclass HuggingFace implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Hugging Face'\n        this.name = 'huggingFace'\n        this.icon = 'huggingface.png'\n        this.type = 'action'\n        this.category = 'Artificial Intelligence'\n        this.version = 1.0\n        this.description = 'Execute HuggingFace Inference API'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Category',\n                name: 'category',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Image Classification',\n                        name: 'image-classification'\n                    },\n                    {\n                        label: 'Feature Extraction',\n                        name: 'feature-extraction'\n                    },\n                    {\n                        label: 'Object Detection',\n                        name: 'object-detection'\n                    },\n                    {\n                        label: 'Text Classification',\n                        name: 'text-classification'\n                    }\n                ]\n            },\n            {\n                label: 'Model',\n                name: 'model',\n                type: 'asyncOptions',\n                loadMethod: 'listModels'\n            },\n            {\n                label: 'Image File',\n                name: 'imageFile',\n                type: 'file',\n                show: {\n                    'actions.category': ['image-classification', 'object-detection']\n                }\n            },\n            {\n                label: 'Input Text',\n                name: 'inputText',\n                type: 'string',\n                rows: 5,\n                show: {\n                    'actions.category': ['feature-extraction', 'text-classification']\n                }\n            },\n            {\n                label: 'Inference Endpoint',\n                name: 'inferenceURL',\n                type: 'string',\n                optional: true,\n                description: 'If this is not specify, the default free URL with limited usage will be used'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'HuggingFace Access Token',\n                        name: 'huggingFaceAccessToken'\n                    }\n                ],\n                default: 'huggingFaceAccessToken'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async listModels(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const actionsData = nodeData.actions\n            if (actionsData === undefined) return returnData\n            const category = actionsData.category as string\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://huggingface.co/api/models?filter=${category}&sort=downloads&direction=-1&limit=30`,\n                    headers: {\n                        'Content-Type': 'application/json; charset=utf-8'\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const models = response.data\n\n                for (let i = 0; i < models.length; i += 1) {\n                    const splitedModel = models[i].id.split('/')\n                    const modelName = splitedModel.length > 1 ? splitedModel[1] : splitedModel[0]\n                    const modelAuthor = splitedModel.length > 1 ? splitedModel[0] : 'HuggingFace'\n\n                    const data = {\n                        label: modelName,\n                        name: models[i].id,\n                        description: `${modelAuthor} (${models[i].downloads} downloads)`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const credentials = nodeData.credentials\n        const actionsData = nodeData.actions\n\n        if (actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credential')\n        }\n\n        let responseData: any\n\n        const returnData: ICommonObject[] = []\n        const model = actionsData.model as string\n        const imageFile = actionsData.imageFile as string\n        const inputText = actionsData.inputText as string\n        const inferenceURL = actionsData.inferenceURL as string\n\n        const url = inferenceURL || `https://api-inference.huggingface.co/models/${model}`\n\n        if (inputText) {\n            const data = { inputs: inputText } as any\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url,\n                    data,\n                    headers: {\n                        'Content-Type': 'application/json; charset=utf-8',\n                        Authorization: `Bearer ${credentials!.accessToken}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        } else if (imageFile) {\n            const splitDataURI = imageFile.split(',')\n\n            const filename = (splitDataURI.pop() || 'filename:').split(':')[1]\n            const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n\n            const formData = new FormData()\n            formData.append('file', bf, filename)\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url,\n                    data: bf,\n                    headers: {\n                        Authorization: `Bearer ${credentials!.accessToken}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: HuggingFace }\n"
  },
  {
    "path": "packages/components/nodes/IfElse/IfElse.ts",
    "content": "import { CommonType, ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\n\ninterface ICondition {\n    type: string\n    value1: CommonType\n    operation: string\n    value2: CommonType\n}\n\nclass IfElse implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'If Else'\n        this.name = 'ifElse'\n        this.icon = 'ifelse.svg'\n        this.version = 1.0\n        this.type = 'action'\n        this.category = 'Utilities'\n        this.description = 'Split flows according to conditions set'\n        this.incoming = 1\n        this.outgoing = 2\n        this.inputParameters = [\n            {\n                label: 'Mode',\n                name: 'mode',\n                type: 'options',\n                options: [\n                    {\n                        label: 'AND',\n                        name: 'and',\n                        description: 'When all conditions are met'\n                    },\n                    {\n                        label: 'OR',\n                        name: 'or',\n                        description: 'When any of the conditions is met'\n                    }\n                ],\n                default: 'or',\n                description: 'Type of conditions'\n            },\n            {\n                label: 'Conditions',\n                name: 'conditions',\n                type: 'array',\n                description: 'Values to compare',\n                array: [\n                    {\n                        label: 'Type',\n                        name: 'type',\n                        type: 'options',\n                        options: [\n                            {\n                                label: 'String',\n                                name: 'string'\n                            },\n                            {\n                                label: 'Number',\n                                name: 'number'\n                            },\n                            {\n                                label: 'Boolean',\n                                name: 'boolean'\n                            }\n                        ],\n                        default: 'string'\n                    },\n                    /////////////////////////////////////// STRING ////////////////////////////////////////\n                    {\n                        label: 'Value 1',\n                        name: 'value1',\n                        type: 'string',\n                        default: '',\n                        description: 'First value to be compared with',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['string']\n                        }\n                    },\n                    {\n                        label: 'Operation',\n                        name: 'operation',\n                        type: 'options',\n                        options: [\n                            {\n                                label: 'Contains',\n                                name: 'contains'\n                            },\n                            {\n                                label: 'Ends With',\n                                name: 'endsWith'\n                            },\n                            {\n                                label: 'Equal',\n                                name: 'equal'\n                            },\n                            {\n                                label: 'Not Contains',\n                                name: 'notContains'\n                            },\n                            {\n                                label: 'Not Equal',\n                                name: 'notEqual'\n                            },\n                            {\n                                label: 'Regex',\n                                name: 'regex'\n                            },\n                            {\n                                label: 'Starts With',\n                                name: 'startsWith'\n                            },\n                            {\n                                label: 'Is Empty',\n                                name: 'isEmpty'\n                            },\n                            {\n                                label: 'Not Empty',\n                                name: 'notEmpty'\n                            }\n                        ],\n                        default: 'equal',\n                        description: 'Type of operation',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['string']\n                        }\n                    },\n                    {\n                        label: 'Value 2',\n                        name: 'value2',\n                        type: 'string',\n                        default: '',\n                        description: 'Second value to be compared with',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['string']\n                        },\n                        hide: {\n                            'inputParameters.conditions[$index].operation': ['isEmpty', 'notEmpty']\n                        }\n                    },\n                    /////////////////////////////////////// NUMBER ////////////////////////////////////////\n                    {\n                        label: 'Value 1',\n                        name: 'value1',\n                        type: 'number',\n                        default: '',\n                        description: 'First value to be compared with',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['number']\n                        }\n                    },\n                    {\n                        label: 'Operation',\n                        name: 'operation',\n                        type: 'options',\n                        options: [\n                            {\n                                label: 'Smaller',\n                                name: 'smaller'\n                            },\n                            {\n                                label: 'Smaller Equal',\n                                name: 'smallerEqual'\n                            },\n                            {\n                                label: 'Equal',\n                                name: 'equal'\n                            },\n                            {\n                                label: 'Not Equal',\n                                name: 'notEqual'\n                            },\n                            {\n                                label: 'Larger',\n                                name: 'larger'\n                            },\n                            {\n                                label: 'Larger Equal',\n                                name: 'largerEqual'\n                            },\n                            {\n                                label: 'Is Empty',\n                                name: 'isEmpty'\n                            },\n                            {\n                                label: 'Not Empty',\n                                name: 'notEmpty'\n                            }\n                        ],\n                        default: 'equal',\n                        description: 'Type of operation',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['number']\n                        }\n                    },\n                    {\n                        label: 'Value 2',\n                        name: 'value2',\n                        type: 'number',\n                        default: 0,\n                        description: 'Second value to be compared with',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['number']\n                        },\n                        hide: {\n                            'inputParameters.conditions[$index].operation': ['isEmpty', 'notEmpty']\n                        }\n                    },\n                    /////////////////////////////////////// BOOLEAN ////////////////////////////////////////\n                    {\n                        label: 'Value 1',\n                        name: 'value1',\n                        type: 'boolean',\n                        default: false,\n                        description: 'First value to be compared with',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['boolean']\n                        }\n                    },\n                    {\n                        label: 'Operation',\n                        name: 'operation',\n                        type: 'options',\n                        options: [\n                            {\n                                label: 'Equal',\n                                name: 'equal'\n                            },\n                            {\n                                label: 'Not Equal',\n                                name: 'notEqual'\n                            }\n                        ],\n                        default: 'equal',\n                        description: 'Type of operation',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['boolean']\n                        }\n                    },\n                    {\n                        label: 'Value 2',\n                        name: 'value2',\n                        type: 'boolean',\n                        default: false,\n                        description: 'Second value to be compared with',\n                        show: {\n                            'inputParameters.conditions[$index].type': ['boolean']\n                        }\n                    }\n                ]\n            }\n        ]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        let returnDataTrue: ICommonObject = {}\n        let returnDataFalse: ICommonObject = {}\n\n        const mode = inputParametersData.mode as string\n\n        const compareOperationFunctions: {\n            [key: string]: (value1: CommonType, value2: CommonType) => boolean\n        } = {\n            contains: (value1: CommonType, value2: CommonType) => (value1 || '').toString().includes((value2 || '').toString()),\n            notContains: (value1: CommonType, value2: CommonType) => !(value1 || '').toString().includes((value2 || '').toString()),\n            endsWith: (value1: CommonType, value2: CommonType) => (value1 as string).endsWith(value2 as string),\n            equal: (value1: CommonType, value2: CommonType) => value1 === value2,\n            notEqual: (value1: CommonType, value2: CommonType) => value1 !== value2,\n            larger: (value1: CommonType, value2: CommonType) => (Number(value1) || 0) > (Number(value2) || 0),\n            largerEqual: (value1: CommonType, value2: CommonType) => (Number(value1) || 0) >= (Number(value2) || 0),\n            smaller: (value1: CommonType, value2: CommonType) => (Number(value1) || 0) < (Number(value2) || 0),\n            smallerEqual: (value1: CommonType, value2: CommonType) => (Number(value1) || 0) <= (Number(value2) || 0),\n            startsWith: (value1: CommonType, value2: CommonType) => (value1 as string).startsWith(value2 as string),\n            isEmpty: (value1: CommonType) => [undefined, null, ''].includes(value1 as string),\n            notEmpty: (value1: CommonType) => ![undefined, null, ''].includes(value1 as string)\n        }\n\n        const conditions = inputParametersData.conditions as unknown as ICondition[]\n        let score = 0\n        const metConditions = []\n        const unmetConditions = []\n\n        for (const condition of conditions) {\n            const value1 = condition.value1\n            const value2 = condition.value2\n            const operation = condition.operation\n\n            const compareOperationResult = compareOperationFunctions[operation](value1, value2)\n            if (compareOperationResult) {\n                score += 1\n                metConditions.push({ value1, operation, value2 })\n            } else {\n                unmetConditions.push({ value1, operation, value2 })\n            }\n        }\n\n        const data = {\n            mode,\n            metConditions,\n            unmetConditions\n        }\n\n        if (mode === 'or') {\n            if (score > 0) returnDataTrue = data\n            else returnDataFalse = data\n        } else if (mode === 'and') {\n            if (score === conditions.length) returnDataTrue = data\n            else returnDataFalse = data\n        }\n\n        const returnData = [returnDataTrue, returnDataFalse]\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: IfElse }\n"
  },
  {
    "path": "packages/components/nodes/ImageEditor/ImageEditor.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport Jimp from 'jimp'\nimport { promisify } from 'util'\n\nclass ImageEditor implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'ImageEditor'\n        this.name = 'imageEditor'\n        this.icon = 'image-editor.svg'\n        this.type = 'action'\n        this.category = 'Utilities'\n        this.version = 1.0\n        this.description = 'Edit image with different manipulation methods'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Selection Method',\n                name: 'method',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Crop',\n                        name: 'crop',\n                        description: 'Crop image'\n                    },\n                    {\n                        label: 'Blur',\n                        name: 'blur',\n                        description: 'Quickly blur an image'\n                    },\n                    {\n                        label: 'Gaussian',\n                        name: 'gaussian',\n                        description: 'Hardcore blur'\n                    },\n                    {\n                        label: 'Invert',\n                        name: 'invert',\n                        description: 'Invert an images colors'\n                    },\n                    {\n                        label: 'Resize',\n                        name: 'resize',\n                        description: 'Resize an image'\n                    },\n                    {\n                        label: 'Cover',\n                        name: 'cover',\n                        description: 'Scale the image so the given width and height keeping the aspect ratio'\n                    },\n                    {\n                        label: 'Rotate',\n                        name: 'rotate',\n                        description: 'Rotate an image'\n                    },\n                    {\n                        label: 'Normalize',\n                        name: 'normalize',\n                        description: 'Normalize the colors in an image'\n                    },\n                    {\n                        label: 'Dither',\n                        name: 'dither',\n                        description: 'Apply a dither effect to an image'\n                    },\n                    {\n                        label: 'Scale',\n                        name: 'scale',\n                        description: 'Uniformly scales the image by a factor'\n                    }\n                ]\n            },\n            {\n                label: 'Image Raw Data (Base64)',\n                name: 'rawData',\n                type: 'string',\n                placeholder: 'data:image/png;base64,<base64_string>'\n            }\n        ]\n        this.inputParameters = [\n            /**\n             * Blur or Gaussian\n             */\n            {\n                label: 'Blur Pixel Radius',\n                name: 'blurPixel',\n                type: 'number',\n                default: 5,\n                show: {\n                    'actions.method': ['blur', 'gaussian']\n                },\n                description: 'The pixel radius of the blur'\n            },\n            /**\n             * Crop\n             */\n            {\n                label: 'Width',\n                name: 'width',\n                type: 'number',\n                default: 500,\n                show: {\n                    'actions.method': ['crop']\n                },\n                description: 'Crop width'\n            },\n            {\n                label: 'Height',\n                name: 'height',\n                type: 'number',\n                default: 500,\n                show: {\n                    'actions.method': ['crop']\n                },\n                description: 'Crop height'\n            },\n            {\n                label: 'Position X',\n                name: 'positionX',\n                type: 'number',\n                default: 10,\n                show: {\n                    'actions.method': ['crop']\n                },\n                description: 'X (horizontal) position to crop from'\n            },\n            {\n                label: 'Position Y',\n                name: 'positionY',\n                type: 'number',\n                default: 10,\n                show: {\n                    'actions.method': ['crop']\n                },\n                description: 'Y (vertical) position to crop from'\n            },\n            /**\n             * Resize or Cover\n             */\n            {\n                label: 'Width',\n                name: 'width',\n                type: 'number',\n                default: 500,\n                show: {\n                    'actions.method': ['resize', 'cover']\n                },\n                description: 'Resize width'\n            },\n            {\n                label: 'Height',\n                name: 'height',\n                type: 'number',\n                default: 500,\n                show: {\n                    'actions.method': ['resize', 'cover']\n                },\n                description: 'Resize height'\n            },\n            /**\n             * Rotate\n             */\n            {\n                label: 'Rotation Degree',\n                name: 'degree',\n                type: 'number',\n                default: 90,\n                show: {\n                    'actions.method': ['rotate']\n                }\n            },\n            /**\n             * Scale\n             */\n            {\n                label: 'Scale Factor',\n                name: 'factor',\n                type: 'number',\n                default: 2,\n                show: {\n                    'actions.method': ['scale']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const actionsData = nodeData.actions\n\n        if (inputParametersData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const returnData: ICommonObject = {}\n\n        const rawData = actionsData.rawData as string\n        const method = actionsData.method as string\n\n        try {\n            const imageData = rawData.split(',').pop() || ''\n            const image = await Jimp.read(Buffer.from(imageData, 'base64'))\n\n            if (method === 'crop') {\n                const positionX = parseInt(inputParametersData.positionX as string, 10)\n                const positionY = parseInt(inputParametersData.positionY as string, 10)\n                const height = parseInt(inputParametersData.height as string, 10)\n                const width = parseInt(inputParametersData.width as string, 10)\n\n                image.crop(positionX, positionY, width, height, (err) => {\n                    if (err) throw handleErrorMessage(err)\n                })\n            } else if (method === 'blur' || method === 'gaussian') {\n                const blurPixel = parseInt(inputParametersData.blurPixel as string, 10)\n\n                if (method === 'blur') {\n                    image.blur(blurPixel, (err) => {\n                        if (err) throw handleErrorMessage(err)\n                    })\n                } else if (method === 'gaussian') {\n                    image.gaussian(blurPixel, (err) => {\n                        if (err) throw handleErrorMessage(err)\n                    })\n                }\n            } else if (method === 'invert') {\n                image.invert((err) => {\n                    if (err) throw handleErrorMessage(err)\n                })\n            } else if (method === 'resize' || method === 'cover') {\n                const height = parseInt(inputParametersData.height as string, 10)\n                const width = parseInt(inputParametersData.width as string, 10)\n\n                if (method === 'resize') {\n                    image.resize(width, height, (err) => {\n                        if (err) throw handleErrorMessage(err)\n                    })\n                } else if (method === 'cover') {\n                    image.cover(width, height, (err) => {\n                        if (err) throw handleErrorMessage(err)\n                    })\n                }\n            } else if (method === 'rotate') {\n                const degree = parseInt(inputParametersData.degree as string, 10)\n\n                image.rotate(degree, (err) => {\n                    if (err) throw handleErrorMessage(err)\n                })\n            } else if (method === 'normalize') {\n                image.normalize((err) => {\n                    if (err) throw handleErrorMessage(err)\n                })\n            } else if (method === 'dither') {\n                image.dither565((err) => {\n                    if (err) throw handleErrorMessage(err)\n                })\n            } else if (method === 'scale') {\n                const factor = parseInt(inputParametersData.factor as string, 10)\n\n                image.scale(factor, (err) => {\n                    if (err) throw handleErrorMessage(err)\n                })\n            }\n\n            const toBase64 = promisify(image.getBase64).bind(image)\n            const mimeType = rawData.split(',')[0].split(';')[0].split(':')[1]\n            const finalImage = await toBase64(mimeType)\n\n            const attachment = {\n                contentType: mimeType,\n                content: finalImage\n            }\n            returnData['data'] = finalImage\n            returnData.attachments = [attachment]\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: ImageEditor }\n"
  },
  {
    "path": "packages/components/nodes/Infura/Infura.ts",
    "content": "import {\n    IAttachment,\n    ICommonObject,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport {\n    infuraHTTPAPIs,\n    ETHNetworks,\n    PolygonNetworks,\n    OptimismNetworks,\n    ArbitrumNetworks,\n    NETWORK,\n    AvalancheNetworks\n} from '../../src/ChainNetwork'\nimport { ethOperations, IETHOperation, operationCategoryMapping, polygonOperations } from '../../src/ETHOperations'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport FormData from 'form-data'\nimport { IPFSOperationsOptions, argParams, fileParams, catParams, dagParams, getParams, objectParams, pinParams } from './extendedOperation'\n\nclass Infura implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Infura'\n        this.name = 'infura'\n        this.icon = 'infura.svg'\n        this.type = 'action'\n        this.category = 'Network Provider'\n        this.version = 1.1\n        this.description = 'Perform Infura onchain operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Chain API',\n                        name: 'chainAPI',\n                        description: 'API for fetching standard onchain data using Infura supported calls.'\n                    },\n                    {\n                        label: 'IPFS API',\n                        name: 'ipfsAPI',\n                        description:\n                            'API for interacting with IPFS, a distributed, peer-to-peer (p2p) storage network used for storing and accessing files, websites, applications, and data.'\n                    }\n                ],\n                default: 'chainAPI'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Infura API Key',\n                        name: 'infuraApi'\n                    }\n                ],\n                default: 'infuraApi'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks, ...AvalancheNetworks],\n                show: {\n                    'actions.api': ['chainAPI']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Chain Category',\n                name: 'chainCategory',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Retrieving Blocks',\n                        name: 'retrievingBlocks',\n                        description: 'Retrieve onchain blocks data'\n                    },\n                    {\n                        label: 'EVM/Smart Contract Execution',\n                        name: 'evmExecution',\n                        description: 'Execute or submit transaction onto blockchain'\n                    },\n                    {\n                        label: 'Reading Transactions',\n                        name: 'readingTransactions',\n                        description: 'Read onchain transactions data'\n                    },\n                    {\n                        label: 'Account Information',\n                        name: 'accountInformation',\n                        description: 'Retrieve onchain account information'\n                    },\n                    {\n                        label: 'Event Logs',\n                        name: 'eventLogs',\n                        description: 'Fetch onchain logs'\n                    },\n                    {\n                        label: 'Chain Information',\n                        name: 'chainInformation',\n                        description: 'Get general selected blockchain information'\n                    },\n                    {\n                        label: 'Retrieving Uncles',\n                        name: 'retrievingUncles',\n                        description: 'Retrieve onchain uncles blocks data'\n                    },\n                    {\n                        label: 'Filters',\n                        name: 'filters',\n                        description: 'Get block filters and logs, or create new filter'\n                    }\n                ],\n                show: {\n                    'actions.api': ['chainAPI']\n                },\n                default: 'retrievingBlocks'\n            },\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'asyncOptions',\n                loadMethod: 'getOperations'\n            },\n            ...fileParams,\n            ...argParams,\n            ...catParams,\n            ...dagParams,\n            ...getParams,\n            ...objectParams,\n            ...pinParams,\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: '[\"param1\", \"param2\"]',\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]',\n                show: {\n                    'actions.api': ['chainAPI']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const actionData = nodeData.actions\n            const networksData = nodeData.networks\n            const inputParametersData = nodeData.inputParameters\n\n            if (actionData === undefined || networksData === undefined || inputParametersData === undefined) {\n                return returnData\n            }\n\n            const api = actionData.api as string\n\n            if (api === 'chainAPI') {\n                const network = networksData.network as NETWORK\n\n                let totalOperations: IETHOperation[] = []\n                const chainCategory = inputParametersData.chainCategory as string\n\n                const filteredOperations = ethOperations.filter(\n                    (op: IETHOperation) =>\n                        Object.prototype.hasOwnProperty.call(op.providerNetworks, 'infura') &&\n                        op.providerNetworks['infura'].includes(network) &&\n                        op.parentGroup === operationCategoryMapping[chainCategory]\n                )\n\n                if (network === NETWORK.MATIC || network === NETWORK.MATIC_MUMBAI) {\n                    totalOperations = [...polygonOperations, ...filteredOperations]\n                } else {\n                    totalOperations = filteredOperations\n                }\n\n                for (const op of totalOperations) {\n                    returnData.push({\n                        label: op.name,\n                        name: op.value,\n                        parentGroup: op.parentGroup,\n                        description: op.description,\n                        inputParameters: op.inputParameters,\n                        exampleParameters: op.exampleParameters,\n                        exampleResponse: op.exampleResponse\n                    })\n                }\n                return returnData\n            } else if (api === 'ipfsAPI') {\n                return IPFSOperationsOptions\n            } else {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        // GET api\n        const api = actionData.api as string\n\n        if (api === 'chainAPI') {\n            // GET network\n            const network = networksData.network as NETWORK\n\n            // GET credentials\n            const apiKey = credentials.apiKey as string\n\n            // GET operation\n            const operation = inputParametersData.operation as string\n\n            const uri = infuraHTTPAPIs[network] + apiKey\n\n            let responseData: any // tslint:disable-line: no-any\n            let bodyParameters: any[] = [] // tslint:disable-line: no-any\n            const returnData: ICommonObject[] = []\n\n            const parameters = inputParametersData.parameters as string\n            if (parameters) {\n                try {\n                    bodyParameters = JSON.parse(parameters.replace(/\\s/g, ''))\n                } catch (error) {\n                    throw handleErrorMessage(error)\n                }\n            }\n\n            try {\n                let totalOperations: IETHOperation[] = []\n                if (api === 'chainAPI') totalOperations = [...polygonOperations, ...ethOperations]\n\n                const result = totalOperations.find((obj) => {\n                    return obj.value === operation\n                })\n\n                if (result === undefined) throw new Error('Invalid Operation')\n\n                const requestBody = JSON.parse(JSON.stringify(result.body))\n                const bodyParams = requestBody.params\n                requestBody.params = Array.isArray(bodyParameters) ? bodyParameters.concat(bodyParams) : bodyParameters\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: result.method as Method,\n                    url: uri,\n                    data: requestBody,\n                    headers: {\n                        'Content-Type': 'application/json'\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n\n            return returnNodeExecutionData(returnData)\n        } else if (api === 'ipfsAPI') {\n            // GET credentials\n            const apiKey = credentials.apiKey as string\n            const secretKey = credentials.secretKey as string\n\n            // GET operation\n            const operation = inputParametersData.operation as string\n\n            let responseData: any // tslint:disable-line: no-any\n            const returnData: ICommonObject[] = []\n            const apiUrl = 'https://ipfs.infura.io:5001/api'\n\n            let url = ''\n            const queryParameters: ICommonObject = {}\n            let queryBody: any = {}\n            let method: Method = 'POST'\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                Authorization: Buffer.from(`${apiKey}:${secretKey}`).toString('base64')\n            }\n\n            if (operation === 'block_get' || operation === 'block_stat') {\n                const arg = inputParametersData.arg as string\n                queryParameters['arg'] = arg\n                method = 'POST'\n                let endpoint = ''\n                if (operation === 'block_stat') {\n                    endpoint = 'block/stat'\n                } else if (operation === 'block_get') {\n                    endpoint = 'block/get'\n                }\n                url = `${apiUrl}/v0/${endpoint}`\n            } else if (operation === 'add' || operation === 'block_put' || operation === 'pin_add') {\n                const fileBase64 = inputParametersData.file as string\n                const splitDataURI = fileBase64.split(',')\n\n                const filename = (splitDataURI.pop() || 'filename:').split(':')[1]\n                const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n\n                const formData = new FormData()\n                formData.append('file', bf, filename)\n\n                method = 'POST'\n                let endpoint = ''\n                if (operation === 'add') {\n                    endpoint = 'add'\n                } else if (operation === 'block_put') {\n                    endpoint = 'block/put'\n                } else if (operation === 'pin_add') {\n                    endpoint = 'pin/add'\n                    const arg = inputParametersData.arg as string\n                    queryParameters['arg'] = arg\n                }\n                url = `${apiUrl}/v0/${endpoint}`\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                queryBody = formData\n            } else if (operation === 'dag_get' || operation === 'dag_resolve') {\n                const arg = inputParametersData.arg as string\n                const outputCodec = inputParametersData['output-codec'] as string\n\n                queryParameters['arg'] = arg\n                if (outputCodec) queryParameters['output-codec'] = outputCodec\n                method = 'POST'\n\n                let endpoint = ''\n                if (operation === 'dag_get') {\n                    endpoint = 'dag/get'\n                } else if (operation === 'dag_resolve') {\n                    endpoint = 'dag/resolve'\n                }\n                url = `${apiUrl}/v0/${endpoint}`\n            } else if (operation === 'dag_put') {\n                const storeCodec = inputParametersData['store-codec'] as string\n                const inputCodec = inputParametersData['input-codec'] as string\n                const pin = inputParametersData.pin as boolean\n                const hash = inputParametersData.hash as string\n\n                if (storeCodec) queryParameters['store-codec'] = storeCodec\n                if (inputCodec) queryParameters['input-codec'] = inputCodec\n                if (pin) queryParameters.pin = pin\n                if (hash) queryParameters.hash = hash\n\n                const fileBase64 = inputParametersData.file as string\n                const splitDataURI = fileBase64.split(',')\n\n                const filename = (splitDataURI.pop() || 'filename:').split(':')[1]\n                const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n\n                const formData = new FormData()\n                formData.append('file', bf, filename)\n\n                method = 'POST'\n                url = `${apiUrl}/v0/dag/put`\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                queryBody = formData\n            } else if (operation === 'object_data' || operation === 'object_stat' || operation === 'object_get') {\n                /*\n            else if (operation === 'get') {\n\n\t\t\t\tconst arg = inputParametersData.arg as string;\n                const output = inputParametersData.output as string;\n                const archive = inputParametersData.archive as boolean;\n                const compress = inputParametersData.compress as boolean;\n                const compressionLevel = inputParametersData['compression-level'] as number;\n\n\t\t\t\tqueryParameters['arg'] = arg;\n\t\t\t\tif (output) queryParameters['output'] = output;\n                if (archive) queryParameters['archive'] = archive;\n                if (compress) queryParameters['compress'] = compress;\n                if (compressionLevel) queryParameters['compressionLevel'] = compressionLevel;\n\n                method = 'POST';\n\t\t\t\turl = `${apiUrl}/v0/get`;\n            }\n            */\n                const arg = inputParametersData.arg as string\n\n                queryParameters['arg'] = arg\n\n                method = 'POST'\n                let endpoint = ''\n                if (operation === 'object_data') {\n                    endpoint = 'object/data'\n                } else if (operation === 'object_get') {\n                    endpoint = 'object/get'\n                } else if (operation === 'object_stat') {\n                    endpoint = 'object/stat'\n                }\n                url = `${apiUrl}/v0/${endpoint}`\n            } else if (operation === 'object_put') {\n                const inputenc = inputParametersData.inputenc as string\n                const datafieldenc = inputParametersData.datafieldenc as string\n                const pin = inputParametersData.pin as boolean\n\n                if (inputenc) queryParameters.inputenc = inputenc\n                if (datafieldenc) queryParameters.datafieldenc = datafieldenc\n                if (pin) queryParameters.pin = pin\n\n                const fileBase64 = inputParametersData.file as string\n                const splitDataURI = fileBase64.split(',')\n\n                const filename = (splitDataURI.pop() || 'filename:').split(':')[1]\n                const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n\n                const formData = new FormData()\n                formData.append('file', bf, filename)\n\n                method = 'POST'\n                url = `${apiUrl}/v0/object/put`\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                queryBody = formData\n            } else if (operation === 'pin_ls' || operation === 'pin_rm') {\n                const arg = inputParametersData.arg as string\n                const type = inputParametersData.type as string\n\n                queryParameters['arg'] = arg\n                if (type) queryParameters['type'] = type\n\n                method = 'POST'\n                let endpoint = ''\n                if (operation === 'pin_ls') {\n                    endpoint = 'pin/ls'\n                } else if (operation === 'pin_rm') {\n                    endpoint = 'pin/rm'\n                }\n                url = `${apiUrl}/v0/${endpoint}`\n            }\n\n            try {\n                const result = IPFSOperationsOptions.find((obj) => {\n                    return obj.name === operation\n                })\n\n                if (result === undefined) throw new Error('Invalid Operation')\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method,\n                    url,\n                    params: queryParameters,\n                    paramsSerializer: (params) => serializeQueryParams(params),\n                    headers,\n                    data: queryBody\n                }\n\n                if (operation === 'cat' || operation === 'get') {\n                    const arg = inputParametersData.arg as string\n                    const ipfsURL = `https://ipfs.infura.io/ipfs/${arg}`\n\n                    const axiosConfig: AxiosRequestConfig = {\n                        method: 'HEAD',\n                        url: ipfsURL\n                    }\n                    const ipfsResponse = await axios(axiosConfig)\n                    const mimeType = ipfsResponse.headers['content-type']\n\n                    const attachment = {\n                        content: ipfsURL,\n                        contentType: mimeType\n                    } as IAttachment\n\n                    const returnData: any = {}\n                    returnData.ipfsURL = ipfsURL\n                    returnData.attachments = [attachment]\n\n                    return returnNodeExecutionData(returnData)\n                } else {\n                    const response = await axios(axiosConfig)\n                    responseData = response.data\n                }\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n\n            return returnNodeExecutionData(returnData)\n        }\n\n        /*\n        else if (api === 'filecoinAPI') {\n         \n            // GET credentials\n            const apiKey = credentials.apiKey as string;\n            const secretKey = credentials.secretKey as string;\n\n            // GET operation\n            const operation = inputParametersData.operation as string;\n\n            let responseData: any; // tslint:disable-line: no-any\n            const returnData: ICommonObject[] = [];\n            const url = `https://${apiKey}:${secretKey}@filecoin.infura.io`;\n            const queryParameters: ICommonObject = {};\n            const queryBody: any = { \n                \"id\": 0,\n                \"jsonrpc\": \"2.0\",\n                \"method\": \"\",\n                \"params\": [] \n            };\n            const method: Method = 'POST';\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                'Authorization': Buffer.from(`${apiKey}:${secretKey}`).toString('base64')\n            };\n\n            if (operation === 'ChainHead') {\n\t\t\t\tqueryBody[\"method\"] = `Filecoin.${operation}`;\n            }\n\n            try {\n                const result = FilecoinOperationsOptions.find(obj => {\n                    return obj.name === operation\n                });\n\n                if (result === undefined) throw new Error('Invalid Operation');\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method,\n                    url,\n                    params: queryParameters,\n                    paramsSerializer: params => serializeQueryParams(params),\n                    headers,\n                    data: queryBody\n                }\n\n                const response = await axios(axiosConfig);\n                responseData = response.data;\n            }\n            catch (error) {\n                throw handleErrorMessage(error);\n            }\n\n            if (Array.isArray(responseData)) returnData.push(...responseData);\n            else returnData.push(responseData);\n\n            return returnNodeExecutionData(returnData);\n        }\n        */\n\n        return returnNodeExecutionData([])\n    }\n}\n\nmodule.exports = { nodeClass: Infura }\n"
  },
  {
    "path": "packages/components/nodes/Infura/InfuraTrigger.ts",
    "content": "import { INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    ArbitrumNetworks,\n    AvalancheNetworks,\n    ETHNetworks,\n    infuraWSSAPIs,\n    NETWORK,\n    OptimismNetworks,\n    PolygonNetworks\n} from '../../src/ChainNetwork'\nimport { subscribeOperations, unsubscribeOperations } from './subscribeOperation'\nimport { IETHOperation } from '../../src/ETHOperations'\nimport WebSocket from 'ws'\n\nclass InfuraTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'Infura Trigger'\n        this.name = 'infuraTrigger'\n        this.icon = 'infura.svg'\n        this.type = 'trigger'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Start workflow whenever subscribed event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks, ...AvalancheNetworks]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Infura API Key',\n                        name: 'infuraApi'\n                    }\n                ],\n                default: 'infuraApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Subscribe Operation',\n                name: 'subscribeOperation',\n                type: 'asyncOptions',\n                loadMethod: 'getSubscribeOperations'\n            },\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: `[\n  \"param1\",\n  \"param2\"\n]`,\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]'\n            },\n            {\n                label: 'Unsubscribe Operation',\n                name: 'unsubscribeOperation',\n                type: 'asyncOptions',\n                loadMethod: 'getUnsubscribeOperations'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getSubscribeOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const networksData = nodeData.networks\n\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            const network = networksData.network as NETWORK\n\n            const totalOperations = subscribeOperations\n            const filteredOperations = totalOperations.filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'infura') && op.providerNetworks['infura'].includes(network)\n            )\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        },\n\n        async getUnsubscribeOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const networksData = nodeData.networks\n\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            const network = networksData.network as NETWORK\n\n            const totalOperations = unsubscribeOperations\n            const filteredOperations = totalOperations.filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'infura') && op.providerNetworks['infura'].includes(network)\n            )\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        // GET network\n        const network = networksData.network as NETWORK\n\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n        const wssProvider = `${infuraWSSAPIs[network]}${apiKey}`\n\n        // GET subscribeOperation\n        const subscribeOperation = inputParametersData.subscribeOperation as string\n\n        // GET parameters\n        let bodyParameters: any\n        const parameters = inputParametersData.parameters as string\n        if (parameters) {\n            try {\n                bodyParameters = JSON.parse(parameters.replace(/\\s/g, ''))\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        const emitEventKey = nodeData.emitEventKey as string\n\n        const result = subscribeOperations.find((obj) => {\n            return obj.value === subscribeOperation\n        })\n\n        if (result === undefined) throw new Error('Invalid Operation')\n\n        const requestBody = result.body\n        requestBody.params = bodyParameters\n\n        const ws = new WebSocket(wssProvider)\n\n        ws.on('open', function open() {\n            ws.send(JSON.stringify(requestBody))\n        })\n\n        let subscriptionID = ''\n        ws.on('message', (data) => {\n            const messageData = JSON.parse(data as any)\n\n            if (messageData.method) {\n                this.emit(emitEventKey, returnNodeExecutionData(messageData))\n            } else {\n                subscriptionID = messageData.result\n                this.providers[emitEventKey] = { provider: ws, filter: subscriptionID }\n            }\n        })\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider: WebSocket = this.providers[emitEventKey].provider\n            const subscriptionID = this.providers[emitEventKey].filter\n\n            const result = unsubscribeOperations.find((obj) => {\n                return obj.value === (inputParametersData.unsubscribeOperation as string)\n            })\n\n            if (result === undefined) throw new Error('Invalid Operation')\n\n            const requestBody = result.body\n            requestBody.params = [subscriptionID]\n\n            provider.send(JSON.stringify(requestBody))\n            provider.close(1000)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: InfuraTrigger }\n"
  },
  {
    "path": "packages/components/nodes/Infura/extendedOperation.ts",
    "content": "import { INodeOptionsValue, INodeParams } from '../../src'\n\nexport const IPFSOperationsOptions = [\n    {\n        label: 'add',\n        name: 'add',\n        description: 'Add a file or directory to IPFS.'\n    },\n    {\n        label: 'get',\n        name: 'get',\n        description: 'Get IPFS objects.'\n    },\n    {\n        label: 'block_get',\n        name: 'block_get',\n        description: 'Get a raw IPFS block.'\n    },\n    {\n        label: 'block_put',\n        name: 'block_put',\n        description: 'Store input as an IPFS block.'\n    },\n    {\n        label: 'block_stat',\n        name: 'block_stat',\n        description: 'Print information of a raw IPFS block.'\n    },\n    {\n        label: 'cat',\n        name: 'cat',\n        description: 'Show IPFS object data. Same as get.'\n    },\n    {\n        label: 'dag_get',\n        name: 'dag_get',\n        description: 'Get a dag node from IPFS.'\n    },\n    {\n        label: 'dag_put',\n        name: 'dag_put',\n        description: 'Add a dag node to IPFS.'\n    },\n    {\n        label: 'dag_resolve',\n        name: 'dag_resolve',\n        description: 'Resolve IPLD block.'\n    },\n    {\n        label: 'object_data',\n        name: 'object_data',\n        description: 'Output the raw bytes of an IPFS object.'\n    },\n    {\n        label: 'object_get',\n        name: 'object_get',\n        description: 'Get and serialize the DAG node named by key.'\n    },\n    {\n        label: 'object_put',\n        name: 'object_put',\n        description: 'Store input as a DAG object, print its key.'\n    },\n    {\n        label: 'object_stat',\n        name: 'object_stat',\n        description: 'Get stats for the DAG node named by key.'\n    },\n    {\n        label: 'pin_add',\n        name: 'pin_add',\n        description: 'Pin objects to local storage.'\n    },\n    {\n        label: 'pin_ls',\n        name: 'pin_ls',\n        description: 'Lists objects pinned to local storage.'\n    },\n    {\n        label: 'pin_rm',\n        name: 'pin_rm',\n        description: 'Remove pinned objects from local storage.'\n    }\n] as INodeOptionsValue[]\n\nexport const fileParams = [\n    {\n        label: 'File',\n        name: 'file',\n        type: 'file',\n        description: 'The path to a file to be added to IPFS.',\n        show: {\n            'inputParameters.operation': ['add', 'block_put', 'dag_put', 'pin_add']\n        }\n    }\n] as INodeParams[]\n\nexport const argParams = [\n    {\n        label: 'Block Hash',\n        name: 'arg',\n        type: 'string',\n        description: 'The base58 multihash of an existing block to get.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['block_get', 'block_stat']\n        }\n    }\n] as INodeParams[]\n\nexport const catParams = [\n    {\n        label: 'IPFS Object Hash',\n        name: 'arg',\n        type: 'string',\n        description: 'The IPFS object hash.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['cat']\n        }\n    }\n] as INodeParams[]\n\nexport const dagParams = [\n    {\n        label: 'Dag Object',\n        name: 'arg',\n        type: 'string',\n        description: 'The DAG object to get/resolve.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['dag_get', 'dag_resolve']\n        }\n    },\n    {\n        label: 'Output Codec',\n        name: 'output-codec',\n        type: 'string',\n        description: 'Format the object will be decoded in. Default: dag-json.',\n        optional: true,\n        default: '',\n        show: {\n            'inputParameters.operation': ['dag_get']\n        }\n    },\n    {\n        label: 'Store Codec',\n        name: 'store-codec',\n        type: 'string',\n        description: 'Codec that the stored object will be encoded with. Default: dag-cbor.',\n        optional: true,\n        default: '',\n        show: {\n            'inputParameters.operation': ['dag_put']\n        }\n    },\n    {\n        label: 'Input Codec',\n        name: 'input-codec',\n        type: 'string',\n        description: 'Codec that the input object is encoded in. Default: dag-json.',\n        optional: true,\n        default: '',\n        show: {\n            'inputParameters.operation': ['dag_put']\n        }\n    },\n    {\n        label: 'Pin',\n        name: 'pin',\n        type: 'boolean',\n        description: 'Pin this object when adding.',\n        optional: true,\n        default: true,\n        show: {\n            'inputParameters.operation': ['dag_put', 'object_put']\n        }\n    },\n    {\n        label: 'Hash',\n        name: 'hash',\n        type: 'string',\n        description: 'Hash function to use. Default: sha2-256.',\n        optional: true,\n        default: '',\n        show: {\n            'inputParameters.operation': ['dag_put']\n        }\n    }\n] as INodeParams[]\n\nexport const getParams = [\n    {\n        label: 'IPFS Object Hash',\n        name: 'arg',\n        type: 'string',\n        description: 'The IPFS object hash.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['get']\n        }\n    }\n    /*\n    {\n        label: 'Output',\n        name: 'output',\n        type: 'string',\n        description: 'The path where the output should be stored.',\n        default: '',\n        optional: true,\n        show: {\n            'inputParameters.operation': [\n                'get',\n            ]\n        }\n    },\n    {\n        label: 'Archive',\n        name: 'archive',\n        type: 'boolean',\n        description: 'Output a TAR archive.',\n        optional: true,\n        default: false,\n        show: {\n            'inputParameters.operation': [\n                'get',\n            ]\n        }\n    },\n    {\n        label: 'Compress',\n        name: 'compress',\n        type: 'boolean',\n        description: 'Compress the output with GZIP compression.',\n        optional: true,\n        default: false,\n        show: {\n            'inputParameters.operation': [\n                'get',\n            ]\n        }\n    },\n    {\n        label: 'Compress Level',\n        name: 'compression-level',\n        type: 'number',\n        description: 'The level of compression (1-9). Default: “-1”',\n        optional: true,\n        default: '',\n        show: {\n            'inputParameters.operation': [\n                'get',\n            ]\n        }\n    }\n    */\n] as INodeParams[]\n\nexport const objectParams = [\n    {\n        label: 'Object Key',\n        name: 'arg',\n        type: 'string',\n        description: 'Key of the object to retrieve, in base58-encoded multihash format.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['object_data', 'object_get', 'object_stat']\n        }\n    },\n    {\n        label: 'File',\n        name: 'file',\n        type: 'file',\n        description: 'The file to be stored as a DAG object.',\n        show: {\n            'inputParameters.operation': ['object_put']\n        }\n    },\n    {\n        label: 'Input Encoding',\n        name: 'inputenc',\n        type: 'options',\n        description: 'Encoding type of input data.',\n        options: [\n            {\n                label: 'Protobuf',\n                name: 'protobuf'\n            },\n            {\n                label: 'Json',\n                name: 'json'\n            }\n        ],\n        default: 'json',\n        show: {\n            'inputParameters.operation': ['object_put']\n        }\n    },\n    {\n        label: 'Data Field Encoding',\n        name: 'datafieldenc',\n        type: 'options',\n        description: 'Encoding type of data field.',\n        options: [\n            {\n                label: 'Text',\n                name: 'text'\n            },\n            {\n                label: 'Base64',\n                name: 'base64'\n            }\n        ],\n        default: 'text',\n        show: {\n            'inputParameters.operation': ['object_put']\n        }\n    }\n] as INodeParams[]\n\nexport const pinParams = [\n    {\n        label: 'Object(s) Path',\n        name: 'arg',\n        type: 'string',\n        description: 'Path to object(s) to be listed.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['pin_ls']\n        }\n    },\n    {\n        label: 'Object(s) Path',\n        name: 'arg',\n        type: 'string',\n        description: 'Path to object(s) to be unpinned.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['pin_rm']\n        }\n    },\n    {\n        label: 'Object(s) Path',\n        name: 'arg',\n        type: 'string',\n        description: 'Path to object(s) to be pinned.',\n        default: '',\n        show: {\n            'inputParameters.operation': ['pin_add']\n        }\n    },\n    {\n        label: 'Type',\n        name: 'inputenc',\n        type: 'options',\n        description: 'The type of pinned keys to list.',\n        options: [\n            {\n                label: 'Direct',\n                name: 'direct'\n            },\n            {\n                label: 'Indirect',\n                name: 'indirect'\n            },\n            {\n                label: 'Recursive',\n                name: 'recursive'\n            },\n            {\n                label: 'All',\n                name: 'all'\n            }\n        ],\n        default: 'all',\n        show: {\n            'inputParameters.operation': ['pin_ls']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/Infura/subscribeOperation.ts",
    "content": "import { NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport { IETHOperation, infuraSupportedNetworks } from '../../src/ETHOperations'\n\nexport const subsOperationsNetworks = [...infuraSupportedNetworks]\n\nexport const subscribeOperations = [\n    {\n        name: 'Eth Subscribe (eth_subscribe)',\n        value: 'eth_subscribe',\n        parentGroup: 'Subscribe',\n        description: 'Starts a subscription to a specific event.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.INFURA]: subsOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'eth_subscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subscription name</code>: <code class=\"inline\">string</code> - The type of event you want to subscribe to (i.e., newHeads, logs, pendingTransactions, newPendingTransactions). This method supports the following subscription types:</li>\n            <ul>\n\t\t\t    <li><code class=\"inline\">syncing</code> - Indicates when the node starts or stops synchronizing. The result can either be a boolean indicating that the synchronization has started (true), finished (false) or an object with various progress indicators.</li>\n\t\t\t    <li><code class=\"inline\">newPendingTransactions</code> - Returns the hash for all transactions that are added to the pending state and are signed with a key that is available in the node. When a transaction that was previously part of the canonical chain isn't part of the new canonical chain after a reogranization its again emitted.</li>\n\t\t\t    <li><code class=\"inline\">newHeads</code> - Subscribing to this, fires a notification each time a new header is appended to the chain, including chain reorganizations. In case of a chain reorganization the subscription will emit all new headers for the new chain. Therefore the subscription can emit multiple headers on the same height.</li>\n\t\t\t    <li><code class=\"inline\">logs</code> - Returns logs that are included in new imported blocks and match the given filter criteria. In case of a chain reorganization previous sent logs that are on the old chain will be resend with the removed property set to true. Logs from transactions that ended up in the new chain are emitted. Therefore a subscription can emit logs for the same transaction multiple times.</li>\n                <ul>\n                    <li><code class=\"inline\">address</code> - (optional) - either an address or an array of addresses. Only logs that are created from these addresses are returned</li>\n                    <li><code class=\"inline\">topics</code> - (optional) - only logs which match the specified topics</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">data</code>: <code class=\"inline\">object</code> - (Optional) - Arguments such as an address, multiple addresses, and topics. Note, only logs that are created from these addresses or match the specified topics will return logs.</li>\n        </ul>`,\n        exampleParameters: `[\"newHeads\"]`\n    }\n] as IETHOperation[]\n\nexport const unsubscribeOperations = [\n    {\n        name: 'Eth Unsubscribe (eth_unsubscribe)',\n        value: 'eth_unsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: 'Cancels an existing subscription so that no further events are sent.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.INFURA]: subsOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'eth_unsubscribe',\n            params: []\n        }\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/MATICBalanceTrigger/MATICBalanceTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { BigNumber, utils } from 'ethers'\nimport { ICronJobs, INode, INodeData, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    PolygonNetworks,\n    networkExplorers,\n    polygonNetworkProviders,\n    NETWORK,\n    NETWORK_PROVIDER,\n    getNetworkProvider,\n    networkProviderCredentials\n} from '../../src/ChainNetwork'\n\nclass MATICBalanceTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'MATIC Balance Trigger'\n        this.name = 'MATICBalanceTrigger'\n        this.icon = 'polygon.svg'\n        this.type = 'trigger'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Start workflow whenever MATIC balance in wallet changes'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...PolygonNetworks],\n                default: 'homestead'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'options',\n                options: [...polygonNetworkProviders],\n                default: 'polygon'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet Address',\n                name: 'address',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'Trigger Condition',\n                name: 'triggerCondition',\n                type: 'options',\n                options: [\n                    {\n                        label: 'When balance increased',\n                        name: 'increase'\n                    },\n                    {\n                        label: 'When balance decreased',\n                        name: 'decrease'\n                    }\n                ],\n                default: 'increase'\n            },\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const address = (inputParametersData.address as string) || ''\n        const pollTime = (inputParametersData.pollTime as string) || '30s'\n        const triggerCondition = (inputParametersData.triggerCondition as string) || 'increase'\n        const cronTimes: string[] = []\n\n        if (pollTime === '15s') {\n            cronTimes.push(`*/15 * * * * *`)\n        } else if (pollTime === '30s') {\n            cronTimes.push(`*/30 * * * * *`)\n        } else if (pollTime === '1min') {\n            cronTimes.push(`*/1 * * * *`)\n        } else if (pollTime === '5min') {\n            cronTimes.push(`*/5 * * * *`)\n        } else if (pollTime === '10min') {\n            cronTimes.push(`*/10 * * * *`)\n        }\n\n        let lastBalance: BigNumber = await provider.getBalance(address)\n\n        const executeTrigger = async () => {\n            const newBalance: BigNumber = await provider.getBalance(address)\n\n            if (!newBalance.eq(lastBalance)) {\n                if (triggerCondition === 'increase' && newBalance.gt(lastBalance)) {\n                    const balanceInMATIC = utils.formatEther(BigNumber.from(newBalance.toString()))\n                    const diffInMATIC = newBalance.sub(lastBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInMATIC} MATIC`,\n                        lastBalance: `${utils.formatEther(BigNumber.from(lastBalance.toString()))} MATIC`,\n                        difference: `${utils.formatEther(BigNumber.from(diffInMATIC.toString()))} MATIC`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'MATIC balance increase'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else if (triggerCondition === 'decrease' && newBalance.lt(lastBalance)) {\n                    const balanceInMATIC = utils.formatEther(BigNumber.from(newBalance.toString()))\n                    const diffInMATIC = lastBalance.sub(newBalance)\n                    const returnItem = {\n                        newBalance: `${balanceInMATIC} MATIC`,\n                        lastBalance: `${utils.formatEther(BigNumber.from(lastBalance.toString()))} MATIC`,\n                        difference: `${utils.formatEther(BigNumber.from(diffInMATIC.toString()))} MATIC`,\n                        explorerLink: `${networkExplorers[network]}/address/${address}`,\n                        triggerCondition: 'MATIC balance decrease'\n                    }\n                    lastBalance = newBalance\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                } else {\n                    lastBalance = newBalance\n                }\n            }\n        }\n\n        // Start the cron-jobs\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n            }\n        } else {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n            }\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: MATICBalanceTrigger }\n"
  },
  {
    "path": "packages/components/nodes/MATICTransfer/MATICTransfer.ts",
    "content": "import { IDbCollection, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, IWallet, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    PolygonNetworks\n} from '../../src'\nimport { ethers } from 'ethers'\n\nclass MATICTransfer implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'MATIC Transfer'\n        this.name = 'MATICTransfer'\n        this.icon = 'polygon.svg'\n        this.type = 'action'\n        this.category = 'Cryptocurrency'\n        this.version = 1.0\n        this.description = 'Send/Transfer MATIC to an address'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...PolygonNetworks]\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Wallet To Transfer',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to send/transfer MATIC',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Address To Receive',\n                name: 'address',\n                type: 'string',\n                default: '',\n                description: 'Address to receive MATIC'\n            },\n            {\n                label: 'Amount',\n                name: 'amount',\n                type: 'number',\n                description: 'Amount of MATIC to transfer'\n            },\n            {\n                label: 'Gas Limit',\n                name: 'gasLimit',\n                type: 'number',\n                optional: true,\n                placeholder: '100000',\n                description: 'Maximum price you are willing to pay when sending a transaction'\n            },\n            {\n                label: 'Max Fee per Gas',\n                name: 'maxFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '200',\n                description:\n                    'The maximum price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            },\n            {\n                label: 'Max Priority Fee per Gas',\n                name: 'maxPriorityFeePerGas',\n                type: 'number',\n                optional: true,\n                placeholder: '5',\n                description:\n                    'The priority fee price (in wei) per unit of gas for transaction. See <a target=\"_blank\" href=\"https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas\">more</a>'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n            const address = inputParametersData.address as string\n            const amount = inputParametersData.amount as string\n            const gasLimit = inputParametersData.gasLimit as number\n            const maxFeePerGas = inputParametersData.maxFeePerGas as number\n            const maxPriorityFeePerGas = inputParametersData.maxPriorityFeePerGas as number\n\n            // Send token\n            const nonce = await provider.getTransactionCount(walletDetails.address)\n            const txOption = {\n                nonce\n            } as any\n\n            if (gasLimit) txOption.gasLimit = gasLimit\n            if (maxFeePerGas) txOption.maxFeePerGas = maxFeePerGas\n            if (maxPriorityFeePerGas) txOption.maxPriorityFeePerGas = maxPriorityFeePerGas\n\n            const tx = await wallet.sendTransaction({\n                to: address,\n                value: ethers.utils.parseEther(amount),\n                ...txOption\n            })\n\n            const txReceipt = await tx.wait()\n\n            const returnItem = {\n                transferFrom: wallet.address,\n                transferTo: address,\n                amount,\n                transactionHash: tx.hash,\n                transactionReceipt: txReceipt as any,\n                link: `${networkExplorers[network]}/tx/${tx.hash}`\n            }\n            return returnNodeExecutionData(returnItem)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: MATICTransfer }\n"
  },
  {
    "path": "packages/components/nodes/Mailchimp/Mailchimp.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\ninterface Auth {\n    username: string\n    password: string\n}\n\nclass Mailchimp implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Mailchimp'\n        this.name = 'mailchimp'\n        this.icon = 'mailchimp.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 2.0\n        this.description = 'Execute Mailchimp API integration'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get list of campaigns',\n                        name: 'listCampaigns',\n                        description: 'Returns the list of campaigns'\n                    },\n                    {\n                        label: 'Get campaign',\n                        name: 'getCampaign',\n                        description: 'Return single campaign'\n                    },\n                    {\n                        label: 'Delete campaign',\n                        name: 'deleteCampaign',\n                        description: 'It will delete campaigns'\n                    },\n                    {\n                        label: 'Add user to subscribe list',\n                        name: 'addUser',\n                        description: 'Add or update user to a subscribe list'\n                    },\n                    {\n                        label: 'Get user',\n                        name: 'getUser',\n                        description: 'Get information about a specific audience'\n                    },\n                    {\n                        label: 'Get list of users',\n                        name: 'listUsers',\n                        description: 'Get information about list of members in a specific audience list'\n                    }\n                ],\n                default: 'listCampaigns'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            // credentialMethod is mandatory field\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Mailchimp Credentials',\n                        name: 'mailChimpCredential'\n                    }\n                ],\n                default: 'mailChimpCredential'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Campaign',\n                name: 'campaignId',\n                type: 'asyncOptions',\n                loadMethod: 'getCampaigns',\n                show: {\n                    'actions.operation': ['deleteCampaign', 'getCampaign']\n                }\n            },\n            {\n                label: 'Audience List',\n                name: 'listId',\n                type: 'asyncOptions',\n                loadMethod: 'getLists',\n                show: {\n                    'actions.operation': ['addUser', 'getUser', 'listUsers']\n                }\n            },\n            {\n                label: 'Customer Email',\n                name: 'email',\n                type: 'string',\n                show: {\n                    'actions.operation': ['addUser', 'getUser']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getCampaigns(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            const apiKey = credentials!.apiKey as string\n            const dc = ((apiKey && apiKey.split('-')[1]) || '') as string\n\n            if (!apiKey || !dc) return returnData\n\n            try {\n                const authObj: Auth = { username: '', password: apiKey }\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://${dc}.api.mailchimp.com/3.0/campaigns`,\n                    headers: { 'Content-Type': 'application/json' },\n                    auth: {\n                        ...authObj\n                    }\n                }\n                const response = await axios(axiosConfig)\n                const campaigns = response.data?.campaigns\n\n                campaigns.forEach((campaign: any) => {\n                    const data = {\n                        label: campaign.settings.title || campaign.web_id,\n                        name: campaign.id\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                })\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getLists(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            const apiKey = credentials!.apiKey as string\n            const dc = ((apiKey && apiKey.split('-')[1]) || '') as string\n\n            if (!apiKey || !dc) return returnData\n\n            try {\n                const authObj: Auth = { username: '', password: apiKey }\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://${dc}.api.mailchimp.com/3.0/lists`,\n                    headers: { 'Content-Type': 'application/json' },\n                    auth: {\n                        ...authObj\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const lists = response.data?.lists\n\n                lists.forEach((list: any) => {\n                    const data = {\n                        label: list.name || list.web_id,\n                        name: list.id\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                })\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        // function to make calls\n        let authObj: Auth\n\n        async function makeApiCall(method: string, url: string, operation: string, body?: ICommonObject): Promise<any[]> {\n            const axiosConfig: AxiosRequestConfig = {\n                method: method as Method,\n                url,\n                headers: { 'Content-Type': 'application/json' },\n                auth: {\n                    ...authObj\n                }\n            }\n            if (method === 'post' && body) axiosConfig.data = body\n            let responseData: any[] = []\n            try {\n                const response = await axios(axiosConfig)\n                if (response?.data) {\n                    responseData.push(response?.data)\n                }\n            } catch (err) {\n                if (operation === 'addUser' && err.response.data.title.includes('Member Exists')) {\n                    // dont throw error\n                } else throw handleErrorMessage(err)\n            }\n            return responseData\n        }\n\n        // function to start running the node\n        const actionData = nodeData.actions\n        const credentials = nodeData.credentials\n        if (actionData === undefined || credentials === undefined) {\n            throw handleErrorMessage({ message: 'Required data missing' })\n        }\n\n        const operation = actionData.operation as string\n        const apiKey = credentials.apiKey as string\n        const dc = ((apiKey && apiKey.split('-')[1]) || '') as string\n\n        if (!apiKey) {\n            throw handleErrorMessage({ message: 'Api key is required' })\n        }\n        if (!dc) {\n            throw handleErrorMessage({ message: 'Date center is required' })\n        }\n\n        let campaignId\n\n        if (['deleteCampaign', 'getCampaign'].includes(operation)) {\n            if (nodeData?.inputParameters?.campaignId === undefined) throw handleErrorMessage({ message: 'Campaign id is required' })\n            else {\n                campaignId = nodeData?.inputParameters?.campaignId\n            }\n        }\n\n        let returnData: ICommonObject[] = []\n        let url = `https://${dc}.api.mailchimp.com/3.0/campaigns`\n        authObj = { username: '', password: apiKey }\n\n        if (['deleteCampaign', 'getCampaign'].includes(operation)) url += `/${campaignId}`\n\n        if (operation === 'listCampaigns') {\n            returnData = await makeApiCall('get', url, operation)\n        } else if (operation === 'getCampaign') {\n            returnData = await makeApiCall('get', url, operation)\n        } else if (operation === 'deleteCampaign') {\n            returnData = await makeApiCall('delete', url, operation)\n        } else if (operation === 'getUser') {\n            const audienceList = nodeData?.inputParameters?.listId as string\n            const email = nodeData?.inputParameters?.email as string\n            url = `https://${dc}.api.mailchimp.com/3.0/lists/${audienceList}/members/${email}`\n            returnData = await makeApiCall('get', url, operation)\n        } else if (operation === 'listUsers') {\n            const audienceList = nodeData?.inputParameters?.listId as string\n            url = `https://${dc}.api.mailchimp.com/3.0/lists/${audienceList}/members`\n            returnData = await makeApiCall('get', url, operation)\n        } else if (operation === 'addUser') {\n            const audienceList = nodeData?.inputParameters?.listId as string\n            const email = nodeData?.inputParameters?.email as string\n            url = `https://${dc}.api.mailchimp.com/3.0/lists/${audienceList}/members`\n            const body = {\n                email_address: email,\n                status: 'subscribed'\n            }\n            await makeApiCall('post', url, operation, body)\n            url = `https://${dc}.api.mailchimp.com/3.0/lists/${audienceList}/members/${email}`\n            returnData = await makeApiCall('get', url, operation)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Mailchimp }\n"
  },
  {
    "path": "packages/components/nodes/Moralis/Moralis.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport { MoralisSupportedNetworks } from './supportedNetwork'\nimport {\n    getBlock,\n    getContractEvents,\n    getDateToBlock,\n    getNativeBalance,\n    getTokenBalances,\n    getTransaction,\n    nativeEvmOperation,\n    runContractFunction\n} from './extendedEVMOperation'\nimport {\n    getContractNFTs,\n    getNFTLowestPrice,\n    getWalletNFTs,\n    getNFTsForContract,\n    getNFTTrades,\n    getWalletNFTTransfers,\n    getNFTTransfersByBlock,\n    getNFTTokenIdMetadata,\n    getWalletNFTCollections,\n    getNFTTokenIdTransfers,\n    nftOperation,\n    reSyncMetadata\n} from './extendedNFTOperation'\nimport { defiOperation, getPairAddress, getPairReserves } from './extendedDeFiOperation'\n\nclass Moralis implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Moralis'\n        this.name = 'moralis'\n        this.icon = 'moralis.svg'\n        this.type = 'action'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Execute Moralis APIs'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'EVM API',\n                        name: 'evmAPI',\n                        description: 'API for interacting/fetching standard onchain data using Moralis API key.'\n                    },\n                    {\n                        label: 'NFT API',\n                        name: 'nftAPI',\n                        description: 'API for interacting/fetching NFT data using Moralis API key.'\n                    },\n                    {\n                        label: 'DeFi API',\n                        name: 'defiAPI',\n                        description: 'API for interacting/fetching DeFi data using Moralis API key.'\n                    },\n                    {\n                        label: 'Upload to IPFS',\n                        name: 'uploadFolder',\n                        description: 'Upload multiple files in a folder to IPFS and place them in a folder directory.'\n                    }\n                ],\n                default: 'evmAPI'\n            },\n            {\n                label: 'Folder',\n                name: 'folderContent',\n                type: 'folder',\n                description: 'The path to a folder to be uploaded.',\n                show: {\n                    'actions.api': ['uploadFolder']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Moralis API Key',\n                        name: 'moralisApi'\n                    }\n                ],\n                default: 'moralisApi'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...MoralisSupportedNetworks],\n                hide: {\n                    'actions.api': ['uploadFolder']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'asyncOptions',\n                loadMethod: 'getOperations',\n                hide: {\n                    'actions.api': ['uploadFolder']\n                }\n            },\n            /**\n             * nativeEvmOperation\n             */\n            ...getBlock,\n            ...getDateToBlock,\n            ...getTransaction,\n            ...getContractEvents,\n            ...runContractFunction,\n            ...getNativeBalance,\n            ...getTokenBalances,\n            /**\n             * nftOperation\n             */\n            ...getNFTTransfersByBlock,\n            ...getWalletNFTs,\n            ...getWalletNFTTransfers,\n            ...getWalletNFTCollections,\n            ...getNFTsForContract,\n            ...getNFTTrades,\n            ...getNFTLowestPrice,\n            ...getContractNFTs,\n            ...reSyncMetadata,\n            ...getNFTTokenIdMetadata,\n            ...getNFTTokenIdTransfers,\n            /**\n             * defiOperation\n             */\n            ...getPairReserves,\n            ...getPairAddress\n        ]\n    }\n\n    loadMethods = {\n        async getOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const actionData = nodeData.actions\n            if (actionData === undefined) {\n                return returnData\n            }\n\n            const api = actionData.api as string\n\n            if (api === 'evmAPI') {\n                return nativeEvmOperation\n            } else if (api === 'nftAPI') {\n                return nftOperation\n            } else if (api === 'defiAPI') {\n                return defiOperation\n            } else if (api === 'ipfsAPI') {\n                return [\n                    {\n                        label: 'Upload Folder',\n                        name: 'uploadFolder',\n                        description: 'Upload multiple files in a folder to IPFS and place them in a folder directory.'\n                    }\n                ]\n            } else {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || networksData === undefined || credentials === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actionData.api as string\n        const operation = inputParametersData.operation as string\n        const chain = networksData.network as string\n        const apiKey = credentials.apiKey as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        if (chain) queryParameters['chain'] = chain\n        let queryBody: any = {}\n        let method: Method = 'GET'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json',\n            'X-Api-Key': apiKey\n        }\n\n        try {\n            /**\n             * nativeEvmOperation\n             */\n            if (operation === 'getBlock') {\n                const block_number_or_hash = inputParametersData.block_number_or_hash as string\n\n                url = `https://deep-index.moralis.io/api/v2/block/${block_number_or_hash}`\n\n                const subdomain = inputParametersData.subdomain as string\n\n                if (subdomain) queryParameters['subdomain'] = chain\n            } else if (operation === 'getDateToBlock') {\n                url = 'https://deep-index.moralis.io/api/v2/dateToBlock'\n\n                const providerUrl = inputParametersData.providerUrl as string\n                const date = Date.parse(inputParametersData.date as string)\n\n                if (providerUrl) queryParameters['providerUrl'] = providerUrl\n                if (date) queryParameters['date'] = date\n            } else if (operation === 'getTransaction') {\n                const transaction_hash = inputParametersData.transaction_hash as string\n\n                url = `https://deep-index.moralis.io/api/v2/transaction/${transaction_hash}`\n\n                const subdomain = inputParametersData.subdomain as string\n\n                if (subdomain) queryParameters['subdomain'] = chain\n            } else if (operation === 'getContractEvents') {\n                method = 'POST'\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/events`\n\n                const topic = inputParametersData.topic as string\n                const subdomain = inputParametersData.subdomain as string\n                const providerUrl = inputParametersData.providerUrl as string\n                const from_date = inputParametersData.from_date as string\n                const to_date = inputParametersData.to_date as string\n                const from_block = inputParametersData.from_block as number\n                const to_block = inputParametersData.to_block as number\n\n                if (topic) queryParameters['topic'] = topic\n                if (subdomain) queryParameters['subdomain'] = chain\n                if (providerUrl) queryParameters['providerUrl'] = providerUrl\n                if (from_date) queryParameters['from_date'] = Date.parse(from_date)\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n                if (from_block) queryParameters['from_block'] = from_block\n                if (to_block) queryParameters['to_block'] = to_block\n            } else if (operation === 'runContractFunction') {\n                method = 'POST'\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/function`\n\n                const function_name = inputParametersData.function_name as string\n                const subdomain = inputParametersData.subdomain as string\n                const providerUrl = inputParametersData.providerUrl as string\n                const abi_str = inputParametersData.abi as string\n                let abi = []\n                if (abi_str) abi = JSON.parse(abi_str.replace(/\\s/g, ''))\n                const params_str = inputParametersData.params as string\n                let params = []\n                if (params_str) params = JSON.parse(params_str.replace(/\\s/g, ''))\n\n                if (function_name) queryParameters['function_name'] = function_name\n                if (subdomain) queryParameters['subdomain'] = chain\n                if (providerUrl) queryParameters['providerUrl'] = providerUrl\n\n                if (abi_str) queryBody['abi'] = abi\n                if (params_str) queryBody['params'] = params\n            } else if (operation === 'getTransactions') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}`\n\n                const subdomain = inputParametersData.subdomain as string\n                const from_date = inputParametersData.from_date as string\n                const to_date = inputParametersData.to_date as string\n                const from_block = inputParametersData.from_block as number\n                const to_block = inputParametersData.to_block as number\n\n                if (subdomain) queryParameters['subdomain'] = chain\n                if (from_date) queryParameters['from_date'] = Date.parse(from_date)\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n                if (from_block) queryParameters['from_block'] = from_block\n                if (to_block) queryParameters['to_block'] = to_block\n            } else if (operation === 'getNativeBalance') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/balance`\n\n                const providerUrl = inputParametersData.providerUrl as string\n                const to_block = inputParametersData.to_block as number\n\n                if (providerUrl) queryParameters['providerUrl'] = providerUrl\n                if (to_block) queryParameters['to_block'] = to_block\n            } else if (operation === 'getTokenBalances') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/erc20`\n\n                const subdomain = inputParametersData.subdomain as string\n                const to_block = inputParametersData.to_block as number\n                const token_addresses_str = inputParametersData.token_addresses as string\n                let token_addresses = []\n                if (token_addresses_str) token_addresses = JSON.parse(token_addresses_str.replace(/\\s/g, ''))\n\n                if (subdomain) queryParameters['subdomain'] = subdomain\n                if (to_block) queryParameters['to_block'] = to_block\n                if (token_addresses) queryParameters['token_addresses'] = token_addresses\n            } else if (operation === 'getTokenTransfers') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/erc20/transfers`\n\n                const subdomain = inputParametersData.subdomain as string\n                const from_date = inputParametersData.from_date as string\n                const to_date = inputParametersData.to_date as string\n                const from_block = inputParametersData.from_block as number\n                const to_block = inputParametersData.to_block as number\n\n                if (subdomain) queryParameters['subdomain'] = chain\n                if (from_date) queryParameters['from_date'] = Date.parse(from_date)\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n                if (from_block) queryParameters['from_block'] = from_block\n                if (to_block) queryParameters['to_block'] = to_block\n            }\n\n            /**\n             * nftOperation\n             */\n            if (operation === 'getNFTTransfersByBlock') {\n                const block_number_or_hash = inputParametersData.block_number_or_hash as string\n\n                url = `https://deep-index.moralis.io/api/v2/block/${block_number_or_hash}/nft/transfers`\n\n                const subdomain = inputParametersData.subdomain as string\n\n                if (subdomain) queryParameters['subdomain'] = chain\n            } else if (operation === 'getWalletNFTs') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/nft`\n\n                const format = inputParametersData.format as string\n                if (format) queryParameters['format'] = format\n\n                const token_addresses_str = inputParametersData.token_addresses as string\n                let token_addresses = []\n                if (token_addresses_str) {\n                    token_addresses = JSON.parse(token_addresses_str.replace(/\\s/g, ''))\n                    queryParameters['token_addresses'] = token_addresses\n                }\n            } else if (operation === 'getWalletNFTTransfers') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/nft/transfers`\n\n                const format = inputParametersData.format as string\n                const direction = inputParametersData.direction as string\n                const from_block = inputParametersData.from_block as number\n                const to_block = inputParametersData.to_block as number\n\n                if (format) queryParameters['format'] = format\n                if (direction) queryParameters['direction'] = direction\n                if (from_block) queryParameters['from_block'] = from_block\n                if (to_block) queryParameters['to_block'] = to_block\n            } else if (operation === 'getWalletNFTCollections') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/nft/collections`\n            } else if (operation === 'getNFTsForContract') {\n                const address = inputParametersData.address as string\n                const token_address = inputParametersData.token_address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${address}/nft/${token_address}`\n\n                const format = inputParametersData.format as string\n                if (format) queryParameters['format'] = format\n            } else if (operation === 'getNFTTrades') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/trades`\n\n                const marketplace = inputParametersData.marketplace as string\n                const format = inputParametersData.format as string\n                const providerUrl = inputParametersData.providerUrl as string\n                const from_block = inputParametersData.from_block as number\n                const to_block = inputParametersData.to_block as number\n                const from_date = inputParametersData.from_date as string\n                const to_date = inputParametersData.to_date as string\n\n                if (format) queryParameters['format'] = format\n                if (marketplace) queryParameters['marketplace'] = marketplace\n                if (providerUrl) queryParameters['providerUrl'] = providerUrl\n                if (from_block) queryParameters['from_block'] = from_block\n                if (to_block) queryParameters['to_block'] = to_block\n                if (from_date) queryParameters['from_date'] = Date.parse(from_date)\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n            } else if (operation === 'getNFTLowestPrice') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/lowestprice`\n\n                const marketplace = inputParametersData.marketplace as string\n                const days = inputParametersData.format as number\n                const providerUrl = inputParametersData.providerUrl as string\n\n                if (days) queryParameters['days'] = days\n                if (marketplace) queryParameters['marketplace'] = marketplace\n                if (providerUrl) queryParameters['providerUrl'] = providerUrl\n            } else if (operation === 'getNFTTransfersFromToBlock') {\n                url = `https://deep-index.moralis.io/api/v2/nft/transfers`\n\n                const format = inputParametersData.format as string\n                const from_block = inputParametersData.from_block as number\n                const to_block = inputParametersData.to_block as number\n                const from_date = inputParametersData.from_date as string\n                const to_date = inputParametersData.to_date as string\n\n                if (format) queryParameters['format'] = format\n                if (from_block) queryParameters['from_block'] = from_block\n                if (to_block) queryParameters['to_block'] = to_block\n                if (from_date) queryParameters['from_date'] = Date.parse(from_date)\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n            } else if (operation === 'getContractNFTs') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}`\n\n                const format = inputParametersData.format as string\n                const totalRanges = inputParametersData.totalRanges as number\n                const range = inputParametersData.range as number\n\n                if (format) queryParameters['format'] = format\n                if (totalRanges) queryParameters['totalRanges'] = totalRanges\n                if (range) queryParameters['range'] = range\n            } else if (operation === 'getNFTContractTransfers') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/transfers`\n\n                const format = inputParametersData.format as string\n                if (format) queryParameters['format'] = format\n            } else if (operation === 'getNFTOwners') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/owners`\n\n                const format = inputParametersData.format as string\n                if (format) queryParameters['format'] = format\n            } else if (operation === 'getNFTMetadata') {\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/metadata`\n            } else if (operation === 'reSyncMetadata') {\n                const address = inputParametersData.address as string\n                const token_id = inputParametersData.token_id as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/${token_id}/metadata/resync`\n\n                const flag = inputParametersData.flag as string\n                const mode = inputParametersData.mode as string\n\n                if (flag) queryParameters['flag'] = flag\n                if (mode) queryParameters['mode'] = mode\n            } else if (operation === 'syncNFTContract') {\n                method = 'PUT'\n                const address = inputParametersData.address as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/sync`\n            } else if (operation === 'getNFTTokenIdMetadata') {\n                const address = inputParametersData.address as string\n                const token_id = inputParametersData.token_id as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/${token_id}`\n\n                const format = inputParametersData.format as string\n                if (format) queryParameters['format'] = format\n            } else if (operation === 'getNFTTokenIdOwners') {\n                const address = inputParametersData.address as string\n                const token_id = inputParametersData.token_id as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/${token_id}/owners`\n\n                const format = inputParametersData.format as string\n                if (format) queryParameters['format'] = format\n            } else if (operation === 'getNFTTokenIdTransfers') {\n                const address = inputParametersData.address as string\n                const token_id = inputParametersData.token_id as string\n\n                url = `https://deep-index.moralis.io/api/v2/nft/${address}/${token_id}/transfers`\n\n                const format = inputParametersData.format as string\n                const order = inputParametersData.order as string\n\n                if (format) queryParameters['format'] = format\n                if (order) queryParameters['order'] = order\n            }\n            /**\n             * defiOperation\n             */\n            if (operation === 'getPairReserves') {\n                const pair_address = inputParametersData.pair_address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${pair_address}/reserves`\n\n                const to_block = inputParametersData.to_block as number\n                const to_date = inputParametersData.to_date as string\n\n                if (to_block) queryParameters['to_block'] = to_block\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n            } else if (operation === 'getPairAddress') {\n                const token0_address = inputParametersData.token0_address as string\n                const token1_address = inputParametersData.token1_address as string\n\n                url = `https://deep-index.moralis.io/api/v2/${token0_address}/${token1_address}/pairAddress`\n\n                const to_block = inputParametersData.to_block as number\n                const to_date = inputParametersData.to_date as string\n                const exchange = inputParametersData.exchange as string\n\n                if (to_block) queryParameters['to_block'] = to_block\n                if (to_date) queryParameters['to_date'] = Date.parse(to_date)\n                if (exchange) queryParameters['exchange'] = exchange\n            }\n            /**\n             * ipfsOperation\n             */\n            if (api === 'uploadFolder') {\n                method = 'POST'\n                url = 'https://deep-index.moralis.io/api/v2/ipfs/uploadFolder'\n\n                const bodyParams = []\n                const folderContent = actionData.folderContent as string\n                const base64Array = JSON.parse(folderContent.replace(/\\s/g, ''))\n\n                for (let i = 0; i < base64Array.length; i += 1) {\n                    const fileBase64 = base64Array[i]\n                    const splitDataURI = fileBase64.split(',')\n\n                    const filepath = (splitDataURI.pop() || 'filepath:').split(':')[1]\n                    const content = splitDataURI.pop() || ''\n                    bodyParams.push({\n                        path: filepath,\n                        content\n                    })\n                }\n                queryBody = bodyParams\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers\n            }\n\n            if (Object.keys(queryParameters).length > 0) {\n                axiosConfig.params = queryParameters\n                axiosConfig.paramsSerializer = (params) => serializeQueryParams(params, true)\n            }\n\n            if (Object.keys(queryBody).length > 0) {\n                axiosConfig.data = queryBody\n            }\n\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) returnData.push(...responseData)\n        else returnData.push(responseData)\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Moralis }\n"
  },
  {
    "path": "packages/components/nodes/Moralis/extendedDeFiOperation.ts",
    "content": "import { INodeOptionsValue, INodeParams } from '../../src'\n\nexport const defiOperation = [\n    {\n        label: 'Get Pair Reserves',\n        name: 'getPairReserves',\n        description: 'Get the liquidity reserves for a given pair address. Only Uniswap V2 based exchanges supported at the moment.'\n    },\n    {\n        label: 'Get Pair Address',\n        name: 'getPairAddress',\n        description:\n            'Fetch the pair data of the provided token0+token1 combination. The token0 and token1 options are interchangable (ie. there is no different outcome in \"token0=WETH and token1=USDT\" or \"token0=USDT and token1=WETH\")'\n    }\n] as INodeOptionsValue[]\n\nexport const getPairReserves = [\n    {\n        label: 'Pair Address',\n        name: 'pairAddress',\n        type: 'string',\n        description: 'Liquidity pair address',\n        show: {\n            'inputParameters.operation': ['getPairReserves']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'string',\n        description: 'To get the reserves at this block number',\n        show: {\n            'inputParameters.operation': ['getPairReserves']\n        }\n    },\n    {\n        label: 'To Date',\n        name: 'to_date',\n        type: 'date',\n        description: 'Get the reserves to this date',\n        show: {\n            'inputParameters.operation': ['getPairReserves']\n        }\n    }\n] as INodeParams[]\n\nexport const getPairAddress = [\n    {\n        label: 'Token0 Address',\n        name: 'token0_address',\n        type: 'string',\n        description: 'Token0 address',\n        show: {\n            'inputParameters.operation': ['getPairAddress']\n        }\n    },\n    {\n        label: 'Token1 Address',\n        name: 'token1_address',\n        type: 'string',\n        description: 'Token1 address',\n        show: {\n            'inputParameters.operation': ['getPairAddress']\n        }\n    },\n    {\n        label: 'Exchange',\n        name: 'exchange',\n        type: 'options',\n        description: 'The factory name or address of the token exchange',\n        options: [\n            {\n                label: 'UniSwapV2',\n                name: 'uniswapv2'\n            },\n            {\n                label: 'UniSwapV3',\n                name: 'uniswapv3'\n            },\n            {\n                label: 'SushiSwapV2',\n                name: 'sushiswapv2'\n            },\n            {\n                label: 'PancakeSwapV2',\n                name: 'pancakeswapv2'\n            },\n            {\n                label: 'PancakeSwapV1',\n                name: 'pancakeswapv1'\n            },\n            {\n                label: 'QuickSwap',\n                name: 'quickswap'\n            }\n        ],\n        default: 'uniswapv2',\n        show: {\n            'inputParameters.operation': ['getPairAddress']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'string',\n        description: 'To get the reserves at this block number',\n        show: {\n            'inputParameters.operation': ['getPairAddress']\n        }\n    },\n    {\n        label: 'To Date',\n        name: 'to_date',\n        type: 'date',\n        description: 'Get the reserves to this date',\n        show: {\n            'inputParameters.operation': ['getPairAddress']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/Moralis/extendedEVMOperation.ts",
    "content": "import { INodeOptionsValue, INodeParams } from '../../src'\n\nexport const nativeEvmOperation = [\n    {\n        label: 'Get Native Balance',\n        name: 'getNativeBalance',\n        description: 'Get native balance for a specific address.'\n    },\n    {\n        label: 'Get Token Balances',\n        name: 'getTokenBalances',\n        description: 'Get token balances for a specific address.'\n    },\n    {\n        label: 'Get Token Transfers',\n        name: 'getTokenTransfers',\n        description: 'Get ERC20 token transactions ordered by block number in descending order.'\n    },\n    {\n        label: 'Get Date To Block',\n        name: 'getDateToBlock',\n        description: 'Get the closest block of the provided date.'\n    },\n    {\n        label: 'Get Block',\n        name: 'getBlock',\n        description: 'Get the contents of a block by block hash.'\n    },\n    {\n        label: 'Get Transaction',\n        name: 'getTransaction',\n        description: 'Get the contents of a transaction by transaction hash.'\n    },\n    {\n        label: 'Get Transactions',\n        name: 'getTransactions',\n        description: 'Get native transactions ordered by block number in descending order.'\n    },\n    {\n        label: 'Get Contract Events',\n        name: 'getContractEvents',\n        description: 'Get events for a specific contract ordered by block number in descending order.'\n    },\n    {\n        label: 'Run Contract Function',\n        name: 'runContractFunction',\n        description: 'Run a given function of a contract abi and retrieve readonly data.'\n    }\n] as INodeOptionsValue[]\n\nexport const getDateToBlock = [\n    {\n        label: 'Provider Url',\n        name: 'providerUrl',\n        type: 'string',\n        optional: true,\n        description: 'Web3 provider url to user when using local dev chain',\n        show: {\n            'inputParameters.operation': ['getDateToBlock']\n        }\n    },\n    {\n        label: 'Date',\n        name: 'date',\n        type: 'date',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getDateToBlock']\n        }\n    }\n] as INodeParams[]\n\nexport const getBlock = [\n    {\n        label: 'Block Number or Hash',\n        name: 'block_number_or_hash',\n        type: 'string',\n        description: 'The block hash or block number',\n        show: {\n            'inputParameters.operation': ['getBlock', 'getNFTTransfersByBlock']\n        }\n    },\n    {\n        label: 'Subdomain',\n        name: 'subdomain',\n        type: 'string',\n        optional: true,\n        description: 'The subdomain of the moralis server to use (Only use when selecting local devchain as chain)',\n        show: {\n            'inputParameters.operation': ['getBlock', 'getNFTTransfersByBlock', 'getTransaction']\n        }\n    }\n] as INodeParams[]\n\nexport const getTransaction = [\n    {\n        label: 'Transaction Hash',\n        name: 'transaction_hash',\n        type: 'string',\n        description: 'The transaction hash',\n        show: {\n            'inputParameters.operation': ['getTransaction']\n        }\n    }\n] as INodeParams[]\n\nexport const getContractEvents = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'Address of the contract',\n        show: {\n            'inputParameters.operation': ['getContractEvents', 'getTransactions', 'getTokenTransfers']\n        }\n    },\n    {\n        label: 'Topic',\n        name: 'topic',\n        type: 'string',\n        description: 'The topic of the event',\n        show: {\n            'inputParameters.operation': ['getContractEvents']\n        }\n    },\n    {\n        label: 'Subdomain',\n        name: 'subdomain',\n        type: 'string',\n        optional: true,\n        description: 'The subdomain of the moralis server to use (Only use when selecting local devchain as chain)',\n        show: {\n            'inputParameters.operation': ['getContractEvents', 'getTransactions', 'getTokenTransfers']\n        }\n    },\n    {\n        label: 'Provider Url',\n        name: 'providerUrl',\n        type: 'string',\n        optional: true,\n        description: 'Web3 provider url to user when using local dev chain',\n        show: {\n            'inputParameters.operation': ['getContractEvents']\n        }\n    },\n    {\n        label: 'From Date',\n        name: 'from_date',\n        type: 'date',\n        description: 'The date from where to get the logs',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getContractEvents', 'getTransactions', 'getTokenTransfers']\n        }\n    },\n    {\n        label: 'To Date',\n        name: 'to_date',\n        type: 'date',\n        description: 'Get the logs to this date',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getContractEvents', 'getTransactions', 'getTokenTransfers']\n        }\n    },\n    {\n        label: 'From Block',\n        name: 'from_block',\n        type: 'number',\n        description: 'The minimum block number from where to get the logs',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getContractEvents', 'getTransactions', 'getTokenTransfers']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'number',\n        description: 'The maximum block number from where to get the logs.',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getContractEvents', 'getTransactions', 'getTokenTransfers']\n        }\n    }\n] as INodeParams[]\n\nexport const runContractFunction = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'Address of the contract',\n        show: {\n            'inputParameters.operation': ['runContractFunction']\n        }\n    },\n    {\n        label: 'Function Name',\n        name: 'function_name',\n        type: 'string',\n        description: 'Function Name of the contract to run',\n        show: {\n            'inputParameters.operation': ['runContractFunction']\n        }\n    },\n    {\n        label: 'ABI',\n        name: 'abi',\n        type: 'json',\n        placeholder: '[ \"event Transfer(address indexed from, address indexed to, uint value)\" ]',\n        description: 'ABI of the contract in array',\n        show: {\n            'inputParameters.operation': ['runContractFunction']\n        }\n    },\n    {\n        label: 'Function Parameters',\n        name: 'params',\n        type: 'json',\n        placeholder: '{\"var1\": \"value1\"}',\n        description: 'Function parameters in json. Ex: {\"var1\": \"value1\"}',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['runContractFunction']\n        }\n    },\n    {\n        label: 'Subdomain',\n        name: 'subdomain',\n        type: 'string',\n        optional: true,\n        description: 'The subdomain of the moralis server to use (Only use when selecting local devchain as chain)',\n        show: {\n            'inputParameters.operation': ['runContractFunction']\n        }\n    },\n    {\n        label: 'Provider Url',\n        name: 'providerUrl',\n        type: 'string',\n        optional: true,\n        description: 'Web3 provider url to user when using local dev chain',\n        show: {\n            'inputParameters.operation': ['runContractFunction']\n        }\n    }\n] as INodeParams[]\n\nexport const getNativeBalance = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'The address for which the native balance will be checked',\n        show: {\n            'inputParameters.operation': ['getNativeBalance']\n        }\n    },\n    {\n        label: 'Provider Url',\n        name: 'providerUrl',\n        type: 'string',\n        optional: true,\n        description: 'Web3 provider url to user when using local dev chain',\n        show: {\n            'inputParameters.operation': ['getNativeBalance']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'number',\n        description: 'The block number on which the balances should be checked.',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getNativeBalance']\n        }\n    }\n] as INodeParams[]\n\nexport const getTokenBalances = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'The address for which token balances will be checked',\n        show: {\n            'inputParameters.operation': ['getTokenBalances']\n        }\n    },\n    {\n        label: 'Token Addresses',\n        name: 'token_addresses',\n        type: 'json',\n        placeholder: '[\"0xa, \"0xb\"]',\n        optional: true,\n        description: 'The addresses to get balances for',\n        show: {\n            'inputParameters.operation': ['getTokenBalances']\n        }\n    },\n    {\n        label: 'Subdomain',\n        name: 'subdomain',\n        type: 'string',\n        optional: true,\n        description: 'The subdomain of the moralis server to use (Only use when selecting local devchain as chain)',\n        show: {\n            'inputParameters.operation': ['getTokenBalances']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'number',\n        description: 'The block number on which the balances should be checked.',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getTokenBalances']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/Moralis/extendedNFTOperation.ts",
    "content": "import { INodeOptionsValue, INodeParams } from '../../src'\n\nexport const nftOperation = [\n    {\n        label: 'Get Wallet NFTs',\n        name: 'getWalletNFTs',\n        description: 'Get NFTs owned by a given address.'\n    },\n    {\n        label: 'Get Wallet NFT Transfers',\n        name: 'getWalletNFTTransfers',\n        description: 'Get the transfers of the tokens matching the given parameters.'\n    },\n    {\n        label: 'Get Wallet NFT Collections',\n        name: 'getWalletNFTCollections',\n        description: 'Get the nft collections owned by an user'\n    },\n    {\n        label: 'Get NFTs For Contract',\n        name: 'getNFTsForContract',\n        description: 'Get NFTs owned by the given address for a specific NFT contract address.'\n    },\n    {\n        label: 'Get NFT Trades',\n        name: 'getNFTTrades',\n        description: 'Get the nft trades for a given contract and marketplace.'\n    },\n    {\n        label: 'Get NFT Lowest Price',\n        name: 'getNFTLowestPrice',\n        description: 'Get the lowest executed price for an NFT token contract for the last x days (only trades paid in ETH).'\n    },\n    {\n        label: 'Get NFT Transfers By Block',\n        name: 'getNFTTransfersByBlock',\n        description: 'Get NFT transfers by block number or block hash.'\n    },\n    {\n        label: 'Get NFT Transfers From To Block',\n        name: 'getNFTTransfersFromToBlock',\n        description: 'Gets the transfers of the tokens from a block number to a block number.'\n    },\n    {\n        label: 'Get Contract NFTs',\n        name: 'getContractNFTs',\n        description: 'Get all NFTs, including metadata (where available), for all NFTs for the given contract address.'\n    },\n    {\n        label: 'Get NFT Contract Transfers',\n        name: 'getNFTContractTransfers',\n        description: 'Get the transfers of the tokens matching the given parameters.'\n    },\n    {\n        label: 'Get NFT Owners',\n        name: 'getNFTOwners',\n        description: 'Get all owners of NFTs within a given contract.'\n    },\n    {\n        label: 'Get NFT Metadata',\n        name: 'getNFTMetadata',\n        description: 'Get the contract level metadata (name, symbol, base token uri) for the given contract.'\n    },\n    {\n        label: 'ReSync Metadata',\n        name: 'reSyncMetadata',\n        description: 'ReSync the metadata for an NFT.'\n    },\n    {\n        label: 'Sync NFT Contract',\n        name: 'syncNFTContract',\n        description: 'Initiates a metadata refresh for an entire NFT collection.'\n    },\n    {\n        label: 'Get NFT Token Id Metadata',\n        name: 'getNFTTokenIdMetadata',\n        description: 'Get NFT data, including metadata (where available), for the given NFT token id of the given contract address.'\n    },\n    {\n        label: 'Get NFT Token Id Owners',\n        name: 'getNFTTokenIdOwners',\n        description: 'Get all owners of a specific NFT given the contract address and token ID.'\n    },\n    {\n        label: 'Get NFT Token Id Transfers',\n        name: 'getNFTTokenIdTransfers',\n        description: 'Get the transfers of an NFT given a contract address and token ID.'\n    }\n] as INodeOptionsValue[]\n\nexport const getWalletNFTs = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'The owner of a given token',\n        show: {\n            'inputParameters.operation': ['getWalletNFTs']\n        }\n    },\n    {\n        label: 'Format',\n        name: 'format',\n        type: 'options',\n        optional: true,\n        description: 'The format of the token id',\n        options: [\n            {\n                label: 'Decimal',\n                name: 'decimal'\n            },\n            {\n                label: 'Hex',\n                name: 'hex'\n            }\n        ],\n        default: 'decimal',\n        show: {\n            'inputParameters.operation': ['getWalletNFTs']\n        }\n    },\n    {\n        label: 'Token Addresses',\n        name: 'token_addresses',\n        type: 'json',\n        placeholder: '[\"0xa, \"0xb\"]',\n        optional: true,\n        description: 'The addresses to get balances for',\n        show: {\n            'inputParameters.operation': ['getWalletNFTs']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTTransfersByBlock = [\n    {\n        label: 'Block Number or Hash',\n        name: 'block_number_or_hash',\n        type: 'string',\n        description: 'The block hash or block number',\n        show: {\n            'inputParameters.operation': ['getNFTTransfersByBlock']\n        }\n    },\n    {\n        label: 'Subdomain',\n        name: 'subdomain',\n        type: 'string',\n        optional: true,\n        description: 'The subdomain of the moralis server to use (Only use when selecting local devchain as chain)',\n        show: {\n            'inputParameters.operation': ['getNFTTransfersByBlock']\n        }\n    }\n] as INodeParams[]\n\nexport const getWalletNFTTransfers = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'The sender or recepient of the transfer',\n        show: {\n            'inputParameters.operation': ['getWalletNFTTransfers']\n        }\n    },\n    {\n        label: 'Format',\n        name: 'format',\n        type: 'options',\n        optional: true,\n        description: 'The format of the token id',\n        options: [\n            {\n                label: 'Decimal',\n                name: 'decimal'\n            },\n            {\n                label: 'Hex',\n                name: 'hex'\n            }\n        ],\n        default: 'decimal',\n        show: {\n            'inputParameters.operation': ['getWalletNFTTransfers']\n        }\n    },\n    {\n        label: 'Direction',\n        name: 'direction',\n        type: 'options',\n        optional: true,\n        description: 'The transfer direction',\n        options: [\n            {\n                label: 'Both',\n                name: 'both'\n            },\n            {\n                label: 'From',\n                name: 'from'\n            },\n            {\n                label: 'To',\n                name: 'to'\n            }\n        ],\n        default: 'both',\n        show: {\n            'inputParameters.operation': ['getWalletNFTTransfers']\n        }\n    },\n    {\n        label: 'From Block',\n        name: 'from_block',\n        type: 'number',\n        description: 'The minimum block number from where to get the transfers',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getWalletNFTTransfers']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'number',\n        description: 'To get the reserves at this block number',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getWalletNFTTransfers']\n        }\n    }\n] as INodeParams[]\n\nexport const getWalletNFTCollections = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'The owner wallet address of the NFT collections',\n        show: {\n            'inputParameters.operation': ['getWalletNFTCollections']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTsForContract = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'The owner of a given token',\n        show: {\n            'inputParameters.operation': ['getNFTsForContract']\n        }\n    },\n    {\n        label: 'Token Address',\n        name: 'token_address',\n        type: 'string',\n        description: 'Address of the contract',\n        show: {\n            'inputParameters.operation': ['getNFTsForContract']\n        }\n    },\n    {\n        label: 'Format',\n        name: 'format',\n        type: 'options',\n        optional: true,\n        description: 'The format of the token id',\n        options: [\n            {\n                label: 'Decimal',\n                name: 'decimal'\n            },\n            {\n                label: 'Hex',\n                name: 'hex'\n            }\n        ],\n        default: 'decimal',\n        show: {\n            'inputParameters.operation': ['getNFTsForContract', 'getNFTTransfersFromToBlock']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTTrades = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'Address of the contract',\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTLowestPrice']\n        }\n    },\n    {\n        label: 'Marketplace',\n        name: 'marketplace',\n        type: 'options',\n        optional: true,\n        description: 'Marketplace from where to get the trades',\n        options: [\n            {\n                label: 'Opensea',\n                name: 'opensea'\n            }\n        ],\n        default: 'opensea',\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTLowestPrice']\n        }\n    },\n    {\n        label: 'Provider Url',\n        name: 'providerUrl',\n        type: 'string',\n        optional: true,\n        description: 'Web3 provider url to user when using local dev chain',\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTLowestPrice']\n        }\n    },\n    {\n        label: 'From Date',\n        name: 'from_date',\n        type: 'date',\n        description: 'The date from where to get the transfers ',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTTransfersFromToBlock']\n        }\n    },\n    {\n        label: 'To Date',\n        name: 'to_date',\n        type: 'date',\n        description: 'Get the transfers to this date',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTTransfersFromToBlock']\n        }\n    },\n    {\n        label: 'From Block',\n        name: 'from_block',\n        type: 'number',\n        description: 'The minimum block number from where to get the transfers.',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTTransfersFromToBlock']\n        }\n    },\n    {\n        label: 'To Block',\n        name: 'to_block',\n        type: 'number',\n        description: 'The maximum block number from where to get the transfers.',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getNFTTrades', 'getNFTTransfersFromToBlock']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTLowestPrice = [\n    {\n        label: 'Days',\n        name: 'days',\n        type: 'number',\n        description: 'The number of days to look back to find the lowest price. If not provided 7 days will be the default',\n        optional: true,\n        show: {\n            'inputParameters.operation': ['getNFTLowestPrice']\n        }\n    }\n] as INodeParams[]\n\nexport const getContractNFTs = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'Address of the contract',\n        show: {\n            'inputParameters.operation': ['getContractNFTs', 'getNFTContractTransfers', 'getNFTOwners', 'getNFTMetadata']\n        }\n    },\n    {\n        label: 'Total Ranges',\n        name: 'totalRanges',\n        type: 'number',\n        optional: true,\n        description: 'The number of subranges to split the results into',\n        show: {\n            'inputParameters.operation': ['getContractNFTs']\n        }\n    },\n    {\n        label: 'Range',\n        name: 'range',\n        type: 'number',\n        optional: true,\n        description: 'The desired subrange to query',\n        show: {\n            'inputParameters.operation': ['getContractNFTs']\n        }\n    },\n    {\n        label: 'Format',\n        name: 'format',\n        type: 'options',\n        optional: true,\n        description: 'The format of the token id',\n        options: [\n            {\n                label: 'Decimal',\n                name: 'decimal'\n            },\n            {\n                label: 'Hex',\n                name: 'hex'\n            }\n        ],\n        default: 'decimal',\n        show: {\n            'inputParameters.operation': ['getContractNFTs', 'getNFTContractTransfers', 'getNFTOwners']\n        }\n    }\n] as INodeParams[]\n\nexport const reSyncMetadata = [\n    {\n        label: 'Address',\n        name: 'address',\n        type: 'string',\n        description: 'Address of the contract',\n        show: {\n            'inputParameters.operation': ['reSyncMetadata', 'getNFTTokenIdMetadata', 'getNFTTokenIdOwners', 'getNFTTokenIdTransfers']\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'token_id',\n        type: 'string',\n        description: 'The id of the token',\n        show: {\n            'inputParameters.operation': ['reSyncMetadata', 'getNFTTokenIdMetadata', 'getNFTTokenIdOwners', 'getNFTTokenIdTransfers']\n        }\n    },\n    {\n        label: 'Flag',\n        name: 'flag',\n        type: 'options',\n        optional: true,\n        description: 'The type of resync to operate',\n        options: [\n            {\n                label: 'URI',\n                name: 'uri'\n            },\n            {\n                label: 'Metadata',\n                name: 'metadata'\n            }\n        ],\n        default: 'uri',\n        show: {\n            'inputParameters.operation': ['reSyncMetadata']\n        }\n    },\n    {\n        label: 'Mode',\n        name: 'mode',\n        type: 'options',\n        optional: true,\n        description: 'To define the behaviour of the endpoint',\n        options: [\n            {\n                label: 'Async',\n                name: 'async'\n            },\n            {\n                label: 'Sync',\n                name: 'sync'\n            }\n        ],\n        default: 'async',\n        show: {\n            'inputParameters.operation': ['reSyncMetadata']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTTokenIdMetadata = [\n    {\n        label: 'Format',\n        name: 'format',\n        type: 'options',\n        optional: true,\n        description: 'The format of the token id',\n        options: [\n            {\n                label: 'Decimal',\n                name: 'decimal'\n            },\n            {\n                label: 'Hex',\n                name: 'hex'\n            }\n        ],\n        default: 'decimal',\n        show: {\n            'inputParameters.operation': ['getNFTTokenIdMetadata', 'getNFTTokenIdOwners', 'getNFTTokenIdTransfers']\n        }\n    }\n] as INodeParams[]\n\nexport const getNFTTokenIdTransfers = [\n    {\n        label: 'Order',\n        name: 'order',\n        type: 'string',\n        optional: true,\n        description:\n            'The field(s) to order on and if it should be ordered in ascending or descending order. Specified by: fieldName1.order,fieldName2.order. Example 1: \"block_number\", \"block_number.ASC\", \"block_number.DESC\", Example 2: \"block_number and contract_type\", \"block_number.ASC,contract_type.DESC\"',\n        show: {\n            'inputParameters.operation': ['getNFTTokenIdTransfers']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/Moralis/supportedNetwork.ts",
    "content": "import { INodeOptionsValue, NETWORK_LABEL } from '../../src'\n\nexport const MoralisSupportedNetworks = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: 'eth'\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: 'goerli'\n    },\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: 'polygon'\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: 'mumbai'\n    },\n    {\n        label: NETWORK_LABEL.BSC,\n        name: 'bsc'\n    },\n    {\n        label: NETWORK_LABEL.BSC_TESTNET,\n        name: 'bsc testnet'\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE,\n        name: 'avalanche'\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE_TESTNET,\n        name: 'avalanche testnet'\n    },\n    {\n        label: NETWORK_LABEL.FANTOM,\n        name: 'fantom'\n    },\n    {\n        label: NETWORK_LABEL.CRONOS,\n        name: 'cronos'\n    },\n    {\n        label: NETWORK_LABEL.CRONOS_TESTNET,\n        name: 'cronos testnet'\n    }\n] as INodeOptionsValue[]\n"
  },
  {
    "path": "packages/components/nodes/NFTMintTrigger/NFTMintTrigger.ts",
    "content": "import { ethers, utils } from 'ethers'\nimport { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    ArbitrumNetworks,\n    ETHNetworks,\n    OptimismNetworks,\n    PolygonNetworks,\n    networkExplorers,\n    openseaExplorers,\n    NETWORK,\n    getNetworkProvidersList,\n    getNetworkProvider,\n    NETWORK_PROVIDER,\n    eventTransferAbi,\n    networkProviderCredentials\n} from '../../src/ChainNetwork'\n\nclass NFTMintTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'NFT Mint Trigger'\n        this.name = 'NFTMintTrigger'\n        this.icon = 'nftmint.png'\n        this.type = 'trigger'\n        this.category = 'NFT'\n        this.version = 1.0\n        this.description = 'Start workflow whenever a NFT is minted'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks],\n                default: 'homestead'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Token Standard',\n                name: 'tokenStandard',\n                type: 'options',\n                options: [\n                    {\n                        label: 'ERC-721',\n                        name: 'ERC721'\n                    }\n                ],\n                default: 'ERC721'\n            },\n            {\n                label: 'To Wallet Address',\n                name: 'toAddress',\n                type: 'string',\n                default: ''\n            },\n            {\n                label: 'NFT Address',\n                name: 'nftAddress',\n                type: 'string',\n                default: '',\n                optional: true\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const nftAddress = inputParametersData.nftAddress as string\n        const toAddress = inputParametersData.toAddress as string\n\n        const ifaceABI = eventTransferAbi\n        const topicId = utils.id('Transfer(address,address,uint256)')\n\n        const filter = {\n            topics: [\n                topicId,\n                utils.hexZeroPad('0x0000000000000000000000000000000000000000', 32), // Mint always has 000 from address\n                toAddress ? utils.hexZeroPad(toAddress, 32) : null\n            ]\n        }\n        if (nftAddress) (filter as any)['address'] = nftAddress\n\n        provider.on(filter, async (log: any) => {\n            const txHash = log.transactionHash\n            const iface = new ethers.utils.Interface(ifaceABI)\n            const logs = await provider.getLogs(filter)\n            const events = logs.map((log) => iface.parseLog(log))\n            const toWallet = events.length ? events[0].args[1] : ''\n\n            //ERC721 has 4 topics length\n            if (log.topics.length === 4) {\n                const returnItem = {} as ICommonObject\n                returnItem['To Wallet'] = toWallet\n                returnItem['NFT Token Address'] = log.address\n\n                if (openseaExplorers[network]) {\n                    let tokenString = ''\n                    const counter = log.topics[log.topics.length - 1]\n                    const strippedZeroCounter = utils.hexStripZeros(counter)\n                    if (strippedZeroCounter !== '0x') {\n                        const counterBigNubmer = ethers.BigNumber.from(strippedZeroCounter)\n                        tokenString = counterBigNubmer.toString()\n                    } else {\n                        tokenString = '0'\n                    }\n                    returnItem['NFT Token Id'] = tokenString\n                    returnItem['txHash'] = txHash\n                    returnItem['explorerLink'] = `${networkExplorers[network]}/tx/${txHash}`\n                    returnItem['openseaLink'] = `${openseaExplorers[network]}/assets/${log.address}/${tokenString}`\n                }\n\n                this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n            }\n        })\n\n        this.providers[emitEventKey] = { provider, filter }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            const filter = this.providers[emitEventKey].filter\n            provider.off(filter)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: NFTMintTrigger }\n"
  },
  {
    "path": "packages/components/nodes/NFTTransferTrigger/NFTTransferTrigger.ts",
    "content": "import { ethers, utils } from 'ethers'\nimport { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport {\n    ArbitrumNetworks,\n    ETHNetworks,\n    OptimismNetworks,\n    PolygonNetworks,\n    networkExplorers,\n    openseaExplorers,\n    getNetworkProvidersList,\n    NETWORK,\n    getNetworkProvider,\n    NETWORK_PROVIDER,\n    eventTransferAbi,\n    erc1155SingleTransferAbi,\n    erc1155BatchTransferAbi,\n    networkProviderCredentials\n} from '../../src/ChainNetwork'\n\nclass NFTTransferTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'NFT Transfer Trigger'\n        this.name = 'NFTTransferTrigger'\n        this.icon = 'nfttransfer.png'\n        this.type = 'trigger'\n        this.category = 'NFT'\n        this.version = 1.0\n        this.description = 'Start workflow whenever a NFT transfer event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks],\n                default: 'homestead'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Token Standard',\n                name: 'tokenStandard',\n                type: 'options',\n                options: [\n                    {\n                        label: 'ERC-721',\n                        name: 'ERC721'\n                    },\n                    {\n                        label: 'ERC-1155',\n                        name: 'ERC1155'\n                    }\n                ],\n                default: 'ERC721'\n            },\n            {\n                label: 'Transfer Method',\n                name: 'tokenMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Single',\n                        name: 'single'\n                    },\n                    {\n                        label: 'Batch',\n                        name: 'batch'\n                    }\n                ],\n                default: 'single',\n                show: {\n                    'inputParameters.tokenStandard': ['ERC1155']\n                }\n            },\n            {\n                label: 'NFT Address',\n                name: 'nftAddress',\n                type: 'string',\n                default: '',\n                optional: true\n            },\n            {\n                label: 'Direction',\n                name: 'direction',\n                type: 'options',\n                options: [\n                    {\n                        label: 'From',\n                        name: 'from',\n                        description: 'Transfer from wallet address'\n                    },\n                    {\n                        label: 'To',\n                        name: 'to',\n                        description: 'Transfer to wallet address'\n                    },\n                    {\n                        label: 'Both From and To',\n                        name: 'fromTo',\n                        description: 'Transfer from a wallet address to another wallet address'\n                    }\n                ],\n                default: ''\n            },\n            {\n                label: 'From Wallet Address',\n                name: 'fromAddress',\n                type: 'string',\n                default: '',\n                show: {\n                    'inputParameters.direction': ['from', 'fromTo']\n                }\n            },\n            {\n                label: 'To Wallet Address',\n                name: 'toAddress',\n                type: 'string',\n                default: '',\n                show: {\n                    'inputParameters.direction': ['to', 'fromTo']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as NETWORK\n\n        const provider = await getNetworkProvider(\n            networksData.networkProvider as NETWORK_PROVIDER,\n            network,\n            credentials,\n            networksData.jsonRPC as string,\n            networksData.websocketRPC as string\n        )\n\n        if (!provider) throw new Error('Invalid Network Provider')\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const nftAddress = inputParametersData.nftAddress as string\n        const fromAddress = inputParametersData.fromAddress as string\n        const toAddress = inputParametersData.toAddress as string\n        const tokenStandard = inputParametersData.tokenStandard as string\n        const tokenMethod = inputParametersData.tokenMethod as string\n\n        let ifaceABI = eventTransferAbi\n        let topicId = utils.id('Transfer(address,address,uint256)')\n        if (tokenStandard === 'ERC1155' && tokenMethod === 'single') {\n            topicId = utils.id('TransferSingle(address,address,address,uint256,uint256)')\n            ifaceABI = erc1155SingleTransferAbi\n        }\n        if (tokenStandard === 'ERC1155' && tokenMethod === 'batch') {\n            topicId = utils.id('TransferBatch(address,address,address,uint256[],uint256[])')\n            ifaceABI = erc1155BatchTransferAbi\n        }\n\n        const filter = {\n            topics: [topicId, fromAddress ? utils.hexZeroPad(fromAddress, 32) : null, toAddress ? utils.hexZeroPad(toAddress, 32) : null]\n        }\n        if (nftAddress) (filter as any)['address'] = nftAddress\n\n        provider.on(filter, async (log: any) => {\n            const txHash = log.transactionHash\n            const iface = new ethers.utils.Interface(ifaceABI)\n            const logs = await provider.getLogs(filter)\n            const events = logs.map((log) => iface.parseLog(log))\n\n            const fromWallet = events.length ? events[0].args[tokenStandard === 'ERC1155' ? 1 : 0] : ''\n            const toWallet = events.length ? events[0].args[tokenStandard === 'ERC1155' ? 2 : 1] : ''\n\n            //ERC721 or ERC1155 has 4 topics length\n            if (log.topics.length === 4) {\n                const returnItem = {} as ICommonObject\n                returnItem['From Wallet'] = fromWallet\n                returnItem['To Wallet'] = toWallet\n                returnItem['NFT Token Address'] = log.address\n\n                if (openseaExplorers[network]) {\n                    let tokenString = ''\n                    const counter = log.topics[log.topics.length - 1]\n                    const strippedZeroCounter = utils.hexStripZeros(counter)\n                    if (strippedZeroCounter !== '0x') {\n                        const counterBigNubmer = ethers.BigNumber.from(strippedZeroCounter)\n                        tokenString = counterBigNubmer.toString()\n                    } else {\n                        tokenString = '0'\n                    }\n                    returnItem['NFT Token Id'] = tokenString\n                    returnItem['txHash'] = txHash\n                    returnItem['explorerLink'] = `${networkExplorers[network]}/tx/${txHash}`\n                    returnItem['openseaLink'] = `${openseaExplorers[network]}/assets/${log.address}/${tokenString}`\n                }\n\n                this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n            }\n        })\n\n        this.providers[emitEventKey] = { provider, filter }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider = this.providers[emitEventKey].provider\n            const filter = this.providers[emitEventKey].filter\n            provider.off(filter)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: NFTTransferTrigger }\n"
  },
  {
    "path": "packages/components/nodes/NodeJS/NodeJS.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport { NodeVM } from 'vm2'\n\nclass NodeJS implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'NodeJS'\n        this.name = 'nodeJS'\n        this.icon = 'nodejs.png'\n        this.type = 'action'\n        this.category = 'Development'\n        this.version = 1.0\n        this.description = 'Execute code within NodeVM sandbox'\n        this.incoming = 1\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'Code',\n                name: 'code',\n                type: 'code',\n                default: `console.info($nodeData);\\nconst example = 'Hello World!';\\nreturn example;`,\n                description: 'Custom code to run'\n            },\n            {\n                label: 'External Modules',\n                name: 'external',\n                type: 'json',\n                placeholder: '[\"axios\"]',\n                description: 'Import installed dependencies within Outerbridge',\n                optional: true\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const returnData: ICommonObject[] = []\n\n        // Global object\n        const sandbox = {\n            $nodeData: nodeData\n        }\n\n        const options = {\n            console: 'inherit',\n            sandbox,\n            require: {\n                external: false as boolean | { modules: string[] },\n                builtin: ['*']\n            }\n        } as any\n\n        const code = (inputParametersData.code as string) || ''\n        const external = inputParametersData.external as string\n        if (external) {\n            const deps = JSON.parse(external)\n            if (deps && deps.length) {\n                options.require.external = {\n                    modules: deps\n                }\n            }\n        }\n\n        const vm = new NodeVM(options)\n\n        let responseData: any // tslint:disable-line: no-any\n\n        try {\n            if (!code) responseData = []\n            else {\n                responseData = await vm.run(`module.exports = async function() {${code}}()`, __dirname)\n            }\n        } catch (e) {\n            return Promise.reject(e)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: NodeJS }\n"
  },
  {
    "path": "packages/components/nodes/Notion/Notion.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass Notion implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Notion'\n        this.name = 'notion'\n        this.icon = 'notion.svg'\n        this.type = 'action'\n        this.category = 'Productivity'\n        this.version = 1.0\n        this.description = 'Work with Notion page, database or block'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Target',\n                name: 'target',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Search all pages',\n                        name: 'searchTarget'\n                    },\n                    {\n                        label: 'Page',\n                        name: 'pageTarget'\n                    },\n                    {\n                        label: 'Database',\n                        name: 'databaseTarget'\n                    },\n                    {\n                        label: 'Block',\n                        name: 'blockTarget'\n                    }\n                ]\n            },\n            {\n                label: 'Page Operations',\n                name: 'pageOperation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Page',\n                        name: 'getPage'\n                    },\n                    {\n                        label: 'Create Page',\n                        name: 'createPage'\n                    },\n                    {\n                        label: 'Update Page',\n                        name: 'updatePage'\n                    },\n                    {\n                        label: 'Delete Page',\n                        name: 'deletePage'\n                    }\n                ],\n                default: 'Get Page',\n                show: {\n                    'actions.target': ['pageTarget']\n                }\n            },\n            {\n                label: 'Database Operations',\n                name: 'databaseOperation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Database',\n                        name: 'getDatabase'\n                    },\n                    {\n                        label: 'Create Database',\n                        name: 'createDatabase'\n                    },\n                    {\n                        label: 'Update Database',\n                        name: 'updateDatabase'\n                    },\n                    {\n                        label: 'Query Database',\n                        name: 'queryDatabase'\n                    }\n                ],\n                default: 'Get Database',\n                show: {\n                    'actions.target': ['databaseTarget']\n                }\n            },\n            {\n                label: 'Block Operations',\n                name: 'blockOperation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Block',\n                        name: 'getBlock'\n                    },\n                    {\n                        label: 'Update Block',\n                        name: 'updateBlock'\n                    },\n                    {\n                        label: 'Get Block Children',\n                        name: 'getBlockChildren'\n                    },\n                    {\n                        label: 'Append Block Children',\n                        name: 'appendBlockChildren'\n                    },\n                    {\n                        label: 'Delete Block',\n                        name: 'deleteBlock'\n                    }\n                ],\n                default: 'Get Block',\n                show: {\n                    'actions.target': ['blockTarget']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Notion Version',\n                name: 'notionVersion',\n                type: 'string',\n                optional: false,\n                description: 'Notion Version to be used in the Headers of the request',\n                default: '2022-06-28'\n            },\n            {\n                label: 'Page ID',\n                name: 'pageID',\n                type: 'string',\n                optional: false,\n                description: 'ID of the page',\n                show: {\n                    'actions.pageOperation': ['getPage', 'updatePage', 'deletePage']\n                }\n            },\n            {\n                label: 'Database ID',\n                name: 'databaseID',\n                type: 'string',\n                optional: false,\n                description: 'ID of the database',\n                show: {\n                    'actions.databaseOperation': ['getDatabase', 'updateDatabase', 'queryDatabase']\n                }\n            },\n            {\n                label: 'Block ID',\n                name: 'blockID',\n                type: 'string',\n                optional: false,\n                description: 'ID of the block',\n                show: {\n                    'actions.blockOperation': ['getBlock', 'updateBlock', 'deleteBlock', 'appendBlockChildren', 'getBlockChildren']\n                }\n            },\n            {\n                label: 'Query String',\n                name: 'queryString',\n                type: 'string',\n                optional: false,\n                description: 'Name of the page (with your Notion Integration) to be searched',\n                show: {\n                    'actions.target': ['searchTarget']\n                }\n            },\n            {\n                label: 'Body',\n                name: 'pageBody',\n                type: 'json',\n                description:\n                    'Refer to the <a target\"_blank\" href=\"https://developers.notion.com/reference/page\">Notion API docs</a> on how to structure the request body for page',\n                show: {\n                    'actions.pageOperation': ['createPage', 'updatePage']\n                },\n                placeholder: `{\n            \"parent\": {\n                \"database_id\": \"{{databaseID}}\"\n            },\n            \"properties\": {\n                \"Name\": {\n                    \"title\": [\n                        {\n                            \"text\": { \"content\": \"New Media Article\" }\n                        }\n                    ]\n                }\n            }\n        }`,\n                optional: false\n            },\n            {\n                label: 'Body',\n                name: 'databaseBody',\n                type: 'json',\n                description:\n                    'Refer to the <a target=\"_blank\" href=\"https://developers.notion.com/reference/database\">Notion API docs</a> on how to structure the request body for database',\n                show: {\n                    'actions.databaseOperation': ['createDatabase', 'updateDatabase', 'queryDatabase']\n                },\n                placeholder: `{\n            \"parent\": {\n                \"type\": \"page_id\",\n                \"page_id\": \"{{pageID}}\"\n            },\n            \"title\": [\n                {\n                    \"type\": \"text\",\n                    \"text\": { \"content\": \"Grocery List\", } }\n            ],\n            \"properties\": {\n                \"Name\": {\n                    \"title\": {}\n                }\n            }\n        }`,\n                optional: false\n            },\n            {\n                label: 'Body',\n                name: 'blockBody',\n                type: 'json',\n                description:\n                    'Refer to the <a target=\"_blank\" href=\"https://developers.notion.com/reference/block\">Notion API docs</> on how to structure the request body for block',\n                show: {\n                    'actions.blockOperation': ['updateBlock', 'appendBlockChildren']\n                },\n                placeholder: `{\n            \"paragraph\": {\n                \"rich_text\": [{\n                    \"type\": \"text\", \n                    \"text\": { \"content\": \"hello to you\"}\n                }]\n            }\n        }`,\n                optional: false\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Notion Integration Token',\n                        name: 'notionApi'\n                    }\n                ],\n                default: 'notionApi'\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionsData = nodeData.actions\n        const inputParameters = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionsData === undefined || credentials === undefined || inputParameters === undefined) {\n            throw new Error('Required information missing')\n        }\n\n        const pageId = inputParameters.pageID\n        const databaseId = inputParameters.databaseID\n        const blockId = inputParameters.blockID\n\n        const returnData: ICommonObject[] = []\n\n        const pageOperation = actionsData.pageOperation as string\n        const databaseOperation = actionsData.databaseOperation as string\n        const blockOperation = actionsData.blockOperation as string\n\n        const target = actionsData.target as string\n        const bearerToken = credentials.integrationToken as string\n\n        //Request Body\n        const pageBody = inputParameters.pageBody as string\n        const databaseBody = inputParameters.databaseBody as string\n        const blockBody = inputParameters.blockBody as string\n\n        //Notion-Version for Headers\n        const notionVersion = inputParameters.notionVersion as string\n\n        //Search Data\n        const queryString = inputParameters.queryString as string\n\n        let responseData: any\n        let requestPageBody,\n            requestDatabaseBody,\n            requestBlockBody: any = {}\n\n        //JSON input params body\n        if (pageBody) {\n            requestPageBody = JSON.parse(pageBody.replace(/\\s/g, ' '))\n        }\n        if (databaseBody) {\n            requestDatabaseBody = JSON.parse(databaseBody.replace(/\\s/g, ' '))\n        }\n        if (blockBody) {\n            requestBlockBody = JSON.parse(blockBody.replace(/\\s/g, ' '))\n        }\n\n        //Search Endpoint\n        if (target === 'searchTarget') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `https://api.notion.com/v1/search`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    },\n                    data: {\n                        query: `${queryString}`,\n                        sort: {\n                            direction: 'ascending',\n                            timestamp: 'last_edited_time'\n                        }\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        //Page Endpoints\n\n        if (pageOperation === 'getPage') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.notion.com/v1/pages/${pageId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (pageOperation === 'createPage') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `https://api.notion.com/v1/pages`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestPageBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (pageOperation === 'updatePage') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'PATCH' as Method,\n                    url: `https://api.notion.com/v1/pages/${pageId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestPageBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (pageOperation === 'deletePage') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'DELETE' as Method,\n                    url: `https://api.notion.com/v1/blocks/${pageId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        //Database Endpoints\n        if (databaseOperation === 'getDatabase') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.notion.com/v1/databases/${databaseId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (databaseOperation === 'createDatabase') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `https://api.notion.com/v1/databases`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestDatabaseBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (databaseOperation === 'updateDatabase') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'PATCH' as Method,\n                    url: `https://api.notion.com/v1/databases/${databaseId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestDatabaseBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (databaseOperation === 'queryDatabase') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `https://api.notion.com/v1/databases/${databaseId}/query`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestDatabaseBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        //Block Endpoints\n        if (blockOperation === 'getBlock') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.notion.com/v1/blocks/${blockId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (blockOperation === 'updateBlock') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'PATCH' as Method,\n                    url: `https://api.notion.com/v1/blocks/${blockId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestBlockBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (blockOperation === 'getBlockChildren') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.notion.com/v1/blocks/${blockId}/children`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (blockOperation === 'appendBlockChildren') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'PATCH' as Method,\n                    url: `https://api.notion.com/v1/blocks/${blockId}/children`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                axiosConfig.data = requestBlockBody\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (blockOperation === 'deleteBlock') {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'DELETE' as Method,\n                    url: `https://api.notion.com/v1/blocks/${blockId}`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`,\n                        'Notion-Version': `${notionVersion}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Notion }\n"
  },
  {
    "path": "packages/components/nodes/OpenAI/OpenAI.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass OpenAI implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'OpenAI'\n        this.name = 'openAI'\n        this.icon = 'openai.png'\n        this.type = 'action'\n        this.category = 'Artificial Intelligence'\n        this.version = 1.0\n        this.description = 'ChatGPT, image generation or text completion from prompt via OpenAI API'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'ChatGPT',\n                        name: 'chatgpt'\n                    },\n                    {\n                        label: 'Generate Image via text prompt',\n                        name: 'generateImage'\n                    },\n                    {\n                        label: 'Generate Text Completion via text prompt',\n                        name: 'textCompletion'\n                    }\n                ]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'OpenAI API Key',\n                        name: 'openAIApi'\n                    }\n                ],\n                default: 'openAIApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Model',\n                name: 'model',\n                type: 'asyncOptions',\n                loadMethod: 'listModels',\n                description: 'AI model to use.',\n                default: 'text-davinci-003',\n                show: {\n                    'actions.operation': ['textCompletion']\n                }\n            },\n            {\n                label: 'Model',\n                name: 'model',\n                type: 'options',\n                options: [\n                    {\n                        label: 'gpt-3.5-turbo',\n                        name: 'gpt-3.5-turbo'\n                    },\n                    {\n                        label: 'gpt-3.5-turbo-0301',\n                        name: 'gpt-3.5-turbo-0301'\n                    }\n                ],\n                description: 'ChatGPT model to use.',\n                default: 'gpt-3.5-turbo',\n                show: {\n                    'actions.operation': ['chatgpt']\n                }\n            },\n            {\n                label: 'Text Prompt',\n                name: 'prompt',\n                type: 'string',\n                default: '',\n                placeholder: 'Write me a 250 words essay on fish',\n                description: 'The prompt for chatpgt',\n                show: {\n                    'actions.operation': ['chatgpt']\n                }\n            },\n            {\n                label: 'Text Prompt',\n                name: 'prompt',\n                type: 'string',\n                default: '',\n                placeholder: 'Say this is a test',\n                description: 'The prompt to generate completions for',\n                show: {\n                    'actions.operation': ['textCompletion']\n                }\n            },\n            {\n                label: 'Image Description',\n                name: 'prompt',\n                type: 'string',\n                default: '',\n                placeholder: 'Photograph of an astronaut riding a horse',\n                description: 'Description of the image you want to generated. The maximum length is 1000 characters.',\n                show: {\n                    'actions.operation': ['generateImage']\n                }\n            },\n            {\n                label: 'Image Number',\n                name: 'imageNumber',\n                type: 'number',\n                placeholder: '1',\n                default: '1',\n                description: 'The number of images to generate. Must be between 1 and 10.',\n                optional: true,\n                show: {\n                    'actions.operation': ['generateImage']\n                }\n            },\n            {\n                label: 'Image Size',\n                name: 'imageSize',\n                type: 'options',\n                description: 'The size of the generated images.',\n                options: [\n                    {\n                        label: '256x256',\n                        name: '256x256'\n                    },\n                    {\n                        label: '512x512',\n                        name: '512x512'\n                    },\n                    {\n                        label: '1024x1024',\n                        name: '1024x1024'\n                    }\n                ],\n                optional: true,\n                default: '1024x1024',\n                show: {\n                    'actions.operation': ['generateImage']\n                }\n            },\n            {\n                label: 'Response Format',\n                name: 'response_format',\n                type: 'options',\n                description: 'The format in which the generated images are returned.',\n                options: [\n                    {\n                        label: 'url',\n                        name: 'url'\n                    },\n                    {\n                        label: 'b64_json',\n                        name: 'b64_json'\n                    }\n                ],\n                default: 'url',\n                optional: true,\n                show: {\n                    'actions.operation': ['generateImage']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async listModels(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) return returnData\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.openai.com/v1/models`,\n                    headers: {\n                        'Content-Type': 'application/json; charset=utf-8',\n                        Authorization: `Bearer ${credentials!.apiKey}`\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                const models = responseData.data\n\n                for (let i = 0; i < models.length; i += 1) {\n                    const data = {\n                        label: models[i].id,\n                        name: models[i].id\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n        const actionssData = nodeData.actions\n\n        if (inputParametersData === undefined || actionssData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credential')\n        }\n\n        const returnData: ICommonObject[] = []\n        const operation = actionssData.operation as string\n        const model = inputParametersData.model as string\n        const prompt = inputParametersData.prompt as string\n        const imageNumber = inputParametersData.imageNumber as string\n        const imageSize = inputParametersData.imageSize as string\n        const response_format = inputParametersData.response_format as string\n\n        let responseData: any\n        let url = ''\n        switch (operation) {\n            case 'generateImage':\n                url = 'https://api.openai.com/v1/images/generations'\n                break\n            case 'textCompletion':\n                url = 'https://api.openai.com/v1/completions'\n                break\n            case 'chatgpt':\n                url = 'https://api.openai.com/v1/chat/completions'\n                break\n        }\n        const data = { prompt } as any\n        if (imageNumber) data.n = parseInt(imageNumber, 10)\n        if (imageSize) data.size = imageSize\n        if (response_format) data.response_format = response_format\n        if (model) data.model = model\n\n        if (operation === 'chatgpt') {\n            delete data.prompt\n            data.temperature = 0.8\n            data.top_p = 1.0\n            data.presence_penalty = 1.0\n            data.messages = [\n                {\n                    role: 'system',\n                    content: `You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible. Current date: ${\n                        new Date().toISOString().split('T')[0]\n                    }`\n                },\n                { role: 'user', content: prompt }\n            ]\n        }\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'POST' as Method,\n                url,\n                data,\n                headers: {\n                    'Content-Type': 'application/json; charset=utf-8',\n                    Authorization: `Bearer ${credentials!.apiKey}`\n                }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: OpenAI }\n"
  },
  {
    "path": "packages/components/nodes/Opensea/Opensea.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport {\n    retrieveACollection,\n    retrieveAContract,\n    retrieveAnAsset,\n    retrieveAssets,\n    retrieveBundles,\n    retrieveCollections,\n    retrieveEvents\n} from './extendedParameters'\n\nclass OpenSea implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'OpenSea'\n        this.name = 'opensea'\n        this.icon = 'opensea.svg'\n        this.type = 'action'\n        this.category = 'NFT'\n        this.version = 1.0\n        this.description = 'Query OpenSea information'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Retrieve Assets',\n                        name: 'retrieveAssets',\n                        description: 'Returns a set of NFTs.'\n                    },\n                    {\n                        label: 'Retrieve Events',\n                        name: 'retrieveEvents',\n                        description: 'Returns a list of Events.'\n                    },\n                    {\n                        label: 'Retrieve Collections',\n                        name: 'retrieveCollections',\n                        description: 'Returns a list of collections supported and vetted by OpenSea.'\n                    },\n                    {\n                        label: 'Retrieve Bundles',\n                        name: 'retrieveBundles',\n                        description: 'Returns a list of bundles. Bundles are groups of NFTs for sale on OpenSea.'\n                    },\n                    {\n                        label: 'Retrieve An Asset',\n                        name: 'retrieveAnAsset',\n                        description: 'Fetch information about a single NFT, based on its contract address and token ID.'\n                    },\n                    {\n                        label: 'Retrieve A Contract',\n                        name: 'retrieveAContract',\n                        description: 'Fetch detailed information about a specified contract.'\n                    },\n                    {\n                        label: 'Retrieve A Collection',\n                        name: 'retrieveACollection',\n                        description:\n                            'Retrieve more in-depth information about an individual collection, including real time statistics such as floor price.'\n                    },\n                    {\n                        label: 'Retrieve Collection Stats',\n                        name: 'retrieveCollectionStats',\n                        description: 'Fetch stats for a specific collection, including real-time floor price data.'\n                    }\n                ],\n                default: ''\n            },\n            {\n                label: 'Event Type',\n                name: 'event_type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Auctions',\n                        name: 'created'\n                    },\n                    {\n                        label: 'Sales',\n                        name: 'successful'\n                    },\n                    {\n                        label: 'Transfer',\n                        name: 'transfer'\n                    },\n                    {\n                        label: 'Approve',\n                        name: 'approve'\n                    },\n                    {\n                        label: 'Bid Entered',\n                        name: 'bid_entered'\n                    },\n                    {\n                        label: 'Bid Withdrawn',\n                        name: 'bid_withdrawn'\n                    },\n                    {\n                        label: 'Cancelled',\n                        name: 'cancelled'\n                    },\n                    {\n                        label: 'All Events',\n                        name: 'all'\n                    }\n                ],\n                default: '',\n                description: 'The event type to filter',\n                show: {\n                    'actions.operation': ['retrieveEvents']\n                }\n            },\n            {\n                label: 'Auction Type',\n                name: 'auction_type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Sell to the highest bidder',\n                        name: 'english',\n                        description: 'The highest bid wins at the end'\n                    },\n                    {\n                        label: 'Sell with a declining price',\n                        name: 'dutch',\n                        description: 'The price falls until someone purchases the item'\n                    },\n                    {\n                        label: 'CryptoPunks Auctions',\n                        name: 'min-price'\n                    }\n                ],\n                default: '',\n                optional: true,\n                description: 'Filter by an auction type',\n                show: {\n                    'actions.operation': ['retrieveEvents'],\n                    'actions.event_type': ['created']\n                }\n            },\n            {\n                label: 'Environment',\n                name: 'environment',\n                type: 'options',\n                description: 'Environment to execute operation: Test or Main',\n                options: [\n                    {\n                        label: 'TEST',\n                        name: 'https://testnets-api.opensea.io/api/v1',\n                        description: 'Testnet: https://testnets.opensea.io/'\n                    },\n                    {\n                        label: 'MAIN',\n                        name: 'https://api.opensea.io/api/v1',\n                        description: 'Mainnet: https://opensea.io/'\n                    }\n                ],\n                default: ''\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'OpenSea API Key',\n                        name: 'openSeaApi',\n                        description: 'How to get API key: https://docs.opensea.io/reference/request-an-api-key'\n                    }\n                ],\n                default: 'openSeaApi',\n                show: {\n                    'actions.environment': ['https://api.opensea.io/api/v1']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            ...retrieveAssets,\n            ...retrieveEvents,\n            ...retrieveCollections,\n            ...retrieveBundles,\n            ...retrieveAnAsset,\n            ...retrieveAContract,\n            ...retrieveACollection\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const actionData = nodeData.actions\n\n        if (actionData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const operation = actionData.operation as string\n        const baseURL = actionData.environment as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        const method: Method = 'GET'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json'\n        }\n\n        if (baseURL === 'https://api.opensea.io/api/v1') {\n            // Mainnet\n            const credentials = nodeData.credentials\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n            const apiKey = credentials!.apiKey as string\n            headers['X-API-KEY'] = apiKey\n        }\n\n        try {\n            if (operation === 'retrieveAssets') {\n                url = `${baseURL}/assets`\n\n                const owner = inputParametersData?.owner as string\n                if (owner) queryParameters['owner'] = owner\n\n                const token_ids_string = inputParametersData?.token_ids as string\n                if (token_ids_string) {\n                    const token_ids = JSON.parse(token_ids_string.replace(/\\s/g, ''))\n                    if (token_ids.length) queryParameters['token_ids'] = token_ids\n                }\n\n                const asset_contract_address = inputParametersData?.asset_contract_address as string\n                if (asset_contract_address) queryParameters['asset_contract_address'] = asset_contract_address\n\n                const asset_contract_addresses_string = inputParametersData?.asset_contract_addresses as string\n                if (asset_contract_addresses_string) {\n                    const asset_contract_addresses = JSON.parse(asset_contract_addresses_string.replace(/\\s/g, ''))\n                    if (asset_contract_addresses.length) queryParameters['asset_contract_addresses'] = asset_contract_addresses\n                }\n\n                const order_by = inputParametersData?.order_by as string\n                if (order_by) queryParameters['order_by'] = order_by\n\n                const order_direction = inputParametersData?.order_direction as string\n                if (order_direction) queryParameters['order_direction'] = order_direction\n\n                const offset = inputParametersData?.offset as number\n                if (offset) queryParameters['offset'] = offset\n\n                const limit = inputParametersData?.limit as number\n                if (limit) queryParameters['limit'] = limit\n\n                const collection = inputParametersData?.collection as string\n                if (collection) queryParameters['collection'] = collection\n\n                const include_orders = inputParametersData?.include_orders as boolean\n                if (include_orders) queryParameters['include_orders'] = include_orders\n            } else if (operation === 'retrieveEvents') {\n                url = `${baseURL}/events`\n\n                const event_type = actionData.event_type as string\n                if (event_type && event_type !== 'all') queryParameters['event_type'] = event_type\n\n                const auction_type = actionData.auction_type as string\n                if (auction_type) queryParameters['auction_type'] = auction_type\n\n                const asset_contract_address = inputParametersData?.asset_contract_address as string\n                if (asset_contract_address) queryParameters['asset_contract_address'] = asset_contract_address\n\n                const collection_slug = inputParametersData?.collection_slug as string\n                if (collection_slug) queryParameters['collection_slug'] = collection_slug\n\n                const token_id = inputParametersData?.token_id as number\n                if (token_id) queryParameters['token_id'] = token_id\n\n                const account_address = inputParametersData?.account_address as string\n                if (account_address) queryParameters['account_address'] = account_address\n\n                const only_opensea = inputParametersData?.only_opensea as boolean\n                if (only_opensea) queryParameters['only_opensea'] = only_opensea\n\n                const offset = inputParametersData?.offset as number\n                if (offset) queryParameters['offset'] = offset\n\n                const limit = inputParametersData?.limit as number\n                if (limit) queryParameters['limit'] = limit\n\n                const occurred_before = Date.parse(inputParametersData?.occurred_before as string)\n                if (occurred_before) queryParameters['occurred_before'] = occurred_before\n\n                const occurred_after = Date.parse(inputParametersData?.occurred_after as string)\n                if (occurred_after) queryParameters['occurred_after'] = occurred_after\n            } else if (operation === 'retrieveCollections') {\n                url = `${baseURL}/collections`\n\n                const asset_owner = inputParametersData?.asset_owner as string\n                if (asset_owner) queryParameters['asset_owner'] = asset_owner\n\n                const offset = inputParametersData?.offset as number\n                if (offset) queryParameters['offset'] = offset\n\n                const limit = inputParametersData?.limit as number\n                if (limit) queryParameters['limit'] = limit\n            } else if (operation === 'retrieveBundles') {\n                url = `${baseURL}/bundles`\n\n                const on_sale = inputParametersData?.on_sale as boolean\n                if (on_sale) queryParameters['on_sale'] = on_sale\n\n                const owner = inputParametersData?.owner as string\n                if (owner) queryParameters['owner'] = owner\n\n                const token_ids_string = inputParametersData?.token_ids as string\n                if (token_ids_string) {\n                    const token_ids = JSON.parse(token_ids_string.replace(/\\s/g, ''))\n                    if (token_ids.length) queryParameters['token_ids'] = token_ids\n                }\n\n                const asset_contract_address = inputParametersData?.asset_contract_address as string\n                if (asset_contract_address) queryParameters['asset_contract_address'] = asset_contract_address\n\n                const asset_contract_addresses_string = inputParametersData?.asset_contract_addresses as string\n                if (asset_contract_addresses_string) {\n                    const asset_contract_addresses = JSON.parse(asset_contract_addresses_string.replace(/\\s/g, ''))\n                    if (asset_contract_addresses.length) queryParameters['asset_contract_addresses'] = asset_contract_addresses\n                }\n\n                const offset = inputParametersData?.offset as number\n                if (offset) queryParameters['offset'] = offset\n\n                const limit = inputParametersData?.limit as number\n                if (limit) queryParameters['limit'] = limit\n            } else if (operation === 'retrieveAnAsset') {\n                const token_id = inputParametersData!.token_id as string\n                const asset_contract_address = inputParametersData!.asset_contract_address as string\n\n                url = `${baseURL}/asset/${asset_contract_address}/${token_id}`\n            } else if (operation === 'retrieveAContract') {\n                const asset_contract_address = inputParametersData!.asset_contract_address as string\n\n                url = `${baseURL}/asset_contract/${asset_contract_address}`\n            } else if (operation === 'retrieveACollection') {\n                const collection_slug = inputParametersData!.collection_slug as string\n\n                url = `${baseURL}/collection/${collection_slug}`\n            } else if (operation === 'retrieveCollectionStats') {\n                const collection_slug = inputParametersData!.collection_slug as string\n\n                url = `${baseURL}/collection/${collection_slug}/stats`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers\n            }\n\n            if (Object.keys(queryParameters).length > 0) {\n                axiosConfig.params = queryParameters\n                axiosConfig.paramsSerializer = (params) => serializeQueryParams(params, true)\n            }\n\n            const response = await axios(axiosConfig)\n\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: OpenSea }\n"
  },
  {
    "path": "packages/components/nodes/Opensea/OpenseaEventTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { ICommonObject, ICronJobs, INode, INodeData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport EventEmitter from 'events'\nimport { eventsTriggerParams } from './extendedParameters'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\n\nclass OpenSeaEventTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'OpenSea Event Trigger'\n        this.name = 'openSeaEventTrigger'\n        this.icon = 'opensea.svg'\n        this.type = 'trigger'\n        this.category = 'NFT'\n        this.version = 1.0\n        this.description = 'Start workflow whenever OpenSea event occurs'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.actions = [\n            {\n                label: 'Event Type',\n                name: 'event_type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'New Auctions',\n                        name: 'created'\n                    },\n                    {\n                        label: 'New Sales',\n                        name: 'successful'\n                    },\n                    {\n                        label: 'New Transfer',\n                        name: 'transfer'\n                    },\n                    {\n                        label: 'New Approve',\n                        name: 'approve'\n                    },\n                    {\n                        label: 'New Bid Entered',\n                        name: 'bid_entered'\n                    },\n                    {\n                        label: 'Bid Withdrawn',\n                        name: 'bid_withdrawn'\n                    },\n                    {\n                        label: 'Cancelled',\n                        name: 'cancelled'\n                    }\n                ],\n                default: '',\n                description: 'The event type to filter'\n            },\n            {\n                label: 'Auction Type',\n                name: 'auction_type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Sell to the highest bidder',\n                        name: 'english',\n                        description: 'The highest bid wins at the end'\n                    },\n                    {\n                        label: 'Sell with a declining price',\n                        name: 'dutch',\n                        description: 'The price falls until someone purchases the item'\n                    },\n                    {\n                        label: 'CryptoPunks Auctions',\n                        name: 'min-price'\n                    }\n                ],\n                default: '',\n                optional: true,\n                description: 'Filter by an auction type',\n                show: {\n                    'actions.event_type': ['created']\n                }\n            },\n            {\n                label: 'Environment',\n                name: 'environment',\n                type: 'options',\n                description: 'Environment to listen to event: Test or Main',\n                options: [\n                    {\n                        label: 'TEST',\n                        name: 'https://testnets-api.opensea.io/api/v1',\n                        description: 'Testnet: https://testnets.opensea.io/'\n                    },\n                    {\n                        label: 'MAIN',\n                        name: 'https://api.opensea.io/api/v1',\n                        description: 'Mainnet: https://opensea.io/'\n                    }\n                ],\n                default: ''\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'OpenSea API Key',\n                        name: 'openSeaApi',\n                        description: 'How to get API key: https://docs.opensea.io/reference/request-an-api-key'\n                    }\n                ],\n                default: 'openSeaApi',\n                show: {\n                    'actions.environment': ['https://api.opensea.io/api/v1']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            ...eventsTriggerParams,\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                description: 'How often should we keep checking the event',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const inputParametersData = nodeData.inputParameters\n        const actionData = nodeData.actions\n\n        if (actionData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const baseURL = actionData.environment as string\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        const method: Method = 'GET'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json'\n        }\n\n        if (baseURL === 'https://api.opensea.io/api/v1') {\n            // Mainnet\n            const credentials = nodeData.credentials\n            if (credentials === undefined) {\n                throw new Error('Missing credentials')\n            }\n            const apiKey = credentials!.apiKey as string\n            headers['X-API-KEY'] = apiKey\n        }\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const pollTime = (inputParametersData?.pollTime as string) || '30s'\n        const cronTimes: string[] = []\n\n        if (pollTime === '15s') {\n            cronTimes.push(`*/15 * * * * *`)\n        } else if (pollTime === '30s') {\n            cronTimes.push(`*/30 * * * * *`)\n        } else if (pollTime === '1min') {\n            cronTimes.push(`*/1 * * * *`)\n        } else if (pollTime === '5min') {\n            cronTimes.push(`*/5 * * * *`)\n        } else if (pollTime === '10min') {\n            cronTimes.push(`*/10 * * * *`)\n        }\n\n        function mapEventName(event: string) {\n            switch (event) {\n                case 'created':\n                    return 'New Auctions'\n                case 'successful':\n                    return 'New Sales'\n                case 'transfer':\n                    return 'New Transfer'\n                case 'approve':\n                    return 'New Approve'\n                case 'bid_entered':\n                    return 'New Bid Entered'\n                case 'bid_withdrawn':\n                    return 'Bid Withdrawn'\n                case 'cancelled':\n                    return 'Cancelled'\n                default:\n                    return ''\n            }\n        }\n\n        async function executeEventAPI(lastEventTimestamp?: any) {\n            // Get initial data\n            url = `${baseURL}/events`\n\n            const event_type = actionData?.event_type as string\n            if (event_type) queryParameters['event_type'] = event_type\n\n            const auction_type = actionData?.auction_type as string\n            if (auction_type) queryParameters['auction_type'] = auction_type\n\n            const asset_contract_address = inputParametersData?.asset_contract_address as string\n            if (asset_contract_address) queryParameters['asset_contract_address'] = asset_contract_address\n\n            const collection_slug = inputParametersData?.collection_slug as string\n            if (collection_slug) queryParameters['collection_slug'] = collection_slug\n\n            const token_id = inputParametersData?.token_id as number\n            if (token_id) queryParameters['token_id'] = token_id\n\n            const account_address = inputParametersData?.account_address as string\n            if (account_address) queryParameters['account_address'] = account_address\n\n            const only_opensea = inputParametersData?.only_opensea as boolean\n            if (only_opensea) queryParameters['only_opensea'] = only_opensea\n\n            if (lastEventTimestamp) queryParameters['occurred_after'] = lastEventTimestamp\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers\n            }\n\n            if (Object.keys(queryParameters).length > 0) {\n                axiosConfig.params = queryParameters\n                axiosConfig.paramsSerializer = (params) => serializeQueryParams(params, true)\n            }\n\n            return await axios(axiosConfig)\n        }\n\n        try {\n            // Get initial data\n\n            let lastEventTimestamp: any\n\n            const response = await executeEventAPI()\n            const responseData = response.data\n\n            if (responseData.asset_events && responseData.asset_events.length)\n                lastEventTimestamp = responseData.asset_events[0].event_timestamp\n\n            // Trigger when cron job hits\n            const executeTrigger = async () => {\n                const newResponse = await executeEventAPI(lastEventTimestamp)\n                const newResponseData = newResponse.data\n\n                if (newResponseData.asset_events && newResponseData.asset_events.length) {\n                    lastEventTimestamp = newResponseData.asset_events[0].event_timestamp\n\n                    const returnItem = {\n                        event: mapEventName(actionData?.event_type as string),\n                        eventData: newResponseData\n                    }\n                    this.emit(emitEventKey, returnNodeExecutionData(returnItem))\n                }\n            }\n\n            // Start the cron-jobs\n            if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n                for (const cronTime of cronTimes) {\n                    // Automatically start the cron job\n                    this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n                }\n            } else {\n                for (const cronTime of cronTimes) {\n                    // Automatically start the cron job\n                    this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n                }\n            }\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: OpenSeaEventTrigger }\n"
  },
  {
    "path": "packages/components/nodes/Opensea/extendedParameters.ts",
    "content": "import { INodeParams } from '../../src'\n\nexport const retrieveAssets = [\n    {\n        label: 'Owner',\n        name: 'owner',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: 'The address of the owner of the assets',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Token Ids',\n        name: 'token_ids',\n        type: 'json',\n        placeholder: '[\"1\", \"2\"]',\n        optional: true,\n        description: 'An array of token IDs to search for',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Asset Contract Address',\n        name: 'asset_contract_address',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: 'The NFT contract address for the assets',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Asset Contract Addresses',\n        name: 'asset_contract_addresses',\n        type: 'json',\n        placeholder: '[\"0xa\", \"0xb\"]',\n        optional: true,\n        description: 'An array of contract addresses to search for',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Order By',\n        name: 'order_by',\n        type: 'options',\n        options: [\n            {\n                label: 'Sale Date',\n                name: 'sale_date',\n                description: `Last sale's transaction's timestamp`\n            },\n            {\n                label: 'Sale Count ',\n                name: 'sale_count',\n                description: `Number of sales`\n            },\n            {\n                label: 'Sale Price ',\n                name: 'sale_price',\n                description: `Last sale's total_price`\n            }\n        ],\n        default: '',\n        optional: true,\n        description: 'How to order the assets returned. By default, the API returns the fastest ordering. ',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Order Direction',\n        name: 'order_direction',\n        type: 'options',\n        options: [\n            {\n                label: 'Descending',\n                name: 'desc'\n            },\n            {\n                label: 'Ascending',\n                name: 'asc'\n            }\n        ],\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Offset',\n        name: 'offset',\n        type: 'number',\n        default: '',\n        optional: true,\n        description: 'Offset for pagination',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        default: 20,\n        optional: true,\n        description: 'Limit for pagination. Defaults to 20, capped at 50.',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Collection',\n        name: 'collection',\n        type: 'string',\n        default: '',\n        optional: true,\n        description:\n            'Limit responses to members of a collection. Case sensitive and must match the collection slug exactly. Will return all assets from all contracts in a collection.',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    },\n    {\n        label: 'Include Orders',\n        name: 'include_orders',\n        type: 'boolean',\n        default: false,\n        optional: true,\n        description: 'A flag determining if order information should be included in the response.',\n        show: {\n            'actions.operation': ['retrieveAssets']\n        }\n    }\n] as INodeParams[]\n\nexport const retrieveEvents = [\n    {\n        label: 'Asset Contract Address',\n        name: 'asset_contract_address',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: 'The NFT contract address for the assets for which to show events',\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Collection Slug',\n        name: 'collection_slug',\n        type: 'string',\n        default: '',\n        optional: true,\n        description:\n            'Events from a collection. For instance: if a collection url is: https://opensea.io/collection/my-collection-1, collection slug is my-collection-1.',\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'token_id',\n        type: 'number',\n        default: '',\n        optional: true,\n        description: `The token's id to optionally filter by`,\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Account Address',\n        name: 'account_address',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: `A user account's wallet address to filter for events on an account`,\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Only Opensea',\n        name: 'only_opensea',\n        type: 'boolean',\n        default: false,\n        optional: true,\n        description: 'Restrict to events on OpenSea auctions',\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Offset',\n        name: 'offset',\n        type: 'number',\n        default: '',\n        description: 'Offset for pagination',\n        optional: true,\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        default: 20,\n        optional: true,\n        description: 'Limit for pagination',\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Occurred Before',\n        name: 'occurred_before',\n        type: 'date',\n        optional: true,\n        description: 'Only show events listed before this date.',\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    },\n    {\n        label: 'Occurred After',\n        name: 'occurred_after',\n        type: 'date',\n        optional: true,\n        description: 'Only show events listed after this date.',\n        show: {\n            'actions.operation': ['retrieveEvents']\n        }\n    }\n] as INodeParams[]\n\nexport const retrieveCollections = [\n    {\n        label: 'Asset Owner',\n        name: 'asset_owner',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: `A wallet address. If specified, will return collections where the owner owns at least one asset belonging to smart contracts in the collection.`,\n        show: {\n            'actions.operation': ['retrieveCollections']\n        }\n    },\n    {\n        label: 'Offset',\n        name: 'offset',\n        type: 'number',\n        default: '',\n        description: 'Offset for pagination',\n        optional: true,\n        show: {\n            'actions.operation': ['retrieveCollections']\n        }\n    },\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        default: 20,\n        optional: true,\n        description: 'Limit for pagination',\n        show: {\n            'actions.operation': ['retrieveCollections']\n        }\n    }\n] as INodeParams[]\n\nexport const retrieveBundles = [\n    {\n        label: 'On Sale',\n        name: 'on_sale',\n        type: 'boolean',\n        default: false,\n        optional: true,\n        description: `If set to true, only show bundles currently on sale. If set to false, only show bundles that have been sold or cancelled.`,\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    },\n    {\n        label: 'Owner',\n        name: 'owner',\n        type: 'string',\n        default: '',\n        description: 'Account address of the owner of a bundle (and all assets within)',\n        optional: true,\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    },\n    {\n        label: 'Asset Contract Address',\n        name: 'asset_contract_address',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: 'Contract address of all the assets in a bundle. Only works for homogenous bundles, not mixed ones.',\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    },\n    {\n        label: 'Asset Contract Addresses',\n        name: 'asset_contract_addresses',\n        type: 'json',\n        placeholder: '[\"0xa\", \"0xb\"]',\n        optional: true,\n        description: 'An array of contract addresses to search for',\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    },\n    {\n        label: 'Token Ids',\n        name: 'token_ids',\n        type: 'json',\n        placeholder: '[\"1\", \"2\"]',\n        optional: true,\n        description: 'A list of token IDs for showing only bundles with at least one of the token IDs in the list',\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    },\n    {\n        label: 'Offset',\n        name: 'offset',\n        type: 'number',\n        default: '',\n        description: 'Offset for pagination',\n        optional: true,\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    },\n    {\n        label: 'Limit',\n        name: 'limit',\n        type: 'number',\n        default: 20,\n        optional: true,\n        description: 'Limit for pagination',\n        show: {\n            'actions.operation': ['retrieveBundles']\n        }\n    }\n] as INodeParams[]\n\nexport const retrieveAnAsset = [\n    {\n        label: 'Asset Contract Address',\n        name: 'asset_contract_address',\n        type: 'string',\n        default: '',\n        description: 'The NFT contract address for the assets',\n        show: {\n            'actions.operation': ['retrieveAnAsset']\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'token_id',\n        type: 'string',\n        default: '',\n        description: 'Token ID for this item',\n        show: {\n            'actions.operation': ['retrieveAnAsset']\n        }\n    }\n] as INodeParams[]\n\nexport const retrieveAContract = [\n    {\n        label: 'Asset Contract Address',\n        name: 'asset_contract_address',\n        type: 'string',\n        default: '',\n        description: 'The NFT contract address for the assets',\n        show: {\n            'actions.operation': ['retrieveAContract']\n        }\n    }\n] as INodeParams[]\n\nexport const retrieveACollection = [\n    {\n        label: 'Collection Slug',\n        name: 'collection_slug',\n        type: 'string',\n        default: '',\n        description:\n            'For instance: if a collection url is: https://opensea.io/collection/my-collection-1, collection slug is my-collection-1.',\n        show: {\n            'actions.operation': ['retrieveACollection', 'retrieveCollectionStats']\n        }\n    }\n] as INodeParams[]\n\nexport const eventsTriggerParams = [\n    {\n        label: 'Listen From',\n        name: 'listenFrom',\n        type: 'options',\n        options: [\n            {\n                label: 'From A NFT',\n                name: 'fromANFT'\n            },\n            {\n                label: 'From A Collection',\n                name: 'fromACollection'\n            }\n        ],\n        default: 'fromANFT',\n        description: 'Listen event from a NFT or collection'\n    },\n    {\n        label: 'Asset Contract Address',\n        name: 'asset_contract_address',\n        type: 'string',\n        default: '',\n        description: 'The NFT contract address for the assets for which to show events',\n        show: {\n            'inputParameters.listenFrom': ['fromANFT']\n        }\n    },\n    {\n        label: 'Token Id',\n        name: 'token_id',\n        type: 'number',\n        default: '',\n        description: `The token's id of NFT`,\n        show: {\n            'inputParameters.listenFrom': ['fromANFT']\n        }\n    },\n    {\n        label: 'Collection Slug',\n        name: 'collection_slug',\n        type: 'string',\n        default: '',\n        optional: true,\n        description:\n            'Events from a collection. For instance: if a collection url is: https://opensea.io/collection/my-collection-1, collection slug is my-collection-1.',\n        show: {\n            'inputParameters.listenFrom': ['fromACollection']\n        }\n    },\n    {\n        label: 'Account Address',\n        name: 'account_address',\n        type: 'string',\n        default: '',\n        optional: true,\n        description: `A user account's wallet address to filter for events on an account`\n    },\n    {\n        label: 'Only Opensea',\n        name: 'only_opensea',\n        type: 'boolean',\n        default: false,\n        optional: true,\n        description: 'Restrict to events on OpenSea auctions'\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/OptimismScan/OptimismScan.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport {\n    OPERATIONS,\n    SORT_BY,\n    GET_ETH_BALANCE,\n    GET_ETH_BALANCE_MULTI,\n    GET_NORMAL_TRANSACTIONS,\n    GET_ERC20_TOKEN_TRANSFER,\n    GET_DEPOSIT,\n    GET_WITHDRAWAL,\n    GET_CONTRACT_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_ACCOUNT_BALANCE\n} from './constants'\n\nclass OptimismScan implements INode {\n    // properties\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    // parameter\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        // properties\n        this.label = 'Optimism Scan'\n        this.name = 'optimismscan'\n        this.icon = 'optimismScan.svg'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Optimism Public API'\n        this.incoming = 1\n        this.outgoing = 1\n\n        // parameter\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                description: 'Choose the API to execute',\n                options: [\n                    // Account\n                    {\n                        label: 'Get ETH Balance',\n                        name: GET_ETH_BALANCE.name,\n                        description: 'Get ETH Balance for a single Address'\n                    },\n                    {\n                        label: 'Get ETH Balance Multi',\n                        name: GET_ETH_BALANCE_MULTI.name,\n                        description: 'Get ETH Balance for multiple Addresses in a single call'\n                    },\n                    {\n                        label: 'Get ERC20 Token Transfer Events',\n                        name: GET_ERC20_TOKEN_TRANSFER.name,\n                        description: 'Get a list of \"ERC-20 - Token Transfer Events\" by Address'\n                    },\n                    {\n                        label: 'Get Deposit History',\n                        name: GET_DEPOSIT.name,\n                        description: 'Get list of L1 Deposits done by Address'\n                    },\n                    {\n                        label: 'Get Withdrawal History',\n                        name: GET_WITHDRAWAL.name,\n                        description: 'Get list of L2 Withdrawals done by Address'\n                    },\n                    {\n                        label: 'Get Contract ABI',\n                        name: GET_CONTRACT_ABI.name,\n                        description: 'Get Contract ABI for Verified Contract Source Codes'\n                    },\n                    {\n                        label: 'Get Contract Source Code',\n                        name: GET_CONTRACT_SOURCE_CODE.name,\n                        description:\n                            'Get Contract Source Code for Verified Contract Source Codes. (Replace the address with the actual contract address)'\n                    },\n                    {\n                        label: 'Get ERC-20 Token Total Supply',\n                        name: GET_ERC20_TOKEN_SUPPLY.name,\n                        description: 'Get ERC-20 Token Total Supply by Contract Address'\n                    },\n                    {\n                        label: 'Get ERC-20 Token Account Balance',\n                        name: GET_ERC20_TOKEN_ACCOUNT_BALANCE.name,\n                        description: 'Get ERC-20 Token Account Balance by Contract Address and Address'\n                    }\n                ],\n                default: GET_ETH_BALANCE.name\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'OptimismScan API Key',\n                        name: 'optimisticEtherscanApi'\n                    }\n                ],\n                default: 'optimisticEtherscanApi'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Address',\n                name: 'address',\n                type: 'string',\n                show: {\n                    'actions.api': [\n                        GET_ETH_BALANCE.name,\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_ERC20_TOKEN_TRANSFER.name,\n                        GET_DEPOSIT.name,\n                        GET_WITHDRAWAL.name,\n                        GET_CONTRACT_ABI.name,\n                        GET_CONTRACT_SOURCE_CODE.name,\n                        GET_ERC20_TOKEN_ACCOUNT_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Addresses',\n                name: 'addresses',\n                type: 'array',\n                array: [\n                    {\n                        label: 'Address',\n                        name: 'address',\n                        type: 'string'\n                    }\n                ],\n                show: {\n                    'actions.api': [GET_ETH_BALANCE_MULTI.name]\n                }\n            },\n            {\n                label: 'Tag',\n                name: 'tag',\n                type: 'options',\n                options: [{ label: 'latest', name: 'latest' }],\n                default: 'latest',\n                show: {\n                    'actions.api': [GET_ETH_BALANCE.name, GET_ETH_BALANCE_MULTI.name, GET_ERC20_TOKEN_ACCOUNT_BALANCE.name]\n                }\n            },\n            {\n                label: 'Start Block',\n                name: 'startBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to start searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_ERC20_TOKEN_TRANSFER.name]\n                },\n                default: 0\n            },\n            {\n                label: 'End Block',\n                name: 'endBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to stop searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_ERC20_TOKEN_TRANSFER.name]\n                }\n            },\n            {\n                label: 'Page',\n                name: 'page',\n                type: 'number',\n                optional: true,\n                description: 'the page number, if pagination is enabled',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_ERC20_TOKEN_TRANSFER.name]\n                },\n                default: 1\n            },\n            {\n                label: 'Offset',\n                name: 'offset',\n                type: 'number',\n                optional: true,\n                description: 'the number of transactions displayed per page',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_ERC20_TOKEN_TRANSFER.name]\n                },\n                default: 10\n            },\n            {\n                label: 'Sort By',\n                name: 'sortBy',\n                type: 'options',\n                options: SORT_BY,\n                optional: true,\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_ERC20_TOKEN_TRANSFER.name, GET_DEPOSIT.name]\n                },\n                default: 'desc'\n            },\n            {\n                label: 'Contract Address [Optional]',\n                name: 'contractAddressOptional',\n                type: 'string',\n                description: 'the contract address of the ERC-20 token',\n                optional: true,\n                show: {\n                    'actions.api': [GET_ERC20_TOKEN_TRANSFER.name]\n                }\n            },\n            {\n                label: 'Contract Address',\n                name: 'contractAddress',\n                type: 'string',\n                description: 'the contract address of the ERC-20 token',\n                show: {\n                    'actions.api': [GET_ERC20_TOKEN_SUPPLY.name, GET_ERC20_TOKEN_ACCOUNT_BALANCE.name]\n                }\n            },\n            {\n                label: 'Token Address',\n                name: 'tokenAddress',\n                type: 'string',\n                description: 'the contract address of the ERC-20 token',\n                optional: true,\n                show: {\n                    'actions.api': [GET_DEPOSIT.name]\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    getBaseParams(api: string) {\n        const operation = OPERATIONS.filter(({ name }) => name === api)[0]\n        return { module: operation.module, action: operation.action }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const url = 'https://api-optimistic.etherscan.io/api'\n        const { actions, inputParameters, credentials } = nodeData\n\n        if (actions === undefined || inputParameters === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actions.api as string\n        const apiKey = credentials.apiKey as string\n\n        const singleAddress = inputParameters.address as string\n        const multiAddresses = (inputParameters.addresses as ICommonObject[]) || []\n        const startblock = inputParameters.startBlock as number\n        const endblock = inputParameters.endBlock as number\n        const page = inputParameters.page as number\n        const offset = inputParameters.offset as number\n        const sort = inputParameters.sortBy as string\n        const tag = inputParameters.tag as string\n        const contractAddress = inputParameters.contractAddress as string\n        const tokenAddress = inputParameters.tokenAddress as string\n        const contractAddressOptional = inputParameters.contractAddressOptional || undefined\n\n        const { module, action } = this.getBaseParams(api)\n        const address = singleAddress || multiAddresses.map(({ address }) => address).join(',')\n        const contractaddress = contractAddress ? contractAddress : contractAddressOptional\n\n        const queryParameters = {\n            module,\n            action,\n            address,\n            tag,\n            apiKey,\n            startblock,\n            endblock,\n            page,\n            offset,\n            sort,\n            tokenAddress,\n            contractaddress\n        }\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: OptimismScan }\n"
  },
  {
    "path": "packages/components/nodes/OptimismScan/constants.ts",
    "content": "// Account\nexport const GET_ETH_BALANCE = {\n    name: 'getEthBalance',\n    module: 'account',\n    action: 'balance'\n}\n\nexport const GET_ETH_BALANCE_MULTI = {\n    name: 'getEthBalanceMulti',\n    module: 'account',\n    action: 'balancemulti'\n}\n\nexport const GET_NORMAL_TRANSACTIONS = {\n    name: 'getNormalTransactions',\n    module: 'account',\n    action: 'txlist'\n}\n\nexport const GET_ERC20_TOKEN_TRANSFER = {\n    name: 'getErc20TokenTransfer',\n    module: 'account',\n    action: 'tokentx'\n}\n\nexport const GET_DEPOSIT = {\n    name: 'getDeposit',\n    module: 'account',\n    action: 'getdeposittxs'\n}\n\nexport const GET_WITHDRAWAL = {\n    name: 'getWithdrawal',\n    module: 'account',\n    action: 'getwithdrawaltxs'\n}\n\n// Contracts\nexport const GET_CONTRACT_ABI = {\n    name: 'getContractAbi',\n    module: 'contract',\n    action: 'getabi'\n}\n\nexport const GET_CONTRACT_SOURCE_CODE = {\n    name: 'getContractSourceCode',\n    module: 'contract',\n    action: 'getsourcecode'\n}\n\n// Tokens\nexport const GET_ERC20_TOKEN_SUPPLY = {\n    name: 'getErc20TokenSupply',\n    module: 'stats',\n    action: 'tokensupply'\n}\n\nexport const GET_ERC20_TOKEN_ACCOUNT_BALANCE = {\n    name: 'getErc20TokenAccountBalance',\n    module: 'account',\n    action: 'tokenbalance'\n}\n\nexport const OPERATIONS = [\n    GET_ETH_BALANCE,\n    GET_ETH_BALANCE_MULTI,\n    GET_NORMAL_TRANSACTIONS,\n    GET_ERC20_TOKEN_TRANSFER,\n    GET_DEPOSIT,\n    GET_WITHDRAWAL,\n    GET_CONTRACT_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_ACCOUNT_BALANCE\n] as const\n\nexport const SORT_BY = [\n    { label: 'Desc', name: 'desc' },\n    { label: 'Asc', name: 'asc' }\n]\n"
  },
  {
    "path": "packages/components/nodes/PancakeSwap/PancakeSwap.ts",
    "content": "import { ethers } from 'ethers'\nimport {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { networkExplorers, binanceNetworkProviders, CHAIN_ID, NETWORK, getNetworkProvider, NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { UniswapPair, UniswapPairSettings, UniswapVersion } from 'simple-uniswap-sdk'\nimport { IToken, nativeTokens } from './extendedTokens'\nimport IWBNB from './abis/WBNB.json'\n\nclass PancakeSwap implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'PancakeSwap'\n        this.name = 'pancakeSwap'\n        this.icon = 'pancakeswap.png'\n        this.type = 'action'\n        this.category = 'Decentralized Finance'\n        this.version = 1.0\n        this.description = 'Execute PancakeSwap operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Swap Tokens',\n                        name: 'swapTokens'\n                    },\n                    {\n                        label: 'Custom Query',\n                        name: 'customQuery',\n                        description: 'Custom subgraph query to retrieve more information. https://github.com/pancakeswap/pancake-subgraph'\n                    }\n                ],\n                default: 'swapTokens'\n            },\n            {\n                label: 'Query Entity',\n                name: 'queryEntity',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Blocks',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/blocks',\n                        description: 'Tracks all blocks on Binance Smart Chain.'\n                    },\n                    {\n                        label: 'Exchange',\n                        name: 'https://bsc.streamingfast.io/subgraphs/name/pancakeswap/exchange-v2',\n                        description: 'Tracks all PancakeSwap Exchange data with price, volume, liquidity'\n                    },\n                    {\n                        label: 'Lottery',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/lottery',\n                        description: 'Tracks all PancakeSwap Lottery with rounds, draws and tickets.'\n                    },\n                    {\n                        label: 'NFT Market (v1)',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/nft-market',\n                        description: 'Tracks all PancakeSwap NFT Market for ERC-721.'\n                    },\n                    {\n                        label: 'Pairs',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/pairs',\n                        description: 'Tracks all PancakeSwap Pairs and Tokens.'\n                    },\n                    {\n                        label: 'Pancake Squad',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/pancake-squad',\n                        description: 'Tracks all Pancake Squad metrics with Owners, Tokens (including metadata), and Transactions.'\n                    },\n                    {\n                        label: 'Prediction (v1)',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/prediction',\n                        description: 'Tracks all PancakeSwap Prediction (v1) with market, rounds, and bets.'\n                    },\n                    {\n                        label: 'Prediction (v2)',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/prediction-v2',\n                        description: 'Tracks all PancakeSwap Prediction (v2) with market, rounds, and bets.'\n                    },\n                    {\n                        label: 'Profile',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/profile',\n                        description: 'Tracks all PancakeSwap Profile with teams, users, points and campaigns.'\n                    },\n                    {\n                        label: 'SmartChef',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/smartchef',\n                        description: 'Tracks all PancakeSwap SmartChef (a.k.a. Syrup Pools) with tokens and rewards.'\n                    },\n                    {\n                        label: 'Timelock',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/timelock',\n                        description: 'Tracks all PancakeSwap Timelock queued, executed, and cancelled transactions.'\n                    },\n                    {\n                        label: 'MasterChef (v2)',\n                        name: 'https://api.thegraph.com/subgraphs/name/pancakeswap/masterchef-v2',\n                        description: 'Tracks data for MasterChefV2.'\n                    }\n                ],\n                default: 'https://api.thegraph.com/subgraphs/name/pancakeswap/pairs',\n                show: {\n                    'actions.operation': ['customQuery']\n                }\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Binance Smart Chain Mainnet',\n                        name: 'bsc',\n                        parentGroup: 'Binance Smart Chain'\n                    }\n                ],\n                default: 'bsc',\n                show: {\n                    'actions.operation': ['customQuery', 'swapTokens']\n                }\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'options',\n                options: [...binanceNetworkProviders],\n                default: 'binance',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC'],\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket'],\n                    'actions.operation': ['swapTokens']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'QuickNode Endpoints',\n                        name: 'quickNodeEndpoints',\n                        show: {\n                            'networks.networkProvider': [NETWORK_PROVIDER.QUICKNODE]\n                        }\n                    }\n                ],\n                show: {\n                    'networks.networkProvider': [NETWORK_PROVIDER.QUICKNODE],\n                    'actions.operation': ['swapTokens']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'From Token',\n                name: 'fromToken',\n                type: 'asyncOptions',\n                description: 'Contract address of the token you want to convert FROM.',\n                loadMethod: 'getTokens',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'To Token',\n                name: 'toToken',\n                type: 'asyncOptions',\n                description: 'Contract address of the token you want to convert TO.',\n                loadMethod: 'getTokens',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Amount To Swap',\n                name: 'amountToSwap',\n                type: 'number',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Select Wallet',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to swap tokens.',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Query',\n                name: 'query',\n                type: 'string',\n                rows: 10,\n                show: {\n                    'actions.operation': ['customQuery']\n                }\n            },\n            {\n                label: 'Slippage Tolerance (%)',\n                name: 'slippage',\n                type: 'number',\n                default: 0.5,\n                optional: true,\n                description: 'How large of a price movement to tolerate before trade will fail to execute. Default to 0.5%.',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Tx Deadline (mins)',\n                name: 'deadlineMinutes',\n                type: 'number',\n                default: 20,\n                optional: true,\n                description: 'Minutes after which the transaction will fail. Default to 20 mins.',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Disable Multihops',\n                name: 'disableMultihops',\n                type: 'boolean',\n                default: false,\n                optional: true,\n                description: 'Restricts swaps to direct pairs only. Default to false.',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.pancakeswap.finance/pancakeswap-extended.json`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                const tokens: IToken[] = responseData.tokens\n                const nativeToken: IToken = nativeTokens[network]\n\n                // Add native token\n                const data = {\n                    label: `${nativeToken.name} (${nativeToken.symbol})`,\n                    name: `${nativeToken.address};${nativeToken.symbol};${nativeToken.name};${nativeToken.decimals}`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.symbol};${token.name};${token.decimals}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || actionsData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                undefined,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get operation\n            const operation = actionsData.operation as string\n\n            if (operation === 'swapTokens') {\n                // Get fromTokenAddress\n                const fromToken = inputParametersData.fromToken as string\n                const [fromTokenContractAddress, fromTokenSymbol] = fromToken.split(';')\n\n                // Get toTokenAddress\n                const toToken = inputParametersData.toToken as string\n                const [toTokenContractAddress, toTokenSymbol] = toToken.split(';')\n\n                // Get wallet instance\n                const walletString = inputParametersData.wallet as string\n                const walletDetails: IWallet = JSON.parse(walletString)\n                const walletCredential = JSON.parse(walletDetails.walletCredential)\n                const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n                // Get amount\n                const amountToSwap = inputParametersData.amountToSwap as string\n\n                if (fromTokenContractAddress.includes(`_ETH`) && toTokenSymbol === 'WBNB') {\n                    const wrapBNBContract = new ethers.Contract(toTokenContractAddress, IWBNB, wallet)\n\n                    const tx = await wrapBNBContract.deposit({ value: ethers.utils.parseUnits(amountToSwap, 18) })\n\n                    const approveReceipt = await tx.wait()\n\n                    if (approveReceipt.status === 0) throw new Error(`Failed to swap BNB to WBNB`)\n\n                    const returnItem = {\n                        transactionHash: tx.hash,\n                        transactionReceipt: approveReceipt as any,\n                        link: `${networkExplorers[network]}/tx/${tx.hash}`\n                    }\n                    return returnNodeExecutionData(returnItem)\n                } else if (toTokenContractAddress.includes(`_ETH`) && fromTokenSymbol === 'WBNB') {\n                    const wrapBNBContract = new ethers.Contract(fromTokenContractAddress, IWBNB, wallet)\n\n                    const tx = await wrapBNBContract.withdraw(ethers.utils.parseUnits(amountToSwap, 18))\n\n                    const approveReceipt = await tx.wait()\n\n                    if (approveReceipt.status === 0) throw new Error(`Failed to swap WBNB to BNB`)\n\n                    const returnItem = {\n                        transactionHash: tx.hash,\n                        transactionReceipt: approveReceipt as any,\n                        link: `${networkExplorers[network]}/tx/${tx.hash}`\n                    }\n                    return returnNodeExecutionData(returnItem)\n                } else {\n                    const slippage = inputParametersData.slippage as string\n                    const deadlineMinutes = inputParametersData.deadlineMinutes as number\n                    const disableMultihops = inputParametersData.disableMultihops as boolean\n\n                    const targets = {\n                        v2Override: {\n                            routerAddress: '0x10ED43C718714eb63d5aA57B78B54704E256024E',\n                            factoryAddress: '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73',\n                            pairAddress: '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73'\n                        }\n                    }\n\n                    const customNetworkData = {\n                        nameNetwork: 'Binance',\n                        multicallContractAddress: '0x65e9a150e06c84003d15ae6a060fc2b1b393342c',\n                        nativeCurrency: {\n                            name: 'BNB Token',\n                            symbol: 'BNB'\n                        },\n                        nativeWrappedTokenInfo: {\n                            chainId: CHAIN_ID.BINANCE_MAINNET as number,\n                            contractAddress: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',\n                            decimals: 18,\n                            symbol: 'WBNB',\n                            name: 'Wrapped BNB'\n                        }\n                    }\n\n                    const uniswapPair = new UniswapPair({\n                        fromTokenContractAddress,\n                        toTokenContractAddress,\n                        ethereumAddress: wallet.address,\n                        ethereumProvider: provider,\n                        settings: new UniswapPairSettings({\n                            slippage: parseFloat(slippage) / 100.0 || 0.0005,\n                            deadlineMinutes: deadlineMinutes || 20,\n                            disableMultihops: disableMultihops || false,\n                            uniswapVersions: [UniswapVersion.v2],\n                            cloneUniswapContractDetails: targets,\n                            customNetwork: customNetworkData\n                        })\n                    })\n\n                    const uniswapPairFactory = await uniswapPair.createFactory()\n\n                    const trade = await uniswapPairFactory.trade(amountToSwap)\n\n                    if (!trade.fromBalance.hasEnough) {\n                        throw new Error('You do not have enough from balance to execute this swap')\n                    }\n\n                    // Why we need two transactions: https://github.com/joshstevens19/simple-uniswap-sdk#ethers-example\n                    if (trade.approvalTransaction) {\n                        const approved = await wallet.sendTransaction(trade.approvalTransaction)\n                        await approved.wait()\n                    }\n\n                    const tradeTransaction = await wallet.sendTransaction(trade.transaction)\n                    const tradeReceipt = await tradeTransaction.wait()\n\n                    trade.destroy()\n\n                    const returnItem = {\n                        operation,\n                        transactionHash: tradeTransaction.hash,\n                        transactionReceipt: tradeReceipt as any,\n                        link: `${networkExplorers[network]}/tx/${tradeTransaction.hash}`\n                    }\n                    return returnNodeExecutionData(returnItem)\n                }\n            } else if (operation === 'customQuery') {\n                let query = inputParametersData.query as string\n                query = query.replace(/\\s/g, ' ')\n                const queryEntity = actionsData.queryEntity as string\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: queryEntity,\n                    data: { query }\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                const returnData: ICommonObject[] = []\n\n                if (Array.isArray(responseData)) returnData.push(...responseData)\n                else returnData.push(responseData)\n\n                return returnNodeExecutionData(returnData)\n            }\n\n            return returnNodeExecutionData([])\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: PancakeSwap }\n"
  },
  {
    "path": "packages/components/nodes/PancakeSwap/abis/WBNB.json",
    "content": "[\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"name\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"string\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"guy\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"approve\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"totalSupply\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"transferFrom\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"withdraw\",\n        \"outputs\": [],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"decimals\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint8\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"address\"\n            }\n        ],\n        \"name\": \"balanceOf\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"symbol\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"string\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"transfer\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [],\n        \"name\": \"deposit\",\n        \"outputs\": [],\n        \"payable\": true,\n        \"stateMutability\": \"payable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"\",\n                \"type\": \"address\"\n            }\n        ],\n        \"name\": \"allowance\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"payable\": true,\n        \"stateMutability\": \"payable\",\n        \"type\": \"fallback\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": true,\n                \"name\": \"guy\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Approval\",\n        \"type\": \"event\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": true,\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Transfer\",\n        \"type\": \"event\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Deposit\",\n        \"type\": \"event\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Withdrawal\",\n        \"type\": \"event\"\n    }\n]\n"
  },
  {
    "path": "packages/components/nodes/PancakeSwap/extendedTokens.ts",
    "content": "import { CHAIN_ID, NETWORK } from '../../src'\n\nexport interface INativeTokens {\n    [key: string]: IToken\n}\n\nexport interface IToken {\n    address: string\n    symbol: string\n    name: string\n    decimals: number\n    chainId: number\n}\n\nexport const nativeTokens: INativeTokens = {\n    [NETWORK.BSC]: {\n        address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c_ETH',\n        symbol: 'BNB',\n        name: 'BNB',\n        decimals: 18,\n        chainId: CHAIN_ID.BINANCE_MAINNET\n    },\n    [NETWORK.BSC_TESTNET]: {\n        address: '0xae13d989dac2f0debff460ac112a837c89baa7cd_ETH',\n        symbol: 'BNB',\n        name: 'BNB',\n        decimals: 18,\n        chainId: CHAIN_ID.BINANCE_TESTNET\n    }\n}\n"
  },
  {
    "path": "packages/components/nodes/Pinata/Pinata.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport FormData from 'form-data'\n\nclass Pinata implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Pinata'\n        this.name = 'pinata'\n        this.icon = 'pinata.svg'\n        this.type = 'action'\n        this.category = 'InterPlanetary File System'\n        this.version = 1.0\n        this.description = 'Pin your content to IPFS with Pinata'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Select Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'JSON',\n                        name: 'json'\n                    },\n                    {\n                        label: 'Multiple JSON',\n                        name: 'multipleJson',\n                        description: 'Wrap each json from an array into a json file (0.json, 1.json..) and store under a directory'\n                    },\n                    {\n                        label: 'File',\n                        name: 'file'\n                    },\n                    {\n                        label: 'Folder',\n                        name: 'folder'\n                    },\n                    {\n                        label: 'CID (Content Identifier)',\n                        name: 'CID'\n                    },\n                    {\n                        label: 'Raw Data (Base64)',\n                        name: 'base64',\n                        description: 'Wrap the raw data in base64 format into a file and store under a directory'\n                    },\n                    {\n                        label: 'Multiple Raw Data (Base64)',\n                        name: 'multipleBase64',\n                        description:\n                            'Wrap each raw data in base64 format from an array into a file (0.png, 1.pdf..) and store under a directory'\n                    },\n                    {\n                        label: 'List Pin By CID Jobs',\n                        name: 'listPin'\n                    },\n                    {\n                        label: 'Update Metadata',\n                        name: 'updateMetadata'\n                    },\n                    {\n                        label: 'Remove Files (Unpin)',\n                        name: 'unpin'\n                    }\n                ],\n                description: 'Content type to be pinned'\n            },\n            {\n                label: 'JSON Content',\n                name: 'jsonContent',\n                type: 'json',\n                placeholder: '{\"var1\": \"value1\"}',\n                description: 'The content of JSON to be pinned.',\n                show: {\n                    'actions.operation': ['json']\n                }\n            },\n            {\n                label: 'JSON Content In Array',\n                name: 'jsonContentArray',\n                type: 'json',\n                placeholder: '[ {\"var1\": \"value1\"}, {\"var2\": \"value2\"} ]',\n                description: 'An array with multiple json objects to be pinned.',\n                show: {\n                    'actions.operation': ['multipleJson']\n                }\n            },\n            {\n                label: 'Raw Data Content (Base64)',\n                name: 'base64Content',\n                type: 'string',\n                placeholder: 'data:image/png;base64,<base64_string>',\n                description: 'Raw data content in base64 format to be pinned. Commonly used with image, pdf',\n                show: {\n                    'actions.operation': ['base64']\n                }\n            },\n            {\n                label: 'Raw Data Content In Array (Base64)',\n                name: 'base64ContentArray',\n                type: 'json',\n                placeholder: '[ \"data:image/png;base64,<base64_string>\", \"data:application/pdf;base64,<base64_string>\" ]',\n                description: 'An array with multiple raw data content in base64 format to be pinned. Commonly used with image, pdf',\n                show: {\n                    'actions.operation': ['multipleBase64']\n                }\n            },\n            {\n                label: 'File',\n                name: 'fileContent',\n                type: 'file',\n                description: 'The path to a file to be pinned.',\n                show: {\n                    'actions.operation': ['file']\n                }\n            },\n            {\n                label: 'Folder',\n                name: 'folderContent',\n                type: 'folder',\n                description: 'The path to a folder to be pinned.',\n                show: {\n                    'actions.operation': ['folder']\n                }\n            },\n            {\n                label: 'Hash To Pin (CID)',\n                name: 'hashToPin',\n                type: 'string',\n                description:\n                    'A CID (or content identifier) is a hash generated by the IPFS protocol and representative of a piece of content.',\n                show: {\n                    'actions.operation': ['CID']\n                },\n                placeholder: 'Qmc5TDt1jynbZGvLiGnJvLh9RqB4uxVUg4vgMAcPqwpEiy'\n            },\n            {\n                label: 'IPFS Pin Hash (CID)',\n                name: 'ipfsPinHash',\n                type: 'string',\n                description:\n                    'A CID (or content identifier) is a hash generated by the IPFS protocol and representative of a piece of content.',\n                show: {\n                    'actions.operation': ['updateMetadata']\n                },\n                placeholder: 'Qmc5TDt1jynbZGvLiGnJvLh9RqB4uxVUg4vgMAcPqwpEiy'\n            },\n            {\n                label: 'Pin To Delete (CID)',\n                name: 'pinToDelete',\n                type: 'string',\n                description:\n                    'A CID (or content identifier) is a hash generated by the IPFS protocol and representative of a piece of content.',\n                show: {\n                    'actions.operation': ['unpin']\n                },\n                placeholder: 'Qmc5TDt1jynbZGvLiGnJvLh9RqB4uxVUg4vgMAcPqwpEiy'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Pinata API Key',\n                        name: 'pinataApi'\n                    }\n                ],\n                default: 'pinataApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'CID Version',\n                name: 'cidVersion',\n                type: 'options',\n                options: [\n                    {\n                        label: '0 (CIDv0)',\n                        name: '0'\n                    },\n                    {\n                        label: '1 (CIDv1)',\n                        name: '1'\n                    }\n                ],\n                optional: true,\n                description: 'The CID Version IPFS will use when creating a hash for your content.',\n                show: {\n                    'actions.operation': ['json', 'multipleJson', 'base64', 'multipleBase64', 'file', 'folder', 'CID']\n                }\n            },\n            {\n                label: 'Wrap With Directory',\n                name: 'wrapWithDirectory',\n                type: 'boolean',\n                default: false,\n                optional: true,\n                description:\n                    'Wrap your content inside of a directory when adding to IPFS. This allows users to retrieve content via a filename instead of just a hash.',\n                show: {\n                    'actions.operation': ['json', 'base64', 'file', 'folder', 'CID']\n                }\n            },\n            {\n                label: 'Metadata Name',\n                name: 'name',\n                type: 'string',\n                default: '',\n                optional: true,\n                description: `A custom name. If you don't provide this value, it will automatically be populated by the original name of the file you've uploaded.`,\n                show: {\n                    'actions.operation': ['json', 'multipleJson', 'base64', 'multipleBase64', 'file', 'folder', 'CID', 'updateMetadata']\n                }\n            },\n            {\n                label: 'Metadata KeyValues',\n                name: 'keyvalues',\n                type: 'json',\n                placeholder: '{\"var1\": \"value1\"}',\n                optional: true,\n                description: `The key values object allows for users to provide any custom key / value pairs they want for the file being uploaded.`,\n                show: {\n                    'actions.operation': ['json', 'multipleJson', 'base64', 'multipleBase64', 'file', 'folder', 'CID', 'updateMetadata']\n                }\n            },\n            {\n                label: 'Host Nodes',\n                name: 'hostNodes',\n                type: 'json',\n                placeholder: '[\"0xa\", \"0xb\"]',\n                optional: true,\n                description: `Multiaddresses of nodes your content is already stored on. Max 5 nodes.`,\n                show: {\n                    'actions.operation': ['CID']\n                }\n            },\n            {\n                label: 'Status',\n                name: 'status',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Prechecking',\n                        name: 'prechecking',\n                        description: 'Pinata is running preliminary validations on your pin request.'\n                    },\n                    {\n                        label: 'Searching',\n                        name: 'searching',\n                        description:\n                            'Pinata is actively searching for your content on the IPFS network. This may take some time if your content is isolated.'\n                    },\n                    {\n                        label: 'Retrieving',\n                        name: 'retrieving',\n                        description: 'Pinata has located your content and is now in the process of retrieving it.'\n                    },\n                    {\n                        label: 'Expired',\n                        name: 'expired',\n                        description: `Pinata wasn't able to find your content after a day of searching the IPFS network. Please make sure your content is hosted on the IPFS network before trying to pin again.`\n                    },\n                    {\n                        label: 'Over Free Limit',\n                        name: 'over_free_limit',\n                        description:\n                            'Pinning this object would put you over the free tier limit. Please add a credit card to continue pinning content.'\n                    },\n                    {\n                        label: 'Over Max Size',\n                        name: 'over_max_size',\n                        description: `This object is too large of an item to pin. If you're seeing this, please contact us for a more custom solution.`\n                    },\n                    {\n                        label: 'Invalid Object',\n                        name: 'invalid_object',\n                        description: `The object you're attempting to pin isn't readable by IPFS nodes. Please contact us if you receive this, as we'd like to better understand what you're attempting to pin.`\n                    },\n                    {\n                        label: 'Bad Host Node',\n                        name: 'bad_host_node',\n                        description:\n                            'You provided a host node that was either invalid or unreachable. Please make sure all provided host nodes are online and reachable.'\n                    }\n                ],\n                description: `Filter by the status of the job in the pinning queue.`,\n                show: {\n                    'actions.operation': ['listPin']\n                }\n            }\n        ]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const operation = actionData.operation as string\n\n        const apiKey = credentials.apiKey as string\n        const secretKey = credentials.secretKey as string\n\n        const cidVersion = inputParametersData.cidVersion as string\n        const wrapWithDirectory = inputParametersData.wrapWithDirectory as boolean\n        const name = inputParametersData.name as string\n        const keyvalues = inputParametersData.keyvalues as string\n        const hostNodes = inputParametersData.hostNodes as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n        let queryBody: any = {}\n        let method: Method = 'POST'\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json',\n            pinata_api_key: apiKey,\n            pinata_secret_api_key: secretKey\n        }\n\n        function getOptionsAndMetadata() {\n            // Metadata\n            const pinataMetadataObj: ICommonObject = {}\n            if (name) pinataMetadataObj.name = name\n            if (keyvalues) {\n                const parsedKeyValues = JSON.parse(keyvalues.replace(/\\s/g, ''))\n                pinataMetadataObj.keyvalues = parsedKeyValues\n            }\n            if (Object.keys(pinataMetadataObj).length > 0) {\n                queryBody.pinataMetadata = pinataMetadataObj\n            }\n\n            // Options\n            const pinataOptionsObj: ICommonObject = {}\n            if (cidVersion) pinataOptionsObj.cidVersion = parseInt(cidVersion)\n            if (wrapWithDirectory) pinataOptionsObj.wrapWithDirectory = wrapWithDirectory\n            if (hostNodes) {\n                const hostNodesArray = JSON.parse(hostNodes.replace(/\\s/g, ''))\n                if (hostNodesArray.length) pinataOptionsObj.hostNodes = hostNodesArray\n            }\n            if (Object.keys(pinataOptionsObj).length > 0) {\n                queryBody.pinataOptions = pinataOptionsObj\n            }\n        }\n\n        function getExtension(base64string: string) {\n            switch (base64string.charAt(0)) {\n                case '/':\n                    return 'jpg'\n                case 'i':\n                    return 'png'\n                case 'R':\n                    return 'gif'\n                case 'U':\n                    return 'webp'\n                case 'P':\n                    return 'svg'\n                default:\n                    return 'png'\n            }\n        }\n\n        try {\n            if (operation === 'json') {\n                // Json Content\n                const jsonContent_str = actionData.jsonContent as string\n\n                if (wrapWithDirectory) {\n                    url = 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n                    const jsonContent_base64 = Buffer.from(jsonContent_str).toString('base64')\n\n                    const filename = name ? `${name}.json` : `0.json`\n                    const bf = Buffer.from(jsonContent_base64, 'base64')\n\n                    const formData = new FormData()\n                    formData.append('file', bf, filename)\n\n                    getOptionsAndMetadata()\n\n                    headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                    if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataOptions'))\n                        formData.append('pinataOptions', JSON.stringify(queryBody.pinataOptions))\n                    if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataMetadata'))\n                        formData.append('pinataMetadata', JSON.stringify(queryBody.pinataMetadata))\n\n                    queryBody = formData\n                } else {\n                    url = 'https://api.pinata.cloud/pinning/pinJSONToIPFS'\n\n                    const parsedInputJson = JSON.parse(jsonContent_str.replace(/\\s/g, ''))\n                    queryBody.pinataContent = parsedInputJson\n\n                    getOptionsAndMetadata()\n                }\n            } else if (operation === 'multipleJson') {\n                // Json Content Array\n                const jsonContentArray_str = actionData.jsonContentArray as string\n                const jsonContentArray = JSON.parse(jsonContentArray_str)\n\n                url = 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n\n                const formData = new FormData()\n                for (let i = 0; i < jsonContentArray.length; i += 1) {\n                    const jsonContent_base64 = Buffer.from(JSON.stringify(jsonContentArray[i])).toString('base64')\n                    const filename = `${i}.json`\n                    const bf = Buffer.from(jsonContent_base64, 'base64')\n                    const foldername = name ? name : `MultipleJsonFolder`\n                    formData.append('file', bf, { filepath: `${foldername}/${filename}` })\n                }\n\n                getOptionsAndMetadata()\n\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataOptions'))\n                    formData.append('pinataOptions', JSON.stringify(queryBody.pinataOptions))\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataMetadata'))\n                    formData.append('pinataMetadata', JSON.stringify(queryBody.pinataMetadata))\n\n                queryBody = formData\n            } else if (operation === 'base64') {\n                // Base64 Content\n                const base64Content_str = actionData.base64Content as string\n                //base64Content_str = data:image/png;base64,<base64_string>\n\n                url = 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n\n                const splitDataURI = base64Content_str.split(',')\n\n                const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n                let ext = 'png'\n                const extension = splitDataURI.pop()?.split(';')[0].split('/')[1]\n                if (extension) ext\n                else ext = getExtension(base64Content_str)\n                const filename = name ? `${name}.${ext}` : `0.${ext}`\n\n                const formData = new FormData()\n                formData.append('file', bf, filename)\n\n                getOptionsAndMetadata()\n\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataOptions'))\n                    formData.append('pinataOptions', JSON.stringify(queryBody.pinataOptions))\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataMetadata'))\n                    formData.append('pinataMetadata', JSON.stringify(queryBody.pinataMetadata))\n\n                queryBody = formData\n            } else if (operation === 'multipleBase64') {\n                // Base64 Content Array\n                const base64ContentArray_str = actionData.base64ContentArray as string\n                const base64ContentArray = JSON.parse(base64ContentArray_str)\n\n                url = 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n\n                const formData = new FormData()\n                for (let i = 0; i < base64ContentArray.length; i += 1) {\n                    const splitDataURI = base64ContentArray[i].split(',')\n                    const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n                    let ext = 'png'\n                    const extension = splitDataURI.pop()?.split(';')[0].split('/')[1]\n                    if (extension) ext\n                    else ext = getExtension(base64ContentArray[i])\n                    const filename = `${i}.${ext}`\n                    const foldername = name ? name : `MultipleRawDataFolder`\n                    formData.append('file', bf, { filepath: `${foldername}/${filename}` })\n                }\n\n                getOptionsAndMetadata()\n\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataOptions'))\n                    formData.append('pinataOptions', JSON.stringify(queryBody.pinataOptions))\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataMetadata'))\n                    formData.append('pinataMetadata', JSON.stringify(queryBody.pinataMetadata))\n\n                queryBody = formData\n            } else if (operation === 'file') {\n                url = 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n\n                const fileBase64 = actionData.fileContent as string\n                const splitDataURI = fileBase64.split(',')\n\n                const filename = (splitDataURI.pop() || 'filename:').split(':')[1]\n                const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n\n                const formData = new FormData()\n                formData.append('file', bf, filename)\n\n                getOptionsAndMetadata()\n\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataOptions'))\n                    formData.append('pinataOptions', JSON.stringify(queryBody.pinataOptions))\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataMetadata'))\n                    formData.append('pinataMetadata', JSON.stringify(queryBody.pinataMetadata))\n\n                queryBody = formData\n            } else if (operation === 'folder') {\n                url = 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n\n                const folderContent = actionData.folderContent as string\n                const base64Array = JSON.parse(folderContent.replace(/\\s/g, ''))\n\n                const formData = new FormData()\n                for (let i = 0; i < base64Array.length; i += 1) {\n                    const fileBase64 = base64Array[i]\n                    const splitDataURI = fileBase64.split(',')\n\n                    const filepath = (splitDataURI.pop() || 'filepath:').split(':')[1]\n                    const bf = Buffer.from(splitDataURI.pop() || '', 'base64')\n                    formData.append('file', bf, { filepath })\n                }\n\n                getOptionsAndMetadata()\n\n                headers['Content-Type'] = 'multipart/form-data; boundary=' + formData.getBoundary()\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataOptions'))\n                    formData.append('pinataOptions', JSON.stringify(queryBody.pinataOptions))\n                if (Object.prototype.hasOwnProperty.call(queryBody, 'pinataMetadata'))\n                    formData.append('pinataMetadata', JSON.stringify(queryBody.pinataMetadata))\n\n                queryBody = formData\n            } else if (operation === 'CID') {\n                url = 'https://api.pinata.cloud/pinning/pinByHash'\n\n                const hashToPin = actionData.hashToPin as string\n                queryBody.hashToPin = hashToPin\n\n                getOptionsAndMetadata()\n            } else if (operation === 'listPin') {\n                url = 'https://api.pinata.cloud/pinning/pinJobs'\n                method = 'GET'\n\n                const status = inputParametersData.status as string\n                queryParameters.status = status\n                queryParameters.sort = 'ASC'\n            } else if (operation === 'updateMetadata') {\n                url = 'https://api.pinata.cloud/pinning/hashMetadata'\n                method = 'PUT'\n\n                const ipfsPinHash = actionData.ipfsPinHash as string\n                queryBody.ipfsPinHash = ipfsPinHash\n\n                if (name) queryBody.name = name\n                if (keyvalues) {\n                    const parsedKeyValues = JSON.parse(keyvalues.replace(/\\s/g, ''))\n                    queryBody.keyvalues = parsedKeyValues\n                }\n            } else if (operation === 'unpin') {\n                const pinToDelete = actionData.pinToDelete as string\n                url = `https://api.pinata.cloud/pinning/unpin/${pinToDelete}`\n                method = 'DELETE'\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers\n            }\n\n            if (Object.keys(queryParameters).length > 0) {\n                axiosConfig.params = queryParameters\n                axiosConfig.paramsSerializer = (params) => serializeQueryParams(params)\n            }\n\n            if (Object.keys(queryBody).length > 0) {\n                axiosConfig.data = queryBody\n            }\n\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) returnData.push(...responseData)\n        else returnData.push(responseData)\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Pinata }\n"
  },
  {
    "path": "packages/components/nodes/PolygonScan/PolygonScan.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport {\n    SORT_BY,\n    OPERATIONS,\n    GET_MATIC_BALANCE,\n    GET_HISTORICAL_MATIC_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_BLOCKS_VALIDATED,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_BALANCE,\n    GET_HISTORICAL_ERC20_TOKEN_SUPPLY,\n    GET_HISTORICAL_ERC20_TOKEN_BALANCE,\n    GET_TOKEN_INFO,\n    GET_MATIC_PRICE,\n    GET_HISTORICAL_MATIC_PRICE\n} from './constants'\n\nclass PolygonScan implements INode {\n    // properties\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    // parameter\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        // properties\n        this.label = 'PolygonScan'\n        this.name = 'polygonscan'\n        this.icon = 'polygonscan.png'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'PolygonScan Public API'\n        this.incoming = 1\n        this.outgoing = 1\n\n        // parameter\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get MATIC Balance',\n                        name: GET_MATIC_BALANCE.name,\n                        description: 'Returns the MATIC balance of a given address. The result is returned in wei.'\n                    },\n                    {\n                        label: 'Get Historical MATIC Balance [PRO]',\n                        name: GET_HISTORICAL_MATIC_BALANCE.name,\n                        description:\n                            'Returns the historical MATIC balance of an address at a certain block height. The result is returned in wei'\n                    },\n                    {\n                        label: 'Get transactions',\n                        name: GET_NORMAL_TRANSACTIONS.name,\n                        description: 'Returns the list of transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions',\n                        name: GET_INTERNAL_TRANSACTIONS.name,\n                        description: 'Returns the list of internal transactions performed by an address, with optional pagination.'\n                    },\n                    {\n                        label: 'Get internal transactions by hash',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_HASH.name,\n                        description: 'Returns the list of internal transactions performed within a transaction.'\n                    },\n                    {\n                        label: 'Get internal transactions by block',\n                        name: GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        description: 'Returns the list of internal transactions performed within a block range, with optional pagination.'\n                    },\n                    {\n                        label: 'Get list of Blocks Validated by Address',\n                        name: GET_BLOCKS_VALIDATED.name,\n                        description: 'Returns the list of blocks validated by an address.'\n                    },\n                    {\n                        label: 'Get Contract ABI',\n                        name: GET_ABI.name,\n                        description: 'Returns the contract Application Binary Interface ( ABI ) of a verified smart contract.'\n                    },\n                    {\n                        label: 'Get Contract Source Code',\n                        name: GET_CONTRACT_SOURCE_CODE.name,\n                        description: 'Returns the Solidity source code of a verified smart contract.'\n                    },\n                    {\n                        label: 'Check Transaction Receipt Status',\n                        name: CHECK_TRANSACTION_RECEIPT_STATUS.name,\n                        description: 'Returns the status code of a transaction execution.'\n                    },\n                    {\n                        label: 'Get ERC20 Token Supply',\n                        name: GET_ERC20_TOKEN_SUPPLY.name,\n                        description: `Returns the total supply of a ERC-20 token. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get ERC20 Token Balance',\n                        name: GET_ERC20_TOKEN_BALANCE.name,\n                        description: `Returns the current balance of a ERC-20 token of an address. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Historical ERC-20 Token TotalSupply by ContractAddress & BlockNo [PRO]',\n                        name: GET_HISTORICAL_ERC20_TOKEN_SUPPLY.name,\n                        description: `Returns the historical amount of a ERC-20 token in circulation at a certain block height. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Historical ERC-20 Token Account Balance by ContractAddress & BlockNo [PRO]',\n                        name: GET_HISTORICAL_ERC20_TOKEN_BALANCE.name,\n                        description: `Returns the balance of a ERC-20 token of an address at a certain block height. The result is returned in the token's smallest decimal representation.\n                        Eg. a token with a balance of 215.241526476136819398 and 18 decimal places will be returned as 215241526476136819398`\n                    },\n                    {\n                        label: 'Get Token Info [PRO]',\n                        name: GET_TOKEN_INFO.name,\n                        description: 'Returns project information and social media links of an ERC-20/ERC-721 token.'\n                    },\n                    {\n                        label: 'Get MATIC Price',\n                        name: GET_MATIC_PRICE.name,\n                        description: 'Returns the latest price of 1 MATIC.'\n                    },\n                    {\n                        label: 'Get Historical MATIC Price [PRO]',\n                        name: GET_HISTORICAL_MATIC_PRICE.name,\n                        description: 'Returns the historical price of 1 MATIC.'\n                    }\n                ],\n                default: GET_MATIC_BALANCE.name\n            }\n        ] as INodeParams[]\n\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                description: 'Network to execute API: Test or Real',\n                options: [\n                    {\n                        label: 'Polygon Testnet',\n                        name: 'testnet'\n                    },\n                    {\n                        label: 'Polygon Mainnet',\n                        name: 'mainnet'\n                    }\n                ],\n                default: 'testnet'\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'PolygonScan API Key',\n                        name: 'polygonscanApi'\n                    }\n                ],\n                default: 'polygonscanApi'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Polygon Address',\n                name: 'address',\n                type: 'string',\n                show: {\n                    'actions.api': [\n                        GET_MATIC_BALANCE.name,\n                        GET_HISTORICAL_MATIC_BALANCE.name,\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_BLOCKS_VALIDATED.name,\n                        GET_ABI.name,\n                        GET_CONTRACT_SOURCE_CODE.name,\n                        GET_ERC20_TOKEN_BALANCE.name,\n                        GET_HISTORICAL_ERC20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Block Number',\n                name: 'blockno',\n                type: 'number',\n                description: 'the block number to check balance for eg. 2000000',\n                show: {\n                    'actions.api': [\n                        GET_HISTORICAL_MATIC_BALANCE.name,\n                        GET_HISTORICAL_ERC20_TOKEN_SUPPLY.name,\n                        GET_HISTORICAL_ERC20_TOKEN_BALANCE.name\n                    ]\n                }\n            },\n            {\n                label: 'Start Block',\n                name: 'startBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to start searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 0\n            },\n            {\n                label: 'End Block',\n                name: 'endBlock',\n                type: 'number',\n                optional: true,\n                description: 'the block number to stop searching for transactions',\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                }\n            },\n            {\n                label: 'Page',\n                name: 'page',\n                type: 'number',\n                optional: true,\n                description: 'the page number, if pagination is enabled',\n                show: {\n                    'actions.api': [\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        GET_BLOCKS_VALIDATED.name\n                    ]\n                },\n                default: 1\n            },\n            {\n                label: 'Offset',\n                name: 'offset',\n                type: 'number',\n                optional: true,\n                description: 'the number of transactions displayed per page',\n                show: {\n                    'actions.api': [\n                        GET_NORMAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS.name,\n                        GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name,\n                        GET_BLOCKS_VALIDATED.name\n                    ]\n                },\n                default: 10\n            },\n            {\n                label: 'Sort By',\n                name: 'sortBy',\n                type: 'options',\n                optional: true,\n                options: SORT_BY,\n                show: {\n                    'actions.api': [GET_NORMAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS.name, GET_INTERNAL_TRANSACTIONS_BY_BLOCK.name]\n                },\n                default: 'desc'\n            },\n            {\n                label: 'Transaction Hash',\n                name: 'txhash',\n                type: 'string',\n                description: 'the string representing the transaction hash to check for internal transactions',\n                show: {\n                    'actions.api': [GET_INTERNAL_TRANSACTIONS_BY_HASH.name, CHECK_TRANSACTION_RECEIPT_STATUS.name]\n                }\n            },\n            {\n                label: 'Block Type',\n                name: 'blockType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'blocks',\n                        name: 'blocks'\n                    }\n                ],\n                default: 'blocks',\n                show: {\n                    'actions.api': [GET_BLOCKS_VALIDATED.name]\n                }\n            },\n            {\n                label: 'Contract Address',\n                name: 'contractAddress',\n                type: 'string',\n                description: 'the contract address of the ERC-20 token',\n                show: {\n                    'actions.api': [\n                        GET_ERC20_TOKEN_SUPPLY.name,\n                        GET_ERC20_TOKEN_BALANCE.name,\n                        GET_HISTORICAL_ERC20_TOKEN_SUPPLY.name,\n                        GET_HISTORICAL_ERC20_TOKEN_BALANCE.name,\n                        GET_TOKEN_INFO.name\n                    ]\n                }\n            },\n            {\n                label: 'Tag',\n                name: 'tag',\n                type: 'options',\n                options: [{ label: 'latest', name: 'latest' }],\n                default: 'latest',\n                show: {\n                    'actions.api': [GET_ERC20_TOKEN_BALANCE.name]\n                }\n            },\n            {\n                label: 'Start Time',\n                name: 'startTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.api': [GET_HISTORICAL_MATIC_PRICE.name]\n                }\n            },\n            {\n                label: 'End Time',\n                name: 'endTime',\n                type: 'date',\n                optional: true,\n                show: {\n                    'actions.api': [GET_HISTORICAL_MATIC_PRICE.name]\n                }\n            }\n        ]\n    }\n\n    getNetwork(network: string): string {\n        switch (network) {\n            case 'mainnet':\n                return 'https://api.polygonscan.com/api'\n            case 'testnet':\n            default:\n                return 'https://api-testnet.polygonscan.com/api'\n        }\n    }\n\n    getBaseParams(api: string) {\n        const operation = OPERATIONS.filter(({ name }) => name === api)[0]\n        return { module: operation.module, action: operation.action }\n    }\n\n    getISODate(date: Date) {\n        return date.toISOString().split('T')[0]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const { actions, networks, inputParameters, credentials } = nodeData\n\n        if (actions === undefined || inputParameters === undefined || credentials === undefined || networks === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actions.api as string\n        const network = networks.network as string\n        const apiKey = credentials.apiKey as string\n\n        const address = inputParameters.address as string\n        const startblock = inputParameters.startBlock as number\n        const endblock = inputParameters.endBlock as number\n        const page = inputParameters.page as number\n        const offset = inputParameters.offset as number\n        const sort = inputParameters.sortBy as string\n        const txhash = inputParameters.txhash as string\n        const blocktype = inputParameters.blockType as string\n        const contractaddress = inputParameters.contractAddress as string\n        const tag = inputParameters.tag as string\n        const startTime = inputParameters.startTime as string\n        const endTime = inputParameters.endTime as string\n\n        const startdate = startTime ? this.getISODate(new Date(startTime)) : undefined\n        const enddate = endTime ? this.getISODate(new Date(endTime)) : undefined\n\n        const url = this.getNetwork(network)\n        const { module, action } = this.getBaseParams(api)\n\n        const queryParameters = {\n            module,\n            action,\n            address,\n            apiKey,\n            startblock,\n            endblock,\n            page,\n            offset,\n            sort,\n            txhash,\n            blocktype,\n            contractaddress,\n            tag,\n            startdate,\n            enddate\n        }\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: PolygonScan }\n"
  },
  {
    "path": "packages/components/nodes/PolygonScan/constants.ts",
    "content": "// Account\nexport const GET_MATIC_BALANCE = {\n    name: 'getMaticBalance',\n    module: 'account',\n    action: 'balance'\n}\n\nexport const GET_HISTORICAL_MATIC_BALANCE = {\n    name: 'getHistoricalMaticBalance',\n    module: 'account',\n    action: 'balancehistory'\n}\n\nexport const GET_NORMAL_TRANSACTIONS = {\n    name: 'getTransactions',\n    module: 'account',\n    action: 'txlist'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS = {\n    name: 'getInternalTransactions',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_HASH = {\n    name: 'getInternalTransactionsByHash',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_INTERNAL_TRANSACTIONS_BY_BLOCK = {\n    name: 'getInternalTransactionsByBlock',\n    module: 'account',\n    action: 'txlistinternal'\n}\n\nexport const GET_BLOCKS_VALIDATED = {\n    name: 'getBlocksValidated',\n    module: 'account',\n    action: 'getminedblocks'\n}\n\n// Contracts\nexport const GET_ABI = {\n    name: 'getAbi',\n    module: 'contract',\n    action: 'getabi'\n}\n\nexport const GET_CONTRACT_SOURCE_CODE = {\n    name: 'getContractSourceCode',\n    module: 'contract',\n    action: 'getsourcecode'\n}\n\n// Transactions\nexport const CHECK_TRANSACTION_RECEIPT_STATUS = {\n    name: 'getTransactionReceiptStatus',\n    module: 'transaction',\n    action: 'gettxreceiptstatus'\n}\n\n// Tokens\nexport const GET_ERC20_TOKEN_SUPPLY = {\n    name: 'getErc20TokenSupply',\n    module: 'stats',\n    action: 'tokensupply'\n}\n\nexport const GET_ERC20_TOKEN_BALANCE = {\n    name: 'getErc20TokenBalance',\n    module: 'account',\n    action: 'tokenbalance'\n}\n\nexport const GET_HISTORICAL_ERC20_TOKEN_SUPPLY = {\n    name: 'getHistoricalErc20TokenSupply',\n    module: 'stats',\n    action: 'tokensupplyhistory'\n}\n\nexport const GET_HISTORICAL_ERC20_TOKEN_BALANCE = {\n    name: 'getHistoricalErc20TokenBalance',\n    module: 'account',\n    action: 'tokenbalancehistory'\n}\n\nexport const GET_TOKEN_INFO = {\n    name: 'getTokenInfo',\n    module: 'token',\n    action: 'tokeninfo'\n}\n\n// Stats\nexport const GET_MATIC_PRICE = {\n    name: 'getMaticPrice',\n    module: 'stats',\n    action: 'maticprice'\n}\n\nexport const GET_HISTORICAL_MATIC_PRICE = {\n    name: 'getHistoricalMaticPrice',\n    module: 'stats',\n    action: 'ethdailyprice'\n}\n\nexport const OPERATIONS = [\n    GET_MATIC_BALANCE,\n    GET_HISTORICAL_MATIC_BALANCE,\n    GET_NORMAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS,\n    GET_INTERNAL_TRANSACTIONS_BY_HASH,\n    GET_INTERNAL_TRANSACTIONS_BY_BLOCK,\n    GET_BLOCKS_VALIDATED,\n    GET_ABI,\n    GET_CONTRACT_SOURCE_CODE,\n    CHECK_TRANSACTION_RECEIPT_STATUS,\n    GET_ERC20_TOKEN_SUPPLY,\n    GET_ERC20_TOKEN_BALANCE,\n    GET_HISTORICAL_ERC20_TOKEN_SUPPLY,\n    GET_HISTORICAL_ERC20_TOKEN_BALANCE,\n    GET_TOKEN_INFO,\n    GET_MATIC_PRICE,\n    GET_HISTORICAL_MATIC_PRICE\n] as const\n\nexport const SORT_BY = [\n    { label: 'Desc', name: 'desc' },\n    { label: 'Asc', name: 'asc' }\n]\n"
  },
  {
    "path": "packages/components/nodes/QuickNode/QuickNode.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { NETWORK } from '../../src/ChainNetwork'\nimport { ethOperations, IETHOperation, operationCategoryMapping, qnSupportedNetworks } from '../../src/ETHOperations'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport {\n    arbTraceOperationsNetworks,\n    avaxOperations,\n    avaxOperationsNetworks,\n    debugOperations,\n    debugOperationsNetworks,\n    fantomOperations,\n    fantomOperationsNetworks,\n    nftOperations,\n    nftOperationsNetworks,\n    platformOperations,\n    tokenOperations,\n    tokenOperationsNetworks,\n    traceOperations,\n    traceOperationsNetworks\n} from './extendedOperation'\nimport { QuickNodeSupportedNetworks } from './supportedNetwork'\nimport { solanaOperations, solanaOperationsNetworks } from './solanaOperation'\n\nclass QuickNode implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'QuickNode'\n        this.name = 'quickNode'\n        this.icon = 'quicknode.svg'\n        this.type = 'action'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Perform QuickNode onchain operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...QuickNodeSupportedNetworks]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'QuickNode Endpoints',\n                        name: 'quickNodeEndpoints'\n                    }\n                ],\n                default: 'quickNodeEndpoints'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'EVM Chain API',\n                        name: 'chainAPI',\n                        description: 'API for fetching standard onchain data using QuickNode supported calls',\n                        show: {\n                            'networks.network': qnSupportedNetworks\n                        }\n                    },\n                    {\n                        label: 'NFT API',\n                        name: 'nftAPI',\n                        description: 'API for fetching cross-chain NFT data using QuickNode supported calls',\n                        show: {\n                            'networks.network': [...nftOperationsNetworks, ...solanaOperationsNetworks]\n                        }\n                    },\n                    {\n                        label: 'Token API',\n                        name: 'tokenAPI',\n                        description: 'API to look up ERC-20 tokens by wallet, token transfers, and token details instantly',\n                        show: {\n                            'networks.network': tokenOperationsNetworks\n                        }\n                    },\n                    {\n                        label: 'Debug API',\n                        name: 'debugAPI',\n                        description: 'API for fetching debug traces using QuickNode supported calls (Trace Mode required)',\n                        show: {\n                            'networks.network': debugOperationsNetworks\n                        }\n                    },\n                    {\n                        label: 'Trace API',\n                        name: 'traceAPI',\n                        description:\n                            'API for fetching traces using QuickNode supported calls (Trace Mode required and supported only on OpenEthereum & Erigon)',\n                        show: {\n                            'networks.network': [...traceOperationsNetworks, ...arbTraceOperationsNetworks]\n                        }\n                    },\n                    {\n                        label: 'Avalanche API',\n                        name: 'avaxAPI',\n                        description: 'API for fetching Avalanche on-chain data using QuickNode supported calls',\n                        show: {\n                            'networks.network': avaxOperationsNetworks\n                        }\n                    },\n                    {\n                        label: 'Platform API',\n                        name: 'platformAPI',\n                        description: 'API for fetching Avalanche Platform data using QuickNode supported calls',\n                        show: {\n                            'networks.network': avaxOperationsNetworks\n                        }\n                    },\n                    {\n                        label: 'Fantom API',\n                        name: 'fantomAPI',\n                        description: 'API for fetching Fantom on-chain data using QuickNode supported calls',\n                        show: {\n                            'networks.network': fantomOperationsNetworks\n                        }\n                    },\n                    {\n                        label: 'Solana API',\n                        name: 'solanaAPI',\n                        description: 'API for fetching Solana on-chain data using QuickNode supported calls',\n                        show: {\n                            'networks.network': solanaOperationsNetworks\n                        }\n                    }\n                ],\n                default: 'chainAPI'\n            },\n            {\n                label: 'Chain Category',\n                name: 'chainCategory',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Retrieving Blocks',\n                        name: 'retrievingBlocks',\n                        description: 'Retrieve onchain blocks data'\n                    },\n                    {\n                        label: 'EVM/Smart Contract Execution',\n                        name: 'evmExecution',\n                        description: 'Execute or submit transaction onto blockchain'\n                    },\n                    {\n                        label: 'Reading Transactions',\n                        name: 'readingTransactions',\n                        description: 'Read onchain transactions data'\n                    },\n                    {\n                        label: 'Account Information',\n                        name: 'accountInformation',\n                        description: 'Retrieve onchain account information'\n                    },\n                    {\n                        label: 'Event Logs',\n                        name: 'eventLogs',\n                        description: 'Fetch onchain logs'\n                    },\n                    {\n                        label: 'Chain Information',\n                        name: 'chainInformation',\n                        description: 'Get general selected blockchain information'\n                    },\n                    {\n                        label: 'Retrieving Uncles',\n                        name: 'retrievingUncles',\n                        description: 'Retrieve onchain uncles blocks data'\n                    },\n                    {\n                        label: 'Filters',\n                        name: 'filters',\n                        description: 'Get block filters and logs, or create new filter'\n                    }\n                ],\n                show: {\n                    'inputParameters.api': ['chainAPI']\n                }\n            },\n            {\n                label: 'Chain Category',\n                name: 'chainCategory',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Reading & Writing Transactions',\n                        name: 'readWriteTransactions',\n                        description: 'Read and Write transactins onto Solana chain'\n                    },\n                    {\n                        label: 'Getting Blocks',\n                        name: 'gettingBlocks',\n                        description: 'Get Solana blocks data'\n                    },\n                    {\n                        label: 'Account Information',\n                        name: 'accountInformation',\n                        description: 'Retrieve Solana onchain account information'\n                    },\n                    {\n                        label: 'Network Information',\n                        name: 'networkInformation',\n                        description: 'Get Solana network onchain information'\n                    },\n                    {\n                        label: 'Slot Information',\n                        name: 'slotInformation',\n                        description: 'Fetch Solana slot information'\n                    },\n                    {\n                        label: 'Node Information',\n                        name: 'nodeInformation',\n                        description: 'Retrieve Solana node onchain information'\n                    },\n                    {\n                        label: 'Token Information',\n                        name: 'tokenInformation',\n                        description: 'Fetch Solana onchain token information'\n                    },\n                    {\n                        label: 'Network Inflation',\n                        name: 'networkInflation',\n                        description: 'Retrieve Solana network inflation onchain data'\n                    }\n                ],\n                show: {\n                    'inputParameters.api': ['solanaAPI']\n                }\n            },\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'asyncOptions',\n                loadMethod: 'getOperations'\n            },\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: `[\n  \"param1\",\n  \"param2\"\n]`,\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]',\n                show: {\n                    'inputParameters.api': ['chainAPI', 'tokenAPI', 'traceAPI', 'debugAPI', 'fantomAPI']\n                }\n            },\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: `{\n  \"param1\": \"value1\"\n}`,\n                optional: true,\n                description: 'Operation parameters in JSON. Ex: {\"param1\": \"value1\"}',\n                show: {\n                    'inputParameters.api': ['nftAPI', 'avaxAPI', 'platformAPI']\n                },\n                hide: {\n                    'inputParameters.operation': ['qn_verifyNFTsOwner', 'qn_fetchNFTsByCreator', 'requestAirdrop']\n                }\n            },\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: `[\n  \"param1\",\n  \"param2\"\n]`,\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]',\n                show: {\n                    'inputParameters.operation': ['qn_verifyNFTsOwner', 'qn_fetchNFTsByCreator', 'requestAirdrop']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const inputParametersData = nodeData.inputParameters\n            const networksData = nodeData.networks\n\n            if (networksData === undefined || inputParametersData === undefined) {\n                return returnData\n            }\n\n            const api = inputParametersData.api as string\n            const chainCategory = inputParametersData.chainCategory as string\n            const network = networksData.network as NETWORK\n\n            const operations = getSelectedOperations(api).filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'quicknode') &&\n                    op.providerNetworks['quicknode'].includes(network)\n            )\n\n            if (api === 'chainAPI' && !chainCategory) return returnData\n            if (api === 'solanaAPI' && !chainCategory) return returnData\n\n            let filteredOperations: IETHOperation[] = operations\n            if (api === 'chainAPI' || api === 'solanaAPI')\n                filteredOperations = operations.filter((op: IETHOperation) => op.parentGroup === operationCategoryMapping[chainCategory])\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        // GET api\n        const api = inputParametersData.api as string\n\n        // GET credentials\n        const httpProvider = credentials.httpProvider as string\n\n        // GET operation\n        const operation = inputParametersData.operation as string\n\n        let uri = httpProvider\n        let responseData: any // tslint:disable-line: no-any\n        let bodyParameters: any[] = [] // tslint:disable-line: no-any\n        const returnData: ICommonObject[] = []\n        const headers: AxiosRequestHeaders = {\n            'Content-Type': 'application/json'\n        }\n\n        const parameters = inputParametersData.parameters as string\n        if (parameters) {\n            try {\n                bodyParameters = JSON.parse(parameters.replace(/\\s/g, ''))\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        try {\n            let totalOperations: IETHOperation[] = []\n            totalOperations = getSelectedOperations(api)\n\n            const result = totalOperations.find((obj) => {\n                return obj.value === operation\n            })\n\n            if (result === undefined) throw new Error('Invalid Operation')\n\n            const requestBody = JSON.parse(JSON.stringify(result.body))\n            const bodyParams = requestBody.params\n            requestBody.params = Array.isArray(bodyParameters) ? bodyParameters.concat(bodyParams) : bodyParameters\n\n            if (result.overrideUrl) {\n                uri = result.overrideUrl.replace('http://sample-endpoint-name.network.quiknode.pro/token-goes-here/', httpProvider)\n            }\n\n            if (api === 'nftAPI') {\n                headers['x-qn-api-version'] = 1\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: result.method as Method,\n                url: uri,\n                data: requestBody,\n                headers\n            }\n\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) returnData.push(...responseData)\n        else returnData.push(responseData)\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nconst getSelectedOperations = (api: string) => {\n    switch (api) {\n        case 'chainAPI':\n            return ethOperations\n        case 'nftAPI':\n            return nftOperations\n        case 'debugAPI':\n            return debugOperations\n        case 'traceAPI':\n            return traceOperations\n        case 'tokenAPI':\n            return tokenOperations\n        case 'avaxAPI':\n            return avaxOperations\n        case 'fantomAPI':\n            return fantomOperations\n        case 'platformAPI':\n            return platformOperations\n        case 'solanaAPI':\n            return solanaOperations\n        default:\n            return ethOperations\n    }\n}\n\nmodule.exports = { nodeClass: QuickNode }\n"
  },
  {
    "path": "packages/components/nodes/QuickNode/QuickNodeTrigger.ts",
    "content": "import { INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport { NETWORK } from '../../src/ChainNetwork'\nimport { QuickNodeSupportedNetworks } from './supportedNetwork'\nimport { subscribeOperations, unsubscribeOperations } from './subscribeOperation'\nimport { IETHOperation } from '../../src/ETHOperations'\nimport WebSocket from 'ws'\n\nclass QuickNodeTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'QuickNode Trigger'\n        this.name = 'quickNodeTrigger'\n        this.icon = 'quicknode.svg'\n        this.type = 'trigger'\n        this.category = 'Network Provider'\n        this.version = 1.0\n        this.description = 'Start workflow whenever subscribed event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...QuickNodeSupportedNetworks]\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'QuickNode Endpoints',\n                        name: 'quickNodeEndpoints'\n                    }\n                ],\n                default: 'quickNodeEndpoints'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Subscribe Operation',\n                name: 'subscribeOperation',\n                type: 'asyncOptions',\n                loadMethod: 'getSubscribeOperations'\n            },\n            {\n                label: 'Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: `[\n  \"param1\",\n  \"param2\"\n]`,\n                optional: true,\n                description: 'Operation parameters in array. Ex: [\"param1\", \"param2\"]'\n            },\n            {\n                label: 'Unsubscribe Operation',\n                name: 'unsubscribeOperation',\n                type: 'asyncOptions',\n                loadMethod: 'getUnsubscribeOperations'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getSubscribeOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const networksData = nodeData.networks\n\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            const network = networksData.network as NETWORK\n\n            const totalOperations = subscribeOperations\n            const filteredOperations = totalOperations.filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'quicknode') &&\n                    op.providerNetworks['quicknode'].includes(network)\n            )\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        },\n\n        async getUnsubscribeOperations(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const networksData = nodeData.networks\n\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            const network = networksData.network as NETWORK\n\n            const totalOperations = unsubscribeOperations\n            const filteredOperations = totalOperations.filter(\n                (op: IETHOperation) =>\n                    Object.prototype.hasOwnProperty.call(op.providerNetworks, 'quicknode') &&\n                    op.providerNetworks['quicknode'].includes(network)\n            )\n\n            for (const op of filteredOperations) {\n                returnData.push({\n                    label: op.name,\n                    name: op.value,\n                    parentGroup: op.parentGroup,\n                    description: op.description,\n                    inputParameters: op.inputParameters,\n                    exampleParameters: op.exampleParameters,\n                    exampleResponse: op.exampleResponse\n                })\n            }\n            return returnData\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (credentials === undefined) {\n            throw new Error('Missing credentials')\n        }\n\n        // GET credentials\n        const wssProvider = credentials.wssProvider as string\n\n        // GET subscribeOperation\n        const subscribeOperation = inputParametersData.subscribeOperation as string\n\n        // GET parameters\n        let bodyParameters: any\n        const parameters = inputParametersData.parameters as string\n        if (parameters) {\n            try {\n                bodyParameters = JSON.parse(parameters.replace(/\\s/g, ''))\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        }\n\n        const emitEventKey = nodeData.emitEventKey as string\n\n        const result = subscribeOperations.find((obj) => {\n            return obj.value === subscribeOperation\n        })\n\n        if (result === undefined) throw new Error('Invalid Operation')\n\n        const requestBody = result.body\n        requestBody.params = bodyParameters\n\n        const ws = new WebSocket(wssProvider)\n\n        ws.on('open', function open() {\n            ws.send(JSON.stringify(requestBody))\n        })\n\n        let subscriptionID = ''\n        ws.on('message', (data) => {\n            const messageData = JSON.parse(data as any)\n\n            if (messageData.method) {\n                this.emit(emitEventKey, returnNodeExecutionData(messageData))\n            } else {\n                subscriptionID = messageData.result\n                this.providers[emitEventKey] = { provider: ws, filter: subscriptionID }\n            }\n        })\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const provider: WebSocket = this.providers[emitEventKey].provider\n            const subscriptionID = this.providers[emitEventKey].filter\n\n            const result = unsubscribeOperations.find((obj) => {\n                return obj.value === (inputParametersData.unsubscribeOperation as string)\n            })\n\n            if (result === undefined) throw new Error('Invalid Operation')\n\n            const requestBody = result.body\n            requestBody.params = [subscriptionID]\n\n            provider.send(JSON.stringify(requestBody))\n            provider.close(1000)\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: QuickNodeTrigger }\n"
  },
  {
    "path": "packages/components/nodes/QuickNode/extendedOperation.ts",
    "content": "import { NETWORK, NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport { IETHOperation, infuraSupportedNetworks } from '../../src/ETHOperations'\n\nexport const debugOperationsNetworks = [...infuraSupportedNetworks, NETWORK.ARBITRUM_NOVA, NETWORK.BSC, NETWORK.BSC_TESTNET, NETWORK.CELO]\n\nexport const debugOperations = [\n    {\n        name: 'Debug Trace Block By Hash (debug_traceBlockByHash)',\n        value: 'debug_traceBlockByHash',\n        parentGroup: 'Debug Traces',\n        description: 'Replay the block that is already present in the database (Trace Mode required)',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: debugOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'debug_traceBlockByHash',\n            params: [{ tracer: 'callTracer' }],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">HASH</code> - Hash of the block to be traced.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x97b49e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576\"\n]`\n    },\n    {\n        name: 'Debug Trace Block By Number (debug_traceBlockByNumber)',\n        value: 'debug_traceBlockByNumber',\n        parentGroup: 'Debug Traces',\n        description: 'Replay the block that is already present in the database (Trace Mode required)',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: debugOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'debug_traceBlockByNumber',\n            params: [{ tracer: 'callTracer' }],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xccde12\"\n]`\n    },\n    {\n        name: 'Debug Trace Call (debug_traceCall)',\n        value: 'debug_traceCall',\n        parentGroup: 'Debug Traces',\n        description: `Let's you run eth_call on top of a block (Trace Mode required)`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: debugOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'debug_traceCall',\n            params: [{ tracer: 'callTracer' }],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The transaction call object</li>\n            <ul>\n                <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n                <li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n                <li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.</li>\n                <li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n                <li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n                <li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n            </ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"from\": \"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\n    \"to\": \"0xd46e8dd67c5d32be8058bb8eb970870f07244567\",\n    \"gas\": \"0x76c0\",\n    \"gasPrice\": \"0x9184e72a000\",\n    \"value\": \"0x9184e72a\",\n    \"data\": \"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\"\n  }, \n  \"latest\"\n]`\n    },\n    {\n        name: 'Debug Trace Transaction (debug_traceTransaction)',\n        value: 'debug_traceTransaction',\n        parentGroup: 'Debug Traces',\n        description: 'Returns all traces of given transaction (Trace Mode required)',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: debugOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'debug_traceBlockByHash',\n            params: [{ tracer: 'callTracer' }],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">HASH</code> - The hash of a transaction.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x97b49e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576\"\n]`\n    }\n] as IETHOperation[]\n\nexport const arbTraceOperationsNetworks = [NETWORK.ARBITRUM, NETWORK.ARBITRUM_GOERLI]\nexport const arbTraceOperations = [\n    {\n        name: 'Arbtrace Block (arbtrace_block )',\n        value: 'arbtrace_block',\n        parentGroup: 'Traces',\n        description: 'Returns traces created at given block (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_block ',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xccb93d\"\n]`\n    },\n    {\n        name: 'Arbtrace Call (arbtrace_call)',\n        value: 'arbtrace_call',\n        parentGroup: 'Traces',\n        description:\n            'Executes a new message call and returns a number of possible traces (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_call',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The transaction call object</li>\n            <ul>\n                <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n                <li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n                <li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.</li>\n                <li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n                <li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n                <li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n            </ul>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"from\":null,\n    \"to\":\"0x6b175474e89094c44da98b954eedeac495271d0f\",\n    \"data\":\"0x70a082310000000000000000000000006E0d01A76C3Cf4288372a29124A26D4353EE51BE\"\n  },\n  [\"trace\"],\n  \"latest\"\n]`\n    },\n    {\n        name: 'Arbtrace Call Many (arbtrace_callMany)',\n        value: 'arbtrace_callMany',\n        parentGroup: 'Traces',\n        description:\n            'Performs multiple call traces on top of the same block. i.e. transaction n will be executed on top of a pending block with all n-1 transactions applied (traced) first. Allows to trace dependent transactions. (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_callMany',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Array</code> - Call array, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">Object</code> - The transaction call object</li>\n                <ul>\n                    <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n                    <li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n                    <li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.</li>\n                    <li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n                    <li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n                    <li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n                </ul>\n                <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n                <ul>\n                    <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                    <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                    <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  [\n    [\n      {\n        \"from\":\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\n        \"to\":\"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\",\n        \"value\":\"0x186a0\"\n      },\n      [\"trace\"]\n    ],\n    [\n      {\n        \"from\":\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\n        \"to\":\"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\",\n        \"value\":\"0x186a0\"\n      },\n      [\"trace\"]\n    ]\n  ],\n  \"latest\"\n]`\n    },\n    {\n        name: 'Arbtrace Filter (arbtrace_filter)',\n        value: 'arbtrace_filter',\n        parentGroup: 'Traces',\n        description: 'Returns traces matching given filter (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_filter',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">fromBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional) integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n                <li><code class=\"inline\">toBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional) integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n                <li><code class=\"inline\">fromaddress</code>: <code class=\"inline\">Array</code> - (optional) Addresses of the Senders.</li>\n                <li><code class=\"inline\">toaddress</code>: <code class=\"inline\">Array</code> - (optional) Addresses of the Receivers.</li>\n                <li><code class=\"inline\">after</code>: <code class=\"inline\">QUANTITY</code> - (optional) The offset trace number.</li>\n                <li><code class=\"inline\">count</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer number of traces to display in a batch.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"fromBlock\":\"0xccb943\",\n    \"toBlock\":\"latest\",\n    \"fromAddress\":[\"0xEdC763b3e418cD14767b3Be02b667619a6374076\"]\n  }\n]`\n    },\n    {\n        name: 'Arbtrace Raw Transaction (arbtrace_rawTransaction)',\n        value: 'arbtrace_rawTransaction',\n        parentGroup: 'Traces',\n        description:\n            'Traces a call to eth_sendRawTransaction without making the call, returning the traces(Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_rawTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Data</code> - Raw transaction data/string.</li>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x02f8740181948459682f0085275c2c9f8b82520894885885521990b53fd00556c143ea056dd2f62a128808cc0c47d9477f9080c080a037437ba52140dbac1d7dc65cdb58531e038930c82314817f91cb8d8ea36a2bd0a001e134479d567b8595d77f61106cad34e62ed356d6971bc08fe0363a0696dd94\",\n  [\"trace\"]\n]`\n    },\n    {\n        name: 'Arbtrace Replay Block Transactions (arbtrace_replayBlockTransactions)',\n        value: 'arbtrace_replayBlockTransactions',\n        parentGroup: 'Traces',\n        description:\n            'Replays all transactions in a block returning the requested traces for each transaction (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_replayBlockTransactions',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xccb93d\",\n  [\"trace\"]\n]`\n    },\n    {\n        name: 'Arbtrace Replay Transaction (arbtrace_replayTransaction)',\n        value: 'arbtrace_replayTransaction',\n        parentGroup: 'Traces',\n        description: 'Replays a transaction, returning the traces (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_replayTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">HASH</code> - The hash of a transaction.</li>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x3277c743c14e482243862c03a70e83ccb52e25cb9e54378b20a8303f15cb985d\",\n  [\"trace\"]\n]`\n    },\n    {\n        name: 'Arbtrace Transaction (arbtrace_transaction)',\n        value: 'arbtrace_transaction',\n        parentGroup: 'Traces',\n        description: 'Returns all traces of given transaction(Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: arbTraceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'arbtrace_transaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">HASH</code> - The hash of a transaction.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x3277c743c14e482243862c03a70e83ccb52e25cb9e54378b20a8303f15cb985d\"\n]`\n    }\n] as IETHOperation[]\n\nexport const traceOperationsNetworks = [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.GNOSIS, NETWORK.BSC, NETWORK.BSC_TESTNET, NETWORK.FANTOM]\nexport const traceOperations = [\n    {\n        name: 'Trace Block (trace_block)',\n        value: 'trace_block',\n        parentGroup: 'Traces',\n        description: 'Returns traces created at given block (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_block',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xccb93d\"\n]`\n    },\n    {\n        name: 'Trace Call (trace_call)',\n        value: 'trace_call',\n        parentGroup: 'Traces',\n        description:\n            'Executes a new message call and returns a number of possible traces (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_call',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The transaction call object</li>\n            <ul>\n                <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n                <li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n                <li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.</li>\n                <li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n                <li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n                <li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n            </ul>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"from\":null,\n    \"to\":\"0x6b175474e89094c44da98b954eedeac495271d0f\",\n    \"data\":\"0x70a082310000000000000000000000006E0d01A76C3Cf4288372a29124A26D4353EE51BE\"\n  },\n  [\"trace\"],\n  \"latest\"\n]`\n    },\n    {\n        name: 'Trace Call Many (trace_callMany)',\n        value: 'trace_callMany',\n        parentGroup: 'Traces',\n        description:\n            'Performs multiple call traces on top of the same block. i.e. transaction n will be executed on top of a pending block with all n-1 transactions applied (traced) first. Allows to trace dependent transactions. (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_callMany',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Array</code> - Call array, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">Object</code> - The transaction call object</li>\n                <ul>\n                    <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n                    <li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n                    <li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.</li>\n                    <li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n                    <li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n                    <li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n                </ul>\n                <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n                <ul>\n                    <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                    <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                    <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  [\n    [\n      {\n        \"from\":\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\n        \"to\":\"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\",\n        \"value\":\"0x186a0\"\n      },\n      [\"trace\"]\n    ],\n    [\n      {\n        \"from\":\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\n        \"to\":\"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\",\n        \"value\":\"0x186a0\"\n      },\n      [\"trace\"]\n    ]\n  ],\n  \"latest\"\n]`\n    },\n    {\n        name: 'Trace Filter (trace_filter)',\n        value: 'trace_filter',\n        parentGroup: 'Traces',\n        description: 'Returns traces matching given filter (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_filter',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">fromBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional) integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n                <li><code class=\"inline\">toBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional) integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n                <li><code class=\"inline\">fromaddress</code>: <code class=\"inline\">Array</code> - (optional) Addresses of the Senders.</li>\n                <li><code class=\"inline\">toaddress</code>: <code class=\"inline\">Array</code> - (optional) Addresses of the Receivers.</li>\n                <li><code class=\"inline\">after</code>: <code class=\"inline\">QUANTITY</code> - (optional) The offset trace number.</li>\n                <li><code class=\"inline\">count</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer number of traces to display in a batch.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"fromBlock\":\"0xccb943\",\n    \"toBlock\":\"latest\",\n    \"fromAddress\":[\"0xEdC763b3e418cD14767b3Be02b667619a6374076\"]\n  }\n]`\n    },\n    {\n        name: 'Trace Raw Transaction (trace_rawTransaction)',\n        value: 'trace_rawTransaction',\n        parentGroup: 'Traces',\n        description:\n            'Traces a call to eth_sendRawTransaction without making the call, returning the traces(Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_rawTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Data</code> - Raw transaction data/string.</li>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x02f8740181948459682f0085275c2c9f8b82520894885885521990b53fd00556c143ea056dd2f62a128808cc0c47d9477f9080c080a037437ba52140dbac1d7dc65cdb58531e038930c82314817f91cb8d8ea36a2bd0a001e134479d567b8595d77f61106cad34e62ed356d6971bc08fe0363a0696dd94\",\n  [\"trace\"]\n]`\n    },\n    {\n        name: 'Trace Replay Block Transactions (trace_replayBlockTransactions)',\n        value: 'trace_replayBlockTransactions',\n        parentGroup: 'Traces',\n        description:\n            'Replays all transactions in a block returning the requested traces for each transaction (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_replayBlockTransactions',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xccb93d\",\n  [\"trace\"]\n]`\n    },\n    {\n        name: 'Trace Replay Transaction (trace_replayTransaction)',\n        value: 'trace_replayTransaction',\n        parentGroup: 'Traces',\n        description: 'Replays a transaction, returning the traces (Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_replayTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">HASH</code> - The hash of a transaction.</li>\n            <li><code class=\"inline\">Array</code> - Type of trace, one or more of:</li>\n            <ul>\n                <li><code class=\"inline\">vmTrace</code>: To get a full trace of virtual machine's state during the execution of the given of given transaction, including for any subcalls.</li>\n                <li><code class=\"inline\">trace</code>: To get the basic trace of the given transaction.</li>\n                <li><code class=\"inline\">statediff</code>: To get information on altered Ethereum state due to execution of the given transaction.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x3277c743c14e482243862c03a70e83ccb52e25cb9e54378b20a8303f15cb985d\",\n  [\"trace\"]\n]`\n    },\n    {\n        name: 'Trace Transaction (trace_transaction)',\n        value: 'trace_transaction',\n        parentGroup: 'Traces',\n        description: 'Returns all traces of given transaction(Trace Mode required, and supported only on OpenEthereum & Erigon).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: traceOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'trace_transaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">HASH</code> - The hash of a transaction.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x3277c743c14e482243862c03a70e83ccb52e25cb9e54378b20a8303f15cb985d\"\n]`\n    },\n    ...arbTraceOperations\n] as IETHOperation[]\n\nexport const nftOperationsNetworks = [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.BSC, NETWORK.BSC_TESTNET]\nexport const solanaNetworks = [NETWORK.SOLANA, NETWORK.SOLANA_DEVNET, NETWORK.SOLANA_TESTNET]\nexport const nftOperations = [\n    {\n        name: 'Fetch NFT Collection Details (qn_fetchNFTCollectionDetails)',\n        value: 'qn_fetchNFTCollectionDetails',\n        parentGroup: 'NFT',\n        description: 'Returns collection details for specified contracts.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: nftOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_fetchNFTCollectionDetails',\n            params: {},\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">contracts</code>: <code class=\"inline\">(Array of Strings)</code> - List of NFT contract addresses you'd like to get collection details data from. You may include up to 10 contract addresses per request.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"contracts\": [\n    \"0x60E4d786628Fea6478F785A6d7e704777c86a7c6\",\n    \"0x7Bd29408f11D2bFC23c34f18275bBf23bB716Bc7\"\n  ]\n}`\n    },\n    {\n        name: 'Fetch NFTs (qn_fetchNFTs)',\n        value: 'qn_fetchNFTs',\n        parentGroup: 'NFT',\n        description: 'Returns aggregated data on NFTs for a given wallet.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: [...nftOperationsNetworks, ...solanaNetworks]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_fetchNFTs',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">wallet</code>: <code class=\"inline\">String</code> - The wallet address to check for NFTs.</li>\n                <li><code class=\"inline\">contracts</code>: <code class=\"inline\">Array of Strings</code> - (optional) List of NFT contract addresses you'd like to get ownership data from. Contract addresses may be optionally suffixed with <code class=\"inline\">:tokenId</code> to specify a specific NFT id to filter on. For example, <code class=\"inline\">0x2106c...7aeaa:1234</code> will filter Loopy Donuts on the NFT token with id <code class=\"inline\">1234</code> only. You may include up to 20 contract addresses per request.</li>\n                <li><code class=\"inline\">omitFields</code>: <code class=\"inline\">Array of Strings</code> - (optional) Optionally omit specific properties of objects from the \"assets\" array of the response. Any property of the asset object can be omitted. If omitFields is not included in the request, response will return all available fields by default.</li>\n                <li><code class=\"inline\">page</code>: <code class=\"inline\">Integer</code> - (optional) The page number you would like returned. Page numbers start at 1 and end at \"totalPages\". If omitted, defaults to the first page (page 1). If the page number requested is less than 1 or higher than \"totalPages\", an empty assets array will be returned.</li>\n                <li><code class=\"inline\">perPage</code>: <code class=\"inline\">Integer</code> - (optional) The maximum amount of NFT assets to return on each page. You can request up to 40 items per page. If omitted, defaults to 20 items per page.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"wallet\": \"0x91b51c173a4bdaa1a60e234fc3f705a16d228740\",\n  \"omitFields\": [\n    \"provenance\",\n    \"traits\"\n  ],\n  \"page\": 1,\n  \"perPage\": 10,\n  \"contracts\": [\n    \"0x2106c00ac7da0a3430ae667879139e832307aeaa\",\n    \"0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D\"\n  ]\n}`\n    },\n    {\n        name: 'Fetch NFTs By Creator (qn_fetchNFTsByCreator)',\n        value: 'qn_fetchNFTsByCreator',\n        parentGroup: 'NFT',\n        description: 'Returns aggregated data on NFTs that have been created by an address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_fetchNFTsByCreator',\n            params: {},\n            id: 12\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">creator</code>: <code class=\"inline\">String</code> - The NFT creator's wallet address to check for.</li>\n                <li><code class=\"inline\">page</code>: <code class=\"inline\">Integer</code> - (optional) The page number you would like returned. Page numbers start at 1 and end at \"totalPages\". If omitted, defaults to the first page (page 1). If the page number requested is less than 1 or higher than \"totalPages\", an empty assets array will be returned.</li>\n                <li><code class=\"inline\">perPage</code>: <code class=\"inline\">Integer</code> - (optional) The maximum amount of NFT assets to return on each page. You can request up to 40 items per page. If omitted, defaults to 20 items per page.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"creator\": \"DznU28LgherhU2JwC2db3KmAeWPqoF9Yx2aVtNUudW6R\",\n    \"page\": 1,\n    \"perPage\": 3\n  }\n]`\n    },\n    {\n        name: 'Request Airdrop (requestAirdrop)',\n        value: 'requestAirdrop',\n        parentGroup: 'NFT',\n        description: 'Requests an airdrop of lamports to a Pubkey (does not work on mainnet-beta).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'requestAirdrop',\n            params: {},\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of program, as base-58 encoded string</li>\n            <li><code class=\"inline\">Integer</code> - lamports, as a u64</li>\n            <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n            <ul>\n                <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"YOUR_WALLET_ADDRESS\",\n  1000000000\n]`\n    },\n    {\n        name: 'Fetch NFT Tokens by Collection (qn_fetchNFTsByCollection)',\n        value: 'qn_fetchNFTsByCollection',\n        parentGroup: 'NFT',\n        description: 'Returns aggregated data on NFTs within a given collection.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: nftOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_fetchNFTsByCollection',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">collection</code>: <code class=\"inline\">String</code> - The contract address of the NFT Collection.</li>\n                <li><code class=\"inline\">tokens</code>: <code class=\"inline\">Array of Strings</code> - (optional) Optionally limit the results to specific tokens within a collection. You may submit up to 20 token IDs in this parameter. If you do not want to limit results to specific tokens, please omit this parameter from your request.</li>\n                <li><code class=\"inline\">omitFields</code>: <code class=\"inline\">Array of Strings</code> - (optional) Optionally omit specific properties of objects from the \"tokens\" array of the response. Any property of the tokens object can be omitted. If omitFields is not included in the request, response will return all available fields by default.</li>\n                <li><code class=\"inline\">page</code>: <code class=\"inline\">Integer</code> - (optional) The page number you would like returned. Page numbers start at 1 and end at \"totalPages\". If omitted, defaults to the first page (page 1). If the page number requested is higher than \"totalPages\", an empty assets array will be returned. If the page number requested is less than 1, an invalid params response will be returned.</li>\n                <li><code class=\"inline\">perPage</code>: <code class=\"inline\">Integer</code> - (optional) The maximum amount of NFT tokens to return on each page. You can request up to 100 items per page. If omitted, defaults to 40 items per page.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"collection\": \"0x60E4d786628Fea6478F785A6d7e704777c86a7c6\",\n  \"omitFields\": [\n    \"imageUrl\",\n    \"traits\"\n  ],\n  \"page\": 1,\n  \"perPage\": 10\n}`\n    },\n    {\n        name: 'Fetch transfers by NFT (qn_getTransfersByNFT)',\n        value: 'qn_getTransfersByNFT',\n        parentGroup: 'NFT',\n        description: 'Returns transfers by given NFT.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: nftOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_getTransfersByNFT',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">collection</code>: <code class=\"inline\">String</code> - The contract address of the NFT Collection.</li>\n                <li><code class=\"inline\">collectionTokenId</code>: <code class=\"inline\">String</code> - The Token ID of this NFT under this collection.</li>\n                <li><code class=\"inline\">page</code>: <code class=\"inline\">Integer</code> - (optional) The page number you would like returned. Page numbers start at 1 and end at \"totalPages\". If omitted, defaults to the first page (page 1). If the page number requested is higher than \"totalPages\", an empty assets array will be returned. If the page number requested is less than 1, an invalid params response will be returned.</li>\n                <li><code class=\"inline\">perPage</code>: <code class=\"inline\">Integer</code> - (optional) The maximum amount of NFT tokens to return on each page. You can request up to 100 items per page. If omitted, defaults to 40 items per page.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"collection\": \"0x60E4d786628Fea6478F785A6d7e704777c86a7c6\",\n  \"collectionTokenId\": \"1\",\n  \"page\": 1,\n  \"perPage\": 10\n}`\n    },\n    {\n        name: 'Verify NFTs Owner (qn_verifyNFTsOwner)',\n        value: 'qn_verifyNFTsOwner',\n        parentGroup: 'NFT',\n        description: 'Confirms ownership of specified NFTs for a given wallet.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: nftOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_verifyNFTsOwner',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">wallet</code>: <code class=\"inline\">String</code> -  The wallet address to check for NFTs.</li>\n            <li><code class=\"inline\">contracts</code>: <code class=\"inline\">Array of Strings</code> - List of ERC-721 and/or ERC-1155 NFT contract addresses. Contract addresses may be optionally suffixed with <code class=\"inline\">:tokenId</code> to specify a specific NFT id to filter on. For example, <code class=\"inline\">0x2106c...7aeaa:1234</code> will will verify ownership of Loopy Donuts' NFT token with ID <code class=\"inline\">1234</code> only. You may include up to 20 contract addresses per request.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x91b51c173a4bdaa1a60e234fc3f705a16d228740\",\n  [\n    \"0x2106c00ac7da0a3430ae667879139e832307aeaa:3643\",\n    \"0xd07dc4262bcdbf85190c01c996b4c06a461d2430:133803\"\n  ]\n]`\n    }\n] as IETHOperation[]\n\nexport const tokenOperationsNetworks = [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.BSC, NETWORK.BSC_TESTNET]\nexport const tokenOperations = [\n    {\n        name: 'Fetch Fungible Token Metadata by Contract Address (qn_getTokenMetadataByContractAddress)',\n        value: 'qn_getTokenMetadataByContractAddress',\n        parentGroup: 'Token',\n        description: 'Returns token details for specified contract.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: tokenOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_getTokenMetadataByContractAddress',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">contract</code>: <code class=\"inline\">String</code> - The ERC-20 contract address you'd like to get token details for.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"contract\": \"0x4d224452801ACEd8B2F0aebE155379bb5D594381\"\n}`\n    },\n    {\n        name: 'Fetch Fungible Token Metadata by Symbol (qn_getTokenMetadataBySymbol)',\n        value: 'qn_getTokenMetadataBySymbol',\n        parentGroup: 'Token',\n        description: 'Returns token details for specified token symbol.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: tokenOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_getTokenMetadataBySymbol',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">symbol</code>: <code class=\"inline\">String</code> - The ERC-20 token symbol you'd like to get details for.</li>\n                <li><code class=\"inline\">page</code>: <code class=\"inline\">Integer</code> - (optional) The page number you would like returned. Page numbers start at 1 and end at \"totalPages\". If omitted, defaults to the first page (page 1). If the page number requested is higher than \"totalPages\", an empty tokens array will be returned. If the page number requested is less than 1, an invalid params response will be returned.</li>\n                <li><code class=\"inline\">perPage</code>: <code class=\"inline\">Integer</code> - (optional) The maximum amount of NFT tokens to return on each page. You can request up to 100 items per page. If omitted, defaults to 40 items per page.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"symbol\": \"USDC\"\n}`\n    },\n    {\n        name: 'Fetch Fungible Tokens and Balances by Wallet (qn_getWalletTokenBalance)',\n        value: 'qn_getWalletTokenBalance',\n        parentGroup: 'Token',\n        description: 'Returns ERC-20 tokens and token balances within a wallet.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: tokenOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_getWalletTokenBalance',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">wallet</code>: <code class=\"inline\">String</code> - The wallet address to check for ERC-20 tokens.</li>\n                <li><code class=\"inline\">contracts</code>: <code class=\"inline\">(Array of Strings)</code> - (optional) List of ERC-20 contract addresses to filter wallet balance results on. You may include up to 100 contract addresses per request.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"wallet\": \"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\"\n}`\n    },\n    {\n        name: 'Fetch Wallet Transactions by Fungible Token (qn_getWalletTokenTransactions)',\n        value: 'qn_getWalletTokenTransactions',\n        parentGroup: 'Token',\n        description: 'Returns transfers of a specified token within a specified wallet address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: tokenOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_getWalletTokenTransactions',\n            params: {},\n            id: 67\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - The filter object</li>\n            <ul>\n                <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code> - The wallet address we want to check for transfers.</li>\n                <li><code class=\"inline\">contract</code>: <code class=\"inline\">String</code> - The ERC-20 contract we want to check for transfers.</li>\n                <li><code class=\"inline\">fromBlock</code>: <code class=\"inline\">String</code> - (optional) First block number to check for transfers (inclusive). If omitted, will default to the genesis block for the provided token contract address.</li>\n                <li><code class=\"inline\">toBlock</code>: <code class=\"inline\">String</code> - (optional) Last block number to check for transfers (inclusive). If omitted, will default to the latest block.</li>\n                <li><code class=\"inline\">page</code>: <code class=\"inline\">Integer</code> - (optional) The page number you would like returned. Page numbers start at 1 and end at \"totalPages\". If omitted, defaults to the first page (page 1). If the page number requested is higher than \"totalPages\", an empty tokens array will be returned. If the page number requested is less than 1, an invalid params response will be returned.</li>\n                <li><code class=\"inline\">perPage</code>: <code class=\"inline\">Integer</code> - (optional) The maximum amount of NFT tokens to return on each page. You can request up to 100 items per page. If omitted, defaults to 40 items per page.</li>\n            </ul>\n\t\t</ul>`,\n        exampleParameters: `{\n  \"address\": \"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\",\n  \"contract\": \"0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE\",\n  \"page\": 1,\n  \"perPage\": 10\n}`\n    }\n] as IETHOperation[]\n\nexport const avaxOperationsNetworks = [NETWORK.AVALANCHE, NETWORK.AVALANCHE_TESTNET]\n\nexport const avaxOperations = [\n    {\n        name: 'Avax Get Atomic Tx (avax.getAtomicTx)',\n        value: 'avax.getAtomicTx',\n        parentGroup: 'Avax',\n        description: 'Returns the specified transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avax.getAtomicTx',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/C/avax',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - The transaction ID. It should be in cb58 format.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) The encoding format to use. Can be either cb58 or hex. Defaults to cb58.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\":\"217PBxjVznQaoPuQT8zEB86FnwoDsq6jeb2yfBgtjwYHD1RPC3\",\n  \"encoding\": \"cb58\"\n}`\n    },\n    {\n        name: 'Avax Get Atomic Tx Status (avax.getAtomicTxStatus)',\n        value: 'avax.getAtomicTxStatus',\n        parentGroup: 'Avax',\n        description: 'Get the status of a transaction sent to the network.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avax.getAtomicTxStatus',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/C/avax',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - The transaction ID. It should be in cb58 format.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\":\"217PBxjVznQaoPuQT8zEB86FnwoDsq6jeb2yfBgtjwYHD1RPC3\"\n}`\n    },\n    {\n        name: 'Avax Get Utx Os (avax.getUTXOs)',\n        value: 'avax.getUTXOs',\n        parentGroup: 'Avax',\n        description: 'Get the UTXOs that reference a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avax.getUTXOs',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/C/avax',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">addresses</code>: <code class=\"inline\">Array of Strings</code> - A list of addresses</li>\n            <li><code class=\"inline\">limit</code>: <code class=\"inline\">Integer</code> - (optional) The limit of UTXOs to return. If limit is omitted or greater than 1024, it is set to 1024.</li>\n            <li><code class=\"inline\">startIndex</code>: <code class=\"inline\">Object</code> - (optional) If startIndex is omitted, will fetch all UTXOs up to limit. When using pagination (i.e when startIndex is provided), UTXOs are not guaranteed to be unique across multiple calls. That is, a UTXO may appear in the result of the first call, and then again in the second call.</li>\n            <ul>\n                <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code></li>\n                <li><code class=\"inline\">utxo</code>: <code class=\"inline\">String</code></li>\n            </ul>\n            <li><code class=\"inline\">sourceChain</code>: <code class=\"inline\">String</code> - The ID or alias of the chain the asset is being imported from.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) Encoding sets the format for the returned UTXOs. Can be either \"cb58\" or \"hex\". Defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"addresses\": [\n    \"C-avax1uqsts4n6j0fwmdhu4p8acmjksemtqu7vedhdks\"\n  ],\n  \"sourceChain\": \"X\",\n  \"limit\": 5,\n  \"encoding\": \"cb58\"\n}`\n    },\n    {\n        name: 'Avax Issue Tx (avax.issueTx)',\n        value: 'avax.issueTx',\n        parentGroup: 'Avax',\n        description: 'Send a signed transaction to the network.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avax.issueTx',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/C/avax',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">tx</code>: <code class=\"inline\">String</code> - The signed transaction (typically signed with a library, using your private key).</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) Encoding sets the format for the returned UTXOs. Can be either \"cb58\" or \"hex\". Defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"tx\":\"0x00\",\n  \"encoding\": \"hex\"\n}`\n    },\n    {\n        name: 'Avm Build Genesis (avm.buildGenesis)',\n        value: 'avm.buildGenesis',\n        parentGroup: 'AVM',\n        description: 'Given a JSON representation of this Virtual Machine’s genesis state, create the byte representation of that state.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.buildGenesis',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/vm/avm',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">genesisData</code>: <code class=\"inline\">JSON</code> - A JSON representing the genesis data.</li>\n            <li><code class=\"inline\">networkID</code>: <code class=\"inline\">Integer</code> - the ID of the network</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) Encoding sets the format for the returned UTXOs. Can be either \"cb58\" or \"hex\". Defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"genesisData\": {\n    \"asset1\": {\n      \"name\": \"asset1\",\n      \"symbol\":\"MFCA\",\n      \"memo\": \"2Zc54v4ek37TEwu4LiV3j41PUMRd6acDDU3ZCVSxE7X\",\n      \"denomination\": 1, \n      \"initialState\": {\n          \"fixedCap\" : [\n              {\n                \"amount\":100000,\n                \"address\": \"local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u\"\n              }\n          ]\n      }\n    },\n    \"asset2\": {\n      \"name\": \"asset2\",\n      \"symbol\":\"MVCA\",\n      \"memo\": \"2Zc54v4ek37TEwu4LiV3j41PUMRd6acDDU3ZCVSxE7X\",\n      \"denomination\": 2, \n      \"initialState\": {\n        \"variableCap\" : [\n          {\n            \"amount\":100000,\n            \"address\": \"local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u\"\n          }\n        ]\n      }\n    }\n  },\n  \"networkId\": 12345,\n  \"encoding\":\"cb58\"\n}`\n    },\n    {\n        name: 'Avm Get Address Txs (avm.getAddressTxs)',\n        value: 'avm.getAddressTxs',\n        parentGroup: 'AVM',\n        description: 'Returns all transactions that change the balance of the given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getAddressTxs',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code> - The address for which we're fetching related transactions</li>\n            <li><code class=\"inline\">cursor</code>: <code class=\"inline\">Uint64</code> - (optional) The page number or offset. Leave empty to get the first page.</li>\n            <li><code class=\"inline\">assetID</code>: <code class=\"inline\">String</code> - Only return transactions that changed the balance of this asset. Must be an ID or an alias for an asset.</li>\n            <li><code class=\"inline\">pageSize</code>: <code class=\"inline\">Uint64</code> - (optional) The number of items to return per page. Optional. Defaults to 1024.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"address\":\"X-avax19pm62y5n0yt76wgp8pd7jdhepmppc67y7yg0cq\",\n  \"assetID\":\"AVAX\",\n  \"pageSize\":20\n}`\n    },\n    {\n        name: 'Avm Get All Balances (avm.getAllBalances)',\n        value: 'avm.getAllBalances',\n        parentGroup: 'AVM',\n        description: 'Get the balances of all assets controlled by a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getAllBalances',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code> - Address you want to fetch balances for.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"address\":\"X-avax16902ur8dhlyxpaa0rva5fx48fhptx6ryh3dv7q\"\n}`\n    },\n    {\n        name: 'Avm Get Asset Description (avm.getAssetDescription)',\n        value: 'avm.getAssetDescription',\n        parentGroup: 'AVM',\n        description: 'Get information about an asset.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getAssetDescription',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">assetid</code>: <code class=\"inline\">String</code> - The id of the asset for which the information is requested.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"assetID\" :\"AVAX\"\n}`\n    },\n    {\n        name: 'Avm Get Balance (avm.getBalance)',\n        value: 'avm.getBalance',\n        parentGroup: 'AVM',\n        description: 'Get the balance of an asset controlled by a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getBalance',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code> - The owner of the asset.</li>\n            <li><code class=\"inline\">assetid</code>: <code class=\"inline\">String</code> - The id of the asset for which the balance is requested.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"address\":\"X-avax16902ur8dhlyxpaa0rva5fx48fhptx6ryh3dv7q\",\n  \"assetID\": \"AVAX\"\n}`\n    },\n    {\n        name: 'Avm Get Tx (avm.getTx)',\n        value: 'avm.getTx',\n        parentGroup: 'AVM',\n        description: 'Returns the specified transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getTx',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - A specific transaction ID.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - The encoding parameter sets the format of the returned transaction. Can be, \"cb58\", \"hex\" or \"json\". Defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\":\"9VBHzPDFeDBJGyhBbzakzXMHJnsYhyLEiSS6ee5AczR2oJcns\",\n  \"encoding\": \"hex\"\n}`\n    },\n    {\n        name: 'Avm Get Tx (avm.getTxStatus)',\n        value: 'avm.getTxStatus',\n        parentGroup: 'AVM',\n        description: 'Get the status of a transaction sent to the network.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getTxStatus',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - A specific transaction ID.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\": \"2HHr7xUJgWiLESV7g8T3oWfWvdpWJXd4Hctj6U3KhzUN6EsgBG\"\n}`\n    },\n    {\n        name: 'Avm Get Utx Os (avm.getUtxOs)',\n        value: 'avm.getUtxOs',\n        parentGroup: 'AVM',\n        description: 'Get the UTXOs that reference a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.getUtxOs',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">addresses</code>: <code class=\"inline\">Array of Strings</code> - A list of UTXOs such that each UTXO references at least one address in addresses.</li>\n            <li><code class=\"inline\">limit</code>: <code class=\"inline\">Integer</code> - (optional) The limit of UTXOs to return. If limit is omitted or greater than 1024, it is set to 1024.</li>\n            <li><code class=\"inline\">startIndex</code>: <code class=\"inline\">Object</code> - (optional) If startIndex is omitted, will fetch all UTXOs up to limit. When using pagination (i.e when startIndex is provided), UTXOs are not guaranteed to be unique across multiple calls. That is, a UTXO may appear in the result of the first call, and then again in the second call.</li>\n            <ul>\n                <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code></li>\n                <li><code class=\"inline\">utxo</code>: <code class=\"inline\">String</code></li>\n            </ul>\n            <li><code class=\"inline\">sourceChain</code>: <code class=\"inline\">String</code> - The ID or alias of the chain the asset is being imported from.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) Encoding sets the format for the returned UTXOs. Can be either \"cb58\" or \"hex\". Defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"addresses\": [\n    \"X-avax16902ur8dhlyxpaa0rva5fx48fhptx6ryh3dv7q\"\n  ],\n  \"limit\": 5,\n  \"sourceChain\": \"X\",\n  \"encoding\": \"hex\"\n}`\n    },\n    {\n        name: 'Avm Issue Tx (avm.issueTx)',\n        value: 'avm.issueTx',\n        parentGroup: 'AVM',\n        description: 'Send a signed transaction to the network.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'avm.issueTx',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/X',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">tx</code>: <code class=\"inline\">String</code> - The signed transaction (typically signed with a library, using your private key).</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) Encoding sets the format for the returned UTXOs. Can be either \"cb58\" or \"hex\". Defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"tx\":\"6sTENqXfk3gahxkJbEPsmX9eJTEFZRSRw83cRJqoHWBiaeAhVbz9QV4i6SLd6Dek4eLsojeR8FbT3arFtsGz9ycpHFaWHLX69edJPEmj2tPApsEqsFd7wDVp7fFxkG6HmySR\",\n  \"encoding\": \"cb58\"\n}`\n    }\n] as IETHOperation[]\n\nexport const fantomOperationsNetworks = [NETWORK.FANTOM]\n\nexport const fantomOperations = [\n    {\n        name: 'Dag Get Event (dag_getEvent)',\n        value: 'dag_getEvent',\n        parentGroup: 'DAG',\n        description: 'Returns Lachesis event by hash or short ID.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: fantomOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'dag_getEvent',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">eventid</code>: <code class=\"inline\">String</code> - The full event ID (hex-encoded 32 bytes) or short event ID.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0x00000001000000039bcda184cc9e2b20386dcee5f39fe3c4f36f7b47c297ff2b\"\n]`\n    },\n    {\n        name: 'Dag Get Event Payload (dag_getEventPayload)',\n        value: 'dag_getEventPayload',\n        parentGroup: 'DAG',\n        description: 'Returns event (including transactions) by hash or short ID.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: fantomOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'dag_getEventPayload',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">eventid</code>: <code class=\"inline\">String</code> - The full event ID (hex-encoded 32 bytes) or short event ID.</li>\n            <li><code class=\"inline\">fulltx</code>: <code class=\"inline\">Boolean</code> - If true it returns the full transaction objects, if false only the hashes of the transactions.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"1:3:a2395846\", \n  true\n]`\n    },\n    {\n        name: 'Dag Get Heads (dag_getHeads)',\n        value: 'dag_getHeads',\n        parentGroup: 'DAG',\n        description: 'Returns IDs of all the epoch events with no descendants in a given epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: fantomOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'dag_getHeads',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">epoch</code>: <code class=\"inline\">String</code> - epoch number encoded as a hexadecimal for a specific epoch (pass “latest” to use latest epoch; pass “pending” to use latest sealed epoch).</li>\n        </ul>`,\n        exampleParameters: `[\n  \"pending\"\n]`\n    }\n] as IETHOperation[]\n\nexport const platformOperations = [\n    {\n        name: 'Platform Get Balance (platform.getBalance)',\n        value: 'platform.getBalance',\n        parentGroup: 'Platform',\n        description: 'Get the balance of an asset controlled by a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getBalance',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">address</code>: <code class=\"inline\">String</code> - The address to get the balance of.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"address\":\"P-avax1tnuesf6cqwnjw7fxjyk7lhch0vhf0v95wj5jvy\"    \n}`\n    },\n    {\n        name: 'Platform Get Blockchain Status (platform.getBlockchainStatus)',\n        value: 'platform.getBlockchainStatus',\n        parentGroup: 'Platform',\n        description: 'Get the status of a blockchain.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getBlockchainStatus',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">blockchainID</code>: <code class=\"inline\">String</code> - The id of the blockchain.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"blockchainID\":\"2NbS4dwGaf2p1MaXb65PrkZdXRwmSX4ZzGnUu7jm3aykgThuZE\"\n}`\n    },\n    {\n        name: 'Platform Get Blockchains (platform.getBlockchains)',\n        value: 'platform.getBlockchains',\n        parentGroup: 'Platform',\n        description: 'Get all the blockchains that exist (excluding the P-Chain).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getBlockchains',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P'\n    },\n    {\n        name: 'Platform Get Current Supply (platform.getCurrentSupply)',\n        value: 'platform.getCurrentSupply',\n        parentGroup: 'Platform',\n        description:\n            'Returns an upper bound on the number of AVAX that exist. This is an upper bound because it does not account for burnt tokens, including transaction fees.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getCurrentSupply',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P'\n    },\n    {\n        name: 'Platform Get Current Validators (platform.getCurrentValidators)',\n        value: 'platform.getCurrentValidators',\n        parentGroup: 'Platform',\n        description: 'List the current validators of the given Subnet.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getCurrentValidators',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - (optional) The subnet whose current validators are returned. If omitted, returns the current validators of the Primary Network.</li>\n            <li><code class=\"inline\">nodeIDs</code>: <code class=\"inline\">Array of Strings</code> - (optional) a list of the nodeIDs of pending validators to request. If omitted, all pending validators are returned. If a specified nodeID is not in the set of pending validators, it will not be included in the response.</li>\n        </ul>`\n    },\n    {\n        name: 'Platform Get Pending Validators (platform.getPendingValidators)',\n        value: 'platform.getPendingValidators',\n        parentGroup: 'Platform',\n        description:\n            'List the validators in the pending validator set of the specified Subnet. Each validator is not currently validating the Subnet but will in the future.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getPendingValidators',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - (optional) The subnet whose current validators are returned. If omitted, returns the current validators of the Primary Network.</li>\n            <li><code class=\"inline\">nodeIDs</code>: <code class=\"inline\">Array of Strings</code> - (optional) a list of the nodeIDs of pending validators to request. If omitted, all pending validators are returned. If a specified nodeID is not in the set of pending validators, it will not be included in the response.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"subnetID\": null,\n  \"nodeIDs\": []\n}`\n    },\n    {\n        name: 'Platform Get Height (platform.getHeight)',\n        value: 'platform.getHeight',\n        parentGroup: 'Platform',\n        description: 'Returns the height of the last accepted block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getHeight',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P'\n    },\n    {\n        name: 'Platform Get Max Stake Amount (platform.getMaxStakeAmount)',\n        value: 'platform.getMaxStakeAmount',\n        parentGroup: 'Platform',\n        description:\n            'Get the maximum amount of AVAX required to validate the Primary Network and the maximum amount of AVAX that can be delegated.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getMaxStakeAmount',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - A Buffer or cb58 string representing a subnet.</li>\n            <li><code class=\"inline\">nodeID</code>: <code class=\"inline\">String</code> - A string representing ID of the node whose stake amount is required during the given duration.</li>\n            <li><code class=\"inline\">startTime</code>: <code class=\"inline\">Integer</code> - A big number denoting start time of the duration during which stake amount of the node is required.</li>\n            <li><code class=\"inline\">endTime</code>: <code class=\"inline\">Integer</code> - A big number denoting end time of the duration during which stake amount of the node is required.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"subnetID\":\"11111111111111111111111111111111LpoYY\",\n  \"nodeID\":\"NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg\",\n  \"startTime\": 1644240334,\n  \"endTime\": 1644240634\n}`\n    },\n    {\n        name: 'Platform Get Min Stake (platform.getMinStake)',\n        value: 'platform.getMinStake',\n        parentGroup: 'Platform',\n        description:\n            'Get the minimum amount of AVAX required to validate the Primary Network and the minimum amount of AVAX that can be delegated.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getMinStake',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P'\n    },\n    {\n        name: 'Platform Get Reward Utx Os (platform.getRewardUtxOs)',\n        value: 'platform.getRewardUtxOs',\n        parentGroup: 'Platform',\n        description: `Returns the UTXOs that were rewarded after the provided transaction's staking or delegation period ended.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getRewardUtxOs',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - The ID of the staking or delegating transaction.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - Specifies the format for the returned UTXOs. Can be either \"cb58\" or \"hex\" and defaults to \"cb58\".</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\": \"2nmH8LithVbdjaXsxVQCQfXtzN9hBbmebrsaEYnLM9T32Uy2Y4\",\n  \"encoding\": \"hex\",\n}`\n    },\n    {\n        name: 'Platform Get Stake (platform.getStake)',\n        value: 'platform.getStake',\n        parentGroup: 'Platform',\n        description: `Returns the staked amount for an array of addresses.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getStake',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">addresses</code>: <code class=\"inline\">Array of Strings</code> - An array of address strings.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"addresses\": [\"P-avax1tnuesf6cqwnjw7fxjyk7lhch0vhf0v95wj5jvy\"]\n}`\n    },\n    {\n        name: 'Platform Get Staking Asset (platform.getStakingAssetId)',\n        value: 'platform.getStakingAssetId',\n        parentGroup: 'Platform',\n        description: `Retrieve an assetID for a subnet’s staking asset. Currently this always returns the Primary Network’s staking assetID.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getStakingAssetId',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - (optional) the subnet whose assetID is requested.</li>\n        </ul>`\n    },\n    {\n        name: 'Platform Get Subnets (platform.getSubnets)',\n        value: 'platform.getSubnets',\n        parentGroup: 'Platform',\n        description: `Get all the Subnets that exist.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getSubnets',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">ids</code>: <code class=\"inline\">Array of Strings</code> - The ids of the subnets to get information about.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"ids\":[\"BE5Nv8objSftNwxzcxkZGwCfVs3FPdJBio4DBwCF2A5i7RasU\"]\n}`\n    },\n    {\n        name: 'Platform Get Timestamp (platform.getTimestamp)',\n        value: 'platform.getTimestamp',\n        parentGroup: 'Platform',\n        description: `Returns the specified transaction.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getTimestamp',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P'\n    },\n    {\n        name: 'Platform Get Total Stake (platform.getTotalStake)',\n        value: 'platform.getTotalStake',\n        parentGroup: 'Platform',\n        description: `Get the total amount of nAVAX staked on the Primary Network.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getTotalStake',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P'\n    },\n    {\n        name: 'Platform Get Tx (platform.getTx)',\n        value: 'platform.getTx',\n        parentGroup: 'Platform',\n        description: `Returns the specified transaction.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getTx',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - The transaction ID. It should be in cb58 format.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) The encoding format to use. Can be either cb58 or hex. Defaults to cb58.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\":\"2dum8LzyddFVZhiYBCUBZw3Xqsb2GdZAP6FsmCPB1gHGJhw1oJ\",\n  \"encoding\": \"cb58\"\n}`\n    },\n    {\n        name: 'Platform Get Tx Status (platform.getTxStatus)',\n        value: 'platform.getTxStatus',\n        parentGroup: 'Platform',\n        description: `Returns the specified transaction.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getTxStatus',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">txID</code>: <code class=\"inline\">String</code> - The transaction ID. It should be in cb58 format.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"txID\":\"2dum8LzyddFVZhiYBCUBZw3Xqsb2GdZAP6FsmCPB1gHGJhw1oJ\",\n}`\n    },\n    {\n        name: 'Platform Get Utx Os (platform.getUtxOs)',\n        value: 'platform.getUtxOs',\n        parentGroup: 'Platform',\n        description: `Get the UTXOs that reference a given address.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getUtxOs',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">addresses</code>: <code class=\"inline\">Array of Strings</code> - the addresses from which we want to retrieve the UTXOs.</li>\n            <li><code class=\"inline\">limit</code>: <code class=\"inline\">Integer</code> - (optional) At most limit UTXOs are returned. If limit is omitted or greater than 1024, it is set to 1024.</li>\n\t\t\t<li><code class=\"inline\">startIndex</code>: <code class=\"inline\">Object</code> - (optional) an object of an address and a utxo.</li>\n\t\t\t<ul>\n                <li><code class=\"inline\">address</code>: <code class=\"inline\">String</code></li>\n\t\t\t    <li><code class=\"inline\">utxo</code>: <code class=\"inline\">String</code></li>\n            </ul>\n            <li><code class=\"inline\">sourceChain</code>: <code class=\"inline\">String</code> - (optional) the chain from which we want to fetch the UTXOs, (X or C).</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) The encoding format to use. Can be either cb58 or hex. Defaults to cb58.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"addresses\":[\"P-avax1tnuesf6cqwnjw7fxjyk7lhch0vhf0v95wj5jvy\"],\n  \"sourceChain\": \"X\",\n  \"limit\": 5,\n  \"encoding\": \"hex\"\n}`\n    },\n    {\n        name: 'Platform Get Validators At (platform.getValidatorsAt)',\n        value: 'platform.getValidatorsAt',\n        parentGroup: 'Platform',\n        description: `Get the validators and their weights of a subnet or the Primary Network at a given P-Chain height.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.getValidatorsAt',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">height</code>: <code class=\"inline\">Integer</code> - the numerical value representing the P-Chain height to get the validator set at.</li>\n            <li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - (optional) the subnet ID to get the validator set of. If not given, gets validator set of the Primary Network.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"height\": 1,\n  \"subnetID\":\"11111111111111111111111111111111LpoYY\"\n}`\n    },\n    {\n        name: 'Platform Issue Tx (platform.issueTx)',\n        value: 'platform.issueTx',\n        parentGroup: 'Platform',\n        description: `Issue a transaction to the Platform Chain.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.issueTx',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">tx</code>: <code class=\"inline\">String</code> - the byte representation of a transaction.</li>\n            <li><code class=\"inline\">encoding</code>: <code class=\"inline\">String</code> - (optional) The encoding format to use. Can be either cb58 or hex. Defaults to cb58.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"tx\":\"0x00\",\n  \"encoding\": \"hex\"\n}`\n    },\n    {\n        name: 'Platform Sample Validators (platform.sampleValidators)',\n        value: 'platform.sampleValidators',\n        parentGroup: 'Platform',\n        description: `Sample validators from the specified Subnet.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.sampleValidators',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">size</code>: <code class=\"inline\">Integer</code> - the number of validators to sample.</li>\n            <li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - (optional) the Subnet to sample from. If omitted, defaults to the Primary Network.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"size\": 2\n}`\n    },\n    {\n        name: 'Platform Validated By (platform.validatedBy)',\n        value: 'platform.validatedBy',\n        parentGroup: 'Platform',\n        description: `Get the Subnet that validates a given blockchain.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.validatedBy',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">blockchainID</code>: <code class=\"inline\">String</code> - the blockchain’s ID.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"blockchainID\": \"2H1pfQqnJq1NEw1YK6CWdoH2ucrvaET8renpkxChLtnNWGYrYa\"\n}`\n    },\n    {\n        name: 'Platform Validates (platform.validates)',\n        value: 'platform.validates',\n        parentGroup: 'Platform',\n        description: `Get the IDs of the blockchains a Subnet validates.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: avaxOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'platform.validates',\n            params: {},\n            id: 1\n        },\n        overrideUrl: 'http://sample-endpoint-name.network.quiknode.pro/token-goes-here/ext/bc/P',\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subnetID</code>: <code class=\"inline\">String</code> - the subnet ID.</li>\n        </ul>`,\n        exampleParameters: `{\n  \"subnetID\":\"BE5Nv8objSftNwxzcxkZGwCfVs3FPdJBio4DBwCF2A5i7RasU\"\n}`\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/QuickNode/solanaOperation.ts",
    "content": "import { NETWORK, NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport { IETHOperation } from '../../src/ETHOperations'\n\nexport const solanaOperationsNetworks = [NETWORK.SOLANA, NETWORK.SOLANA_DEVNET, NETWORK.SOLANA_TESTNET]\n\nexport const solanaOperations = [\n    {\n        name: 'Get Account Info (getAccountInfo)',\n        value: 'getAccountInfo',\n        parentGroup: 'Account Information',\n        description: 'Returns all information associated with the account of provided Pubkey.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getAccountInfo',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">String</code> - Pubkey of account to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\", or \"jsonParsed\".</li>\n                <li><code class=\"inline\">dataSlice</code> - (optional) limit the returned account data using the provided <code class=\"inline\">offset: 'usize'</code> and <code class=\"inline\">length: 'usize'</code> fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg\",\n  {\"encoding\": \"base58\"}\n]`\n    },\n    {\n        name: 'Get Balance (getBalance)',\n        value: 'getBalance',\n        parentGroup: 'Account Information',\n        description: 'Returns the balance of the account of provided Pubkey.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBalance',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">String</code> - Pubkey of account to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg\"\n]`\n    },\n    {\n        name: 'Get Block (getBlock)',\n        value: 'getBlock',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns identity and transaction information about a confirmed block in the ledger.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlock',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">slot_number</code> - slot, as u64 (64-bit unsigned integer) integer.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">encoding</code> - (optional) encoding for each returned Transaction, either \"json\", \"jsonParsed\", \"base58\" (slow), \"base64\". If parameter not provided, the default encoding is \"json\".</li>\n                <li><code class=\"inline\">transactionDetails</code> - (optional) level of transaction detail to return, either \"full\", \"signatures\", or \"none\". If parameter not provided, the default detail level is \"full\".</li>\n                <li><code class=\"inline\">rewards</code> - (optional) Boolean value, whether to populate the rewards array. If parameter not provided, the default includes rewards.</li>\n                <li><code class=\"inline\">maxSupportedTransactionVersion</code> - (optional) set the max transaction version to return in responses. If the requested block contains a transaction with a higher version, an error will be returned.</li>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  94101948, \n  {\n    \"encoding\": \"json\",\n    \"transactionDetails\":\"full\",\n    \"rewards\":false\n  }\n]`\n    },\n    {\n        name: 'Get Block Commitment (getBlockCommitment)',\n        value: 'getBlockCommitment',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns commitment for particular block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlockCommitment',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">slot_number</code> - slot, as u64 (64-bit unsigned integer) integer.</li>\n        </ul>`,\n        exampleParameters: `[\n  94101948\n]`\n    },\n    {\n        name: 'Get Block Height (getBlockHeight)',\n        value: 'getBlockHeight',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns the latest block number of the blockchain.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlockHeight',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`\n    },\n    {\n        name: 'Get Block Production (getBlockProduction)',\n        value: 'getBlockProduction',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns recent block production information from the current or previous epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlockProduction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">range</code>: <code class=\"inline\">ENUM</code> - (optional) Slot range to return block production for. If parameter not provided, defaults to current epoch.</li>\n                <ul>\n                    <li><code class=\"inline\">firstSlot</code> -  first slot to return block production information for (inclusive).</li>\n                    <li><code class=\"inline\">lastSlot</code> - (optional) last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot.</li>\n                </ul>\n                <li><code class=\"inline\">identity</code> - (optional) Only return results for this validator identity (base-58 encoded).</li>\n            </ul>\n        </ul>`\n    },\n    {\n        name: 'Get Block Time (getBlockTime)',\n        value: 'getBlockTime',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns the estimated production time of a block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlockTime',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">slot_number</code> - slot, as u64 (64-bit unsigned integer) integer.</li>\n        </ul>`,\n        exampleParameters: `[\n  94101948\n]`\n    },\n    {\n        name: 'Get Blocks (getBlocks)',\n        value: 'getBlocks',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns a list of confirmed blocks between two slots.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlocks',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">start_slot</code> - Start slot, as an u64 integer.</li>\n            <li><code class=\"inline\">end_slot</code> - (optional) end slot as an u64 integer.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  5, 10\n]`\n    },\n    {\n        name: 'Get Blocks With Limit (getBlocksWithLimit)',\n        value: 'getBlocksWithLimit',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns a list of confirmed blocks starting at the given slot',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getBlocksWithLimit',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">start_slot</code> - Start slot, as an u64 integer.</li>\n            <li><code class=\"inline\">limit</code> - limit, as u64 integer.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  5, 3\n]`\n    },\n    {\n        name: 'Get Cluster Nodes (getClusterNodes)',\n        value: 'getClusterNodes',\n        parentGroup: 'Node Information',\n        description: 'Returns information about all the nodes participating in the cluster.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getClusterNodes',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Confirmed Block (getConfirmedBlock)',\n        value: 'getConfirmedBlock',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns identity and transaction information about a confirmed block in the ledger.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getConfirmedBlock',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">slot_number</code> - slot, as u64 (64-bit unsigned integer) integer.</li>\n            <li><code class=\"inline\">limit</code> - limit, as u64 integer.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - (optional) encoding for each returned Transaction, either \"json\", \"jsonParsed\", \"base58\" (slow), \"base64\". If parameter not provided, the default encoding is \"json\".</li>\n                <li><code class=\"inline\">transactionDetails</code> - (optional) level of transaction detail to return, either \"full\", \"signatures\", or \"none\". If parameter not provided, the default detail level is \"full\".</li>\n                <li><code class=\"inline\">rewards</code> - (optional) Boolean value, whether to populate the rewards array. If parameter not provided, the default includes rewards.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  94101948, \n  {\n    \"encoding\": \"json\",\n    \"transactionDetails\":\"full\",\n    \"rewards\":false\n  }\n]`\n    },\n    {\n        name: 'Get Confirmed Blocks (getConfirmedBlocks)',\n        value: 'getConfirmedBlocks',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns a list of confirmed blocks between two slots.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getConfirmedBlocks',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">start_slot</code> - start_slot, as u64 (64-bit unsigned integer).</li>\n            <li><code class=\"inline\">end_slot</code> - (optional) end_slot, as u64 (64-bit unsigned integer).</li>\n            <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n            <ul>\n                <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  94101945,\n  94101948\n]`\n    },\n    {\n        name: 'Get Confirmed Blocks With Limit (getConfirmedBlocksWithLimit)',\n        value: 'getConfirmedBlocksWithLimit',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns a list of confirmed blocks starting at the given slot.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getConfirmedBlocksWithLimit',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">start_slot</code> - start_slot, as u64 (64-bit unsigned integer).</li>\n            <li><code class=\"inline\">limit</code> - (optional) limit, as u64 (64-bit unsigned integer).</li>\n            <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n            <ul>\n                <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  94101945, \n  3\n]`\n    },\n    {\n        name: 'Get Confirmed Signatures For Address2 (getConfirmedSignaturesForAddress2)',\n        value: 'getConfirmedSignaturesForAddress2',\n        parentGroup: 'Reading & Writing Transactions',\n        description:\n            'Returns confirmed signatures for transactions involving an address backwards in time from the provided signature or most recent confirmed block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getConfirmedSignaturesForAddress2',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">acc_add</code> - account address as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">limit</code> - (optional) maximum transaction signatures to return (between 1 and 1,000, default: 1,000).</li>\n                <li><code class=\"inline\">before</code> - (optional) start searching backwards from this transaction signature. If not provided the search starts from the top of the highest max confirmed block.</li>\n                <li><code class=\"inline\">until</code> - (optional) search until this transaction signature, if found before limit reached.</li>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"Vote111111111111111111111111111111111111111\",\n  {\"limit\": 1}\n]`\n    },\n    {\n        name: 'Get Confirmed Transaction (getConfirmedTransaction)',\n        value: 'getConfirmedTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Returns transaction details for a confirmed transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getConfirmedTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">tx_sig</code> - transaction signature as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">encoding</code> - Tencoding for each returned Transaction, either \"json\", \"jsonParsed\", \"base58\" (slow), \"base64\".</li>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"3Pdh1xgS7HYXcPquN1JQQXs8C6Tn2ZTkHg86wXMwDEEnJxVVZsE3WgxHSx258boUtHcMVkKCGbT9dYWWfk7CWV2m\",\n  {\n    \"encoding\": \"json\",\n  }\n]`\n    },\n    {\n        name: 'Get Epoch Info (getEpochInfo)',\n        value: 'getEpochInfo',\n        parentGroup: 'Network Information',\n        description: 'Returns information about the current epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getEpochInfo',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`\n    },\n    {\n        name: 'Get Epoch Schedule (getEpochSchedule)',\n        value: 'getEpochSchedule',\n        parentGroup: 'Network Information',\n        description: `Returns epoch schedule information from this cluster's genesis config.`,\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getEpochSchedule',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Fee Calculator For Blockhash (getFeeCalculatorForBlockhash)',\n        value: 'getFeeCalculatorForBlockhash',\n        parentGroup: 'Network Information',\n        description: 'Returns the fee calculator associated with the query blockhash, or null if the blockhash has expired.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFeeCalculatorForBlockhash',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">string</code> - (optional) query blockhash as a Base58 encoded string.</li>\n            <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n            <ul>\n                <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\"6EUDAG2UBZ1J7CbpixutsELc5c6s4k8YzaWawyKH2Pit\"]`\n    },\n    {\n        name: 'Get Fee For Message (getFeeForMessage)',\n        value: 'getFeeForMessage',\n        parentGroup: 'Network Information',\n        description: 'Get the fee the network will charge for a particular message.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFeeForMessage',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Message</code> - Base-64 encoded Message</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"AQABAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAA\",\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Fee Rate Governor (getFeeRateGovernor)',\n        value: 'getFeeRateGovernor',\n        parentGroup: 'Network Information',\n        description: 'Returns the fee rate governor information from the root bank.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFeeRateGovernor',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Fees (getFees)',\n        value: 'getFees',\n        parentGroup: 'Network Information',\n        description:\n            'Returns a recent block hash from the ledger, a fee schedule that can be used to compute the cost of submitting a transaction using it, and the last slot in which the blockhash will be valid.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFees',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get First Available Block (getFirstAvailableBlock)',\n        value: 'getFirstAvailableBlock',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns the slot of the lowest confirmed block that has not been purged from the ledger',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getFirstAvailableBlock',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Genesis Hash (getGenesisHash)',\n        value: 'getGenesisHash',\n        parentGroup: 'Network Information',\n        description: 'Returns the genesis hash.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getGenesisHash',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Health (getHealth)',\n        value: 'getHealth',\n        parentGroup: 'Node Information',\n        description: 'Returns the current health of the node.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getHealth',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Highest Snapshot Slot (getHighestSnapshotSlot)',\n        value: 'getHighestSnapshotSlot',\n        parentGroup: 'Node Information',\n        description:\n            'Returns the highest slot that the node has a snapshot for. This will find the highest full snapshot slot, and the highest incremental snapshot slot based on the full snapshot slot, if there is one.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getHighestSnapshotSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Identity (getIdentity)',\n        value: 'getIdentity',\n        parentGroup: 'Node Information',\n        description: 'Returns the identity pubkey for the current node.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getIdentity',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Inflation Governor (getInflationGovernor)',\n        value: 'getInflationGovernor',\n        parentGroup: 'Network Inflation',\n        description: 'Returns the current inflation governor.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getInflationGovernor',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Inflation Rate (getInflationRate)',\n        value: 'getInflationRate',\n        parentGroup: 'Network Inflation',\n        description: 'Returns the specific inflation values for the current epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getInflationRate',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Inflation Reward (getInflationReward)',\n        value: 'getInflationReward',\n        parentGroup: 'Network Inflation',\n        description: 'Returns the inflation / staking reward for a list of addresses for an epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getInflationReward',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Array</code> - An array of addresses to query, as base-58 encoded strings</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">epoch</code> - (optional) An epoch for which the reward occurs, as u64 (64-bit unsigned integer) integer. If omitted, the previous epoch will be used.</li>\n            <li><code class=\"inline\">minContextSlot</code> - (optional) set the minimum slot that the request can be evaluated at.</li>\n            <li><code class=\"inline\">maxSupportedTransactionVersion</code> - (optional) set the max transaction version to return in responses. If the requested block contains a transaction with a higher version, an error will be returned.</li>\n        </ul>`,\n        exampleParameters: `[\n  [\n    \"ADDRESS_TO_SEARCH_1\", \n    \"ADDRESS_TO_SEARCH_2\"\n  ], \n  {\"epoch\": 2}\n]`\n    },\n    {\n        name: 'Get Largest Accounts (getLargestAccounts)',\n        value: 'getLargestAccounts',\n        parentGroup: 'Account Information',\n        description: 'Returns the 20 largest accounts, by lamport balance (results may be cached up to two hours).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getLargestAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">filter</code> - (optional) filter results by account type; currently supported: <code class=\"inline\">circulating</code> | <code class=\"inline\">nonCirculating</code></li>\n        </ul>`\n    },\n    {\n        name: 'Get Latest Blockhash (getLatestBlockhash)',\n        value: 'getLatestBlockhash',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns the latest blockhash.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getLatestBlockhash',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">minContextSlot</code> - - set the minimum slot that the request can be evaluated at.</li>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Leader Schedule (getLeaderSchedule)',\n        value: 'getLeaderSchedule',\n        parentGroup: 'Slot Information',\n        description: 'Returns the leader schedule for an epoch.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getLeaderSchedule',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">64-bit unsigned integer</code> - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetched.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">identity</code> - (optional) Only return results for this validator identity (base-58 encoded).</li>\n            </ul>\n        </ul>`\n    },\n    {\n        name: 'Get Max Retransmit Slot (getMaxRetransmitSlot)',\n        value: 'getMaxRetransmitSlot',\n        parentGroup: 'Slot Information',\n        description: 'Get the max slot seen from retransmit stage.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMaxRetransmitSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Max Shred Insert Slot (getMaxShredInsertSlot)',\n        value: 'getMaxShredInsertSlot',\n        parentGroup: 'Slot Information',\n        description: 'Get the max slot seen from after shred insert.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMaxShredInsertSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Minimum Balance For Rent Exemption (getMinimumBalanceForRentExemption)',\n        value: 'getMinimumBalanceForRentExemption',\n        parentGroup: 'Slot Information',\n        description: 'Returns minimum balance required to make account rent exempt.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMinimumBalanceForRentExemption',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">usize</code> - account data length</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[50]`\n    },\n    {\n        name: 'Get Multiple Accounts (getMultipleAccounts)',\n        value: 'getMultipleAccounts',\n        parentGroup: 'Account Information',\n        description: 'Returns the account information for a list of Pubkeys.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getMultipleAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">array</code> - An array of Pubkeys to query, as base-58 encoded strings.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\", or \"jsonParsed\".</li>\n                <li><code class=\"inline\">dataSlice</code> - (optional) limit the returned account data using the provided <code class=\"inline\">offset: 'usize'</code> and <code class=\"inline\">length: 'usize'</code> fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  [\n    \"vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg\",\n    \"4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA\"\n  ],\n  {\n    \"dataSlice\": {\n      \"offset\": 0,\n      \"length\": 0\n    }\n  }\n]`\n    },\n    {\n        name: 'Get Program Accounts (getProgramAccounts)',\n        value: 'getProgramAccounts',\n        parentGroup: 'Account Information',\n        description: 'Returns all accounts owned by the provided program Pubkey.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getProgramAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of program, as base-58 encoded string</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - (optional) encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\", or \"jsonParsed\". \"base58\" is limited to Account data of less than 129 bytes. \"base64\" will return base64 encoded data for Account data of any size. \"base64+zstd\" compresses the Account data using Zstandard and base64-encodes the result. \"jsonParsed\" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data.</li>\n                <li><code class=\"inline\">dataSlice</code> - (optional) limit the returned account data using the provided offset: usize and length: usize fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n                <li><code class=\"inline\">filters</code> - (optional) filter results using up to 4 filter objects; account must meet all filter criteria to be included in results.</li>\n                <li><code class=\"inline\">withContext</code> - (optional) wrap the result in an RpcResponse JSON object.</li>\n                <li><code class=\"inline\">minContextSlot</code> - (optional) set the minimum slot that the request can be evaluated at.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\"PROGRAM_TO_SEARCH\"]`\n    },\n    {\n        name: 'Get Recent Blockhash (getRecentBlockhash)',\n        value: 'getRecentBlockhash',\n        parentGroup: 'Getting Blocks',\n        description:\n            'Returns a recent block hash from the ledger, and a fee schedule that can be used to compute the cost of submitting a transaction using it.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getRecentBlockhash',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Recent Performance Samples (getRecentPerformanceSamples)',\n        value: 'getRecentPerformanceSamples',\n        parentGroup: 'Slot Information',\n        description:\n            'Returns a list of recent performance samples, in reverse slot order. Performance samples are taken every 60 seconds and include the number of transactions and slots that occur in a given time window.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getRecentPerformanceSamples',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">limit</code> - (optional) number of samples to return (maximum 720).</li>\n        </ul>`,\n        exampleParameters: `[4]`\n    },\n    {\n        name: 'Get Signature Statuses (getSignatureStatuses)',\n        value: 'getSignatureStatuses',\n        parentGroup: 'Reading & Writing Transactions',\n        description:\n            'Returns the statuses of a list of signatures. Unless the searchTransactionHistory configuration parameter is included, this method only searches the recent status cache of signatures, which retains statuses for all active slots plus MAX_RECENT_BLOCKHASHES rooted slots.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSignatureStatuses',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">array</code> - An array of transaction signatures to confirm, as base-58 encoded strings.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">searchTransactionHistory</code> -  Boolean value, if true, a Solana node will search its ledger cache for any signatures not found in the recent status cache.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  [\n    \"5tGfZLNDxtCtWsW1BJoeTyHvnfGqpADDfBkUgkKENQJ8iz5yTN3ae51j8m8GRFevJx82gyuKnEX7iexFsqf7X2vS\",\n    \"D13jTJYXoQBcRY9AfT5xRtsew7ENgCkNs6mwwwAcUCp4ZZCEM7YwZ7en4tVsoDa7Gu75Jjj2FgLXNUz8Zmgedff\"\n  ],\n  {\"searchTransactionHistory\": true}\n]`\n    },\n    {\n        name: 'Get Signatures For Address (getSignaturesForAddress)',\n        value: 'getSignaturesForAddress',\n        parentGroup: 'Reading & Writing Transactions',\n        description:\n            'Returns confirmed signatures for transactions involving an address backwards in time from the provided signature or most recent confirmed block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSignaturesForAddress',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">account_address</code> -The account address as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Limit</code> - (optional) Maximum number of transaction signatures to return (between 1 and 1,000, default: 1,000).</li>\n                <li><code class=\"inline\">Before</code> - (optional) Start searching backwards from this transaction signature. If not provided the search starts from the top of the highest max confirmed block.</li>\n                <li><code class=\"inline\">Until</code> - (optional) search until this transaction signature, if found before limit reached.</li>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"Vote111111111111111111111111111111111111111\",\n  {\"limit\": 1}\n]`\n    },\n    {\n        name: 'Get Slot (getSlot)',\n        value: 'getSlot',\n        parentGroup: 'Slot Information',\n        description: 'Returns the slot that has reached the given or default commitment level.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSlot',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Slot Leader (getSlotLeader)',\n        value: 'getSlotLeader',\n        parentGroup: 'Slot Information',\n        description: 'Returns the current slot leader.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSlotLeader',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\"commitment\": \"processed\"}\n]`\n    },\n    {\n        name: 'Get Slot Leaders (getSlotLeaders)',\n        value: 'getSlotLeaders',\n        parentGroup: 'Slot Information',\n        description: 'Returns the slot leaders for a given slot range.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSlotLeaders',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Start slot</code> - Start slot, as 64-bit unsigned integer.</li>\n            <li><code class=\"inline\">Limit</code> - Limit, as 64-bit unsigned integer.</li>\n        </ul>`,\n        exampleParameters: `[140630426, 10]`\n    },\n    {\n        name: 'Get Snapshot Slot (getSnapshotSlot)',\n        value: 'getSnapshotSlot',\n        parentGroup: 'Slot Information',\n        description: 'Returns the highest slot that the node has a snapshot for.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSnapshotSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Stake Activation (getStakeActivation)',\n        value: 'getStakeActivation',\n        parentGroup: 'Account Information',\n        description: 'Returns epoch activation information for a stake account.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getStakeActivation',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of stake account to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">epoch</code> - (optional) epoch for which to calculate activation details. If parameter not provided, defaults to current epoch.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\"Buc3N8TitzhVtvy7sm85YWpY2F5PAAKV2iLP1cZAbwrJ\"]`\n    },\n    {\n        name: 'Get Supply (getSupply)',\n        value: 'getSupply',\n        parentGroup: 'Network Information',\n        description: 'Returns information about the current supply.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getSupply',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n            <ul>\n                <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n        </ul>`\n    },\n    {\n        name: 'Get Token Account Balance (getTokenAccountBalance)',\n        value: 'getTokenAccountBalance',\n        parentGroup: 'Token Information',\n        description: 'Returns the token balance of an SPL Token account.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenAccountBalance',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of account delegate to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n            <ul>\n                <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\"DhzDoryP2a4rMK2bcWwJxrE2uW6ir81ES8ZwJJPPpxDN\"]`\n    },\n    {\n        name: 'Get Token Accounts By Delegate (getTokenAccountsByDelegate)',\n        value: 'getTokenAccountsByDelegate',\n        parentGroup: 'Token Information',\n        description: 'Returns all SPL Token accounts by approved Delegate.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenAccountsByDelegate',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of account delegate to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - Either:</li>\n            <ul>\n                <li><code class=\"inline\">mint</code> - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string.</li>\n                <li><code class=\"inline\">programId</code> - Pubkey of the Token program ID that owns the accounts, as base-58 encoded string.</li>\n            </ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - (encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\" or \"jsonParsed\". \"jsonParsed\" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If \"jsonParsed\" is requested but a valid mint cannot be found for a particular account, that account will be filtered out from results.</li>\n                <li><code class=\"inline\">dataSlice</code> - limit the returned account data using the provided offset: (unsigned integer) and length: (unsigned integer) fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T\",\n  {\n    \"programId\": \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\"\n  },\n  {\n    \"encoding\": \"jsonParsed\"\n  }\n]`\n    },\n    {\n        name: 'Get Token Accounts By Owner (getTokenAccountsByOwner)',\n        value: 'getTokenAccountsByOwner',\n        parentGroup: 'Token Information',\n        description: 'Returns all SPL Token accounts by token owner.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenAccountsByOwner',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of account delegate to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - Either:</li>\n            <ul>\n                <li><code class=\"inline\">mint</code> - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string.</li>\n                <li><code class=\"inline\">programId</code> - Pubkey of the Token program ID that owns the accounts, as base-58 encoded string.</li>\n            </ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - (encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\" or \"jsonParsed\". \"jsonParsed\" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If \"jsonParsed\" is requested but a valid mint cannot be found for a particular account, that account will be filtered out from results.</li>\n                <li><code class=\"inline\">dataSlice</code> - limit the returned account data using the provided offset: (unsigned integer) and length: (unsigned integer) fields; only available for \"base58\", \"base64\" or \"base64+zstd\" encodings.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"GgPpTKg78vmzgDtP1DNn72CHAYjRdKY7AV6zgszoHCSa\",\n  {\n    \"mint\": \"1YDQ35V8g68FGvcT85haHwAXv1U7XMzuc4mZeEXfrjE\"\n  },\n  {\n    \"encoding\": \"jsonParsed\"\n  }\n]`\n    },\n    {\n        name: 'Get Token Largest Accounts (getTokenLargestAccounts)',\n        value: 'getTokenLargestAccounts',\n        parentGroup: 'Token Information',\n        description: 'Returns the 20 largest accounts of a particular SPL Token type.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenLargestAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of account delegate to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"1YDQ35V8g68FGvcT85haHwAXv1U7XMzuc4mZeEXfrjE\",\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Token Supply (getTokenSupply)',\n        value: 'getTokenSupply',\n        parentGroup: 'Token Information',\n        description: 'Returns the total supply of an SPL Token type.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTokenSupply',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Pubkey of token Mint to query, as base-58 encoded string</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU\",\n  {\n    \"commitment\": \"processed\"\n  }\n]`\n    },\n    {\n        name: 'Get Transaction (getTransaction)',\n        value: 'getTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Returns transaction details for a confirmed transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">tx_sig</code> - transaction signature as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - Tencoding for each returned Transaction, either \"json\", \"jsonParsed\", \"base58\" (slow), \"base64\". If parameter not provided, the default encoding is \"json\".</li>\n                <li><code class=\"inline\">maxSupportedTransactionVersion</code> - (optional) set the max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"D13jTJYXoQBcRY9AfT5xRtsew7ENgCkNs6mwwwAcUCp4ZZCEM7YwZ7en4tVsoDa7Gu75Jjj2FgLXNUz8Zmgedff\",\n  {\"encoding\": \"json\"}\n]`\n    },\n    {\n        name: 'Get Transaction Count (getTransactionCount)',\n        value: 'getTransactionCount',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Returns the current transaction count from the ledger.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getTransactionCount',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\"commitment\": \"processed\"}\n]`\n    },\n    {\n        name: 'Get Version (getVersion)',\n        value: 'getVersion',\n        parentGroup: 'Network Information',\n        description: 'Returns the current solana version running on the node.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getVersion',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Get Vote Accounts (getVoteAccounts)',\n        value: 'getVoteAccounts',\n        parentGroup: 'Account Information',\n        description: 'Returns the account info and associated stake for all the voting accounts in the current bank.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'getVoteAccounts',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">votePubkey</code> - (optional) Only return results for this validator vote address. Passed as a string, base-58 encoded.</li>\n                <li><code class=\"inline\">keepUnstakedDelinquents</code> - (optional) Boolean that determines whether or not to filter out delinquent validators with no stake.</li>\n                <li><code class=\"inline\">delinquentSlotDistance</code> - (optional) Specify the number of slots behind the tip that a validator must fall to be considered delinquent. Passed as an integer. It is not recomended to specify this argument.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  {\"commitment\": \"processed\"}\n]`\n    },\n    {\n        name: 'Is Blockhash Valid (isBlockhashValid)',\n        value: 'isBlockhashValid',\n        parentGroup: 'Getting Blocks',\n        description: 'Returns whether a blockhash is still valid or not',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'isBlockhashValid',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">blockhash</code> - the blockhash of this block, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n            <li><code class=\"inline\">minContextSlot</code> - (optional) set the minimum slot that the request can be evaluated at</li>\n        </ul>`,\n        exampleParameters: `[\n  \"ENTER_BLOCKHASH_ID_HERE\",\n  {\"commitment\": \"processed\"}\n]`\n    },\n    {\n        name: 'Minimum Ledger Slot (minimumLedgerSlot)',\n        value: 'minimumLedgerSlot',\n        parentGroup: 'Slot Information',\n        description: 'Returns the lowest slot that the node has information of in its ledger.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'minimumLedgerSlot',\n            params: [],\n            id: 1\n        }\n    },\n    {\n        name: 'Send Transaction (sendTransaction)',\n        value: 'sendTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Submits a signed transaction to the cluster for processing.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'sendTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - fully-signed Transaction, as encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">skipPreflight</code> - if true, skip the preflight transaction checks (default: false).</li>\n                <li><code class=\"inline\">preflightCommitment</code> - (optional) Commitment level to use for preflight (default: \"finalized\").</li>\n                <li><code class=\"inline\">encoding</code> - (optional) Encoding used for the transaction data. Either \"base58\" (slow, DEPRECATED), or \"base64\". (default: \"base58\").</li>\n                <li><code class=\"inline\">maxRetries</code> - (optional).</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"ENTER_ENCODED_TRANSACTION_ID\",\n]`\n    },\n    {\n        name: 'Simulate Transaction (simulateTransaction)',\n        value: 'simulateTransaction',\n        parentGroup: 'Reading & Writing Transactions',\n        description: 'Simulate sending a transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'simulateTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">String</code> - Transaction, as an encoded string. The transaction must have a valid blockhash, but is not required to be signed.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">sigVerify</code> -  if true the transaction signatures will be verified (default: false, conflicts with replaceRecentBlockhash).</li>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - (optional) Encoding used for the transaction data. Either \"base58\" (slow, DEPRECATED), or \"base64\". (default: \"base58\").</li>\n                <li><code class=\"inline\">replaceRecentBlockhash</code> - (optional) if true the transaction recent blockhash will be replaced with the most recent blockhash. (default: false, conflicts with sigVerify).</li>\n                <li><code class=\"inline\">accounts </code>: <code class=\"inline\">Object</code> - (optional) Accounts configuration object containing the following fields:</li>\n                <ul>\n                    <li><code class=\"inline\">encoding</code> - (optional) encoding for returned Account data, either \"base64\" (default), \"base64+zstd\" or \"jsonParsed\". \"jsonParsed\" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If \"jsonParsed\" is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the data field is type string.</li>\n                    <li><code class=\"inline\">addresses</code> - An array of accounts to return, as base-58 encoded strings.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"ENTER_ENCODED_TRANSACTION_ID\",\n]`\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/QuickNode/subscribeOperation.ts",
    "content": "import { NETWORK, NETWORK_PROVIDER } from '../../src/ChainNetwork'\nimport { IETHOperation, infuraSupportedNetworks } from '../../src/ETHOperations'\nimport { solanaOperationsNetworks } from './solanaOperation'\n\nexport const subsOperationsNetworks = [\n    ...infuraSupportedNetworks,\n    NETWORK.ARBITRUM_NOVA,\n    NETWORK.BSC,\n    NETWORK.BSC_TESTNET,\n    NETWORK.CELO,\n    NETWORK.GNOSIS,\n    NETWORK.FANTOM\n]\n\nexport const subscribeOperations = [\n    {\n        name: 'Eth Subscribe (eth_subscribe)',\n        value: 'eth_subscribe',\n        parentGroup: 'Subscribe',\n        description: 'Starts a subscription to a specific event.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: subsOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'eth_subscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">subscription name</code>: <code class=\"inline\">string</code> - The type of event you want to subscribe to (i.e., newHeads, logs, pendingTransactions, newPendingTransactions). This method supports the following subscription types:</li>\n            <ul>\n\t\t\t    <li><code class=\"inline\">pendingTransactions</code> - Returns full transactions that are sent to the network, marked as pending, and are sent from or to a certain address.</li>\n\t\t\t    <li><code class=\"inline\">newPendingTransactions</code> - Returns the hash for all transactions that are added to the pending state and are signed with a key that is available in the node.</li>\n\t\t\t    <li><code class=\"inline\">newHeads</code> - Fires a notification each time a new header is appended to the chain, including chain reorganizations.</li>\n\t\t\t    <li><code class=\"inline\">logs</code> - Returns logs that are included in new imported blocks and match the given filter criteria.</li>\n            </ul>\n            <li><code class=\"inline\">data</code>: <code class=\"inline\">object</code> - (Optional) - Arguments such as an address, multiple addresses, and topics. Note, only logs that are created from these addresses or match the specified topics will return logs.</li>\n        </ul>`,\n        exampleParameters: `[\"newHeads\"]`\n    },\n    {\n        name: 'Account Subscribe (accountSubscribe)',\n        value: 'accountSubscribe',\n        parentGroup: 'Subscribe',\n        description:\n            '(Subscription Websocket) Subscribe to an account to receive notifications when the lamports or data for a given account public key changes.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'accountSubscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">String</code> - Pubkey of account to query, as base-58 encoded string.</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\", or \"jsonParsed\".</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"E645TckHQnDcavVv92Etc6xSWQaq8zzPtPRGBheviRAk\",\n  {\n    \"encoding\": \"base64\",\n    \"commitment\": \"finalized\"\n  }\n]`\n    },\n    {\n        name: 'Log Subscribe (logSubscribe)',\n        value: 'logSubscribe',\n        parentGroup: 'Subscribe',\n        description: '(Subscription Websocket) Subscribe to transaction logging.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'logSubscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">filters</code>: <code class=\"inline\">ENUM</code> - filter criteria for the logs to receive results by account type; currently supported</li>\n            <ul>\n                <li><code class=\"inline\">all</code> - subscribe to all transactions except for simple vote transactions</li>\n                <li><code class=\"inline\">allWithVotes</code> - subscribe to all transactions including simple vote transactions</li>\n                <li><code class=\"inline\">mentions</code> - subscribe to all transactions that mention the provided Pubkey (as base-58 encoded string)</li>\n            </ul>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\", or \"jsonParsed\".</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"all\",\n  {\n    \"encoding\": \"base64\",\n    \"commitment\": \"finalized\"\n  }\n]`\n    },\n    {\n        name: 'Program Subscribe (programSubscribe)',\n        value: 'programSubscribe',\n        parentGroup: 'Subscribe',\n        description:\n            '(Subscription Websocket) Subscribe to a program to receive notifications when the lamports or data for a given account owned by the program changes.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'programSubscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">program_id</code> - Pubkey, as base-58 encoded string</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n                <li><code class=\"inline\">encoding</code> - encoding for Account data, either \"base58\" (slow), \"base64\", \"base64+zstd\", or \"jsonParsed\".</li>\n                <li><code class=\"inline\">filters</code> - filter results using various filter objects; account must meet all filter criteria to be included in results.</li>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"11111111111111111111111111111111\",\n  {\n    \"encoding\": \"base64\",\n    \"commitment\": \"finalized\"\n  }\n]`\n    },\n    {\n        name: 'Root Subscribe (rootSubscribe)',\n        value: 'rootSubscribe',\n        parentGroup: 'Subscribe',\n        description: '(Subscription Websocket) Subscribe to receive notification anytime a new root is set by the validator.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'rootSubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Signature Subscribe (signatureSubscribe)',\n        value: 'signatureSubscribe',\n        parentGroup: 'Subscribe',\n        description:\n            '(Subscription Websocket) Subscribe to a transaction signature to receive notification when the transaction is confirmed On signatureNotification, the subscription is automatically cancelled.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'signatureSubscribe',\n            params: []\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">String</code> - Transaction Signature, as base-58 encoded string</li>\n            <li><code class=\"inline\">Object</code> - (optional) Configuration object containing the following optional fields:</li>\n            <ul>\n                <li><code class=\"inline\">Commitment</code>: <code class=\"inline\">ENUM</code> - (optional) All fields are strings.</li>\n                <ul>\n                    <li><code class=\"inline\">Finalized</code> - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized.</li>\n                    <li><code class=\"inline\">Confirmed</code> - the node will query the most recent block that has been voted on by supermajority of the cluster.</li>\n                    <li><code class=\"inline\">Processed</code> - the node will query its most recent block. Note that the block may not be complete.</li>\n                </ul>\n            </ul>\n        </ul>`,\n        exampleParameters: `[\n  \"51y9Hf2cFzrUPDH24qvL6b6PtPMDGQSX3WwiHsSvkdfGiFTKdoJwGkvqS3gny6XNPLtUtRwGERAs45639EfR5XfT\",\n  {\n    \"commitment\": \"finalized\"\n  }\n]`\n    },\n    {\n        name: 'Slot Subscribe (slotSubscribe)',\n        value: 'slotSubscribe',\n        parentGroup: 'Subscribe',\n        description: '(Subscription Websocket) Subscribe to receive notification anytime a slot is processed by the validator.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'slotSubscribe',\n            params: []\n        }\n    }\n] as IETHOperation[]\n\nexport const unsubscribeOperations = [\n    {\n        name: 'Eth Unsubscribe (eth_unsubscribe)',\n        value: 'eth_unsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: 'Cancels an existing subscription so that no further events are sent.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: subsOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'eth_unsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Account Unsubscribe (accountUnsubscribe)',\n        value: 'accountUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from account change notifications',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'accountUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Log Unsubscribe (logUnsubscribe)',\n        value: 'logUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from transaction logging.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'logUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Program Unsubscribe (programUnsubscribe)',\n        value: 'programUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from program-owned account change notifications.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'programUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Root Unsubscribe (rootUnsubscribe)',\n        value: 'rootUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from root notifications.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'rootUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Signature Unsubscribe (signatureUnsubscribe)',\n        value: 'signatureUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from signature confirmation notification.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'signatureUnsubscribe',\n            params: []\n        }\n    },\n    {\n        name: 'Slot Unsubscribe (slotUnsubscribe)',\n        value: 'slotUnsubscribe',\n        parentGroup: 'Unsubscribe',\n        description: '(Subscription Websocket) Unsubscribe from slot notifications.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: solanaOperationsNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            id: 1,\n            method: 'slotUnsubscribe',\n            params: []\n        }\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/nodes/QuickNode/supportedNetwork.ts",
    "content": "import { INodeOptionsValue } from '../../src'\nimport { NETWORK, NETWORK_LABEL } from '../../src/ChainNetwork'\n\nexport const QuickNodeSupportedNetworks = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: NETWORK.MAINNET\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: NETWORK.GÖRLI\n    },\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: NETWORK.MATIC\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: NETWORK.MATIC_MUMBAI\n    },\n    {\n        label: NETWORK_LABEL.BSC,\n        name: NETWORK.BSC\n    },\n    {\n        label: NETWORK_LABEL.BSC_TESTNET,\n        name: NETWORK.BSC_TESTNET\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM,\n        name: NETWORK.ARBITRUM\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n        name: NETWORK.ARBITRUM_GOERLI\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_NOVA,\n        name: NETWORK.ARBITRUM_NOVA\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE,\n        name: NETWORK.AVALANCHE\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE_TESTNET,\n        name: NETWORK.AVALANCHE_TESTNET\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM,\n        name: NETWORK.OPTIMISM\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM_GOERLI,\n        name: NETWORK.OPTIMISM_GOERLI\n    },\n    {\n        label: NETWORK_LABEL.FANTOM,\n        name: NETWORK.FANTOM\n    },\n    {\n        label: NETWORK_LABEL.CELO,\n        name: NETWORK.CELO\n    },\n    {\n        label: NETWORK_LABEL.GNOSIS,\n        name: NETWORK.GNOSIS\n    },\n    {\n        label: NETWORK_LABEL.SOLANA,\n        name: NETWORK.SOLANA\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_DEVNET,\n        name: NETWORK.SOLANA_DEVNET\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_TESTNET,\n        name: NETWORK.SOLANA_TESTNET\n    }\n] as INodeOptionsValue[]\n\n//REMAINING: ALGORAND, HARMONY, STACKS\n"
  },
  {
    "path": "packages/components/nodes/RequestFinance/RequestFinance.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport moment from 'moment'\nimport { invoiceParameters, salaryParameters } from './constants'\n\ninterface IInvoiceItems {\n    name: string\n    unitPrice: number\n    quantity: number\n    taxType?: string\n    taxAmount?: number\n}\n\nclass RequestFinance implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Request Finance'\n        this.name = 'requestFinance'\n        this.icon = 'requestFinance.svg'\n        this.type = 'action'\n        this.category = 'Accounting'\n        this.version = 1.0\n        this.description = 'Issue invoices and accept payments in cryptocurrency using RequestFinance API'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get Invoice',\n                        name: 'getSingleInvoice',\n                        description: 'Returns single invoice from RequestFinance account.'\n                    },\n                    {\n                        label: 'Create Invoice',\n                        name: 'createInvoice',\n                        description: 'Creates a new payable invoice.'\n                    },\n                    {\n                        label: 'Create Salary Payment',\n                        name: 'createSalaryPayment',\n                        description: 'Creates a new payable salary payment.'\n                    }\n                ]\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'RequestFinance API Key',\n                        name: 'requestFinanceApi'\n                    }\n                ],\n                default: 'requestFinanceApi'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Invoice',\n                name: 'invoiceId',\n                type: 'asyncOptions',\n                loadMethod: 'getInvoices',\n                show: {\n                    'actions.operation': ['getSingleInvoice']\n                }\n            },\n            ...salaryParameters,\n            ...invoiceParameters\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getInvoiceCurrency(): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.request.finance/currency/list/invoicing`\n                }\n\n                const response = await axios(axiosConfig)\n                const tokens = response.data\n                const tokenSet: Set<string> = new Set()\n\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    if (!token.symbol.includes('-')) tokenSet.add(`${token.symbol} | ${token.decimals}`)\n                }\n\n                tokenSet.forEach((tkn) => {\n                    const data = {\n                        label: tkn.split('|')[0].trim(),\n                        name: tkn\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                })\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getPaymentCurrency(): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.request.finance/currency/list/invoicing`\n                }\n\n                const response = await axios(axiosConfig)\n                const tokens = response.data\n                const tokenSet: Set<string> = new Set()\n\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    if (token.id.includes('-')) tokenSet.add(`${token.id} | ${token.decimals}`)\n                }\n\n                tokenSet.forEach((tkn) => {\n                    const data = {\n                        label: tkn.split('|')[0].trim(),\n                        name: tkn\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                })\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getCountry(): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://restcountries.com/v2/all`\n                }\n\n                const response = await axios(axiosConfig)\n                const countries = response.data\n\n                for (let i = 0; i < countries.length; i += 1) {\n                    const country = countries[i]\n                    const data = {\n                        label: country.name,\n                        name: country.alpha2Code\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getClients(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            const apiKey = credentials!.apiKey as string\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.request.finance/clients?type=customer`,\n                    headers: {\n                        Accept: 'application/json',\n                        'Content-Type': 'application/json',\n                        Authorization: apiKey\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const clients = response.data\n\n                for (let i = 0; i < clients.length; i += 1) {\n                    const client = clients[i]\n                    const data = {\n                        label: client.email,\n                        name: JSON.stringify(client)\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getEmployees(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            const apiKey = credentials!.apiKey as string\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.request.finance/clients?type=employee`,\n                    headers: {\n                        Accept: 'application/json',\n                        'Content-Type': 'application/json',\n                        Authorization: apiKey\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const employees = response.data\n\n                for (let i = 0; i < employees.length; i += 1) {\n                    const employee = employees[i]\n                    const data = {\n                        label: employee.email,\n                        name: JSON.stringify(employee)\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getInvoices(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            const apiKey = credentials!.apiKey as string\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.request.finance/invoices?filterBy=sent&variant=rnf_invoice`,\n                    headers: {\n                        Accept: 'application/json',\n                        'Content-Type': 'application/json',\n                        Authorization: apiKey\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const invoices = response.data\n\n                for (let i = 0; i < invoices.length; i += 1) {\n                    const invoice = invoices[i]\n                    const data = {\n                        label: `${invoice.invoiceNumber} (${moment(invoice.creationDate).format('MMMM D, YYYY')})`,\n                        name: invoice.id\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionsData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing!')\n        }\n        if (credentials === undefined) {\n            throw new Error('Missing credentials!')\n        }\n\n        // Get Operation\n        const operation = actionsData.operation as string\n\n        // Get API key\n        const apiKey = credentials.apiKey as string\n\n        // Get Input Params\n        const invoiceItems = inputParametersData.invoiceItems as unknown as IInvoiceItems[]\n        const invoiceCurrencyValue = inputParametersData.invoiceCurrency as string\n        const invoiceCurrency = invoiceCurrencyValue ? invoiceCurrencyValue.split('|')[0].trim() : ''\n        const invoiceCurrencyDecimals = invoiceCurrencyValue ? invoiceCurrencyValue.split('|')[1].trim() : ''\n        const paymentAddress = inputParametersData.paymentAddress as string\n        const paymentCurrencyValue = inputParametersData.paymentCurrency as string\n        const paymentCurrency = paymentCurrencyValue ? paymentCurrencyValue.split('|')[0].trim() : ''\n        const paymentDueDate = inputParametersData.paymentDueDate as string\n        const clientType = inputParametersData.clientType as string\n        const existingClient = inputParametersData.existingClient as string\n        const buyerInfoEmail = inputParametersData.buyerInfoEmail as string\n        const buyerInfoBusinessName = inputParametersData.buyerInfoBusinessName as string\n        const buyerInfoFirstName = inputParametersData.buyerInfoFirstName as string\n        const buyerInfoLastName = inputParametersData.buyerInfoLastName as string\n        const buyerInfoStreetAddress = inputParametersData.buyerInfoStreetAddress as string\n        const buyerInfoExtendedAddress = inputParametersData.buyerInfoExtendedAddress as string\n        const buyerInfoPostalCode = inputParametersData.buyerInfoPostalCode as string\n        const buyerInfoRegion = inputParametersData.buyerInfoRegion as string\n        const buyerInfoCountry = inputParametersData.buyerInfoCountry as string\n        const buyerInfoTaxRegistration = inputParametersData.buyerInfoTaxRegistration as string\n        const invoiceId = inputParametersData.invoiceId as string\n\n        const salaryCurrencyValue = inputParametersData.salaryCurrency as string\n        const salaryCurrency = salaryCurrencyValue ? salaryCurrencyValue.split('|')[0].trim() : ''\n        const salaryCurrencyDecimals = salaryCurrencyValue ? salaryCurrencyValue.split('|')[1].trim() : ''\n        const salaryAmount = inputParametersData.salaryAmount as string\n        const salaryPaymentCurrencyValue = inputParametersData.salaryPaymentCurrency as string\n        const salaryPaymentCurrency = salaryPaymentCurrencyValue ? salaryPaymentCurrencyValue.split('|')[0].trim() : ''\n        const employeeType = inputParametersData.employeeType as string\n        const existingEmployee = inputParametersData.existingEmployee as string\n        const employeeEmail = inputParametersData.employeeEmail as string\n        const employeePaymentAddress = inputParametersData.employeePaymentAddress as string\n        const employeeFirstName = inputParametersData.employeeFirstName as string\n        const employeeLastName = inputParametersData.employeeLastName as string\n        const companyEmail = inputParametersData.companyEmail as string\n        const companyBusinessName = inputParametersData.companyBusinessName as string\n        const companyFirstName = inputParametersData.companyFirstName as string\n        const companyLastName = inputParametersData.companyLastName as string\n        const companyStreetAddress = inputParametersData.companyStreetAddress as string\n        const companyExtendedAddress = inputParametersData.companyExtendedAddress as string\n        const companyPostalCode = inputParametersData.companyPostalCode as string\n        const companyRegion = inputParametersData.companyRegion as string\n        const companyCountry = inputParametersData.companyCountry as string\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n\n        let url = ''\n        let method: Method = 'GET'\n        let body: ICommonObject = {}\n\n        if (operation === 'createInvoice') {\n            url = 'https://api.request.finance/invoices'\n            method = 'POST'\n\n            const invoicesSent = await getNumberOfInvoiceSent(apiKey)\n            body.invoiceNumber = (invoicesSent + 1).toString()\n\n            //invoiceItems\n            const invoiceItemsArray: any[] = []\n            for (let j = 0; j < invoiceItems.length; j += 1) {\n                const invoiceItem: ICommonObject = {\n                    currency: invoiceCurrency,\n                    name: invoiceItems[j].name,\n                    quantity: Number(invoiceItems[j].quantity),\n                    unitPrice: formatUnitPrice(invoiceItems[j].unitPrice, Number(invoiceCurrencyDecimals))\n                }\n\n                if (invoiceItems[j].taxType && invoiceItems[j].taxAmount) {\n                    invoiceItem.tax = {\n                        type: invoiceItems[j].taxType,\n                        amount: invoiceItems[j].taxAmount\n                    }\n                }\n                invoiceItemsArray.push(invoiceItem)\n            }\n            body.invoiceItems = invoiceItemsArray\n\n            //payment\n            if (paymentDueDate) {\n                const dueDate = { dueDate: paymentDueDate }\n                body.paymentTerms = dueDate\n            }\n            body.paymentAddress = paymentAddress\n            body.paymentCurrency = paymentCurrency\n\n            //buyer\n            const buyerInfo: ICommonObject = {}\n            if (clientType === 'existing') {\n                const client = JSON.parse(existingClient)\n                buyerInfo.email = client.email\n                if (client.firstName) buyerInfo.firstName = client.firstName\n                if (client.lastName) buyerInfo.lastName = client.lastName\n                if (client.businessName) buyerInfo.businessName = client.businessName\n                if (client.taxRegistration) buyerInfo.taxRegistration = client.taxRegistration\n\n                const buyerAddress = {\n                    streetAddress: client.address.streetAddress,\n                    extendedAddress: client.address.extendedAddress,\n                    postalCode: client.address.postalCode,\n                    region: client.address.region,\n                    city: client.address.city,\n                    country: client.address.country\n                }\n                buyerInfo.address = buyerAddress\n            } else {\n                buyerInfo.email = buyerInfoEmail\n                if (buyerInfoFirstName) buyerInfo.firstName = buyerInfoFirstName\n                if (buyerInfoLastName) buyerInfo.lastName = buyerInfoLastName\n                if (buyerInfoBusinessName) buyerInfo.businessName = buyerInfoBusinessName\n                if (buyerInfoTaxRegistration) buyerInfo.taxRegistration = buyerInfoTaxRegistration\n\n                const buyerAddress = {\n                    streetAddress: buyerInfoStreetAddress || '',\n                    extendedAddress: buyerInfoExtendedAddress || '',\n                    postalCode: buyerInfoPostalCode || '',\n                    region: buyerInfoRegion || '',\n                    city: buyerInfoRegion || '',\n                    country: buyerInfoCountry || ''\n                }\n                buyerInfo.address = buyerAddress\n            }\n            body.buyerInfo = buyerInfo\n        }\n\n        if (operation === 'getSingleInvoice') {\n            url = `https://api.request.finance/invoices/${invoiceId}`\n            method = 'GET'\n        }\n\n        if (operation === 'createSalaryPayment') {\n            url = 'https://api.request.finance/invoices'\n            method = 'POST'\n            body.meta = {\n                format: 'rnf_salary',\n                version: '0.0.3'\n            }\n\n            const salarySent = await getNumberOfSalarySent(apiKey)\n            body.invoiceNumber = (salarySent + 1).toString()\n\n            //invoiceItems\n            const invoiceItem: ICommonObject = {\n                currency: salaryCurrency,\n                name: 'Salary',\n                quantity: 1,\n                unitPrice: formatUnitPrice(salaryAmount, Number(salaryCurrencyDecimals))\n            }\n            body.invoiceItems = [invoiceItem]\n\n            //payment\n            const dueDate = { dueDate: moment().endOf('day').toString() }\n            body.paymentTerms = dueDate\n            body.paymentCurrency = salaryPaymentCurrency\n\n            //seller\n            const sellerInfo: ICommonObject = {}\n            if (employeeType === 'existing') {\n                const employee = JSON.parse(existingEmployee)\n                sellerInfo.email = employee.email\n                body.paymentAddress = employee.salaryPaymentAddress\n                if (employee.firstName) sellerInfo.firstName = employee.firstName\n                if (employee.lastName) sellerInfo.lastName = employee.lastName\n            } else {\n                sellerInfo.email = employeeEmail\n                body.paymentAddress = employeePaymentAddress\n                if (employeeFirstName) sellerInfo.firstName = employeeFirstName\n                if (employeeLastName) sellerInfo.lastName = employeeLastName\n            }\n            body.sellerInfo = sellerInfo\n\n            //buyer\n            const buyerInfo: ICommonObject = {}\n            buyerInfo.email = companyEmail\n            if (companyFirstName) buyerInfo.firstName = companyFirstName\n            if (companyLastName) buyerInfo.lastName = companyLastName\n            if (companyBusinessName) buyerInfo.businessName = companyBusinessName\n\n            const buyerAddress = {\n                streetAddress: companyStreetAddress || '',\n                extendedAddress: companyExtendedAddress || '',\n                postalCode: companyPostalCode || '',\n                region: companyRegion || '',\n                city: companyRegion || '',\n                country: companyCountry || ''\n            }\n            buyerInfo.address = buyerAddress\n            body.buyerInfo = buyerInfo\n        }\n\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers: {\n                    Accept: 'application/json',\n                    'Content-Type': 'application/json',\n                    Authorization: apiKey\n                }\n            }\n            if (Object.keys(body).length) axiosConfig.data = body\n            const response = await axios(axiosConfig)\n            responseData = response.data\n\n            // Convert to on-chain request\n            if (operation === 'createInvoice' || operation === 'createSalaryPayment') {\n                const invoiceId = responseData.id\n                const axiosConfig2: AxiosRequestConfig = {\n                    method,\n                    url: `https://api.request.finance/invoices/${invoiceId}`,\n                    data: body,\n                    headers: {\n                        Accept: 'application/json',\n                        'Content-Type': 'application/json',\n                        Authorization: apiKey\n                    }\n                }\n                await axios(axiosConfig2)\n            }\n        } catch (error) {\n            console.error(error.response.data.errors)\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nconst getNumberOfInvoiceSent = async (apiKey: string) => {\n    const axiosConfig: AxiosRequestConfig = {\n        method: 'GET',\n        url: 'https://api.request.finance/invoices?filterBy=sent&variant=rnf_invoice',\n        headers: {\n            Accept: 'application/json',\n            'Content-Type': 'application/json',\n            Authorization: apiKey\n        }\n    }\n    const response = await axios(axiosConfig)\n    return response.data.length || 0\n}\n\nconst getNumberOfSalarySent = async (apiKey: string) => {\n    const axiosConfig: AxiosRequestConfig = {\n        method: 'GET',\n        url: 'https://api.request.finance/invoices?variant=rnf_salary',\n        headers: {\n            Accept: 'application/json',\n            'Content-Type': 'application/json',\n            Authorization: apiKey\n        }\n    }\n    const response = await axios(axiosConfig)\n    return response.data.length || 0\n}\n\nconst addTrailingZeros = (num: number, totalLength: number) => {\n    return Number(String(num).padEnd(totalLength + 1, '0'))\n}\n\nconst formatUnitPrice = (x: number | string, decimals: number) => {\n    return (Number(x) * addTrailingZeros(1, decimals)).toFixed(0)\n}\n\nmodule.exports = { nodeClass: RequestFinance }\n"
  },
  {
    "path": "packages/components/nodes/RequestFinance/RequestFinanceTrigger.ts",
    "content": "import { CronJob } from 'cron'\nimport { ICronJobs, INode, INodeData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\nimport moment from 'moment'\n\nclass RequestFinanceTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'Request Finance Trigger'\n        this.name = 'requestFinanceTrigger'\n        this.icon = 'requestFinance.svg'\n        this.type = 'trigger'\n        this.category = 'Accounting'\n        this.version = 1.0\n        this.description = 'Start workflow whenever receive new invoice or invoice status has changed'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.actions = [\n            {\n                label: 'Invoice Status',\n                name: 'invoiceStatus',\n                type: 'options',\n                options: [\n                    {\n                        label: 'New',\n                        name: 'new',\n                        description: 'Trigger workflow when new invoice received'\n                    },\n                    {\n                        label: 'Accepted',\n                        name: 'accepted',\n                        description: 'The invoice has been approved by the buyer.'\n                    },\n                    {\n                        label: 'Declared Paid',\n                        name: 'declaredPaid',\n                        description:\n                            'The buyer declared the invoice as paid. The seller has to confirm before the invoice can move into the paid status. This is necessary for currencies, where the Request Network does not yet support payment detection.'\n                    },\n                    {\n                        label: 'Paid',\n                        name: 'paid',\n                        description: 'Seller has confirmed and marked the invoice as paid, i.e: the buyer paid the invoice.'\n                    },\n                    {\n                        label: 'Canceled',\n                        name: 'canceled',\n                        description: 'The seller canceled the invoice.'\n                    },\n                    {\n                        label: 'Rejected',\n                        name: 'rejected',\n                        description: 'The buyer rejected the invoice.'\n                    }\n                ],\n                default: '',\n                description: 'Status of an invoice'\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'RequestFinance API Key',\n                        name: 'requestFinanceApi'\n                    }\n                ],\n                default: 'requestFinanceApi'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Select Invoice',\n                name: 'invoiceType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Retrieve from existing invoices',\n                        name: 'existingInvoice'\n                    },\n                    {\n                        label: 'Custom invoice ID',\n                        name: 'customInvoice'\n                    }\n                ],\n                hide: {\n                    'actions.invoiceStatus': ['new']\n                }\n            },\n            {\n                label: 'Invoice ID',\n                name: 'invoiceId',\n                type: 'string',\n                placeholder: '63e2c662d24445b79ada9c3d',\n                hide: {\n                    'actions.invoiceStatus': ['new']\n                },\n                show: {\n                    'inputParameters.invoiceType': ['customInvoice']\n                }\n            },\n            {\n                label: 'Invoice',\n                name: 'invoiceId',\n                type: 'asyncOptions',\n                loadMethod: 'getInvoices',\n                hide: {\n                    'actions.invoiceStatus': ['new']\n                },\n                show: {\n                    'inputParameters.invoiceType': ['existingInvoice']\n                }\n            },\n            {\n                label: 'Polling Time',\n                name: 'pollTime',\n                type: 'options',\n                description: 'How often should we keep checking the invoice status',\n                options: [\n                    {\n                        label: 'Every 15 secs',\n                        name: '15s'\n                    },\n                    {\n                        label: 'Every 30 secs',\n                        name: '30s'\n                    },\n                    {\n                        label: 'Every 1 min',\n                        name: '1min'\n                    },\n                    {\n                        label: 'Every 5 min',\n                        name: '5min'\n                    },\n                    {\n                        label: 'Every 10 min',\n                        name: '10min'\n                    }\n                ],\n                default: '30s'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getInvoices(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            const apiKey = credentials!.apiKey as string\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.request.finance/invoices?variant=rnf_invoice`,\n                    headers: {\n                        Accept: 'application/json',\n                        'Content-Type': 'application/json',\n                        Authorization: apiKey\n                    }\n                }\n\n                const response = await axios(axiosConfig)\n                const invoices = response.data\n\n                for (let i = 0; i < invoices.length; i += 1) {\n                    const invoice = invoices[i]\n                    const data = {\n                        label: `#${invoice.invoiceNumber} (${moment(invoice.creationDate).format('MMMM D, YYYY')})`,\n                        name: invoice.id\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const inputParametersData = nodeData.inputParameters\n        const actionData = nodeData.actions\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n        if (credentials === undefined) {\n            throw new Error('Missing credentials!')\n        }\n\n        // Get API key\n        const apiKey = credentials.apiKey as string\n\n        // Get Invoice\n        const invoiceId = inputParametersData.invoiceId as string\n        const invoiceStatus = actionData.invoiceStatus as string\n\n        let url = ''\n        const method: Method = 'GET'\n        const headers: AxiosRequestHeaders = {\n            Accept: 'application/json',\n            'Content-Type': 'application/json',\n            Authorization: apiKey\n        }\n\n        const emitEventKey = nodeData.emitEventKey as string\n        const pollTime = (inputParametersData?.pollTime as string) || '30s'\n        const cronTimes: string[] = []\n\n        if (pollTime === '15s') {\n            cronTimes.push(`*/15 * * * * *`)\n        } else if (pollTime === '30s') {\n            cronTimes.push(`*/30 * * * * *`)\n        } else if (pollTime === '1min') {\n            cronTimes.push(`*/1 * * * *`)\n        } else if (pollTime === '5min') {\n            cronTimes.push(`*/5 * * * *`)\n        } else if (pollTime === '10min') {\n            cronTimes.push(`*/10 * * * *`)\n        }\n\n        async function getInvoiceStatusAPI() {\n            url = `https://api.request.finance/invoices/${invoiceId}`\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers\n            }\n            return await axios(axiosConfig)\n        }\n\n        async function getReceivedInvoices() {\n            url = `https://api.request.finance/invoices?filterBy=received&variant=rnf_invoice&status=open`\n            const axiosConfig: AxiosRequestConfig = {\n                method,\n                url,\n                headers\n            }\n            return await axios(axiosConfig)\n        }\n\n        let lastInvoicesAmount = 0\n\n        try {\n            // Get initial data\n            if (invoiceStatus === 'new') {\n                const response = await getReceivedInvoices()\n                const invoices = response.data || []\n                lastInvoicesAmount = invoices.length\n            }\n\n            // Trigger when cron job hits\n            const executeTrigger = async () => {\n                if (invoiceStatus === 'new') {\n                    const newResponse = await getReceivedInvoices()\n                    const newResponseData = newResponse.data || []\n                    const newInvoicesAmount = newResponseData.length\n\n                    if (newInvoicesAmount > lastInvoicesAmount) {\n                        const differenceAmount = newInvoicesAmount - lastInvoicesAmount\n                        const returnData = []\n                        for (let i = 0; i < differenceAmount; i += 1) {\n                            returnData.push(newResponseData[i])\n                        }\n                        lastInvoicesAmount = newInvoicesAmount\n                        this.emit(emitEventKey, returnNodeExecutionData(returnData))\n                    }\n                } else {\n                    const newResponse = await getInvoiceStatusAPI()\n                    const newResponseData = newResponse.data\n\n                    if (newResponseData.status === invoiceStatus) {\n                        this.emit(emitEventKey, returnNodeExecutionData(newResponseData))\n                    }\n                }\n            }\n\n            // Start the cron-jobs\n            if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n                for (const cronTime of cronTimes) {\n                    // Automatically start the cron job\n                    this.cronJobs[emitEventKey].push(new CronJob(cronTime, executeTrigger, undefined, true))\n                }\n            } else {\n                for (const cronTime of cronTimes) {\n                    // Automatically start the cron job\n                    this.cronJobs[emitEventKey] = [new CronJob(cronTime, executeTrigger, undefined, true)]\n                }\n            }\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: RequestFinanceTrigger }\n"
  },
  {
    "path": "packages/components/nodes/RequestFinance/constants.ts",
    "content": "import { INodeParams } from '../../src'\n\nexport const salaryParameters = [\n    {\n        label: 'Salary Currency',\n        name: 'salaryCurrency',\n        description: 'Currency in which the salary will be issued in.',\n        type: 'asyncOptions',\n        loadMethod: 'getInvoiceCurrency',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Salary Amount',\n        name: 'salaryAmount',\n        description: 'Amount of salary to be paid',\n        type: 'number',\n        placeholder: '1000',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'How do you want to pay?',\n        name: 'salaryPaymentCurrency',\n        type: 'asyncOptions',\n        loadMethod: 'getPaymentCurrency',\n        description: 'Currency in which the employer would like to pay in.',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Employee Type',\n        name: 'employeeType',\n        type: 'options',\n        options: [\n            {\n                label: 'Existing',\n                name: 'existing'\n            },\n            {\n                label: 'New',\n                name: 'new'\n            }\n        ],\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Employee',\n        name: 'existingEmployee',\n        type: 'asyncOptions',\n        loadMethod: 'getEmployees',\n        show: {\n            'actions.operation': ['createSalaryPayment'],\n            'inputParameters.employeeType': ['existing']\n        }\n    },\n    {\n        label: 'Employee Email',\n        name: 'employeeEmail',\n        type: 'string',\n        description: 'Email of the employee',\n        default: '',\n        show: {\n            'actions.operation': ['createSalaryPayment'],\n            'inputParameters.employeeType': ['new']\n        }\n    },\n    {\n        label: 'Employee Wallet Address',\n        name: 'employeePaymentAddress',\n        type: 'string',\n        description: 'Wallet address of the employee to receive salary',\n        default: '',\n        show: {\n            'actions.operation': ['createSalaryPayment'],\n            'inputParameters.employeeType': ['new']\n        }\n    },\n    {\n        label: 'Employee First Name',\n        name: 'employeeFirstName',\n        type: 'string',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment'],\n            'inputParameters.employeeType': ['new']\n        }\n    },\n    {\n        label: 'Employee Last Name',\n        name: 'employeeLastName',\n        type: 'string',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment'],\n            'inputParameters.employeeType': ['new']\n        }\n    },\n    {\n        label: 'Owner Email',\n        name: 'companyEmail',\n        type: 'string',\n        description:\n            'Email of the owner that issues salary payment. Refer to <a target=\"_blank\" href=\"https://app.request.finance/account/profile\">Account page</a>',\n        default: '',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Owner First Name',\n        name: 'companyFirstName',\n        description:\n            'First name of the owner that issues salary payment. Refer to <a target=\"_blank\" href=\"https://app.request.finance/account/profile\">Account page</a>',\n        type: 'string',\n        default: '',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company Last Name',\n        name: 'companyLastName',\n        description:\n            'Last name of the owner that issues salary payment. Refer to <a target=\"_blank\" href=\"https://app.request.finance/account/profile\">Account page</a>',\n        type: 'string',\n        default: '',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company Business Name',\n        name: 'companyBusinessName',\n        type: 'string',\n        description:\n            'Business name of the company. Refer to <a target=\"_blank\" href=\"https://app.request.finance/account/profile\">Account page</a>',\n        optional: true,\n        default: '',\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company Address Line 1',\n        name: 'companyStreetAddress',\n        type: 'string',\n        description: 'Street, house, apartment of the company.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company Address Line 2',\n        name: 'companyExtendedAddress',\n        type: 'string',\n        description: 'Street, house, apartment of the company.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company Postal Code',\n        name: 'companyPostalCode',\n        type: 'string',\n        description: 'Post code of the company.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company State',\n        name: 'companyRegion',\n        type: 'string',\n        description: 'Region of the company (e.g. “California”).',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    },\n    {\n        label: 'Company Country',\n        name: 'companyCountry',\n        description: 'Country of the company (e.g. US).',\n        type: 'asyncOptions',\n        loadMethod: 'getCountry',\n        optional: true,\n        show: {\n            'actions.operation': ['createSalaryPayment']\n        }\n    }\n] as INodeParams[]\n\nexport const invoiceParameters = [\n    {\n        label: 'Invoice Currency',\n        name: 'invoiceCurrency',\n        description:\n            'Currency in which the invoice is denominated. For example, invoices can be denominated in USD, but buyers can pay in crypto.',\n        type: 'asyncOptions',\n        loadMethod: 'getInvoiceCurrency',\n        show: {\n            'actions.operation': ['createInvoice']\n        }\n    },\n    {\n        label: 'Invoice Items',\n        name: 'invoiceItems',\n        type: 'array',\n        array: [\n            {\n                label: 'Name',\n                name: 'name',\n                type: 'string',\n                description: 'Name of the product/service for which the invoice is created.',\n                default: ''\n            },\n            {\n                label: 'Unit Price',\n                name: 'unitPrice',\n                description: 'Price of the product/service, excl. taxes. Max 2 decimal places',\n                type: 'number'\n            },\n            {\n                label: 'Quantity',\n                name: 'quantity',\n                description: 'Quantity of the product/service that was provided.',\n                type: 'number'\n            },\n            {\n                label: 'Tax Type',\n                name: 'taxType',\n                description:\n                    'Currency code in which the invoice is denominated. For example, invoices can be denominated in USD, but buyers can pay in crypto.',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Fixed',\n                        name: 'fixed'\n                    },\n                    {\n                        label: 'Percentage',\n                        name: 'percentage'\n                    }\n                ],\n                optional: true\n            },\n            {\n                label: 'Tax Amount',\n                name: 'taxAmount',\n                description: 'Amount of the tax.',\n                type: 'number',\n                show: {\n                    'inputParameters.invoiceItems[$index].taxType': ['fixed', 'percentage']\n                }\n            }\n        ],\n        show: {\n            'actions.operation': ['createInvoice']\n        }\n    },\n    {\n        label: 'Payment Address',\n        name: 'paymentAddress',\n        type: 'string',\n        description: 'Address which will receive the payment.',\n        default: '',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        show: {\n            'actions.operation': ['createInvoice']\n        }\n    },\n    {\n        label: 'Payment Currency',\n        name: 'paymentCurrency',\n        description: 'Currency in which the invoice can be paid.',\n        type: 'asyncOptions',\n        loadMethod: 'getPaymentCurrency',\n        show: {\n            'actions.operation': ['createInvoice']\n        }\n    },\n    {\n        label: 'Payment Due Date',\n        name: 'paymentDueDate',\n        description: 'Due date of the invoice. Last date the buyer can pay.',\n        type: 'date',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice']\n        }\n    },\n    {\n        label: 'Client Type',\n        name: 'clientType',\n        type: 'options',\n        options: [\n            {\n                label: 'Existing',\n                name: 'existing'\n            },\n            {\n                label: 'New',\n                name: 'new'\n            }\n        ],\n        show: {\n            'actions.operation': ['createInvoice']\n        }\n    },\n    {\n        label: 'Client',\n        name: 'existingClient',\n        type: 'asyncOptions',\n        loadMethod: 'getClients',\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['existing']\n        }\n    },\n    {\n        label: 'Client Email',\n        name: 'buyerInfoEmail',\n        type: 'string',\n        description: 'Email of the buyer/customer/client',\n        default: '',\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Business Name',\n        name: 'buyerInfoBusinessName',\n        type: 'string',\n        description: 'Business name of the buyer/customer/client',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client First Name',\n        name: 'buyerInfoFirstName',\n        type: 'string',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Last Name',\n        name: 'buyerInfoLastName',\n        type: 'string',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Address Line 1',\n        name: 'buyerInfoStreetAddress',\n        type: 'string',\n        description: 'Street, house, apartment of the buyer.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Address Line 2',\n        name: 'buyerInfoExtendedAddress',\n        type: 'string',\n        description: 'Street, house, apartment of the buyer.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Postal Code',\n        name: 'buyerInfoPostalCode',\n        type: 'string',\n        description: 'Post code of the buyer.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client State',\n        name: 'buyerInfoRegion',\n        type: 'string',\n        description: 'Region of the buyer (e.g. “California”).',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Country',\n        name: 'buyerInfoCountry',\n        description: 'Country of the buyer (e.g. US).',\n        type: 'asyncOptions',\n        loadMethod: 'getCountry',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    },\n    {\n        label: 'Client Tax Registration',\n        name: 'buyerInfoTaxRegistration',\n        type: 'string',\n        description: 'Tax registration number of the buyer.',\n        default: '',\n        optional: true,\n        show: {\n            'actions.operation': ['createInvoice'],\n            'inputParameters.clientType': ['new']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/Scheduler/Scheduler.ts",
    "content": "import { ICommonObject, ICronJobs, INode, INodeData, INodeParams, NodeType } from '../../src/Interface'\nimport { CronJob } from 'cron'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\n\ninterface IScheduleTimes {\n    mode: string\n    timezone: string\n    hour?: number\n    minute?: number\n    dayOfMonth?: number\n    weekday: string\n    value?: number\n    unit?: string\n    specificDateTime?: string\n}\n\nclass Scheduler extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n    cronJobs: ICronJobs\n\n    constructor() {\n        super()\n        this.label = 'Scheduler'\n        this.name = 'scheduler'\n        this.icon = 'scheduler.svg'\n        this.type = 'trigger'\n        this.category = 'Utilities'\n        this.version = 1.1\n        this.description = 'Start workflow at scheduled times'\n        this.incoming = 0\n        this.outgoing = 1\n        this.cronJobs = {}\n        this.inputParameters = [\n            {\n                label: 'Pattern',\n                name: 'pattern',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Repetitive',\n                        name: 'repetitive',\n                        description: 'Workflow will be triggered repetitively every X'\n                    },\n                    {\n                        label: 'Once',\n                        name: 'once',\n                        description: 'Workflow will be triggered only once at specific time'\n                    }\n                ],\n                default: 'repetitive'\n            },\n            {\n                label: 'Date Time',\n                name: 'specificDateTime',\n                type: 'date',\n                description: 'Choose a specific date time to trigger the workflow once',\n                show: {\n                    'inputParameters.pattern': ['once']\n                }\n            },\n            {\n                label: 'Schedules',\n                name: 'scheduleTimes',\n                type: 'array',\n                show: {\n                    'inputParameters.pattern': ['repetitive']\n                },\n                array: [\n                    {\n                        label: 'Mode',\n                        name: 'mode',\n                        type: 'options',\n                        options: [\n                            {\n                                label: 'Every Day',\n                                name: 'everyDay'\n                            },\n                            {\n                                label: 'Every Week',\n                                name: 'everyWeek'\n                            },\n                            {\n                                label: 'Every Month',\n                                name: 'everyMonth'\n                            },\n                            {\n                                label: 'Every X',\n                                name: 'everyX'\n                            },\n                            {\n                                label: 'Every Specific Time',\n                                name: 'specific'\n                            }\n                        ],\n                        default: 'everyDay'\n                    },\n                    {\n                        label: 'Specific Date Time',\n                        name: 'specificDateTime',\n                        type: 'date',\n                        description: 'Choose a specific date time to trigger the workflow',\n                        show: {\n                            'inputParameters.scheduleTimes[$index].mode': ['specific']\n                        }\n                    },\n                    {\n                        label: 'Hour',\n                        name: 'hour',\n                        type: 'number',\n                        hide: {\n                            'inputParameters.scheduleTimes[$index].mode': ['everyX', 'specific']\n                        },\n                        default: new Date().getHours(),\n                        description: '[24H Format] Scheduled hour to trigger workflow'\n                    },\n                    {\n                        label: 'Minute',\n                        name: 'minute',\n                        type: 'number',\n                        hide: {\n                            'inputParameters.scheduleTimes[$index].mode': ['everyX', 'specific']\n                        },\n                        default: new Date().getMinutes(),\n                        description: '[0 - 59] Scheduled minute to trigger workflow'\n                    },\n                    {\n                        label: 'Day of Month',\n                        name: 'dayOfMonth',\n                        type: 'number',\n                        show: {\n                            'inputParameters.scheduleTimes[$index].mode': ['everyMonth']\n                        },\n                        default: new Date().getDate(),\n                        description: '[1 - 31] Scheduled day to trigger workflow'\n                    },\n                    {\n                        label: 'Weekday',\n                        name: 'weekday',\n                        type: 'options',\n                        show: {\n                            'inputParameters.scheduleTimes[$index].mode': ['everyWeek']\n                        },\n                        options: [\n                            {\n                                label: 'Monday',\n                                name: '1'\n                            },\n                            {\n                                label: 'Tuesday',\n                                name: '2'\n                            },\n                            {\n                                label: 'Wednesday',\n                                name: '3'\n                            },\n                            {\n                                label: 'Thursday',\n                                name: '4'\n                            },\n                            {\n                                label: 'Friday',\n                                name: '5'\n                            },\n                            {\n                                label: 'Saturday',\n                                name: '6'\n                            },\n                            {\n                                label: 'Sunday',\n                                name: '0'\n                            }\n                        ],\n                        default: new Date().getDay().toString(),\n                        description: 'Scheduled weekday to trigger workflow'\n                    },\n                    {\n                        label: 'Value',\n                        name: 'value',\n                        type: 'number',\n                        show: {\n                            'inputParameters.scheduleTimes[$index].mode': ['everyX']\n                        },\n                        default: 1,\n                        description: 'Scheduled X seconds/minutes/hours to trigger workflow'\n                    },\n                    {\n                        label: 'Unit',\n                        name: 'unit',\n                        type: 'options',\n                        show: {\n                            'inputParameters.scheduleTimes[$index].mode': ['everyX']\n                        },\n                        options: [\n                            {\n                                label: 'Seconds',\n                                name: 'seconds'\n                            },\n                            {\n                                label: 'Minutes',\n                                name: 'minutes'\n                            },\n                            {\n                                label: 'Hours',\n                                name: 'hours'\n                            }\n                        ],\n                        default: 'hours',\n                        description: 'Units of scheduled X seconds / minutes / hours'\n                    }\n                ]\n            }\n        ]\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const pattern = inputParametersData.pattern as string\n        const scheduleTimes = inputParametersData.scheduleTimes as unknown as IScheduleTimes[]\n\n        const cronTimes: string[] = []\n\n        if (pattern === 'once') {\n            const specificDateTime = inputParametersData.specificDateTime as string\n            cronTimes.push(dateToCron(new Date(specificDateTime)))\n        } else {\n            for (const scheduleItem of scheduleTimes) {\n                if (scheduleItem.mode === 'everyX') {\n                    if (scheduleItem.unit === 'seconds') {\n                        // Every X seconds\n                        cronTimes.push(`*/${scheduleItem.value} * * * * *`)\n                    } else if (scheduleItem.unit === 'minutes') {\n                        // Every X minutes\n                        cronTimes.push(`*/${scheduleItem.value} * * * *`)\n                    } else if (scheduleItem.unit === 'hours') {\n                        // At 0 minutes past the hour, every X hours\n                        cronTimes.push(`0 */${scheduleItem.value} * * *`)\n                    }\n                }\n\n                if (scheduleItem.mode === 'everyDay') {\n                    const minute = scheduleItem.minute || '0'\n                    const hour = scheduleItem.hour || '0'\n                    // At XX:XX AM/PM, every days\n                    cronTimes.push(`${minute} ${hour} * * *`)\n                }\n\n                if (scheduleItem.mode === 'everyWeek') {\n                    const minute = scheduleItem.minute || '0'\n                    const hour = scheduleItem.hour || '0'\n                    const weekday = scheduleItem.weekday || '0'\n                    // At XX:XX AM/PM, only on Monday/Tuesday...\n                    cronTimes.push(`${minute} ${hour} * * ${weekday}`)\n                }\n\n                if (scheduleItem.mode === 'everyMonth') {\n                    const minute = scheduleItem.minute || '0'\n                    const hour = scheduleItem.hour || '0'\n                    const dayOfMonth = scheduleItem.dayOfMonth || '0'\n                    // At XX:XX AM/PM, on day X of the month\n                    cronTimes.push(`${minute} ${hour} ${dayOfMonth} * *`)\n                }\n\n                if (scheduleItem.mode === 'specific') {\n                    const specificDateTime = scheduleItem.specificDateTime as string\n                    cronTimes.push(dateToCron(new Date(specificDateTime)))\n                }\n            }\n        }\n\n        const emitEventKey = nodeData.emitEventKey as string\n\n        // The function to fire at the specified time\n        const onTick = () => {\n            const returnData: ICommonObject[] = []\n            returnData.push({\n                date: new Date().toDateString(),\n                time: new Date().toTimeString(),\n                cron: 'SUCCESS'\n            })\n            this.emit(emitEventKey, returnNodeExecutionData(returnData))\n            if (pattern === 'once') this.removeTrigger(nodeData)\n        }\n\n        // Start the cron-jobs\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey].push(new CronJob(cronTime, onTick, undefined, true))\n            }\n        } else {\n            for (const cronTime of cronTimes) {\n                // Automatically start the cron job\n                this.cronJobs[emitEventKey] = [new CronJob(cronTime, onTick, undefined, true)]\n            }\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.cronJobs, emitEventKey)) {\n            const cronJobs = this.cronJobs[emitEventKey]\n            for (const cronJob of cronJobs) {\n                cronJob.stop()\n            }\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nconst dateToCron = (date: Date) => {\n    const minutes = date.getMinutes()\n    const hours = date.getHours()\n    const days = date.getDate()\n    const months = date.getMonth()\n    const dayOfWeek = date.getDay()\n    return `${minutes} ${hours} ${days} ${months} ${dayOfWeek}`\n}\n\nmodule.exports = { nodeClass: Scheduler }\n"
  },
  {
    "path": "packages/components/nodes/Slack/Slack.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\ninterface ISlackWebhook {\n    text?: string\n    blocks?: any[]\n}\n\nclass Slack implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Slack'\n        this.name = 'slack'\n        this.icon = 'slack.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Post message in Slack channel'\n        this.incoming = 1\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'Webhook URL',\n                name: 'webhookUrl',\n                type: 'string',\n                default: '',\n                description: 'Webhook URL for the channel. Learn more: https://api.slack.com/messaging/webhooks'\n            },\n            {\n                label: 'Message',\n                description: 'Message contents',\n                name: 'text',\n                type: 'string',\n                default: ''\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const returnData: ICommonObject[] = []\n        const body: ISlackWebhook = {}\n\n        const webhookUrl = inputParametersData.webhookUrl as string\n        const text = inputParametersData.text as string\n        body.text = text\n\n        let responseData: any\n        let maxRetries = 5\n\n        do {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `${webhookUrl}`,\n                    data: body,\n                    headers: {\n                        'Content-Type': 'application/json; charset=utf-8'\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                // Rate limit exceeded\n                if (error.response && error.response.status === 429) {\n                    const retryAfter = error.response?.headers['retry-after'] || 60\n                    await new Promise<void>((resolve, _) => {\n                        setTimeout(() => {\n                            resolve()\n                        }, retryAfter * 1000)\n                    })\n                    continue\n                }\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error posting message to Slack channel. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Slack }\n"
  },
  {
    "path": "packages/components/nodes/SnowTrace/SnowTrace.ts",
    "content": "import axios, { AxiosRequestConfig, Method } from 'axios'\nimport { etherscanAPIs, NETWORK } from '../../src'\nimport { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, serializeQueryParams } from '../../src/utils'\n\nclass SnowTrace implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'SnowTrace'\n        this.name = 'snowtrace'\n        this.icon = 'snowtrace.svg'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Perform SnowTrace operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get AVAX Balance for a Single Address',\n                        name: 'getAvaxBalance',\n                        description: 'Returns the AVAX balance of a given address.'\n                    },\n                    {\n                        label: 'Get AVAX Balance for Multiple Addresses',\n                        name: 'getAvaxMultipleBalance',\n                        description: 'Returns the AVAX balance of given addresses.'\n                    },\n                    {\n                        label: 'Get a list of \"Normal\" Transactions By Address',\n                        name: 'getNormalTransactions',\n                        description: 'Returns a list of \"Normal\" transaction by address.'\n                    },\n                    {\n                        label: 'Get a list of \"Internal\" Transactions by Address',\n                        name: 'getInternalTransactions',\n                        description: 'Returns a list of \"Internal\" transaction by address.'\n                    },\n                    {\n                        label: 'Get \"Internal Transactions\" by Transaction Hash',\n                        name: 'getInternalTransactionsByHash',\n                        description: 'Returns \"Internal Transactions\" by hash.'\n                    },\n                    {\n                        label: 'Get \"Internal Transactions\" by Block Range',\n                        name: 'getInternalTransactionsByBlockRange',\n                        description: 'Returns \"Internal Transactions\" transaction by block range.'\n                    },\n                    {\n                        label: 'Get a list of \"ERC20 - Token Transfer Events\" by Address',\n                        name: 'getErc20TokenTransferEvents',\n                        description: 'Returns a list of \"ERC20 - Token Transfer Events\" by address.'\n                    },\n                    {\n                        label: 'Get a list of \"ERC721 - Token Transfer Events\" by Address',\n                        name: 'getErc721TokenTransferEvents',\n                        description: 'Returns a list of \"ERC721 - Token Transfer Events\" by address.'\n                    },\n                    {\n                        label: 'Get list of Blocks Validated by Address',\n                        name: 'getBlocksValidated',\n                        description: 'Returns a list of blocks validated by address.'\n                    },\n                    {\n                        label: 'Get Contract ABI for Verified Contract Source Codes',\n                        name: 'getContractABI',\n                        description: 'Returns the contract ABI for verified contract source codes.'\n                    }\n                ],\n                default: 'getAvaxBalance'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Avalanche Mainnet',\n                        name: NETWORK.AVALANCHE\n                    },\n                    {\n                        label: 'Avalanche Testnet',\n                        name: NETWORK.AVALANCHE_TESTNET\n                    }\n                ],\n                default: NETWORK.AVALANCHE\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'SnowTrace API Key',\n                        name: 'snowtraceApi'\n                    }\n                ],\n                default: 'snowtraceApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Address',\n                name: 'address',\n                type: 'string',\n                description: 'The address to check for balance',\n                show: {\n                    'actions.api': [\n                        'getAvaxBalance',\n                        'getNormalTransactions',\n                        'getInternalTransactions',\n                        'getErc20TokenTransferEvents',\n                        'getErc721TokenTransferEvents',\n                        'getBlocksValidated',\n                        'getContractABI'\n                    ]\n                }\n            },\n            {\n                label: 'Addresses',\n                name: 'addresses',\n                type: 'json',\n                placeholder: `[\n  '0x0000000000000000000000000000000000001004',\n  '0x0000000000000000000000000000000000001000'\n]`,\n                description: 'The addresses to check for balance',\n                show: {\n                    'actions.api': ['getAvaxMultipleBalance']\n                }\n            },\n            {\n                label: 'Transaction Hash',\n                name: 'hash',\n                type: 'string',\n                placeholder: '0x4d74a6fc84d57f18b8e1dfa07ee517c4feb296d16a8353ee41adc03669982028',\n                description: 'The hash of the transaction',\n                show: {\n                    'actions.api': ['getInternalTransactionsByHash']\n                }\n            },\n            {\n                label: 'Start Block',\n                name: 'startBlock',\n                type: 'number',\n                placeholder: '0',\n                description: 'The starting block to check for internal transactions',\n                show: {\n                    'actions.api': ['getInternalTransactionsByBlockRange']\n                }\n            },\n            {\n                label: 'End Block',\n                name: 'endBlock',\n                type: 'number',\n                placeholder: '2702578',\n                description: 'The ending block to check for internal transactions',\n                show: {\n                    'actions.api': ['getInternalTransactionsByBlockRange']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    async fetch(url: string, queryParameters: any) {\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            return response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n    }\n\n    getQueryParameters(api: string, value: any) {\n        switch (api) {\n            case 'getAvaxBalance':\n                return {\n                    module: 'account',\n                    action: 'balance',\n                    tag: 'latest',\n                    address: value\n                }\n            case 'getAvaxMultipleBalance':\n                return {\n                    module: 'account',\n                    action: 'balancemulti',\n                    tag: 'latest',\n                    address: value\n                }\n            case 'getNormalTransactions':\n                return {\n                    module: 'account',\n                    action: 'txlist',\n                    address: value\n                }\n            case 'getInternalTransactions':\n                return {\n                    module: 'account',\n                    action: 'txlistinternal',\n                    address: value\n                }\n            case 'getInternalTransactionsByHash':\n                return {\n                    module: 'account',\n                    action: 'txlistinternal',\n                    txhash: value\n                }\n            case 'getInternalTransactionsByBlockRange':\n                return {\n                    module: 'account',\n                    action: 'txlistinternal',\n                    startblock: value[0],\n                    endblock: value[1]\n                }\n            case 'getErc20TokenTransferEvents':\n                return {\n                    module: 'account',\n                    action: 'tokentx',\n                    address: value\n                }\n            case 'getErc721TokenTransferEvents':\n                return {\n                    module: 'account',\n                    action: 'tokennfttx',\n                    address: value\n                }\n\n            case 'getBlocksValidated':\n                return {\n                    module: 'account',\n                    action: 'getminedblocks',\n                    address: value\n                }\n            case 'getContractABI':\n                return {\n                    module: 'contract',\n                    action: 'getabi',\n                    address: value\n                }\n            default:\n                return {}\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const networksData = nodeData.networks\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actionData.api as string\n\n        const network = networksData.network as NETWORK\n\n        const apiKey = credentials.apiKey as string\n\n        const address = inputParametersData.address as string\n        const addresses = (inputParametersData.addresses as string) || '[]'\n        let addressesArray = []\n        if (addresses) addressesArray = JSON.parse(addresses.replace(/\\s/g, ''))\n        const hash = inputParametersData.hash as string\n        const startBlock = inputParametersData.startBlock as number\n        const endBlock = inputParametersData.endBlock as number\n\n        const url = etherscanAPIs[network] as string\n\n        let responseData: any\n        const returnData: ICommonObject[] = []\n        let queryParameters: ICommonObject = {}\n\n        switch (api) {\n            case 'getContractABI':\n            case 'getBlocksValidated':\n            case 'getErc721TokenTransferEvents':\n            case 'getErc20TokenTransferEvents':\n            case 'getInternalTransactions':\n            case 'getNormalTransactions':\n            case 'getAvaxBalance':\n                queryParameters = { ...this.getQueryParameters(api, address), apiKey }\n                break\n            case 'getAvaxMultipleBalance':\n                queryParameters = { ...this.getQueryParameters(api, addressesArray.join(',')), apiKey }\n                break\n            case 'getInternalTransactionsByHash':\n                queryParameters = { ...this.getQueryParameters(api, hash), apiKey }\n                break\n            case 'getInternalTransactionsByBlockRange':\n                queryParameters = { ...this.getQueryParameters(api, [startBlock, endBlock]), apiKey }\n                break\n            default:\n                break\n        }\n\n        responseData = await this.fetch(url, queryParameters)\n\n        if (Array.isArray(responseData)) returnData.push(...responseData)\n        else returnData.push(responseData)\n        return responseData\n    }\n}\n\nmodule.exports = { nodeClass: SnowTrace }\n"
  },
  {
    "path": "packages/components/nodes/Solidity/Solidity.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { getNodeModulesPackagePath, handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { ethers } from 'ethers'\nimport * as fs from 'fs'\nimport {\n    ArbitrumNetworks,\n    BSCNetworks,\n    ETHNetworks,\n    getNetworkProvider,\n    getNetworkProvidersList,\n    NETWORK,\n    networkExplorers,\n    networkProviderCredentials,\n    NETWORK_PROVIDER,\n    OptimismNetworks,\n    PolygonNetworks\n} from '../../src/ChainNetwork'\n\n// @ts-expect-error no type definition\nimport solc from 'solc'\n\nfunction findImports(_path: string) {\n    // Remove all ../ from the path\n    while (_path.includes('../')) {\n        _path = _path.replace('../', '')\n    }\n    const filepath = getNodeModulesPackagePath(_path)\n    const contents = fs.readFileSync(filepath).toString()\n    return { contents }\n}\n\nclass Solidity implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Solidity'\n        this.name = 'solidity'\n        this.icon = 'solidity.svg'\n        this.type = 'action'\n        this.category = 'Smart Contract'\n        this.version = 1.0\n        this.description = 'Compile and Deploy Solidity Code'\n        this.incoming = 1\n        this.outgoing = 1\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks, ...PolygonNetworks, ...ArbitrumNetworks, ...OptimismNetworks, ...BSCNetworks],\n                default: 'goerli'\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [...networkProviderCredentials] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Solidity Code',\n                name: 'code',\n                type: 'code',\n                placeholder: `// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MyToken is ERC20 {\n    constructor() ERC20(\"MyToken\", \"MTK\") {}\n}`,\n                description: 'Custom Solidity code to compile and deploy'\n            },\n            {\n                label: 'Contract Name',\n                name: 'contractName',\n                description: 'Contract Name to deploy the Solidity Code',\n                type: 'string',\n                default: '',\n                placeholder: 'MyContract'\n            },\n            {\n                label: 'Select Wallet',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to deploy Solidity code',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets'\n            },\n            {\n                label: 'Constructor Parameters',\n                name: 'parameters',\n                type: 'json',\n                placeholder: '[ \"param1\", \"param2\" ]',\n                description: 'Input parameters for constructor',\n                optional: true\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const walletString = inputParametersData.wallet as string\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get wallet instance\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n            // Get input parameters\n            const code = inputParametersData.code as string\n            const solidityContractName = inputParametersData.contractName as string\n            const parameters = inputParametersData.parameters as string\n\n            const input = {\n                language: 'Solidity',\n                sources: {},\n                settings: {\n                    outputSelection: {\n                        '*': {\n                            '*': ['*']\n                        }\n                    }\n                }\n            } as any\n\n            input.sources[solidityContractName + '.sol'] = { content: code }\n            const output = JSON.parse(solc.compile(JSON.stringify(input), { import: findImports }))\n\n            const contractOutput = output.contracts[solidityContractName + '.sol']\n\n            const contractName = Object.keys(contractOutput)[0]\n\n            const bytecode = contractOutput[contractName].evm.bytecode.object\n            const abi = contractOutput[contractName].abi\n\n            const factory = new ethers.ContractFactory(abi, bytecode, wallet)\n\n            let contractParameters: any[] = []\n\n            if (parameters) {\n                contractParameters = JSON.parse(parameters)\n            }\n\n            let deployedContract: ethers.Contract\n\n            if (contractParameters.length > 0) deployedContract = await factory.deploy(...contractParameters)\n            else deployedContract = await factory.deploy()\n\n            // The contract is NOT deployed yet; we must wait until it is mined\n            await deployedContract.deployed()\n            const returnItem: ICommonObject = {\n                explorerLink: `${networkExplorers[network]}/address/${deployedContract.address}`,\n                address: deployedContract.address,\n                transactionHash: deployedContract.deployTransaction.hash\n            }\n\n            return returnNodeExecutionData(returnItem)\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: Solidity }\n"
  },
  {
    "path": "packages/components/nodes/Solscan/Solscan.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { OPERATION, SORT_BY, SORT_DIRECTION } from './constants'\n\nclass Solscan implements INode {\n    // properties\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    // parameter\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        // properties\n        this.label = 'Solscan'\n        this.name = 'solscan'\n        this.icon = 'solscan.png'\n        this.type = 'action'\n        this.category = 'Block Explorer'\n        this.version = 1.0\n        this.description = 'Solscan Public API'\n        this.incoming = 1\n        this.outgoing = 1\n\n        // parameter\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                description: 'Choose the API to execute',\n                options: [\n                    // Account\n                    {\n                        label: 'Get token balances',\n                        name: OPERATION.GET_TOKEN_BALANCES,\n                        description: 'Get tokens balances of the given address'\n                    },\n                    {\n                        label: 'Get transactions',\n                        name: OPERATION.GET_TRANSACTIONS,\n                        description: 'Get list of transactions of the given account. MaxLimit 50 records per request'\n                    },\n                    {\n                        label: 'Get staking accounts',\n                        name: OPERATION.GET_STAKING_ACCOUNTS,\n                        description: 'Get staking accounts of the given account'\n                    },\n                    {\n                        label: 'Get token transfers',\n                        name: OPERATION.GET_TOKEN_TRANSFERS,\n                        description: 'Get list of transactions make tokenBalance changes. MaxLimit 50 records per request'\n                    },\n                    {\n                        label: 'Get Solana transfers',\n                        name: OPERATION.GET_SOL_TRANSFERS,\n                        description: 'Get list of SOL transfers. MaxLimit 50 records per request'\n                    },\n                    {\n                        label: 'Get account info',\n                        name: OPERATION.GET_ACCOUNT_INFO,\n                        description: 'Get overall account information, including program account, NFT metadata information'\n                    },\n\n                    // Transaction\n                    {\n                        label: 'Get last transactions',\n                        name: OPERATION.GET_LAST_TRANSACTIONS,\n                        description: 'Get last [limit] transactions'\n                    },\n                    {\n                        label: 'Get transaction info',\n                        name: OPERATION.GET_TRANSACTION_INFO,\n                        description: 'Detail information of given transaction signature'\n                    },\n\n                    // Token\n                    {\n                        label: 'Get token holder',\n                        name: OPERATION.GET_TOKEN_HOLDER,\n                        description: 'Get token holders list'\n                    },\n                    {\n                        label: 'Get token info',\n                        name: OPERATION.GET_TOKEN_INFO,\n                        description: 'Get metadata of given token'\n                    },\n                    {\n                        label: 'Get token list',\n                        name: OPERATION.GET_TOKENS,\n                        description: 'Get list of tokens. MaxLimit 50 records per request'\n                    },\n\n                    // Market\n                    {\n                        label: 'Get token market info',\n                        name: OPERATION.GET_TOKEN_MARKET_INFO,\n                        description: 'Get market information of the given token'\n                    },\n\n                    // Chain info\n                    {\n                        label: 'Get chain info',\n                        name: OPERATION.GET_CHAIN_INFO,\n                        description: 'Blockchain overall information'\n                    }\n                ],\n                default: OPERATION.GET_TOKEN_BALANCES\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Solana Address',\n                name: 'address',\n                type: 'string',\n                description: 'The Solana address',\n                show: {\n                    'actions.api': [\n                        OPERATION.GET_TOKEN_BALANCES,\n                        OPERATION.GET_TRANSACTIONS,\n                        OPERATION.GET_STAKING_ACCOUNTS,\n                        OPERATION.GET_TOKEN_TRANSFERS,\n                        OPERATION.GET_SOL_TRANSFERS,\n                        OPERATION.GET_ACCOUNT_INFO\n                    ]\n                }\n            },\n            {\n                label: 'Limit',\n                name: 'limit',\n                type: 'number',\n                default: 10,\n                optional: true,\n                show: {\n                    'actions.api': [OPERATION.GET_TOKEN_HOLDER, OPERATION.GET_LAST_TRANSACTIONS]\n                }\n            },\n            {\n                label: 'Offset',\n                name: 'offset',\n                type: 'number',\n                default: 0,\n                optional: true,\n                show: {\n                    'actions.api': [OPERATION.GET_TOKEN_HOLDER, OPERATION.GET_TOKENS]\n                }\n            },\n            {\n                label: 'Transaction Signature',\n                name: 'signature',\n                type: 'string',\n                show: {\n                    'actions.api': [OPERATION.GET_TRANSACTION_INFO]\n                }\n            },\n            {\n                label: 'Token Address',\n                name: 'tokenAddress',\n                type: 'string',\n                show: {\n                    'actions.api': [OPERATION.GET_TOKEN_HOLDER, OPERATION.GET_TOKEN_INFO, OPERATION.GET_TOKEN_MARKET_INFO]\n                }\n            },\n            {\n                label: 'Sort By',\n                name: 'sortBy',\n                type: 'options',\n                optional: true,\n                options: SORT_BY,\n                show: {\n                    'actions.api': [OPERATION.GET_TOKENS]\n                }\n            },\n            {\n                label: 'Sort Direction',\n                name: 'direction',\n                optional: true,\n                type: 'options',\n                options: SORT_DIRECTION,\n                show: {\n                    'actions.api': [OPERATION.GET_TOKENS]\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    getEndpoint(operation: string, options?: { address?: string; signature?: string; tokenAddress?: string }): string {\n        const baseUrl = 'https://public-api.solscan.io'\n        switch (operation) {\n            case OPERATION.GET_TOKEN_BALANCES:\n                return `${baseUrl}/account/tokens`\n\n            case OPERATION.GET_TRANSACTIONS:\n                return `${baseUrl}/account/transactions`\n\n            case OPERATION.GET_STAKING_ACCOUNTS:\n                return `${baseUrl}/account/stakeAccounts`\n\n            case OPERATION.GET_TOKEN_TRANSFERS:\n                return `${baseUrl}/account/splTransfers`\n\n            case OPERATION.GET_SOL_TRANSFERS:\n                return `${baseUrl}/account/solTransfers`\n\n            case OPERATION.GET_ACCOUNT_INFO:\n                return `${baseUrl}/account/${options?.address}`\n\n            case OPERATION.GET_LAST_TRANSACTIONS:\n                return `${baseUrl}/transaction/last`\n\n            case OPERATION.GET_TRANSACTION_INFO:\n                return `${baseUrl}/transaction/${options?.signature}`\n\n            case OPERATION.GET_TOKEN_HOLDER:\n                return `${baseUrl}/token/holders`\n\n            case OPERATION.GET_TOKEN_INFO:\n                return `${baseUrl}/token/meta`\n\n            case OPERATION.GET_TOKENS:\n                return `${baseUrl}/token/list`\n\n            case OPERATION.GET_TOKEN_MARKET_INFO:\n                return `${baseUrl}/market/token/${options?.tokenAddress}`\n\n            case OPERATION.GET_CHAIN_INFO:\n                return `${baseUrl}/chaininfo`\n            default:\n                return baseUrl\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const api = actionData.api as string\n\n        const address = inputParametersData.address as string\n        const limit = inputParametersData.limit as number\n        const offset = inputParametersData.offset as number\n        const signature = inputParametersData.signature as string\n        const tokenAddress = inputParametersData.tokenAddress as string\n        const sortBy = inputParametersData.sortBy as string\n        const direction = inputParametersData.direction as string\n\n        const url = this.getEndpoint(api, { address, signature, tokenAddress })\n\n        const queryParameters = {\n            account: address,\n            tokenAddress,\n            limit,\n            offset,\n            sortBy,\n            direction\n        }\n\n        const returnData: ICommonObject[] = []\n        let responseData: any\n        try {\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url,\n                params: queryParameters,\n                paramsSerializer: (params) => serializeQueryParams(params),\n                headers: { 'Content-Type': 'application/json' }\n            }\n            const response = await axios(axiosConfig)\n            responseData = response.data\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Solscan }\n"
  },
  {
    "path": "packages/components/nodes/Solscan/constants.ts",
    "content": "export const OPERATION = {\n    // Account\n    GET_TOKEN_BALANCES: 'getTokenBalances',\n    GET_TRANSACTIONS: 'getTransactions',\n    GET_STAKING_ACCOUNTS: 'getStakingAccounts',\n    GET_TOKEN_TRANSFERS: 'getTokenTransfers',\n    GET_SOL_TRANSFERS: 'getSolTransfers',\n    GET_ACCOUNT_INFO: 'getAccountInfo',\n\n    // Transaction\n    GET_LAST_TRANSACTIONS: 'getLastTransactions',\n    GET_TRANSACTION_INFO: 'getTransactionInfo',\n\n    // Token\n    GET_TOKEN_HOLDER: 'getTokenHolder',\n    GET_TOKEN_INFO: 'getTokenInfo',\n    GET_TOKENS: 'getTokens',\n\n    // Market\n    GET_TOKEN_MARKET_INFO: 'getMarketTokenInfo',\n\n    // Chain Info\n    GET_CHAIN_INFO: 'getChainInfo'\n} as const\n\nexport const SORT_BY = [\n    { label: 'Market cap', name: 'market_cap' },\n    { label: 'Volume', name: 'volume' },\n    { label: 'Holder', name: 'holder' },\n    { label: 'Price', name: 'price' },\n    { label: 'Price change 24 h', name: 'price_change_24h' },\n    { label: 'Price change 7 d', name: 'price_change_7d' },\n    { label: 'Price change 14 d', name: 'price_change_14d' },\n    { label: 'Price change 30 d', name: 'price_change_30d' },\n    { label: 'Price change 60 d', name: 'price_change_60d' },\n    { label: 'Price change 200 d', name: 'price_change_200d' },\n    { label: 'Price change 1 y', name: 'price_change_1y' }\n] as const\n\nexport const SORT_DIRECTION = [\n    { label: 'Desc', name: 'desc' },\n    { label: 'Asc', name: 'asc' }\n] as const\n"
  },
  {
    "path": "packages/components/nodes/Teams/Teams.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\ninterface ITeamsWebhook {\n    text?: string\n}\n\nclass Teams implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Teams'\n        this.name = 'teams'\n        this.icon = 'teams.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Post message in Teams channel'\n        this.incoming = 1\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'Webhook URL',\n                name: 'webhookUrl',\n                type: 'string',\n                default: '',\n                description:\n                    'Webhook URL for the channel. Learn more: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook'\n            },\n            {\n                label: 'Message',\n                description: 'Message contents',\n                name: 'text',\n                type: 'string',\n                default: ''\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const returnData: ICommonObject[] = []\n        const body: ITeamsWebhook = {}\n\n        const webhookUrl = inputParametersData.webhookUrl as string\n        const text = inputParametersData.text as string\n        body.text = text\n\n        let responseData: any\n        let maxRetries = 5\n\n        do {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `${webhookUrl}`,\n                    data: body,\n                    headers: {\n                        'Content-Type': 'application/json; charset=utf-8'\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                // Rate limit exceeded\n                if (error.response && error.response.status === 429) {\n                    const retryAfter = error.response?.headers['retry-after'] || 60\n                    await new Promise<void>((resolve, _) => {\n                        setTimeout(() => {\n                            resolve()\n                        }, retryAfter * 1000)\n                    })\n                    continue\n                }\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error posting message to Teams channel. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Teams }\n"
  },
  {
    "path": "packages/components/nodes/Telegram/Telegram.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios from 'axios'\n\nclass Telegram implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters: INodeParams[]\n    credentials: INodeParams[]\n\n    constructor() {\n        this.label = 'Telegram'\n        this.name = 'telegram'\n        this.icon = 'telegram.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Perform Telegram operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Telegram Bot Token',\n                        name: 'telegramApi'\n                    }\n                ],\n                placeholder: 'eg: 1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI',\n                default: ''\n            }\n        ]\n        this.inputParameters = [\n            {\n                label: 'Channel ID',\n                name: 'channelID',\n                type: 'string',\n                placeholder: 'eg: MyAwesomeChannel',\n                default: '',\n                description:\n                    'Your channel ID. <a target=\"_blank\" href=\"https://www.youtube.com/watch?v=gk_tPOY1TDM\">See how to how to add bot in your channel.</a>',\n                optional: true\n            },\n            {\n                label: 'Content',\n                description: 'Message contents (up to 2000 characters)',\n                name: 'content',\n                type: 'string',\n                default: ''\n            }\n        ]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n        const botToken = credentials.botToken as string\n        const channelID = inputParametersData.channelID as string\n        const content = inputParametersData.content as string\n        const returnData: ICommonObject[] = []\n        try {\n            const response = await axios.get(`https://api.telegram.org/bot${botToken}/sendMessage?chat_id=@${channelID}&text=${content}`)\n            returnData.push(response.data)\n        } catch (error) {\n            throw handleErrorMessage(error)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Telegram }\n"
  },
  {
    "path": "packages/components/nodes/ThirdWeb/ThirdWeb.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport { NodeVM } from 'vm2'\nimport { networkLookup, prebuiltType, ThirdWebSupportedNetworks, ThirdWebSupportedPrebuiltContract } from './supportedNetwork'\nimport {\n    MarketplaceContractDeployMetadata,\n    MultiwrapContractDeployMetadata,\n    NFTContractDeployMetadata,\n    SplitContractDeployMetadata,\n    ThirdwebSDK as ThirdwebEVMSDK,\n    TokenContractDeployMetadata,\n    VoteContractDeployMetadata\n} from '@thirdweb-dev/sdk'\nimport {\n    marketplaceParameters,\n    multiWrapParameters,\n    nftDropParameters,\n    splitContractParameters,\n    tokenDropParameters,\n    voteParameters\n} from './constants'\nimport { networkExplorers } from '../../src/ChainNetwork'\n//import { ThirdwebSDK as ThirdwebSolanaSDK } from '@thirdweb-dev/sdk/solana'\n\nclass ThirdWeb implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'ThirdWeb'\n        this.name = 'thirdWeb'\n        this.icon = 'thirdweb.svg'\n        this.type = 'action'\n        this.category = 'Development'\n        this.version = 2.0\n        this.description = 'Execute ThirdWeb SDK code snippet'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Read data from contracts',\n                        name: 'read'\n                    },\n                    {\n                        label: 'Execute transactions on contracts',\n                        name: 'execute'\n                    },\n                    {\n                        label: 'Deploy new contract',\n                        name: 'deploy'\n                    }\n                ],\n                default: 'read'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ThirdWebSupportedNetworks],\n                default: 'mainnet'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Contract Address',\n                name: 'contract',\n                type: 'string',\n                description: 'Navigate to ThirdWeb -> Code -> Getting started -> Javascript, copy the address used in the code section',\n                placeholder: '0x6a8c7F715D5f044437dA5b0576eD1289eC9b7eB6',\n                show: {\n                    'actions.operation': ['read', 'execute']\n                }\n            },\n            {\n                label: 'Prebuilt Contract Type',\n                name: 'contractType',\n                type: 'options',\n                description:\n                    'Navigate to ThirdWeb -> Code -> Getting started -> Javascript, select the prebuilt contract used in the code section. Ex: await sdk.getContract(\"0x...\", \"nft-drop\")',\n                options: [...ThirdWebSupportedPrebuiltContract]\n            },\n            {\n                label: 'Code',\n                name: 'code',\n                type: 'code',\n                placeholder: `const ethers = require('ethers');\\n\\n//Get NFT Balance\\nconst walletAddress = \"0xE597E574889537A3A9120d1B5793cdFAEf6B6c10\";\\nconst balance = await contract.balanceOf(walletAddress);\\nreturn ethers.utils.formatEther(balance);`,\n                description: 'Custom code to run',\n                show: {\n                    'actions.operation': ['read', 'execute']\n                }\n            },\n            {\n                label: 'External Modules',\n                name: 'external',\n                type: 'json',\n                placeholder: '[\"ethers\"]',\n                description: `Import installed dependencies within Outerbridge and use it within code. Ex: const ethers = require('ethers');`,\n                optional: true,\n                show: {\n                    'actions.operation': ['read', 'execute']\n                }\n            },\n            {\n                label: 'Wallet Account',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to execute transaction or deploy contract.',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets',\n                show: {\n                    'actions.operation': ['execute', 'deploy']\n                }\n            },\n            ...nftDropParameters,\n            ...marketplaceParameters,\n            ...multiWrapParameters,\n            ...splitContractParameters,\n            ...tokenDropParameters,\n            ...voteParameters\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const actionsData = nodeData.actions\n        const networksData = nodeData.networks\n\n        if (inputParametersData === undefined || actionsData === undefined || networksData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const operation = actionsData.operation as string\n        const contractAddress = inputParametersData.contract as string\n        const contractType = inputParametersData.contractType as prebuiltType\n        const network = networksData.network as string\n        const walletString = inputParametersData.wallet as string\n\n        const imageType = inputParametersData.imageType as string\n        const name = inputParametersData.name as string\n        const description = inputParametersData.description as string\n        const symbol = inputParametersData.symbol as string\n        const primary_sale_recipient = inputParametersData.primary_sale_recipient as string\n        const fee_recipient = inputParametersData.fee_recipient as string\n        const seller_fee_basis_points = inputParametersData.seller_fee_basis_points as string\n        const platform_fee_recipient = inputParametersData.platform_fee_recipient as string\n        const platform_fee_basis_points = inputParametersData.platform_fee_basis_points as string\n        const external_link = inputParametersData.external_link as string\n        const trusted_forwarders = inputParametersData.trusted_forwarders as string\n        const recipients = inputParametersData.recipients as string\n        const voting_token_address = inputParametersData.voting_token_address as string\n        const proposal_token_threshold = inputParametersData.proposal_token_threshold as string\n        const voting_delay_in_blocks = inputParametersData.voting_delay_in_blocks as string\n        const voting_period_in_blocks = inputParametersData.voting_period_in_blocks as string\n        const voting_quorum_fraction = inputParametersData.voting_quorum_fraction as string\n\n        let responseData: any\n        const returnData: ICommonObject[] = []\n\n        if (operation === 'read' || operation === 'execute') {\n            const contract = await getThirdWebSDK(operation, network, contractAddress, contractType, walletString)\n\n            // Global object\n            const sandbox = {\n                $nodeData: nodeData,\n                $contract: contract\n            }\n\n            const options = {\n                console: 'inherit',\n                sandbox,\n                require: {\n                    external: false as boolean | { modules: string[] },\n                    builtin: ['*']\n                }\n            } as any\n\n            const code = inputParametersData.code as string\n            const external = inputParametersData.external as string\n            if (external) {\n                const deps = JSON.parse(external)\n                if (deps && deps.length) {\n                    options.require.external = {\n                        modules: deps\n                    }\n                }\n            }\n\n            const vm = new NodeVM(options)\n\n            try {\n                if (!code) responseData = []\n                else {\n                    const initCode = `const contract = $contract;\\n`\n                    responseData = await vm.run(`module.exports = async function() {${initCode}${code}}()`, __dirname)\n                }\n            } catch (e) {\n                return Promise.reject(e)\n            }\n        } else if (operation === 'deploy') {\n            const walletDetails: IWallet = JSON.parse(walletString)\n            const walletCredential = JSON.parse(walletDetails.walletCredential)\n            const sdk = ThirdwebEVMSDK.fromPrivateKey(walletCredential.privateKey, network)\n\n            if (\n                contractType === 'nft-drop' ||\n                contractType === 'nft-collection' ||\n                contractType === 'edition' ||\n                contractType === 'edition-drop' ||\n                contractType === 'pack' ||\n                contractType === 'signature-drop'\n            ) {\n                const metadata: NFTContractDeployMetadata = {\n                    name,\n                    primary_sale_recipient\n                }\n                if (imageType) metadata.image = getImage(imageType, inputParametersData)\n                if (symbol) metadata.symbol = symbol\n                if (description) metadata.description = description\n                if (fee_recipient) metadata.fee_recipient = fee_recipient\n                if (seller_fee_basis_points) metadata.seller_fee_basis_points = parseInt(seller_fee_basis_points, 10)\n                if (platform_fee_recipient) metadata.platform_fee_recipient = platform_fee_recipient\n                if (platform_fee_basis_points) metadata.platform_fee_basis_points = parseInt(platform_fee_basis_points, 10)\n                if (external_link) metadata.external_link = external_link\n                if (trusted_forwarders) {\n                    try {\n                        metadata.trusted_forwarders = JSON.parse(trusted_forwarders)\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n\n                let contractAddress = ''\n                if (contractType === 'nft-drop') contractAddress = await sdk.deployer.deployNFTDrop(metadata)\n                else if (contractType === 'nft-collection') contractAddress = await sdk.deployer.deployNFTCollection(metadata)\n                else if (contractType === 'edition') contractAddress = await sdk.deployer.deployEdition(metadata)\n                else if (contractType === 'edition-drop') contractAddress = await sdk.deployer.deployEditionDrop(metadata)\n                else if (contractType === 'pack') contractAddress = await sdk.deployer.deployPack(metadata)\n                else if (contractType === 'signature-drop') contractAddress = await sdk.deployer.deploySignatureDrop(metadata)\n\n                const returnItem: ICommonObject = {\n                    address: contractAddress,\n                    explorerLink: `${networkExplorers[networkLookup[network]]}/address/${contractAddress}`\n                }\n                return returnNodeExecutionData(returnItem)\n            } else if (contractType === 'marketplace') {\n                const metadata: MarketplaceContractDeployMetadata = {\n                    name\n                }\n                if (imageType) metadata.image = getImage(imageType, inputParametersData)\n                if (description) metadata.description = description\n                if (platform_fee_recipient) metadata.platform_fee_recipient = platform_fee_recipient\n                if (platform_fee_basis_points) metadata.platform_fee_basis_points = parseInt(platform_fee_basis_points, 10)\n                if (external_link) metadata.external_link = external_link\n                if (trusted_forwarders) {\n                    try {\n                        metadata.trusted_forwarders = JSON.parse(trusted_forwarders)\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n\n                const contractAddress = await sdk.deployer.deployMarketplace(metadata)\n                const returnItem: ICommonObject = {\n                    address: contractAddress,\n                    explorerLink: `${networkExplorers[networkLookup[network]]}/address/${contractAddress}`\n                }\n                return returnNodeExecutionData(returnItem)\n            } else if (contractType === 'multiwrap') {\n                const metadata: MultiwrapContractDeployMetadata = {\n                    name\n                }\n                if (imageType) metadata.image = getImage(imageType, inputParametersData)\n                if (symbol) metadata.symbol = symbol\n                if (description) metadata.description = description\n                if (fee_recipient) metadata.fee_recipient = fee_recipient\n                if (seller_fee_basis_points) metadata.seller_fee_basis_points = parseInt(seller_fee_basis_points, 10)\n                if (external_link) metadata.external_link = external_link\n                if (trusted_forwarders) {\n                    try {\n                        metadata.trusted_forwarders = JSON.parse(trusted_forwarders)\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n\n                const contractAddress = await sdk.deployer.deployMultiwrap(metadata)\n                const returnItem: ICommonObject = {\n                    address: contractAddress,\n                    explorerLink: `${networkExplorers[networkLookup[network]]}/address/${contractAddress}`\n                }\n                return returnNodeExecutionData(returnItem)\n            } else if (contractType === 'split') {\n                let listOfRecipients = []\n                if (recipients) {\n                    try {\n                        listOfRecipients = JSON.parse(recipients.replace(/\\s/g, ''))\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n                const metadata: SplitContractDeployMetadata = {\n                    name,\n                    recipients: listOfRecipients\n                }\n                if (imageType) metadata.image = getImage(imageType, inputParametersData)\n                if (description) metadata.description = description\n                if (external_link) metadata.external_link = external_link\n                if (trusted_forwarders) {\n                    try {\n                        metadata.trusted_forwarders = JSON.parse(trusted_forwarders)\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n\n                const contractAddress = await sdk.deployer.deploySplit(metadata)\n                const returnItem: ICommonObject = {\n                    address: contractAddress,\n                    explorerLink: `${networkExplorers[networkLookup[network]]}/address/${contractAddress}`\n                }\n                return returnNodeExecutionData(returnItem)\n            } else if (contractType === 'token' || contractType === 'token-drop') {\n                const metadata: TokenContractDeployMetadata = {\n                    name,\n                    primary_sale_recipient\n                }\n                if (imageType) metadata.image = getImage(imageType, inputParametersData)\n                if (symbol) metadata.symbol = symbol\n                if (description) metadata.description = description\n                if (platform_fee_recipient) metadata.platform_fee_recipient = platform_fee_recipient\n                if (platform_fee_basis_points) metadata.platform_fee_basis_points = parseInt(platform_fee_basis_points, 10)\n                if (external_link) metadata.external_link = external_link\n                if (trusted_forwarders) {\n                    try {\n                        metadata.trusted_forwarders = JSON.parse(trusted_forwarders)\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n\n                let contractAddress = ''\n                if (contractType === 'token') contractAddress = await sdk.deployer.deployToken(metadata)\n                else if (contractType === 'token-drop') contractAddress = await sdk.deployer.deployTokenDrop(metadata)\n\n                const returnItem: ICommonObject = {\n                    address: contractAddress,\n                    explorerLink: `${networkExplorers[networkLookup[network]]}/address/${contractAddress}`\n                }\n                return returnNodeExecutionData(returnItem)\n            } else if (contractType === 'vote') {\n                const metadata: VoteContractDeployMetadata = {\n                    name,\n                    voting_token_address\n                }\n                if (imageType) metadata.image = getImage(imageType, inputParametersData)\n                if (description) metadata.description = description\n                if (voting_delay_in_blocks) metadata.voting_delay_in_blocks = parseInt(voting_delay_in_blocks, 10)\n                if (voting_period_in_blocks) metadata.voting_period_in_blocks = parseInt(voting_period_in_blocks, 10)\n                if (voting_quorum_fraction) metadata.voting_quorum_fraction = parseInt(voting_quorum_fraction, 10)\n                if (proposal_token_threshold) metadata.proposal_token_threshold = proposal_token_threshold\n                if (external_link) metadata.external_link = external_link\n                if (trusted_forwarders) {\n                    try {\n                        metadata.trusted_forwarders = JSON.parse(trusted_forwarders)\n                    } catch (error) {\n                        throw handleErrorMessage(error)\n                    }\n                }\n                const contractAddress = await sdk.deployer.deployVote(metadata)\n                const returnItem: ICommonObject = {\n                    address: contractAddress,\n                    explorerLink: `${networkExplorers[networkLookup[network]]}/address/${contractAddress}`\n                }\n                return returnNodeExecutionData(returnItem)\n            }\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nconst getImage = (imageType: string, inputParametersData: ICommonObject) => {\n    let image\n    if (imageType === 'url') {\n        image = inputParametersData.imageURL as string\n    } else if (imageType === 'base64') {\n        const imageBase64 = inputParametersData.imageBase64 as string\n        const splitDataURI = imageBase64.split(',')\n        image = Buffer.from(splitDataURI.pop() || '', 'base64')\n    }\n    return image\n}\n\nconst getThirdWebSDK = async (\n    operation: string,\n    network: string,\n    contractAddress: string,\n    contractType: prebuiltType,\n    walletString: string\n) => {\n    if (operation === 'read') {\n        //return await ThirdwebSolanaSDK.fromNetwork(network).getProgram(contractAddress, contractType as any)\n        return await new ThirdwebEVMSDK(network).getContract(contractAddress, contractType)\n    } else {\n        const walletDetails: IWallet = JSON.parse(walletString)\n        const walletCredential = JSON.parse(walletDetails.walletCredential)\n        //return await ThirdwebSolanaSDK.fromPrivateKey(walletCredential.privateKey, network).getProgram(contractAddress, contractType as any)\n        return await ThirdwebEVMSDK.fromPrivateKey(walletCredential.privateKey, network).getContract(contractAddress, contractType)\n    }\n}\n\nmodule.exports = { nodeClass: ThirdWeb }\n"
  },
  {
    "path": "packages/components/nodes/ThirdWeb/ThirdWebTrigger.ts",
    "content": "import { INode, INodeData, INodeOptionsValue, INodeParams, IProviders, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\nimport EventEmitter from 'events'\nimport { ThirdwebSDK as ThirdwebEVMSDK } from '@thirdweb-dev/sdk'\nimport {\n    editionDropEvents,\n    editionEvents,\n    marketplaceEvents,\n    multiWrapEvents,\n    nftCollectionEvents,\n    nftDropEvents,\n    packEvents,\n    prebuiltType,\n    signatureDropEvents,\n    splitEvents,\n    ThirdWebSupportedNetworks,\n    ThirdWebSupportedPrebuiltContract,\n    tokenDropEvents,\n    tokenEvents,\n    voteEvents\n} from './supportedNetwork'\n\nclass ThirdWebEventTrigger extends EventEmitter implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    actions?: INodeParams[]\n    providers: IProviders\n\n    constructor() {\n        super()\n        this.label = 'ThirdWeb Event Trigger'\n        this.name = 'thirdWebEventTrigger'\n        this.icon = 'thirdweb.svg'\n        this.type = 'trigger'\n        this.category = 'Development'\n        this.version = 1.0\n        this.description = 'Start workflow whenever a ThirdWeb event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.providers = {}\n        this.actions = [\n            {\n                label: 'Contract Address',\n                name: 'contract',\n                type: 'string',\n                description: 'Navigate to ThirdWeb -> Code -> Getting started -> Javascript, copy the address used in the code section',\n                placeholder: '0x6a8c7F715D5f044437dA5b0576eD1289eC9b7eB6'\n            },\n            {\n                label: 'Prebuilt Contract Type',\n                name: 'contractType',\n                type: 'options',\n                description:\n                    'Navigate to ThirdWeb -> Code -> Getting started -> Javascript, select the prebuilt contract used in the code section. Ex: await sdk.getContract(\"0x...\", \"nft-drop\")',\n                options: [...ThirdWebSupportedPrebuiltContract]\n            },\n            {\n                label: 'Event',\n                name: 'event',\n                type: 'asyncOptions',\n                loadMethod: 'getEvents'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ThirdWebSupportedNetworks],\n                default: 'mainnet'\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getEvents(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            const actionsData = nodeData.actions\n\n            if (actionsData === undefined || networksData === undefined) {\n                return returnData\n            }\n\n            const contractType = actionsData.contractType as prebuiltType\n\n            switch (contractType) {\n                case 'edition':\n                    return editionEvents\n                case 'edition-drop':\n                    return editionDropEvents\n                case 'marketplace':\n                    return marketplaceEvents\n                case 'multiwrap':\n                    return multiWrapEvents\n                case 'nft-collection':\n                    return nftCollectionEvents\n                case 'nft-drop':\n                    return nftDropEvents\n                case 'pack':\n                    return packEvents\n                case 'signature-drop':\n                    return signatureDropEvents\n                case 'split':\n                    return splitEvents\n                case 'token':\n                    return tokenEvents\n                case 'token-drop':\n                    return tokenDropEvents\n                case 'vote':\n                    return voteEvents\n                default:\n                    return []\n            }\n        }\n    }\n\n    async runTrigger(nodeData: INodeData): Promise<void> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n\n        if (networksData === undefined || actionsData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const network = networksData.network as string\n        const emitEventKey = nodeData.emitEventKey as string\n        const contractAddress = actionsData.contract as string\n        const eventName = actionsData.event as string\n\n        const contract = await new ThirdwebEVMSDK(network).getContract(contractAddress)\n\n        let eventData = ''\n        const provider = contract.events\n        const filter = {\n            network,\n            eventName,\n            contractAddress\n        }\n\n        /********** WORKAROUND FOR THIRDWEB REMOVEEVENTLISTENER BUG *********\n         ** If this emitEventKey hasn't been called before OR emitEventKey has been called but not this filter\n         ** This prevents from adding multiple event listener\n         **/\n        if (\n            !Object.prototype.hasOwnProperty.call(this.providers, emitEventKey) ||\n            !Object.prototype.hasOwnProperty.call(this.providers[emitEventKey].filter, JSON.stringify(filter))\n        ) {\n            provider.addEventListener(eventName, (event) => {\n                if (eventData !== JSON.stringify(event)) {\n                    eventData = JSON.stringify(event)\n\n                    if (JSON.stringify(this.providers[emitEventKey].filter.currentFilter) === JSON.stringify(filter)) {\n                        this.emit(emitEventKey, returnNodeExecutionData(event))\n                    }\n                }\n            })\n        }\n\n        /** Example of providers\n        this.providers = {\n            'W03MAR23-LPQ88HJB_thirdWebEventTrigger_0': {\n                provider: ContractEvents { contractWrapper: [ContractWrapper] },\n                filter: {\n                    '{\"network\":\"mumbai\",\"eventName\":\"TokensMinted\",\"contractAddress\":\"0x...\"}': {\n                        network: 'mumbai',\n                        eventName: 'TokensMinted',\n                        contractAddress: '0x...'\n                    },\n                    '{\"network\":\"mumbai\",\"eventName\":\"PlatformFeeInfoUpdated\",\"contractAddress\":\"0x..\"}': {\n                        network: 'mumbai',\n                        eventName: 'PlatformFeeInfoUpdated',\n                        contractAddress: '0x...'\n                    },\n                    currentFilter: {\n                        network: 'mumbai',\n                        eventName: 'PlatformFeeInfoUpdated',\n                        contractAddress: '0x...'\n                    },\n                }\n            }\n        }\n        */\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            const newFilter = {\n                ...this.providers[emitEventKey].filter,\n                [JSON.stringify(filter)]: filter,\n                currentFilter: filter\n            }\n            this.providers[emitEventKey] = { provider, filter: newFilter }\n        } else {\n            const newFilter = {\n                [JSON.stringify(filter)]: filter,\n                currentFilter: filter\n            }\n            this.providers[emitEventKey] = { provider, filter: newFilter }\n        }\n    }\n\n    async removeTrigger(nodeData: INodeData): Promise<void> {\n        const emitEventKey = nodeData.emitEventKey as string\n\n        if (Object.prototype.hasOwnProperty.call(this.providers, emitEventKey)) {\n            /** Disabling this because thirdweb removeEventListener bug, it doesnt remove listener\n            const provider = this.providers[emitEventKey].provider\n            provider.removeEventListener(eventName, (event: any) => {\n                console.log(event)\n            }) \n            **/\n            this.removeAllListeners(emitEventKey)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: ThirdWebEventTrigger }\n"
  },
  {
    "path": "packages/components/nodes/ThirdWeb/constants.ts",
    "content": "import { notEmptyRegex } from '../../src'\nimport { INodeParams } from '../../src/Interface'\n\nexport const nftDropParameters = [\n    {\n        label: 'Name',\n        name: 'name',\n        description: 'Name of the contract',\n        type: 'string',\n        placeholder: 'MyNFT',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Image Type',\n        name: 'imageType',\n        type: 'options',\n        options: [\n            {\n                label: 'Base64',\n                name: 'base64',\n                description: 'Base64 (raw) image'\n            },\n            {\n                label: 'URL',\n                name: 'url',\n                description: 'URL path of the image'\n            }\n        ],\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Image URL',\n        name: 'imageURL',\n        description: 'URL path of the image',\n        type: 'string',\n        placeholder: 'ipfs://QmSYJ5PJ6SriDWyXtzA62NDfCCMpZt5FWqh9Y5xmhXsWhS/SquirrelV1.png',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop'],\n            'inputParameters.imageType': ['url']\n        }\n    },\n    {\n        label: 'Image Base64',\n        name: 'imageBase64',\n        description: 'Base64 (raw) image',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop'],\n            'inputParameters.imageType': ['base64']\n        }\n    },\n    {\n        label: 'Description',\n        name: 'description',\n        description: 'Description of the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Symbol',\n        name: 'symbol',\n        description: 'Symbol for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Primary Sale Recipient Address',\n        name: 'primary_sale_recipient',\n        description: 'The address that will receive the proceeds from primary sales',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Royalties Recipient Address',\n        name: 'fee_recipient',\n        description: 'The address that will receive the proceeds from secondary sales',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Royalties Percentage',\n        name: 'seller_fee_basis_points',\n        description: 'The percentage (in basis points) of royalties for secondary sales. Eg: 50 = 0.5%',\n        type: 'number',\n        placeholder: '100',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.fee_recipient': notEmptyRegex,\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Platform Fee Recipient Address',\n        name: 'platform_fee_recipient',\n        description: 'The address that will receive the proceeds from platform fees',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Platform Fee Percentage',\n        name: 'platform_fee_basis_points',\n        description: 'The percentage (in basis points) of platform fees. Eg: 50 = 0.5%',\n        type: 'number',\n        placeholder: '100',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.platform_fee_recipient': notEmptyRegex,\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'External Link',\n        name: 'external_link',\n        description: 'Url for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    },\n    {\n        label: 'Trusted Forwarders',\n        name: 'trusted_forwarders',\n        description: 'Custom gasless trusted forwarder addresses',\n        type: 'json',\n        optional: true,\n        placeholder: '[\"0x359B1408130241E115EE2285bA2635ebFC2109C6\", \"0xE597E474889537A3A9120d1B5793cdFAEf6B6c10\"]',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['nft-drop', 'nft-collection', 'edition', 'edition-drop', 'pack', 'signature-drop']\n        }\n    }\n] as INodeParams[]\n\nexport const marketplaceParameters = [\n    {\n        label: 'Name',\n        name: 'name',\n        description: 'Name of the contract',\n        type: 'string',\n        placeholder: 'MyMarketplace',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace']\n        }\n    },\n    {\n        label: 'Image Type',\n        name: 'imageType',\n        type: 'options',\n        options: [\n            {\n                label: 'Base64',\n                name: 'base64',\n                description: 'Base64 (raw) image'\n            },\n            {\n                label: 'URL',\n                name: 'url',\n                description: 'URL path of the image'\n            }\n        ],\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace']\n        }\n    },\n    {\n        label: 'Image URL',\n        name: 'imageURL',\n        description: 'URL path of the image',\n        type: 'string',\n        placeholder: 'ipfs://QmSYJ5PJ6SriDWyXtzA62NDfCCMpZt5FWqh9Y5xmhXsWhS/SquirrelV1.png',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace'],\n            'inputParameters.imageType': ['url']\n        }\n    },\n    {\n        label: 'Image Base64',\n        name: 'imageBase64',\n        description: 'Base64 (raw) image',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace'],\n            'inputParameters.imageType': ['base64']\n        }\n    },\n    {\n        label: 'Description',\n        name: 'description',\n        description: 'Description of the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace']\n        }\n    },\n    {\n        label: 'Platform Fee Recipient Address',\n        name: 'platform_fee_recipient',\n        description: 'The address that will receive the proceeds from platform fees',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace']\n        }\n    },\n    {\n        label: 'Platform Fee Percentage',\n        name: 'platform_fee_basis_points',\n        description: 'The percentage (in basis points) of platform fees. Eg: 50 = 0.5%',\n        type: 'number',\n        placeholder: '100',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.platform_fee_recipient': notEmptyRegex,\n            'inputParameters.contractType': ['marketplace']\n        }\n    },\n    {\n        label: 'External Link',\n        name: 'external_link',\n        description: 'Url for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace']\n        }\n    },\n    {\n        label: 'Trusted Forwarders',\n        name: 'trusted_forwarders',\n        description: 'Custom gasless trusted forwarder addresses',\n        type: 'json',\n        optional: true,\n        placeholder: '[\"0x359B1408130241E115EE2285bA2635ebFC2109C6\", \"0xE597E474889537A3A9120d1B5793cdFAEf6B6c10\"]',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['marketplace']\n        }\n    }\n] as INodeParams[]\n\nexport const multiWrapParameters = [\n    {\n        label: 'Name',\n        name: 'name',\n        description: 'Name of the contract',\n        type: 'string',\n        placeholder: 'MyMultiWrap',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'Image Type',\n        name: 'imageType',\n        type: 'options',\n        options: [\n            {\n                label: 'Base64',\n                name: 'base64',\n                description: 'Base64 (raw) image'\n            },\n            {\n                label: 'URL',\n                name: 'url',\n                description: 'URL path of the image'\n            }\n        ],\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'Image URL',\n        name: 'imageURL',\n        description: 'URL path of the image',\n        type: 'string',\n        placeholder: 'ipfs://QmSYJ5PJ6SriDWyXtzA62NDfCCMpZt5FWqh9Y5xmhXsWhS/SquirrelV1.png',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap'],\n            'inputParameters.imageType': ['url']\n        }\n    },\n    {\n        label: 'Image Base64',\n        name: 'imageBase64',\n        description: 'Base64 (raw) image',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap'],\n            'inputParameters.imageType': ['base64']\n        }\n    },\n    {\n        label: 'Description',\n        name: 'description',\n        description: 'Description of the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'Symbol',\n        name: 'symbol',\n        description: 'Symbol for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'Royalties Recipient Address',\n        name: 'fee_recipient',\n        description: 'The address that will receive the proceeds from secondary sales',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'Royalties Percentage',\n        name: 'seller_fee_basis_points',\n        description: 'The percentage (in basis points) of royalties for secondary sales. Eg: 50 = 0.5%',\n        type: 'number',\n        placeholder: '100',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.fee_recipient': notEmptyRegex,\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'External Link',\n        name: 'external_link',\n        description: 'Url for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    },\n    {\n        label: 'Trusted Forwarders',\n        name: 'trusted_forwarders',\n        description: 'Custom gasless trusted forwarder addresses',\n        type: 'json',\n        optional: true,\n        placeholder: '[\"0x359B1408130241E115EE2285bA2635ebFC2109C6\", \"0xE597E474889537A3A9120d1B5793cdFAEf6B6c10\"]',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['multiwrap']\n        }\n    }\n] as INodeParams[]\n\nexport const splitContractParameters = [\n    {\n        label: 'Name',\n        name: 'name',\n        description: 'Name of the contract',\n        type: 'string',\n        placeholder: 'MySplit',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split']\n        }\n    },\n    {\n        label: 'Image Type',\n        name: 'imageType',\n        type: 'options',\n        options: [\n            {\n                label: 'Base64',\n                name: 'base64',\n                description: 'Base64 (raw) image'\n            },\n            {\n                label: 'URL',\n                name: 'url',\n                description: 'URL path of the image'\n            }\n        ],\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split']\n        }\n    },\n    {\n        label: 'Image URL',\n        name: 'imageURL',\n        description: 'URL path of the image',\n        type: 'string',\n        placeholder: 'ipfs://QmSYJ5PJ6SriDWyXtzA62NDfCCMpZt5FWqh9Y5xmhXsWhS/SquirrelV1.png',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split'],\n            'inputParameters.imageType': ['url']\n        }\n    },\n    {\n        label: 'Image Base64',\n        name: 'imageBase64',\n        description: 'Base64 (raw) image',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split'],\n            'inputParameters.imageType': ['base64']\n        }\n    },\n    {\n        label: 'Description',\n        name: 'description',\n        description: 'Description of the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split']\n        }\n    },\n    {\n        label: 'Recipients',\n        name: 'recipients',\n        description: 'The list of recipients and their share of the split',\n        type: 'json',\n        placeholder: `[\n    {\n        \"address\": '0x359B1408130241E115EE2285bA2635ebFC2109C6', // The recipient address\n        \"sharesBps\": 500, // The shares in basis point (5% = 500)\n    },\n    {\n        \"address\": '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        \"sharesBps\": 400,\n    }\n]\n        `,\n        default: `[\n    {\n        \"address\": \"your-address\",\n        \"sharesBps\": 8000\n    },\n    {\n        \"address\": \"another-address\",\n        \"sharesBps\": 2000\n    }\n]`,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split']\n        }\n    },\n    {\n        label: 'External Link',\n        name: 'external_link',\n        description: 'Url for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split']\n        }\n    },\n    {\n        label: 'Trusted Forwarders',\n        name: 'trusted_forwarders',\n        description: 'Custom gasless trusted forwarder addresses',\n        type: 'json',\n        optional: true,\n        placeholder: '[\"0x359B1408130241E115EE2285bA2635ebFC2109C6\", \"0xE597E474889537A3A9120d1B5793cdFAEf6B6c10\"]',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['split']\n        }\n    }\n] as INodeParams[]\n\nexport const tokenDropParameters = [\n    {\n        label: 'Name',\n        name: 'name',\n        description: 'Name of the contract',\n        type: 'string',\n        placeholder: 'MyToken',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Image Type',\n        name: 'imageType',\n        type: 'options',\n        options: [\n            {\n                label: 'Base64',\n                name: 'base64',\n                description: 'Base64 (raw) image'\n            },\n            {\n                label: 'URL',\n                name: 'url',\n                description: 'URL path of the image'\n            }\n        ],\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Image URL',\n        name: 'imageURL',\n        description: 'URL path of the image',\n        type: 'string',\n        placeholder: 'ipfs://QmSYJ5PJ6SriDWyXtzA62NDfCCMpZt5FWqh9Y5xmhXsWhS/SquirrelV1.png',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token'],\n            'inputParameters.imageType': ['url']\n        }\n    },\n    {\n        label: 'Image Base64',\n        name: 'imageBase64',\n        description: 'Base64 (raw) image',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token'],\n            'inputParameters.imageType': ['base64']\n        }\n    },\n    {\n        label: 'Description',\n        name: 'description',\n        description: 'Description of the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Symbol',\n        name: 'symbol',\n        description: 'Symbol for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Primary Sale Recipient Address',\n        name: 'primary_sale_recipient',\n        description: 'The address that will receive the proceeds from primary sales',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Platform Fee Recipient Address',\n        name: 'platform_fee_recipient',\n        description: 'The address that will receive the proceeds from platform fees',\n        type: 'string',\n        placeholder: '0xE597E474889537A3A9120d1B5793cdFAEf6B6c10',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Platform Fee Percentage',\n        name: 'platform_fee_basis_points',\n        description: 'The percentage (in basis points) of platform fees. Eg: 50 = 0.5%',\n        type: 'number',\n        placeholder: '100',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.platform_fee_recipient': notEmptyRegex,\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'External Link',\n        name: 'external_link',\n        description: 'Url for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    },\n    {\n        label: 'Trusted Forwarders',\n        name: 'trusted_forwarders',\n        description: 'Custom gasless trusted forwarder addresses',\n        type: 'json',\n        optional: true,\n        placeholder: '[\"0x359B1408130241E115EE2285bA2635ebFC2109C6\", \"0xE597E474889537A3A9120d1B5793cdFAEf6B6c10\"]',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['token-drop', 'token']\n        }\n    }\n] as INodeParams[]\n\nexport const voteParameters = [\n    {\n        label: 'Name',\n        name: 'name',\n        description: 'Name of the contract',\n        type: 'string',\n        placeholder: 'MyVoteDAO',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Image Type',\n        name: 'imageType',\n        type: 'options',\n        options: [\n            {\n                label: 'Base64',\n                name: 'base64',\n                description: 'Base64 (raw) image'\n            },\n            {\n                label: 'URL',\n                name: 'url',\n                description: 'URL path of the image'\n            }\n        ],\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Image URL',\n        name: 'imageURL',\n        description: 'URL path of the image',\n        type: 'string',\n        placeholder: 'ipfs://QmSYJ5PJ6SriDWyXtzA62NDfCCMpZt5FWqh9Y5xmhXsWhS/SquirrelV1.png',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote'],\n            'inputParameters.imageType': ['url']\n        }\n    },\n    {\n        label: 'Image Base64',\n        name: 'imageBase64',\n        description: 'Base64 (raw) image',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote'],\n            'inputParameters.imageType': ['base64']\n        }\n    },\n    {\n        label: 'Description',\n        name: 'description',\n        description: 'Description of the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Voting Token Address',\n        name: 'voting_token_address',\n        description: 'The address of the governance token contract representing votes',\n        type: 'string',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Proposal Token Threshold',\n        name: 'proposal_token_threshold',\n        description: 'The minimum amount in governance token owned to be able to create a proposal',\n        type: 'number',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Voting Delay in Blocks',\n        name: 'voting_delay_in_blocks',\n        description:\n            'The delay in blocks before voting can begin on proposals Specified in number of blocks. Assuming block time of around 13.14 seconds, 1 day = 6570 blocks, 1 week = 45992 blocks.',\n        type: 'number',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Voting Period in Blocks',\n        name: 'voting_period_in_blocks',\n        description:\n            'The duration in blocks of the open voting window Specified in number of blocks. Assuming block time of around 13.14 seconds, 1 day = 6570 blocks, 1 week = 45992 blocks.',\n        type: 'number',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Voting Quorum Fraction',\n        name: 'voting_quorum_fraction',\n        description: 'The minimum fraction to be met to pass a proposal',\n        type: 'number',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'External Link',\n        name: 'external_link',\n        description: 'Url for the contract',\n        type: 'string',\n        optional: true,\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    },\n    {\n        label: 'Trusted Forwarders',\n        name: 'trusted_forwarders',\n        description: 'Custom gasless trusted forwarder addresses',\n        type: 'json',\n        optional: true,\n        placeholder: '[\"0x359B1408130241E115EE2285bA2635ebFC2109C6\", \"0xE597E474889537A3A9120d1B5793cdFAEf6B6c10\"]',\n        show: {\n            'actions.operation': ['deploy'],\n            'inputParameters.contractType': ['vote']\n        }\n    }\n] as INodeParams[]\n"
  },
  {
    "path": "packages/components/nodes/ThirdWeb/supportedNetwork.ts",
    "content": "import { INodeOptionsValue } from '../../src'\nimport { NETWORK, NETWORK_LABEL } from '../../src/ChainNetwork'\n\nexport type prebuiltType =\n    | 'edition'\n    | 'edition-drop'\n    | 'marketplace'\n    | 'multiwrap'\n    | 'nft-collection'\n    | 'nft-drop'\n    | 'pack'\n    | 'signature-drop'\n    | 'split'\n    | 'token'\n    | 'token-drop'\n    | 'vote'\n\nexport const ThirdWebSupportedNetworks = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: 'mainnet'\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: 'goerli'\n    },\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: 'polygon'\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: 'mumbai'\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM,\n        name: NETWORK.ARBITRUM\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n        name: NETWORK.ARBITRUM_GOERLI\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE,\n        name: NETWORK.AVALANCHE\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE_TESTNET,\n        name: NETWORK.AVALANCHE_TESTNET\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM,\n        name: NETWORK.OPTIMISM\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM_GOERLI,\n        name: NETWORK.OPTIMISM_GOERLI\n    },\n    {\n        label: NETWORK_LABEL.FANTOM,\n        name: NETWORK.FANTOM\n    },\n    {\n        label: NETWORK_LABEL.FANTOM_TESTNET,\n        name: NETWORK.FANTOM_TESTNET\n    },\n    {\n        label: NETWORK_LABEL.BSC,\n        name: 'binance'\n    },\n    {\n        label: NETWORK_LABEL.BSC_TESTNET,\n        name: 'binance-testnet'\n    }\n    /*\n    {\n        label: NETWORK_LABEL.SOLANA,\n        name: 'mainnet-beta'\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_DEVNET,\n        name: 'devnet'\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_TESTNET,\n        name: 'testnet'\n    }\n    */\n] as INodeOptionsValue[]\n\nexport const ThirdWebSupportedPrebuiltContract = [\n    {\n        label: 'Edition',\n        name: 'edition',\n        description: 'Create editions of ERC1155 tokens'\n    },\n    {\n        label: 'Edition Drop',\n        name: 'edition-drop',\n        description: 'Release ERC1155 tokens for a set price.'\n    },\n    {\n        label: 'Marketplace',\n        name: 'marketplace',\n        description: 'Buy and sell ERC721/ERC1155 tokens'\n    },\n    {\n        label: 'Multiwrap',\n        name: 'multiwrap',\n        description: 'Bundle multiple ERC721/ERC1155/ERC20 tokens into a single ERC721.'\n    },\n    {\n        label: 'NFT Collection',\n        name: 'nft-collection',\n        description: 'Create collection of unique NFTs.'\n    },\n    {\n        label: 'NFT Drop',\n        name: 'nft-drop',\n        description: 'Release collection of unique NFTs for a set price'\n    },\n    {\n        label: 'Pack',\n        name: 'pack',\n        description: 'Pack multiple tokens into ERC1155 NFTs that act as randomized loot boxes'\n    },\n    {\n        label: 'Signature Drop',\n        name: 'signature-drop',\n        description: 'Signature based minting of ERC721 tokens.'\n    },\n    {\n        label: 'Split',\n        name: 'split',\n        description: 'Distribute funds among multiple recipients'\n    },\n    {\n        label: 'Token',\n        name: 'token',\n        description: 'Create cryptocurrency compliant with ERC20 standard'\n    },\n    {\n        label: 'Token Drop',\n        name: 'token-drop',\n        description: 'Release new ERC20 tokens for a set price'\n    },\n    {\n        label: 'Vote',\n        name: 'vote',\n        description: 'Create and vote on proposals'\n    }\n] as INodeOptionsValue[]\n\ninterface IThirdWebNetwork {\n    [key: string]: NETWORK\n}\n\nexport const networkLookup: IThirdWebNetwork = {\n    mainnet: NETWORK.MAINNET,\n    goerli: NETWORK.GÖRLI,\n    polygon: NETWORK.MATIC,\n    mumbai: NETWORK.MATIC_MUMBAI,\n    binance: NETWORK.BSC,\n    'binance-testnet': NETWORK.BSC_TESTNET,\n    [NETWORK.ARBITRUM]: NETWORK.ARBITRUM,\n    [NETWORK.ARBITRUM_GOERLI]: NETWORK.ARBITRUM_GOERLI,\n    [NETWORK.AVALANCHE]: NETWORK.AVALANCHE,\n    [NETWORK.AVALANCHE_TESTNET]: NETWORK.AVALANCHE_TESTNET,\n    [NETWORK.OPTIMISM]: NETWORK.OPTIMISM,\n    [NETWORK.OPTIMISM_GOERLI]: NETWORK.OPTIMISM_GOERLI,\n    [NETWORK.FANTOM]: NETWORK.FANTOM,\n    [NETWORK.FANTOM_TESTNET]: NETWORK.FANTOM_TESTNET\n}\n\nexport const nftDropEvents = [\n    {\n        label: 'TokensClaimed',\n        name: 'TokensClaimed'\n    },\n    {\n        label: 'TokensLazyMinted',\n        name: 'TokensLazyMinted'\n    },\n    {\n        label: 'Transfer',\n        name: 'Transfer'\n    },\n    {\n        label: 'Approval',\n        name: 'Approval'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'ClaimConditionsUpdated',\n        name: 'ClaimConditionsUpdated'\n    },\n    {\n        label: 'ContractURIUpdated',\n        name: 'ContractURIUpdated'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    },\n    {\n        label: 'MaxTotalSupplyUpdated',\n        name: 'MaxTotalSupplyUpdated'\n    },\n    {\n        label: 'ClaimConditionsUpdated',\n        name: 'ClaimConditionsUpdated'\n    },\n    {\n        label: 'OperatorRestriction',\n        name: 'OperatorRestriction'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    },\n    {\n        label: 'TokenURIRevealed',\n        name: 'TokenURIRevealed'\n    }\n] as INodeOptionsValue[]\n\nexport const nftCollectionEvents = [\n    {\n        label: 'TokensMinted',\n        name: 'TokensMinted'\n    },\n    {\n        label: 'TokensMintedWithSignature',\n        name: 'TokensMintedWithSignature'\n    },\n    {\n        label: 'Transfer',\n        name: 'Transfer'\n    },\n    {\n        label: 'Approval',\n        name: 'Approval'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    },\n    {\n        label: 'OperatorRestriction',\n        name: 'OperatorRestriction'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    }\n] as INodeOptionsValue[]\n\nexport const marketplaceEvents = [\n    {\n        label: 'NewSale',\n        name: 'NewSale'\n    },\n    {\n        label: 'NewOffer',\n        name: 'NewOffer'\n    },\n    {\n        label: 'ListingAdded',\n        name: 'ListingAdded'\n    },\n    {\n        label: 'ListingUpdated',\n        name: 'ListingUpdated'\n    },\n    {\n        label: 'ListingRemoved',\n        name: 'ListingRemoved'\n    },\n    {\n        label: 'AuctionClosed',\n        name: 'AuctionClosed'\n    },\n    {\n        label: 'AuctionBuffersUpdated',\n        name: 'AuctionBuffersUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    }\n] as INodeOptionsValue[]\n\nexport const editionDropEvents = [\n    {\n        label: 'TokensClaimed',\n        name: 'TokensClaimed'\n    },\n    {\n        label: 'TokensLazyMinted',\n        name: 'TokensLazyMinted'\n    },\n    {\n        label: 'TransferBatch',\n        name: 'TransferBatch'\n    },\n    {\n        label: 'TransferSingle',\n        name: 'TransferSingle'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'ClaimConditionsUpdated',\n        name: 'ClaimConditionsUpdated'\n    },\n    {\n        label: 'ContractURIUpdated',\n        name: 'ContractURIUpdated'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    },\n    {\n        label: 'MaxTotalSupplyUpdated',\n        name: 'MaxTotalSupplyUpdated'\n    },\n    {\n        label: 'OperatorRestriction',\n        name: 'OperatorRestriction'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    },\n    {\n        label: 'SaleRecipientForTokenUpdated',\n        name: 'SaleRecipientForTokenUpdated'\n    },\n    {\n        label: 'URI',\n        name: 'URI'\n    }\n] as INodeOptionsValue[]\n\nexport const tokenEvents = [\n    {\n        label: 'TokensMinted',\n        name: 'TokensMinted'\n    },\n    {\n        label: 'TokensMintedWithSignature',\n        name: 'TokensMintedWithSignature'\n    },\n    {\n        label: 'Transfer',\n        name: 'Transfer'\n    },\n    {\n        label: 'Approval',\n        name: 'Approval'\n    },\n    {\n        label: 'DelegateChanged',\n        name: 'DelegateChanged'\n    },\n    {\n        label: 'DelegateVotesChanged',\n        name: 'DelegateVotesChanged'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    }\n] as INodeOptionsValue[]\n\nexport const tokenDropEvents = [\n    {\n        label: 'TokensClaimed',\n        name: 'TokensClaimed'\n    },\n    {\n        label: 'Transfer',\n        name: 'Transfer'\n    },\n    {\n        label: 'Approval',\n        name: 'Approval'\n    },\n    {\n        label: 'ClaimConditionsUpdated',\n        name: 'ClaimConditionsUpdated'\n    },\n    {\n        label: 'ContractURIUpdated',\n        name: 'ContractURIUpdated'\n    },\n    {\n        label: 'DelegateChanged',\n        name: 'DelegateChanged'\n    },\n    {\n        label: 'DelegateVotesChanged',\n        name: 'DelegateVotesChanged'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    },\n    {\n        label: 'MaxTotalSupplyUpdated',\n        name: 'MaxTotalSupplyUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    }\n] as INodeOptionsValue[]\n\nexport const editionEvents = [\n    {\n        label: 'TokensMinted',\n        name: 'TokensMinted'\n    },\n    {\n        label: 'TokensMintedWithSignature',\n        name: 'TokensMintedWithSignature'\n    },\n    {\n        label: 'TransferBatch',\n        name: 'TransferBatch'\n    },\n    {\n        label: 'TransferSingle',\n        name: 'TransferSingle'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'FlatPlatformFeeUpdated',\n        name: 'FlatPlatformFeeUpdated'\n    },\n    {\n        label: 'OperatorRestriction',\n        name: 'OperatorRestriction'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PlatformFeeTypeUpdated',\n        name: 'PlatformFeeTypeUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    },\n    {\n        label: 'URI',\n        name: 'URI'\n    }\n] as INodeOptionsValue[]\n\nexport const multiWrapEvents = [\n    {\n        label: 'TokensUnwrapped',\n        name: 'TokensUnwrapped'\n    },\n    {\n        label: 'TokensWrapped',\n        name: 'TokensWrapped'\n    },\n    {\n        label: 'Transfer',\n        name: 'Transfer'\n    },\n    {\n        label: 'Approval',\n        name: 'Approval'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'ContractURIUpdated',\n        name: 'ContractURIUpdated'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    }\n] as INodeOptionsValue[]\n\nexport const packEvents = [\n    {\n        label: 'PackCreated',\n        name: 'PackCreated'\n    },\n    {\n        label: 'PackOpened',\n        name: 'PackOpened'\n    },\n    {\n        label: 'PackUpdated',\n        name: 'PackUpdated'\n    },\n    {\n        label: 'TransferBatch',\n        name: 'TransferBatch'\n    },\n    {\n        label: 'TransferSingle',\n        name: 'TransferSingle'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'ContractURIUpdated',\n        name: 'ContractURIUpdated'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    },\n    {\n        label: 'URI',\n        name: 'URI'\n    }\n] as INodeOptionsValue[]\n\nexport const signatureDropEvents = [\n    {\n        label: 'TokensClaimed',\n        name: 'TokensClaimed'\n    },\n    {\n        label: 'TokensLazyMinted',\n        name: 'TokensLazyMinted'\n    },\n    {\n        label: 'TokensMintedWithSignature',\n        name: 'TokensMintedWithSignature'\n    },\n    {\n        label: 'TokenURIRevealed',\n        name: 'TokenURIRevealed'\n    },\n    {\n        label: 'Transfer',\n        name: 'Transfer'\n    },\n    {\n        label: 'Approval',\n        name: 'Approval'\n    },\n    {\n        label: 'ApprovalForAll',\n        name: 'ApprovalForAll'\n    },\n    {\n        label: 'ClaimConditionUpdated',\n        name: 'ClaimConditionUpdated'\n    },\n    {\n        label: 'ContractURIUpdated',\n        name: 'ContractURIUpdated'\n    },\n    {\n        label: 'DefaultRoyalty',\n        name: 'DefaultRoyalty'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    },\n    {\n        label: 'OwnerUpdated',\n        name: 'OwnerUpdated'\n    },\n    {\n        label: 'PlatformFeeInfoUpdated',\n        name: 'PlatformFeeInfoUpdated'\n    },\n    {\n        label: 'PrimarySaleRecipientUpdated',\n        name: 'PrimarySaleRecipientUpdated'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    },\n    {\n        label: 'RoyaltyForToken',\n        name: 'RoyaltyForToken'\n    }\n] as INodeOptionsValue[]\n\nexport const splitEvents = [\n    {\n        label: 'ERC20PaymentReleased',\n        name: 'ERC20PaymentReleased'\n    },\n    {\n        label: 'PaymentReceived',\n        name: 'PaymentReceived'\n    },\n    {\n        label: 'PaymentReleased',\n        name: 'PaymentReleased'\n    },\n    {\n        label: 'PayeeAdded',\n        name: 'PayeeAdded'\n    },\n    {\n        label: 'RoleAdminChanged',\n        name: 'RoleAdminChanged'\n    },\n    {\n        label: 'RoleGranted',\n        name: 'RoleGranted'\n    },\n    {\n        label: 'RoleRevoked',\n        name: 'RoleRevoked'\n    }\n] as INodeOptionsValue[]\n\nexport const voteEvents = [\n    {\n        label: 'ProposalCreated',\n        name: 'ProposalCreated'\n    },\n    {\n        label: 'ProposalExecuted',\n        name: 'ProposalExecuted'\n    },\n    {\n        label: 'ProposalCanceled',\n        name: 'ProposalCanceled'\n    },\n    {\n        label: 'ProposalThresholdSet',\n        name: 'ProposalThresholdSet'\n    },\n    {\n        label: 'VoteCast',\n        name: 'VoteCast'\n    },\n    {\n        label: 'VoteCastWithParams',\n        name: 'VoteCastWithParams'\n    },\n    {\n        label: 'VotingDelaySet',\n        name: 'VotingDelaySet'\n    },\n    {\n        label: 'VotingPeriodSet',\n        name: 'VotingPeriodSet'\n    },\n    {\n        label: 'QuorumNumeratorUpdated',\n        name: 'QuorumNumeratorUpdated'\n    },\n    {\n        label: 'Initialized',\n        name: 'Initialized'\n    }\n] as INodeOptionsValue[]\n"
  },
  {
    "path": "packages/components/nodes/Twitter/Twitter.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass Twitter implements INode {\n    label: string\n    name: string\n    icon: string\n    type: NodeType\n    description: string\n    version: number\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n    credentials?: INodeParams[]\n\n    constructor() {\n        this.label = 'Twitter'\n        this.name = 'twitter'\n        this.icon = 'Twitter-Logo.png'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.description = \"Search Twitter User's tweets by keyword\"\n        this.version = 1.0\n        this.incoming = 1\n        this.outgoing = 1\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Twitter Bearer Token',\n                        name: 'twitterApi'\n                    }\n                ],\n                default: 'twitterApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Twitter ID',\n                name: 'TwitterId',\n                type: 'string',\n                default: '',\n                description: ''\n            },\n            {\n                label: 'Keyword',\n                name: 'Keyword',\n                type: 'string',\n                default: '',\n                description: 'Message contents (up to 512 characters long)'\n            },\n            {\n                label: 'From',\n                name: 'fromDate',\n                type: 'options',\n                description: 'Date of start search',\n                options: [\n                    {\n                        label: 'From Today UTC',\n                        name: 'fromTodayUTC'\n                    }\n                ],\n                default: 'fromTodayUTC'\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const returnData: ICommonObject[] = []\n\n        const bearerToken = credentials.bearerToken as string\n        const TwitterId = inputParametersData.TwitterId as string\n        const keyword = inputParametersData.Keyword as string\n        const query = 'from:' + TwitterId + ' ' + keyword\n        const timestamp = new Date()\n        timestamp.setUTCHours(0, 0, 0, 0)\n\n        let responseData: any\n        let maxRetries = 5\n\n        do {\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://api.twitter.com/2/tweets/search/recent?query=${query}&start_time=${timestamp.toISOString()}&tweet.fields=created_at&sort_order=recency`,\n                    headers: {\n                        Authorization: `Bearer ${bearerToken}`\n                    }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error getting message from twitter API. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData)\n    }\n}\n\nmodule.exports = { nodeClass: Twitter }\n"
  },
  {
    "path": "packages/components/nodes/Typeform/Typeform.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass Typeform implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Typeform'\n        this.name = 'typeform'\n        this.icon = 'typeform-icon.svg'\n        this.type = 'action'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Perform Typeform operations'\n        this.incoming = 1\n        this.outgoing = 1\n\n        this.actions = [\n            {\n                label: 'API',\n                name: 'api',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get all forms',\n                        name: 'getAllForms',\n                        description: 'Returns all the forms associated with your account'\n                    },\n                    {\n                        label: 'Get Typeform Responses',\n                        name: 'getTypeformResponses',\n                        description: 'Returns the submissions for your typeforms in JSON format'\n                    },\n                    {\n                        label: 'Create Typeform ',\n                        name: 'createTypeform',\n                        description: 'Creates a typeform for you'\n                    }\n                ],\n                default: 'getAllForms'\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            // credentialMethod is mandatory field\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Typeform API Key',\n                        name: 'typeformApi'\n                    }\n                ],\n                default: 'typeformApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Form Id',\n                name: 'formId',\n                type: 'string',\n                description: 'The form id to retrieve all the responses to your typeform',\n                show: {\n                    'actions.api': ['getTypeformResponses']\n                }\n            },\n            {\n                label: 'Request Body',\n                name: 'requestBody',\n                type: 'json',\n                description: 'The json object to create or update your typeform',\n                show: {\n                    'actions.api': ['createTypeform']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        // function to start running the node\n        const actionData = nodeData.actions\n        const inputParametersData = nodeData.inputParameters\n        const credentials = nodeData.credentials\n\n        if (actionData === undefined || inputParametersData === undefined || credentials === undefined) {\n            throw new Error('Required data missing')\n        }\n        // GET api\n        const api = actionData.api as string\n        // GET credentials\n        const apiKey = credentials.apiKey as string\n        // GET formId\n        const formId = inputParametersData.formId as string\n        let requestBody = inputParametersData.requestBody as string\n        requestBody = requestBody ? requestBody.replace(/\\s/g, '') : requestBody\n        const returnData: ICommonObject[] = []\n        let responseData: any\n        if (api === 'getAllForms') {\n            try {\n                const queryParameters = {}\n\n                let url = `https://api.typeform.com/forms`\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url,\n                    params: queryParameters,\n                    paramsSerializer: (params) => serializeQueryParams(params),\n                    headers: { 'Content-Type': 'application/json', authorization: `bearer ${apiKey}` }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n            return returnNodeExecutionData(returnData)\n        } else if (api === 'getTypeformResponses') {\n            try {\n                const queryParameters = {}\n\n                let url = `https://api.typeform.com/forms/${formId}/responses`\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url,\n                    params: queryParameters,\n                    paramsSerializer: (params) => serializeQueryParams(params),\n                    headers: { 'Content-Type': 'application/json', authorization: `bearer ${apiKey}` }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n            return returnNodeExecutionData(returnData)\n        } else if (api === 'createTypeform') {\n            try {\n                const body = JSON.parse(requestBody)\n                let url = `https://api.typeform.com/forms`\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url,\n                    data: Object.assign({}, body),\n                    headers: { 'Content-Type': 'application/json; charset=utf-8', authorization: `bearer ${apiKey}` }\n                }\n                const response = await axios(axiosConfig)\n                responseData = response.data\n            } catch (error) {\n                throw handleErrorMessage(error)\n            }\n            if (Array.isArray(responseData)) returnData.push(...responseData)\n            else returnData.push(responseData)\n            return returnNodeExecutionData(returnData)\n        }\n        return returnNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: Typeform }\n"
  },
  {
    "path": "packages/components/nodes/TypeformWebhook/TypeformWebhook.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeParams, IWebhookNodeExecutionData, NodeType } from '../../src/Interface'\nimport { returnWebhookNodeExecutionData, handleErrorMessage } from '../../src/utils'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\n\nclass TypeformWebhook implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Typeform Webhook'\n        this.name = 'typeformWebhook'\n        this.icon = 'typeform-webhook.svg'\n        this.type = 'webhook'\n        this.category = 'Communication'\n        this.version = 1.0\n        this.description = 'Start workflow whenever Typeform webhook event happened'\n        this.incoming = 0\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Event',\n                name: 'webhook_type',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Typeform Submission',\n                        name: 'typeformSubmission',\n                        description: 'Triggered anytime form typeform submit a response sent through typeform webhook.'\n                    }\n                ],\n                default: 'typeformSubmission'\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            // credentialMethod is mandatory field\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Typeform Access Token',\n                        name: 'typeformApi'\n                    }\n                ],\n                default: 'typeformApi'\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'Form Id',\n                name: 'formId',\n                type: 'string',\n                description:\n                    'Unique ID for the form. Find in your form URL. For example, in the URL \"https://mysite.typeform.com/to/u6nXL7\" the form_id is u6nXL7',\n                show: {\n                    'actions.webhook_type': ['typeformSubmission']\n                }\n            },\n            {\n                label: 'Webhook Tag',\n                name: 'tag',\n                type: 'string',\n                placeholder: 'mywebhook',\n                description: 'The name you want to use for your webhook',\n                show: {\n                    'actions.webhook_type': ['typeformSubmission']\n                }\n            }\n        ] as INodeParams[]\n    }\n    webhookMethods = {\n        async createWebhook(nodeData: INodeData, webhookFullUrl: string): Promise<string | undefined> {\n            // check for the webhooks\n\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined) {\n                throw handleErrorMessage({ message: 'Required data missing' })\n            }\n            if (credentials === undefined) {\n                throw handleErrorMessage({ message: 'Missing credentials' })\n            }\n            const accesToken = credentials.accessToken as string\n            const tag = inputParametersData?.tag\n            const formId = inputParametersData?.formId\n            let webhookExist: boolean = false\n            let webhookId: string = ''\n\n            if (!accesToken) throw handleErrorMessage({ message: 'Access token required' })\n            if (!tag) throw handleErrorMessage({ message: 'Tag is required' })\n            if (!formId) throw handleErrorMessage({ message: 'FormId is required' })\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET' as Method,\n                url: `https://api.typeform.com/forms/${formId}/webhooks/${tag}`,\n                headers: { Authorization: `Bearer ${accesToken}` }\n            }\n            try {\n                const res = await axios(axiosConfig)\n                webhookId = res?.data?.id\n                webhookExist = true\n            } catch (err) {\n                if (err.response.status !== 404) throw new Error(err)\n            }\n\n            if (!webhookExist) {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'PUT' as Method,\n                    url: `https://api.typeform.com/forms/${formId}/webhooks/${tag}`,\n                    headers: { Authorization: `Bearer ${accesToken}` },\n                    data: {\n                        enabled: true,\n                        url: webhookFullUrl\n                    }\n                }\n                try {\n                    const res = await axios(axiosConfig)\n                    webhookId = res?.data?.id\n                } catch (err) {\n                    return\n                }\n            }\n\n            return webhookId\n        },\n        async deleteWebhook(nodeData: INodeData): Promise<boolean> {\n            // delete webhook\n            const credentials = nodeData.credentials\n            const inputParametersData = nodeData.inputParameters\n            const actionsData = nodeData.actions\n\n            if (inputParametersData === undefined || actionsData === undefined) {\n                throw handleErrorMessage({ message: 'Required data missing' })\n            }\n            if (credentials === undefined) {\n                throw handleErrorMessage({ message: 'Missing credentials' })\n            }\n            const accesToken = credentials.accessToken as string\n            const tag = inputParametersData?.tag\n            const formId = inputParametersData?.formId\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'DELETE' as Method,\n                url: `https://api.typeform.com/forms/${formId}/webhooks/${tag}`,\n                headers: { Authorization: `Bearer ${accesToken}` }\n            }\n            try {\n                await axios(axiosConfig)\n            } catch (err) {\n                return false\n            }\n            return true\n        }\n    }\n\n    async runWebhook(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const req = nodeData.req\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n        if (req === undefined) {\n            throw new Error('Missing request')\n        }\n\n        const returnData: ICommonObject[] = []\n        returnData.push({\n            headers: req?.headers,\n            params: req?.params,\n            query: req?.query,\n            body: req?.body,\n            rawBody: (req as any).rawBody,\n            url: req?.url\n        })\n        return returnWebhookNodeExecutionData(returnData)\n    }\n}\nmodule.exports = { nodeClass: TypeformWebhook }\n"
  },
  {
    "path": "packages/components/nodes/Uniswap/Uniswap.ts",
    "content": "import { ContractInterface, ethers } from 'ethers'\nimport {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeExecutionData,\n    INodeOptionsValue,\n    INodeParams,\n    IWallet,\n    NodeType\n} from '../../src/Interface'\nimport { handleErrorMessage, returnNodeExecutionData } from '../../src/utils'\nimport {\n    networkExplorers,\n    ETHNetworks,\n    chainIdLookup,\n    nativeCurrency,\n    getNetworkProvidersList,\n    NETWORK,\n    getNetworkProvider,\n    NETWORK_PROVIDER,\n    NETWORK_LABEL\n} from '../../src/ChainNetwork'\nimport IWETH from '@uniswap/v2-periphery/build/IWETH.json'\nimport axios, { AxiosRequestConfig, Method } from 'axios'\nimport { UniswapPair, UniswapPairSettings } from 'simple-uniswap-sdk'\nimport { IToken, nativeTokens } from './nativeTokens'\n\nclass Uniswap implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    networks?: INodeParams[]\n    credentials?: INodeParams[]\n    actions?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Uniswap'\n        this.name = 'uniswap'\n        this.icon = 'uniswap.png'\n        this.type = 'action'\n        this.category = 'Decentralized Finance'\n        this.version = 1.0\n        this.description = 'Execute Uniswap operations'\n        this.incoming = 1\n        this.outgoing = 1\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Swap Tokens',\n                        name: 'swapTokens',\n                        description: 'Supports uniswap v2 and v3 prices together and returns the best price for swapping.'\n                    },\n                    {\n                        label: 'Get Pairs',\n                        name: 'getPairs',\n                        description: 'Get most liquid pairs'\n                    },\n                    {\n                        label: 'Custom Query',\n                        name: 'customQuery',\n                        description:\n                            'Custom subgraph query to retrieve more information. https://docs.uniswap.org/protocol/V2/reference/API/queries'\n                    }\n                ],\n                default: 'swapTokens'\n            }\n        ] as INodeParams[]\n        this.networks = [\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [\n                    {\n                        label: NETWORK_LABEL.MAINNET,\n                        name: NETWORK.MAINNET,\n                        parentGroup: 'Ethereum'\n                    }\n                ],\n                default: 'homestead',\n                show: {\n                    'actions.operation': ['getPairs', 'customQuery']\n                }\n            },\n            {\n                label: 'Network',\n                name: 'network',\n                type: 'options',\n                options: [...ETHNetworks],\n                default: 'homestead',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Network Provider',\n                name: 'networkProvider',\n                type: 'asyncOptions',\n                loadMethod: 'getNetworkProviders'\n            },\n            {\n                label: 'RPC Endpoint',\n                name: 'jsonRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customRPC'],\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Websocket Endpoint',\n                name: 'websocketRPC',\n                type: 'string',\n                default: '',\n                show: {\n                    'networks.networkProvider': ['customWebsocket'],\n                    'actions.operation': ['swapTokens']\n                }\n            }\n        ] as INodeParams[]\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Alchemy API Key',\n                        name: 'alchemyApi',\n                        show: {\n                            'networks.networkProvider': [NETWORK_PROVIDER.ALCHEMY]\n                        }\n                    },\n                    {\n                        label: 'Infura API Key',\n                        name: 'infuraApi',\n                        show: {\n                            'networks.networkProvider': [NETWORK_PROVIDER.INFURA]\n                        }\n                    },\n                    {\n                        label: 'QuickNode Endpoints',\n                        name: 'quickNodeEndpoints',\n                        show: {\n                            'networks.networkProvider': [NETWORK_PROVIDER.QUICKNODE]\n                        }\n                    }\n                ],\n                default: '',\n                show: {\n                    'networks.networkProvider': [NETWORK_PROVIDER.ALCHEMY, NETWORK_PROVIDER.INFURA, NETWORK_PROVIDER.QUICKNODE],\n                    'actions.operation': ['swapTokens']\n                }\n            }\n        ] as INodeParams[]\n        this.inputParameters = [\n            {\n                label: 'From Token',\n                name: 'fromToken',\n                type: 'asyncOptions',\n                description: 'Contract address of the token you want to convert FROM.',\n                loadMethod: 'getTokens',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'To Token',\n                name: 'toToken',\n                type: 'asyncOptions',\n                description: 'Contract address of the token you want to convert TO.',\n                loadMethod: 'getTokens',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Amount To Swap',\n                name: 'amountToSwap',\n                type: 'number',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Select Wallet',\n                name: 'wallet',\n                type: 'asyncOptions',\n                description: 'Wallet account to swap tokens.',\n                loadFromDbCollections: ['Wallet'],\n                loadMethod: 'getWallets',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Query',\n                name: 'query',\n                type: 'string',\n                rows: 10,\n                show: {\n                    'actions.operation': ['customQuery']\n                }\n            },\n            {\n                label: 'Slippage Tolerance (%)',\n                name: 'slippage',\n                type: 'number',\n                default: 0.5,\n                optional: true,\n                description: 'How large of a price movement to tolerate before trade will fail to execute. Default to 0.5%.',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Tx Deadline (mins)',\n                name: 'deadlineMinutes',\n                type: 'number',\n                default: 20,\n                optional: true,\n                description: 'Minutes after which the transaction will fail. Default to 20 mins.',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            },\n            {\n                label: 'Disable Multihops',\n                name: 'disableMultihops',\n                type: 'boolean',\n                default: false,\n                optional: true,\n                description: 'Restricts swaps to direct pairs only. Default to false.',\n                show: {\n                    'actions.operation': ['swapTokens']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getTokens(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n\n            try {\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'GET' as Method,\n                    url: `https://tokens.uniswap.org`\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                let tokens: IToken[] = responseData.tokens\n                const nativeToken: IToken = nativeTokens[network]\n\n                // Add native token\n                const data = {\n                    label: `${nativeToken.name} (${nativeToken.symbol})`,\n                    name: `${nativeToken.address};${nativeToken.symbol};${nativeToken.name}`\n                } as INodeOptionsValue\n                returnData.push(data)\n\n                // Add other tokens\n                tokens = tokens.filter((tkn) => tkn.chainId === chainIdLookup[network])\n                for (let i = 0; i < tokens.length; i += 1) {\n                    const token = tokens[i]\n                    const data = {\n                        label: `${token.name} (${token.symbol})`,\n                        name: `${token.address};${token.symbol};${token.name}`\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getWallets(nodeData: INodeData, dbCollection?: IDbCollection): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) {\n                return returnData\n            }\n\n            try {\n                if (dbCollection === undefined || !dbCollection || !dbCollection.Wallet) {\n                    return returnData\n                }\n\n                const wallets: IWallet[] = dbCollection.Wallet\n\n                for (let i = 0; i < wallets.length; i += 1) {\n                    const wallet = wallets[i]\n                    const data = {\n                        label: `${wallet.name} (${wallet.network})`,\n                        name: JSON.stringify(wallet),\n                        description: wallet.address\n                    } as INodeOptionsValue\n                    returnData.push(data)\n                }\n\n                return returnData\n            } catch (e) {\n                return returnData\n            }\n        },\n\n        async getNetworkProviders(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const networksData = nodeData.networks\n            if (networksData === undefined) return returnData\n\n            const network = networksData.network as NETWORK\n            return getNetworkProvidersList(network)\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const networksData = nodeData.networks\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (networksData === undefined || actionsData === undefined || inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        try {\n            const network = networksData.network as NETWORK\n\n            const provider = await getNetworkProvider(\n                networksData.networkProvider as NETWORK_PROVIDER,\n                network,\n                credentials,\n                networksData.jsonRPC as string,\n                networksData.websocketRPC as string\n            )\n\n            if (!provider) throw new Error('Invalid Network Provider')\n\n            // Get operation\n            const operation = actionsData.operation as string\n\n            if (operation === 'swapTokens') {\n                // Get fromTokenAddress\n                const fromToken = inputParametersData.fromToken as string\n                const [fromTokenContractAddress, fromTokenSymbol] = fromToken.split(';')\n\n                // Get toTokenAddress\n                const toToken = inputParametersData.toToken as string\n                const [toTokenContractAddress, toTokenSymbol] = toToken.split(';')\n\n                // Get wallet instance\n                const walletString = inputParametersData.wallet as string\n                const walletDetails: IWallet = JSON.parse(walletString)\n                const walletCredential = JSON.parse(walletDetails.walletCredential)\n                const wallet = new ethers.Wallet(walletCredential.privateKey as string, provider)\n\n                // Get amount\n                const amountToSwap = inputParametersData.amountToSwap as string\n\n                if (fromTokenContractAddress.includes(`_${nativeCurrency[network]}`) && toTokenSymbol === 'WETH') {\n                    const wrapEthContract = new ethers.Contract(toTokenContractAddress, IWETH['abi'] as ContractInterface, wallet)\n\n                    const tx = await wrapEthContract.deposit({ value: ethers.utils.parseUnits(amountToSwap, 18) })\n\n                    const approveReceipt = await tx.wait()\n\n                    if (approveReceipt.status === 0) throw new Error(`Failed to swap ETH to WETH`)\n\n                    const returnItem = {\n                        transactionHash: tx.hash,\n                        transactionReceipt: approveReceipt as any,\n                        link: `${networkExplorers[network]}/tx/${tx.hash}`\n                    }\n                    return returnNodeExecutionData(returnItem)\n                } else if (toTokenContractAddress.includes(`_${nativeCurrency[network]}`) && fromTokenSymbol === 'WETH') {\n                    const wrapEthContract = new ethers.Contract(fromTokenContractAddress, IWETH['abi'] as ContractInterface, wallet)\n\n                    const tx = await wrapEthContract.withdraw(ethers.utils.parseUnits(amountToSwap, 18))\n\n                    const approveReceipt = await tx.wait()\n\n                    if (approveReceipt.status === 0) throw new Error(`Failed to swap WETH to ETH`)\n\n                    const returnItem = {\n                        transactionHash: tx.hash,\n                        transactionReceipt: approveReceipt as any,\n                        link: `${networkExplorers[network]}/tx/${tx.hash}`\n                    }\n                    return returnNodeExecutionData(returnItem)\n                } else {\n                    const slippage = inputParametersData.slippage as string\n                    const deadlineMinutes = inputParametersData.deadlineMinutes as number\n                    const disableMultihops = inputParametersData.disableMultihops as boolean\n\n                    const uniswapPair = new UniswapPair({\n                        fromTokenContractAddress,\n                        toTokenContractAddress,\n                        ethereumAddress: wallet.address,\n                        ethereumProvider: provider,\n                        chainId: chainIdLookup[network] as number,\n                        settings: new UniswapPairSettings({\n                            slippage: parseFloat(slippage) / 100.0 || 0.0005,\n                            deadlineMinutes: deadlineMinutes || 20,\n                            disableMultihops: disableMultihops || false\n                        })\n                    })\n\n                    const uniswapPairFactory = await uniswapPair.createFactory()\n\n                    const trade = await uniswapPairFactory.trade(amountToSwap)\n\n                    if (!trade.fromBalance.hasEnough) {\n                        throw new Error('You do not have enough from balance to execute this swap')\n                    }\n\n                    // Why we need two transactions: https://github.com/joshstevens19/simple-uniswap-sdk#ethers-example\n                    if (trade.approvalTransaction) {\n                        const approved = await wallet.sendTransaction(trade.approvalTransaction)\n                        await approved.wait()\n                    }\n\n                    const tradeTransaction = await wallet.sendTransaction(trade.transaction)\n                    const tradeReceipt = await tradeTransaction.wait()\n\n                    trade.destroy()\n\n                    const returnItem = {\n                        operation,\n                        transactionHash: tradeTransaction.hash,\n                        transactionReceipt: tradeReceipt as any,\n                        link: `${networkExplorers[network]}/tx/${tradeTransaction.hash}`\n                    }\n                    return returnNodeExecutionData(returnItem)\n                }\n            } else if (operation === 'getPairs' || operation === 'customQuery') {\n                let query = ''\n                if (operation === 'customQuery') query = inputParametersData.query as string\n                else {\n                    query = `{\n\t\t\t\t\t\tpairs(\n\t\t\t\t\t\t\tfirst: 100 \n\t\t\t\t\t\t\torderBy: reserveUSD\n\t\t\t\t\t\t\torderDirection: desc\n\t\t\t\t\t\t) { \n\t\t\t\t\t\t\tid \n\t\t\t\t\t\t\ttoken0 { \n\t\t\t\t\t\t\t\tid \n\t\t\t\t\t\t\t\tsymbol \n\t\t\t\t\t\t\t\tname \n\t\t\t\t\t\t\t} \n\t\t\t\t\t\t\ttoken1 { \n\t\t\t\t\t\t\t\tid \n\t\t\t\t\t\t\t\tsymbol \n\t\t\t\t\t\t\t\tname \n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treserveUSD\n\t\t\t\t\t\t\tvolumeUSD\n\t\t\t\t\t\t}\n\t\t\t\t\t}`\n                }\n                query = query.replace(/\\s/g, ' ')\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method: 'POST' as Method,\n                    url: `https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2`,\n                    data: { query }\n                }\n\n                const response = await axios(axiosConfig)\n                const responseData = response.data\n                const returnData: ICommonObject[] = []\n\n                if (Array.isArray(responseData)) returnData.push(...responseData)\n                else returnData.push(responseData)\n\n                return returnNodeExecutionData(returnData)\n            }\n\n            return returnNodeExecutionData([])\n        } catch (e) {\n            throw handleErrorMessage(e)\n        }\n    }\n}\n\nmodule.exports = { nodeClass: Uniswap }\n"
  },
  {
    "path": "packages/components/nodes/Uniswap/nativeTokens.ts",
    "content": "import { ETH, ChainId } from 'simple-uniswap-sdk'\n\nexport interface INativeTokens {\n    [key: string]: IToken\n}\n\nexport interface IToken {\n    address: string\n    symbol: string\n    name: string\n    decimals: number\n    chainId: number\n}\n\nexport const nativeTokens: INativeTokens = {\n    homestead: {\n        address: ETH.MAINNET().contractAddress,\n        symbol: 'ETH',\n        name: 'ETH',\n        decimals: 18,\n        chainId: ChainId.MAINNET\n    },\n    goerli: {\n        address: ETH.GORLI().contractAddress,\n        symbol: 'ETH',\n        name: 'ETH',\n        decimals: 18,\n        chainId: ChainId.GÖRLI\n    }\n}\n"
  },
  {
    "path": "packages/components/nodes/Wait/Wait.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeParams, NodeType } from '../../src/Interface'\nimport { returnNodeExecutionData } from '../../src/utils'\n\nclass Wait implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Wait'\n        this.name = 'wait'\n        this.icon = 'wait.svg'\n        this.type = 'action'\n        this.category = 'Utilities'\n        this.version = 1.0\n        this.description = 'Wait before continuing with the execution'\n        this.incoming = 1\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'Unit',\n                name: 'unit',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Seconds',\n                        name: 'seconds'\n                    },\n                    {\n                        label: 'Minutes',\n                        name: 'minutes'\n                    },\n                    {\n                        label: 'Hours',\n                        name: 'hours'\n                    },\n                    {\n                        label: 'Days',\n                        name: 'days'\n                    }\n                ],\n                default: 'seconds',\n                description: 'The time unit of the duration to wait'\n            },\n            {\n                label: 'Duration',\n                name: 'duration',\n                type: 'number',\n                default: 10,\n                description: 'Duration to wait before continuing with the execution'\n            }\n        ]\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        const startWaitDate = new Date().toUTCString()\n\n        const unit = inputParametersData.unit as string\n\n        let duration = (inputParametersData.duration as number) || 1\n\n        if (unit === 'minutes') {\n            duration *= 60\n        }\n        if (unit === 'hours') {\n            duration *= 60 * 60\n        }\n        if (unit === 'days') {\n            duration *= 60 * 60 * 24\n        }\n\n        duration *= 1000\n\n        const endWaitDate = new Date(new Date().getTime() + duration).toUTCString()\n\n        const returnData: ICommonObject[] = [\n            {\n                start: startWaitDate,\n                end: endWaitDate,\n                duration,\n                unit\n            }\n        ]\n\n        return new Promise((resolve, _) => {\n            setTimeout(() => {\n                resolve(returnNodeExecutionData(returnData))\n            }, duration)\n        })\n    }\n}\n\nmodule.exports = { nodeClass: Wait }\n"
  },
  {
    "path": "packages/components/nodes/Webhook/Webhook.ts",
    "content": "import {\n    ICommonObject,\n    IDbCollection,\n    INode,\n    INodeData,\n    INodeOptionsValue,\n    INodeParams,\n    IWebhookNodeExecutionData,\n    NodeType\n} from '../../src/Interface'\nimport { compareKeys, returnWebhookNodeExecutionData } from '../../src/utils'\n\nclass Webhook implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Webhook'\n        this.icon = 'webhook.svg'\n        this.name = 'webhook'\n        this.type = 'webhook'\n        this.category = 'Utilities'\n        this.version = 2.0\n        this.description = 'Start workflow when webhook is called'\n        this.incoming = 0\n        this.outgoing = 1\n        this.inputParameters = [\n            {\n                label: 'HTTP Method',\n                name: 'httpMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'GET',\n                        name: 'GET'\n                    },\n                    {\n                        label: 'POST',\n                        name: 'POST'\n                    }\n                ],\n                default: 'GET',\n                description: 'The HTTP method to listen to.'\n            },\n            {\n                label: 'Authorization',\n                name: 'authorization',\n                type: 'options',\n                options: [\n                    {\n                        label: 'API',\n                        name: 'headerAuth',\n                        description: 'Webhook header must contains \"X-API-KEY\" with matching key'\n                    },\n                    {\n                        label: 'None',\n                        name: 'none'\n                    }\n                ],\n                default: 'none',\n                description: 'The way to authorize incoming webhook.'\n            },\n            {\n                label: 'API key',\n                name: 'apiKey',\n                type: 'asyncOptions',\n                loadMethod: 'getAPIKeys',\n                description:\n                    'Incoming call must consists header \"x-api-key\" with matching API key. You can create new key from the dashboard',\n                show: {\n                    'inputParameters.authorization': ['headerAuth']\n                }\n            },\n            {\n                label: 'Response Code',\n                name: 'responseCode',\n                type: 'number',\n                default: 200,\n                description: 'The HTTP response code to return when a HTTP request is made to this endpoint URL. Valid range: 1XX - 5XX'\n            },\n            {\n                label: 'What/How to Return',\n                name: 'returnType',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Immediate Reponse',\n                        name: 'immediateResponse',\n                        description: 'Returns response immediately once webhook is called'\n                    },\n                    {\n                        label: 'When Last Node Finishes',\n                        name: 'lastNodeResponse',\n                        description: 'Returns output response of the last executed node'\n                    }\n                ],\n                default: 'immediateResponse',\n                description: 'What data or message, and how should Webhook node return upon successful calling'\n            },\n            {\n                label: 'Response Data',\n                name: 'responseData',\n                type: 'string',\n                default: '',\n                description:\n                    'Custom response data to return when a HTTP request is made to this webhook endpoint URL. If not provided, default to: Webhook received!',\n                optional: true,\n                show: {\n                    'inputParameters.returnType': ['immediateResponse']\n                }\n            }\n        ]\n    }\n\n    loadMethods = {\n        async getAPIKeys(nodeData: INodeData, dbCollection?: IDbCollection, apiKeys?: ICommonObject[]): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            if (!apiKeys || !apiKeys.length) return returnData\n\n            for (let i = 0; i < apiKeys.length; i += 1) {\n                const key = apiKeys[i]\n                const data = {\n                    label: key.keyName,\n                    description: key.apiKey,\n                    name: key.apiSecret\n                } as INodeOptionsValue\n                returnData.push(data)\n            }\n            return returnData\n        }\n    }\n\n    async runWebhook(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null> {\n        const inputParametersData = nodeData.inputParameters\n        const req = nodeData.req\n\n        if (inputParametersData === undefined) {\n            throw new Error('Required data missing')\n        }\n\n        if (req === undefined) {\n            throw new Error('Missing request')\n        }\n\n        const responseData = (inputParametersData.responseData as string) || ''\n        const authorization = inputParametersData.authorization as string\n        const apiSecret = inputParametersData.apiKey as string\n\n        const returnData: ICommonObject[] = []\n\n        if (authorization === 'headerAuth') {\n            let suppliedKey = ''\n            if (req.headers['X-API-KEY']) suppliedKey = req.headers['X-API-KEY'] as string\n            if (req.headers['x-api-key']) suppliedKey = req.headers['x-api-key'] as string\n            if (!suppliedKey) throw new Error('401: Missing API Key')\n            const isKeyValid = compareKeys(apiSecret, suppliedKey)\n            if (!isKeyValid) throw new Error('403: Unauthorized API Key')\n        }\n\n        returnData.push({\n            headers: req?.headers,\n            params: req?.params,\n            query: req?.query,\n            body: req?.body,\n            rawBody: (req as any).rawBody,\n            url: req?.url\n        })\n\n        return returnWebhookNodeExecutionData(returnData, responseData)\n    }\n}\n\nmodule.exports = { nodeClass: Webhook }\n"
  },
  {
    "path": "packages/components/nodes/Xero/xero.ts",
    "content": "import { ICommonObject, INode, INodeData, INodeExecutionData, INodeOptionsValue, INodeParams, NodeType } from '../../src/Interface'\nimport { handleErrorMessage, refreshOAuth2Token, returnNodeExecutionData, serializeQueryParams } from '../../src/utils'\nimport axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from 'axios'\n\nclass Xero implements INode {\n    label: string\n    name: string\n    type: NodeType\n    description: string\n    version: number\n    icon: string\n    category: string\n    incoming: number\n    outgoing: number\n    actions: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n\n    constructor() {\n        this.label = 'Xero'\n        this.name = 'xero'\n        this.icon = 'xero.svg'\n        this.type = 'action'\n        this.category = 'Accounting'\n        this.version = 1.0\n        this.description = 'Perform Xero operations'\n        this.incoming = 1\n        this.outgoing = 1\n\n        this.actions = [\n            {\n                label: 'Operation',\n                name: 'operation',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Get all invoices',\n                        name: 'getAllInvoices',\n                        description: 'Returns all the invoices from Xero account.'\n                    },\n                    {\n                        label: 'Get single invoice',\n                        name: 'getSingleInvoice',\n                        description: 'Returns single invoice from Xero account.'\n                    },\n                    {\n                        label: 'Create Invoice',\n                        name: 'createInvoice',\n                        description: 'Creates a new draft invoice.'\n                    },\n                    {\n                        label: 'Send email',\n                        name: 'sendToEmail',\n                        description: 'Send email with invoice to primary email.'\n                    }\n                ]\n            }\n        ] as INodeParams[]\n\n        this.credentials = [\n            {\n                label: 'Credential Method',\n                name: 'credentialMethod',\n                type: 'options',\n                options: [\n                    {\n                        label: 'Xero OAuth2',\n                        name: 'xeroOAuth2Api'\n                    }\n                ],\n                default: 'xeroOAuth2Api'\n            }\n        ] as INodeParams[]\n\n        this.inputParameters = [\n            {\n                label: 'Tenant',\n                name: 'tenant',\n                type: 'asyncOptions',\n                loadMethod: 'getTenants'\n            },\n            {\n                label: 'Invoice',\n                name: 'invoice',\n                type: 'asyncOptions',\n                loadMethod: 'getInvoices',\n                show: {\n                    'actions.operation': ['getSingleInvoice', 'sendToEmail']\n                }\n            },\n            // Get existing contacts\n            {\n                label: 'Who do you want to bill?',\n                name: 'contactName',\n                type: 'asyncOptions',\n                loadMethod: 'getContacts',\n                show: {\n                    'actions.operation': ['createInvoice']\n                }\n            },\n            {\n                label: 'Type of invoice',\n                name: 'invoiceType',\n                type: 'options',\n                description: 'Which type of invoice? (ACCPAY or ACCREC)',\n                options: [\n                    { label: 'ACCPAY - Accounts Payable', name: 'ACCPAY' },\n                    { label: 'ACCREC - Accounts Receivable', name: 'ACCREC' }\n                ],\n                show: {\n                    'actions.operation': ['createInvoice']\n                }\n            },\n            {\n                label: 'Description',\n                name: 'lineItem',\n                type: 'string',\n                description: 'Service or product to be billed.',\n                placeholder: 'What service / product is to be billed?',\n                show: {\n                    'actions.operation': ['createInvoice']\n                }\n            },\n            {\n                label: 'Cost',\n                name: 'unitAmount',\n                type: 'number',\n                description: 'Cost of product / service',\n                show: {\n                    'actions.operation': ['createInvoice']\n                }\n            },\n            {\n                label: 'Quantity',\n                name: 'unitQuantity',\n                type: 'number',\n                description: 'Quantity of product / service',\n                show: {\n                    'actions.operation': ['createInvoice']\n                }\n            }\n        ] as INodeParams[]\n    }\n\n    loadMethods = {\n        async getTenants(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const credentials = nodeData.credentials\n\n            if (credentials === undefined) {\n                return returnData\n            }\n\n            // Get credentials\n            const token_type = credentials!.token_type as string\n            const access_token = credentials!.access_token as string\n            const headers: AxiosRequestHeaders = {\n                'Content-Type': 'application/json',\n                Authorization: `${token_type} ${access_token}`\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `https://api.xero.com/connections`,\n                headers\n            }\n            let maxRetries = 5\n            do {\n                try {\n                    const response = await axios(axiosConfig)\n                    const responseData = response.data\n                    for (const tenant of responseData || []) {\n                        returnData.push({\n                            label: tenant.tenantName as string,\n                            name: tenant.tenantId as string\n                        })\n                    }\n                    return returnData\n                } catch (e) {\n                    if (e.response && e.response.status === 401) {\n                        const { access_token } = await refreshOAuth2Token(credentials)\n                        headers['Authorization'] = `${token_type} ${access_token}`\n                        continue\n                    }\n                    return returnData\n                }\n            } while (--maxRetries)\n            return returnData\n        },\n\n        async getInvoices(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const credentials = nodeData.credentials\n            const inputParameters = nodeData.inputParameters\n\n            if (credentials === undefined || inputParameters === undefined) {\n                return returnData\n            }\n\n            // Get credentials\n            const tenantId = inputParameters.tenant as string\n            const token_type = credentials!.token_type as string\n            const access_token = credentials!.access_token as string\n\n            const headers: AxiosRequestHeaders = {\n                Accept: 'application/json',\n                Authorization: `${token_type} ${access_token}`,\n                'Xero-tenant-id': tenantId\n            }\n\n            if (tenantId === undefined || token_type === undefined || access_token === undefined) {\n                return returnData\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `https://api.xero.com/api.xro/2.0/Invoices`,\n                headers\n            }\n            let maxRetries = 5\n            do {\n                try {\n                    const response = await axios(axiosConfig)\n\n                    const responseData = response.data\n\n                    for (const invoice of responseData.Invoices || []) {\n                        returnData.push({\n                            label: `${invoice.Contact.Name as string} - ${invoice.InvoiceNumber as string} `,\n                            name: invoice.InvoiceID as string\n                        })\n                    }\n\n                    return returnData\n                } catch (e) {\n                    if (e.response && e.response.status === 401) {\n                        const { access_token } = await refreshOAuth2Token(credentials)\n                        headers['Authorization'] = `${token_type} ${access_token}`\n                        continue\n                    }\n                    return returnData\n                }\n            } while (--maxRetries)\n            return returnData\n        },\n\n        async getContacts(nodeData: INodeData): Promise<INodeOptionsValue[]> {\n            const returnData: INodeOptionsValue[] = []\n\n            const credentials = nodeData.credentials\n            const inputParameters = nodeData.inputParameters\n\n            if (credentials === undefined || inputParameters === undefined) {\n                return returnData\n            }\n\n            const tenantId = inputParameters.tenant as string\n            const token_type = credentials!.token_type as string\n            const access_token = credentials!.access_token as string\n\n            const headers: AxiosRequestHeaders = {\n                Accept: 'application/json',\n                Authorization: `${token_type} ${access_token}`,\n                'Xero-tenant-id': tenantId\n            }\n\n            if (tenantId === undefined || token_type === undefined || access_token === undefined) {\n                return returnData\n            }\n\n            const axiosConfig: AxiosRequestConfig = {\n                method: 'GET',\n                url: `https://api.xero.com/api.xro/2.0/contacts`,\n                headers\n            }\n\n            let maxRetries = 5\n            do {\n                try {\n                    const response = await axios(axiosConfig)\n                    const responseData = response.data\n\n                    for (const contact of responseData.Contacts || []) {\n                        returnData.push({\n                            label: `${contact.Name as string}`,\n                            name: `${contact.ContactID as string}`\n                        })\n                    }\n                    return returnData\n                } catch (e) {\n                    if (e.response && e.response.status === 401) {\n                        const { access_token } = await refreshOAuth2Token(credentials)\n                        headers['Authorization'] = `${token_type} ${access_token}`\n                        continue\n                    }\n                    return returnData\n                }\n            } while (--maxRetries)\n            return returnData\n        }\n    }\n\n    async run(nodeData: INodeData): Promise<INodeExecutionData[] | null> {\n        // function to start running node\n        const actionsData = nodeData.actions\n        const credentials = nodeData.credentials\n        const inputParametersData = nodeData.inputParameters\n\n        if (actionsData === undefined) {\n            throw new Error('Required data missing!')\n        }\n        if (credentials === undefined) {\n            throw new Error('Missing credentials!')\n        }\n\n        // Get Operation\n        const operation = actionsData.operation as string\n\n        // Get credentials https://developer.xero.com/documentation/guides/oauth2/auth-flow/#4-receive-your-tokens\n        const token_type = credentials!.token_type as string\n        const access_token = credentials!.access_token as string\n\n        const returnData: ICommonObject[] = []\n\n        let responseData: any\n\n        let url = ''\n        const queryParameters: ICommonObject = {}\n\n        const invoiceId = inputParametersData?.invoice as string\n        const tenantId = inputParametersData?.tenant as string\n\n        const invoiceType = inputParametersData?.invoiceType as string\n        const contactId = inputParametersData?.contactName as string\n        const lineItem = inputParametersData?.lineItem as string\n        const unitAmount = inputParametersData?.unitAmount as string\n        const unitQuantity = inputParametersData?.unitQuantity as string\n        const lineItems = {\n            Description: lineItem,\n            UnitAmount: unitAmount,\n            Quantity: unitQuantity\n        }\n        const Contact = {\n            ContactID: contactId\n        }\n\n        let queryBody: any = {}\n        let method: Method = 'POST'\n\n        const headers: AxiosRequestHeaders = {\n            Accept: 'application/json',\n            authorization: `${token_type} ${access_token}`,\n            'Xero-tenant-id': tenantId\n        }\n\n        let maxRetries = 5\n        let oAuth2RefreshedData: any = {}\n\n        do {\n            try {\n                if (operation === 'getAllInvoices') {\n                    method = 'GET'\n                    url = `https://api.xero.com/api.xro/2.0/Invoices`\n                } else if (operation === 'getSingleInvoice') {\n                    method = 'GET'\n                    url = `https://api.xero.com/api.xro/2.0/Invoices/${invoiceId}`\n                } else if (operation === 'sendToEmail') {\n                    // Will fail in the 'Test Node' because the test creates a req.body\n                    // The Endpoint requires and empty body to return successfully.\n                    method = 'POST'\n                    url = `https://api.xero.com/api.xro/2.0/Invoices/${invoiceId}/Email`\n                } else if (operation === 'createInvoice') {\n                    method = 'POST'\n                    url = `https://api.xero.com/api.xro/2.0/Invoices`\n                    queryBody = {\n                        Type: invoiceType,\n                        LineItems: [lineItems],\n                        Contact: Contact\n                    }\n                }\n\n                const axiosConfig: AxiosRequestConfig = {\n                    method,\n                    url,\n                    headers\n                }\n\n                if (Object.keys(queryParameters).length > 0) {\n                    axiosConfig.params = queryParameters\n                    axiosConfig.paramsSerializer = (params) => serializeQueryParams(params)\n                }\n\n                if (Object.keys(queryBody).length > 0) {\n                    axiosConfig.data = queryBody\n                }\n\n                const response = await axios(axiosConfig)\n                responseData = response.data\n                break\n            } catch (error) {\n                if (error.response && error.response.status === 401) {\n                    const { access_token, expires_in } = await refreshOAuth2Token(credentials)\n                    headers['Authorization'] = `${token_type} ${access_token}`\n                    oAuth2RefreshedData = { access_token, expires_in }\n                    continue\n                }\n                throw handleErrorMessage(error)\n            }\n        } while (--maxRetries)\n\n        if (maxRetries <= 0) {\n            throw new Error('Error executing Xero node. Max retries limit was reached.')\n        }\n\n        if (Array.isArray(responseData)) {\n            returnData.push(...responseData)\n        } else {\n            returnData.push(responseData)\n        }\n\n        return returnNodeExecutionData(returnData, oAuth2RefreshedData)\n    }\n}\n\nmodule.exports = { nodeClass: Xero }\n"
  },
  {
    "path": "packages/components/package.json",
    "content": "{\n    \"name\": \"outerbridge-components\",\n    \"version\": \"1.0.12\",\n    \"description\": \"Outerbridge Components\",\n    \"main\": \"dist/src/index\",\n    \"types\": \"dist/src/index.d.ts\",\n    \"scripts\": {\n        \"build\": \"tsc && gulp\",\n        \"dev\": \"tsc --watch\"\n    },\n    \"keywords\": [],\n    \"homepage\": \"https://outerbridge.io\",\n    \"author\": {\n        \"name\": \"Henry Heng\",\n        \"email\": \"henryheng@outerbridge.io\"\n    },\n    \"license\": \"SEE LICENSE IN LICENSE.md\",\n    \"dependencies\": {\n        \"@openzeppelin/contracts\": \"^4.7.3\",\n        \"@thirdweb-dev/sdk\": \"^3.6.3\",\n        \"@uniswap/v2-periphery\": \"^1.1.0-beta.0\",\n        \"axios\": \"^0.27.2\",\n        \"client-oauth2\": \"^4.3.3\",\n        \"cron\": \"^1.8.3\",\n        \"dotenv\": \"^16.0.0\",\n        \"ethers\": \"^5.6.8\",\n        \"express\": \"^4.17.3\",\n        \"form-data\": \"^4.0.0\",\n        \"imap\": \"^0.8.19\",\n        \"jimp\": \"^0.16.2\",\n        \"mailparser\": \"^3.5.0\",\n        \"moment\": \"^2.29.3\",\n        \"nodemailer\": \"^6.7.5\",\n        \"simple-uniswap-sdk\": \"^3.7.0\",\n        \"solc\": \"^0.8.15\",\n        \"vm2\": \"^3.9.9\",\n        \"ws\": \"^8.9.0\"\n    },\n    \"devDependencies\": {\n        \"@types/cron\": \"^1.7.3\",\n        \"@types/gulp\": \"4.0.9\",\n        \"@types/imap\": \"^0.8.35\",\n        \"@types/mailparser\": \"^3.4.0\",\n        \"@types/nodemailer\": \"^6.4.4\",\n        \"@types/ws\": \"^8.5.3\",\n        \"gulp\": \"^4.0.2\",\n        \"typescript\": \"^4.8.4\"\n    }\n}\n"
  },
  {
    "path": "packages/components/src/ChainNetwork.ts",
    "content": "import { ICommonObject, INetworkMapping, INodeOptionsValue } from '.'\nimport { ethers } from 'ethers'\n\n/**\n * ENUMS\n */\nexport enum NETWORK {\n    MAINNET = 'homestead',\n    GÖRLI = 'goerli',\n    MATIC_MUMBAI = 'maticmum',\n    MATIC = 'matic',\n    OPTIMISM = 'optimism',\n    OPTIMISM_GOERLI = 'optimism-goerli',\n    ARBITRUM = 'arbitrum',\n    ARBITRUM_GOERLI = 'arbitrum-goerli',\n    ARBITRUM_NOVA = 'arbitrum-nova',\n    BSC = 'bsc',\n    BSC_TESTNET = 'bsc-testnet',\n    AVALANCHE = 'avalanche',\n    AVALANCHE_TESTNET = 'avalanche-testnet',\n    FANTOM = 'fantom',\n    FANTOM_TESTNET = 'fantom-testnet',\n    CRONOS = 'cronos',\n    CRONOS_TESTNET = 'cronos-testnet',\n    GNOSIS = 'gnosis',\n    CELO = 'celo',\n    SOLANA = 'solana',\n    SOLANA_TESTNET = 'solana-testnet',\n    SOLANA_DEVNET = 'solana-devnet',\n    HECO = 'heco',\n    HARMONY = 'harmony',\n    MOONRIVER = 'moonriver',\n    MOONBEAM = 'moonbeam',\n    METIS = 'metis',\n    KLATYN_TESTNET = 'klaytn-testnet',\n    FLOW = 'mainnet',\n    FLOW_TESTNET = 'testnet'\n}\n\nexport enum NETWORK_LABEL {\n    MAINNET = 'Mainnet',\n    GÖRLI = 'Goerli',\n    MATIC_MUMBAI = 'Polygon Mumbai',\n    MATIC = 'Polygon Mainnet',\n    OPTIMISM = 'Optimism Mainnet',\n    OPTIMISM_GOERLI = 'Optimism Goerli',\n    ARBITRUM = 'Arbitrum Mainnet',\n    ARBITRUM_GOERLI = 'Arbitrum Goerli',\n    ARBITRUM_NOVA = 'Arbitrum Nova',\n    BSC = 'Binance Smart Chain Mainnet',\n    BSC_TESTNET = 'Binance Smart Chain Testnet',\n    AVALANCHE = 'Avalanche Mainnet',\n    AVALANCHE_TESTNET = 'Avalanche Testnet',\n    FANTOM = 'Fantom Mainnet',\n    FANTOM_TESTNET = 'Fantom Testnet',\n    CRONOS = 'Cronos Mainnet',\n    CRONOS_TESTNET = 'Cronos Testnet',\n    GNOSIS = 'Gnosis Mainnet',\n    CELO = 'Celo Mainnet',\n    SOLANA = 'Solana Mainnet',\n    SOLANA_TESTNET = 'Solana Testnet',\n    SOLANA_DEVNET = 'Solana Devnet',\n    HECO = 'Huobi ECO Chain Mainnet',\n    HARMONY = 'Harmony Mainnet',\n    MOONRIVER = 'Moonriver Mainnet',\n    MOONBEAM = 'Moonbeam Mainnet',\n    METIS = 'Metis Mainnet',\n    KLATYN_TESTNET = 'Klaytn Baobab Testnet',\n    FLOW = 'Flow',\n    FLOW_TESTNET = 'Flow Testnet'\n}\n\nexport enum NETWORK_PROVIDER {\n    INFURA = 'infura',\n    ALCHEMY = 'alchemy',\n    QUICKNODE = 'quicknode',\n    CLOUDFARE = 'cloudfare',\n    CUSTOMRPC = 'customRPC',\n    CUSTOMWSS = 'customWebsocket',\n    BINANCE = 'binance',\n    POLYGON = 'polygon',\n    AVAX = 'avalanche',\n    GNOSIS = 'gnosis',\n    HECO = 'heco',\n    FANTOM = 'fantom',\n    SOLANA = 'solana',\n    HARMONY = 'harmony',\n    MOONRIVER = 'moonriver',\n    MOONBEAM = 'moonbeam',\n    METIS = 'metis',\n    KLAYTN = 'klaytn',\n    FLOW = 'flow',\n    FLOW_TESTNET = 'flow_testnet'\n}\n\nexport enum CHAIN_ID {\n    MAINNET = 1,\n    GÖRLI = 5,\n    BINANCE_MAINNET = 56,\n    BINANCE_TESTNET = 97,\n    MATIC = 137,\n    MATIC_MUMBAI = 80001,\n    ARB_MAINNET = 42161,\n    ARB_TESTNET_GOERLI = 421613,\n    ARB_NOVA = 42170,\n    OPT_MAINNET = 10,\n    OPT_TESTNET_GOERLI = 420,\n    CRONOS_MAINNET = 25,\n    CRONOS_TESTNET = 338,\n    AVALANCHE_MAINNET = 43114,\n    AVALANCHE_TESTNET = 43113,\n    FANTOM_MAINNET = 250,\n    FANTOM_TESTNET = 4002,\n    GNOSIS = 100,\n    CELO = 42220,\n    HECO = 128,\n    HARMONY = 1666600000,\n    MOONRIVER = 1285,\n    MOONBEAM = 1284,\n    METIS = 1088\n}\n\nexport enum DOMAIN_ID {\n    MAINNET = 6648936,\n    GÖRLI = 3331,\n    MATIC_MUMBAI = 9991\n}\n\n/**\n * Networks\n */\nexport const ETHNetworks = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: NETWORK.MAINNET,\n        parentGroup: 'Ethereum'\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: NETWORK.GÖRLI,\n        parentGroup: 'Ethereum'\n    }\n] as INodeOptionsValue[]\n\nexport const BSCNetworks = [\n    {\n        label: NETWORK_LABEL.BSC,\n        name: NETWORK.BSC,\n        parentGroup: 'Binance Smart Chain'\n    },\n    {\n        label: NETWORK_LABEL.BSC_TESTNET,\n        name: NETWORK.BSC_TESTNET,\n        parentGroup: 'Binance Smart Chain'\n    }\n] as INodeOptionsValue[]\n\nexport const PolygonNetworks = [\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: NETWORK.MATIC,\n        parentGroup: 'Polygon'\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: NETWORK.MATIC_MUMBAI,\n        parentGroup: 'Polygon'\n    }\n] as INodeOptionsValue[]\n\nexport const ArbitrumNetworks = [\n    {\n        label: NETWORK_LABEL.ARBITRUM,\n        name: NETWORK.ARBITRUM,\n        parentGroup: 'Arbitrum'\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n        name: NETWORK.ARBITRUM_GOERLI,\n        parentGroup: 'Arbitrum'\n    }\n] as INodeOptionsValue[]\n\nexport const OptimismNetworks = [\n    {\n        label: NETWORK_LABEL.OPTIMISM,\n        name: NETWORK.OPTIMISM,\n        parentGroup: 'Optimism'\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM_GOERLI,\n        name: NETWORK.OPTIMISM_GOERLI,\n        parentGroup: 'Optimism'\n    }\n] as INodeOptionsValue[]\n\nexport const AvalancheNetworks = [\n    {\n        label: NETWORK_LABEL.AVALANCHE,\n        name: NETWORK.AVALANCHE,\n        parentGroup: 'Avalanche'\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE_TESTNET,\n        name: NETWORK.AVALANCHE_TESTNET,\n        parentGroup: 'Avalanche'\n    }\n] as INodeOptionsValue[]\n\nexport const SolanaNetworks = [\n    {\n        label: NETWORK_LABEL.SOLANA,\n        name: NETWORK.SOLANA,\n        parentGroup: 'Solana'\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_DEVNET,\n        name: NETWORK.SOLANA_DEVNET,\n        parentGroup: 'Solana'\n    },\n    {\n        label: NETWORK_LABEL.SOLANA_TESTNET,\n        name: NETWORK.SOLANA_TESTNET,\n        parentGroup: 'Solana'\n    }\n] as INodeOptionsValue[]\n\nexport const FantomNetworks = [\n    {\n        label: NETWORK_LABEL.FANTOM,\n        name: NETWORK.FANTOM,\n        parentGroup: 'Fantom'\n    },\n    {\n        label: NETWORK_LABEL.FANTOM_TESTNET,\n        name: NETWORK.FANTOM_TESTNET,\n        parentGroup: 'Fantom'\n    }\n] as INodeOptionsValue[]\n\nexport const GnosisNetworks = [\n    {\n        label: NETWORK_LABEL.GNOSIS,\n        name: NETWORK.GNOSIS,\n        parentGroup: 'Gnosis'\n    }\n] as INodeOptionsValue[]\n\nexport const HecoNetworks = [\n    {\n        label: NETWORK_LABEL.HECO,\n        name: NETWORK.HECO,\n        parentGroup: 'Heco'\n    }\n] as INodeOptionsValue[]\n\nexport const HarmonyNetworks = [\n    {\n        label: NETWORK_LABEL.HARMONY,\n        name: NETWORK.HARMONY,\n        parentGroup: 'Harmony'\n    }\n] as INodeOptionsValue[]\n\nexport const MoonRiverNetworks = [\n    {\n        label: NETWORK_LABEL.MOONRIVER,\n        name: NETWORK.MOONRIVER,\n        parentGroup: 'MoonRiver'\n    }\n] as INodeOptionsValue[]\n\nexport const MoonBeamNetworks = [\n    {\n        label: NETWORK_LABEL.MOONBEAM,\n        name: NETWORK.MOONBEAM,\n        parentGroup: 'MoonBeam'\n    }\n] as INodeOptionsValue[]\n\nexport const MetisNetworks = [\n    {\n        label: NETWORK_LABEL.METIS,\n        name: NETWORK.METIS,\n        parentGroup: 'Metis'\n    }\n] as INodeOptionsValue[]\n\nexport const KlatynNetworks = [\n    {\n        label: NETWORK_LABEL.KLATYN_TESTNET,\n        name: NETWORK.KLATYN_TESTNET,\n        parentGroup: 'Klatyn'\n    }\n] as INodeOptionsValue[]\n\nexport const FLOWNetworks = [\n    {\n        label: NETWORK_LABEL.FLOW,\n        name: NETWORK.FLOW,\n        parentGroup: 'Flow Mainnet'\n    },\n    {\n        label: NETWORK_LABEL.FLOW_TESTNET,\n        name: NETWORK.FLOW_TESTNET,\n        parentGroup: 'Flow Testnet'\n    }\n] as INodeOptionsValue[]\n\n/**\n * Network Providers\n */\nexport const customNetworkProviders = [\n    {\n        label: 'Custom RPC Endpoint',\n        name: NETWORK_PROVIDER.CUSTOMRPC,\n        description: 'HTTP endpoint',\n        parentGroup: 'Custom Nodes'\n    },\n    {\n        label: 'Custom Websocket Endpoint',\n        name: NETWORK_PROVIDER.CUSTOMWSS,\n        description: 'WSS Endpoint',\n        parentGroup: 'Custom Nodes'\n    }\n] as INodeOptionsValue[]\n\nexport const infuraNetworkProviders = [\n    {\n        label: 'Infura',\n        name: NETWORK_PROVIDER.INFURA,\n        description: 'Infura RPC/Websocket',\n        parentGroup: 'Private Nodes'\n    }\n] as INodeOptionsValue[]\n\nexport const alchemyNetworkProviders = [\n    {\n        label: 'Alchemy',\n        name: NETWORK_PROVIDER.ALCHEMY,\n        description: 'Alchemy RPC/Websocket',\n        parentGroup: 'Private Nodes'\n    }\n] as INodeOptionsValue[]\n\nexport const quickNodeNetworkProviders = [\n    {\n        label: 'QuickNode',\n        name: NETWORK_PROVIDER.QUICKNODE,\n        description: 'QuickNode HTTP and WSS Endpoints',\n        parentGroup: 'Private Nodes'\n    }\n] as INodeOptionsValue[]\n\nexport const ethTestNetworkProviders = [\n    ...alchemyNetworkProviders,\n    ...infuraNetworkProviders,\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const ethNetworkProviders = [\n    {\n        label: 'Cloudfare',\n        name: NETWORK_PROVIDER.CLOUDFARE,\n        description: 'Public Cloudfare RPC',\n        parentGroup: 'Public Nodes'\n    },\n    ...alchemyNetworkProviders,\n    ...infuraNetworkProviders,\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const polygonNetworkProviders = [\n    {\n        label: 'Polygon',\n        name: NETWORK_PROVIDER.POLYGON,\n        description: 'Public Polygon RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...alchemyNetworkProviders,\n    ...infuraNetworkProviders,\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const binanceNetworkProviders = [\n    {\n        label: 'Binance',\n        name: NETWORK_PROVIDER.BINANCE,\n        description: 'Public Binance RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const avalancheNetworkProviders = [\n    {\n        label: 'Avalanche',\n        name: NETWORK_PROVIDER.AVAX,\n        description: 'Public Avalanche RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...infuraNetworkProviders,\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const fantomNetworkProviders = [\n    {\n        label: 'Fantom',\n        name: NETWORK_PROVIDER.FANTOM,\n        description: 'Public Fantom RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const gnosisNetworkProviders = [\n    {\n        label: 'Gnosis',\n        name: NETWORK_PROVIDER.GNOSIS,\n        description: 'Public Gnosis RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const hecoNetworkProviders = [\n    {\n        label: 'Huobi ECO Chain',\n        name: NETWORK_PROVIDER.HECO,\n        description: 'Public HECO RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const solanaNetworkProviders = [...quickNodeNetworkProviders, ...customNetworkProviders] as INodeOptionsValue[]\n\nexport const harmonyNetworkProviders = [\n    {\n        label: 'Harmony',\n        name: NETWORK_PROVIDER.HARMONY,\n        description: 'Public Harmony RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...quickNodeNetworkProviders,\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const moonriverNetworkProviders = [\n    {\n        label: 'Moonriver',\n        name: NETWORK_PROVIDER.MOONRIVER,\n        description: 'Public Moonriver RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const moonbeamNetworkProviders = [\n    {\n        label: 'Moonbeam',\n        name: NETWORK_PROVIDER.MOONBEAM,\n        description: 'Public Moonbeam RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const metisNetworkProviders = [\n    {\n        label: 'Metis',\n        name: NETWORK_PROVIDER.METIS,\n        description: 'Public Metis RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const klaytnNetworkProviders = [\n    {\n        label: 'Klaytn',\n        name: NETWORK_PROVIDER.KLAYTN,\n        description: 'Public Klaytn RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const flowNetworkProviders = [\n    {\n        label: 'Flow',\n        name: NETWORK_PROVIDER.FLOW,\n        description: 'Public FLOW RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport const flowTestnetNetworkProviders = [\n    {\n        label: 'Flow Testnet',\n        name: NETWORK_PROVIDER.FLOW_TESTNET,\n        description: 'Public FLOW testnet RPC/Websocket',\n        parentGroup: 'Public Nodes'\n    },\n    ...customNetworkProviders\n] as INodeOptionsValue[]\n\nexport function getCustomRPCProvider(jsonRPC: string) {\n    return new ethers.providers.JsonRpcProvider(jsonRPC)\n}\n\nexport function getCustomWebsocketProvider(websocketRPC: string) {\n    return new ethers.providers.WebSocketProvider(websocketRPC)\n}\n\nexport async function getBscMainnetProvider() {\n    return await getFallbackProvider(binanceMainnetRPC, 'binance', CHAIN_ID.BINANCE_MAINNET)\n}\n\nexport async function getBscTestnetProvider() {\n    return await new ethers.providers.JsonRpcProvider(binanceTestnetRPC[0])\n}\n\nexport async function getPolygonMainnetProvider() {\n    return await getFallbackProvider(polygonMainnetRPC, 'polygon', CHAIN_ID.MATIC)\n}\n\nexport async function getPolygonTestnetProvider() {\n    return await getFallbackProvider(polygonMumbaiRPC, 'polygon', CHAIN_ID.MATIC_MUMBAI)\n}\n\nexport async function getAvalancheTestnetProvider() {\n    return await getFallbackProvider(avalancheTestnetRPC, 'avalanche', CHAIN_ID.AVALANCHE_TESTNET)\n}\n\nexport async function getAvalancheMainnetProvider() {\n    return await getFallbackProvider(avalancheMainnetRPC, 'avalanche', CHAIN_ID.AVALANCHE_MAINNET)\n}\n\nexport async function getGnosisMainnetProvider() {\n    return new ethers.providers.JsonRpcProvider(gnosisMainnetRPC[0])\n}\n\nexport async function getHecoMainnetProvider() {\n    return new ethers.providers.JsonRpcProvider(hecoMainnetRPC[0])\n}\n\nexport async function getFantomMainnetProvider() {\n    return new ethers.providers.JsonRpcProvider(fantomMainnetRPC[0])\n}\n\nexport async function getFantomTestnetProvider() {\n    return new ethers.providers.JsonRpcProvider(fantomTestnetRPC[0])\n}\n\nexport async function getHarmonyMainnetProvider() {\n    return await getFallbackProvider(harmoneyMainnetRPC, 'harmony', CHAIN_ID.HARMONY)\n}\n\nexport async function getMoonriverMainnetProvider() {\n    return new ethers.providers.JsonRpcProvider(moonriverMainnetRPC[0])\n}\n\nexport async function getMoonbeamMainnetProvider() {\n    return new ethers.providers.JsonRpcProvider(moonbeamMainnetRPC[0])\n}\n\nexport async function getMetisMainnetProvider() {\n    return new ethers.providers.JsonRpcProvider(metisMainnetRPC[0])\n}\n\nexport async function getKlaytnTestnetProvider() {\n    return new ethers.providers.JsonRpcProvider(klaytnTestnetRPC[0])\n}\n\nexport async function getFallbackProvider(rpcs: string[], network: string, chainId: CHAIN_ID) {\n    const prvs = []\n    for (let i = 0; i < rpcs.length; i++) {\n        const node = rpcs[i]\n        const prv = new ethers.providers.StaticJsonRpcProvider(\n            { url: node, timeout: 1000 },\n            {\n                name: network,\n                chainId\n            }\n        )\n        await prv.ready\n        prvs.push({\n            provider: prv,\n            stallTimeout: 1000\n        })\n    }\n    return new ethers.providers.FallbackProvider(prvs)\n}\n\nexport async function getNetworkProvider(\n    networkProvider: NETWORK_PROVIDER,\n    network: NETWORK,\n    credentials: ICommonObject | undefined,\n    jsonRPC?: string,\n    websocketRPC?: string,\n    isWebSocket?: boolean\n): Promise<ethers.providers.JsonRpcProvider | ethers.providers.FallbackProvider | ethers.providers.WebSocketProvider | null> {\n    if (\n        credentials === undefined &&\n        (networkProvider === NETWORK_PROVIDER.INFURA ||\n            networkProvider === NETWORK_PROVIDER.ALCHEMY ||\n            networkProvider === NETWORK_PROVIDER.QUICKNODE)\n    ) {\n        throw new Error('Missing credentials')\n    }\n\n    switch (networkProvider) {\n        case NETWORK_PROVIDER.ALCHEMY:\n            return isWebSocket\n                ? new ethers.providers.WebSocketProvider(`${alchemyWSSAPIs[network]}${credentials!.apiKey}`)\n                : new ethers.providers.AlchemyProvider(network, credentials!.apiKey)\n        case NETWORK_PROVIDER.INFURA:\n            return isWebSocket\n                ? new ethers.providers.WebSocketProvider(`${infuraWSSAPIs[network]}${credentials!.apiKey}`)\n                : new ethers.providers.InfuraProvider(network, {\n                      apiKey: credentials!.apiKey,\n                      secretKey: credentials!.secretKey\n                  })\n        case NETWORK_PROVIDER.QUICKNODE:\n            return isWebSocket\n                ? new ethers.providers.WebSocketProvider(credentials!.wssProvider as string)\n                : new ethers.providers.JsonRpcProvider(credentials!.httpProvider as string)\n        case NETWORK_PROVIDER.CLOUDFARE:\n            return new ethers.providers.CloudflareProvider()\n        case NETWORK_PROVIDER.BINANCE:\n            if (network === NETWORK.BSC) return await getBscMainnetProvider()\n            else if (network === NETWORK.BSC_TESTNET) return await getBscTestnetProvider()\n            else return null\n        case NETWORK_PROVIDER.POLYGON:\n            if (network === NETWORK.MATIC) return await getPolygonMainnetProvider()\n            else if (network === NETWORK.MATIC_MUMBAI) return await getPolygonTestnetProvider()\n            else return null\n        case NETWORK_PROVIDER.AVAX:\n            if (network === NETWORK.AVALANCHE) return await getAvalancheMainnetProvider()\n            else if (network === NETWORK.AVALANCHE_TESTNET) return await getAvalancheTestnetProvider()\n            else return null\n        case NETWORK_PROVIDER.FANTOM:\n            if (network === NETWORK.FANTOM) return await getFantomMainnetProvider()\n            else if (network === NETWORK.FANTOM_TESTNET) return await getFantomTestnetProvider()\n            else return null\n        case NETWORK_PROVIDER.GNOSIS:\n            return await getGnosisMainnetProvider()\n        case NETWORK_PROVIDER.HECO:\n            return await getHecoMainnetProvider()\n        case NETWORK_PROVIDER.HARMONY:\n            return await getHarmonyMainnetProvider()\n        case NETWORK_PROVIDER.MOONRIVER:\n            return await getMoonriverMainnetProvider()\n        case NETWORK_PROVIDER.MOONBEAM:\n            return await getMoonbeamMainnetProvider()\n        case NETWORK_PROVIDER.METIS:\n            return await getMetisMainnetProvider()\n        case NETWORK_PROVIDER.KLAYTN:\n            return await getKlaytnTestnetProvider()\n        case NETWORK_PROVIDER.CUSTOMRPC:\n            return jsonRPC ? getCustomRPCProvider(jsonRPC) : null\n        case NETWORK_PROVIDER.CUSTOMWSS:\n            return websocketRPC ? getCustomWebsocketProvider(websocketRPC) : null\n        default:\n            return null\n    }\n}\n\nexport function getNetworkProvidersList(network: NETWORK): INodeOptionsValue[] {\n    switch (network) {\n        case NETWORK.MAINNET:\n            return ethNetworkProviders\n        case NETWORK.GÖRLI:\n            return ethTestNetworkProviders\n        case NETWORK.MATIC:\n        case NETWORK.MATIC_MUMBAI:\n            return polygonNetworkProviders\n        case NETWORK.OPTIMISM:\n        case NETWORK.OPTIMISM_GOERLI:\n            return ethTestNetworkProviders\n        case NETWORK.ARBITRUM:\n        case NETWORK.ARBITRUM_GOERLI:\n            return ethTestNetworkProviders\n        case NETWORK.AVALANCHE:\n        case NETWORK.AVALANCHE_TESTNET:\n            return avalancheNetworkProviders\n        case NETWORK.FANTOM:\n        case NETWORK.FANTOM_TESTNET:\n            return fantomNetworkProviders\n        case NETWORK.SOLANA:\n        case NETWORK.SOLANA_DEVNET:\n        case NETWORK.SOLANA_TESTNET:\n            return solanaNetworkProviders\n        case NETWORK.BSC:\n        case NETWORK.BSC_TESTNET:\n            return binanceNetworkProviders\n        case NETWORK.GNOSIS:\n            return gnosisNetworkProviders\n        case NETWORK.HECO:\n            return hecoNetworkProviders\n        case NETWORK.HARMONY:\n            return harmonyNetworkProviders\n        case NETWORK.MOONRIVER:\n            return moonriverNetworkProviders\n        case NETWORK.MOONBEAM:\n            return moonbeamNetworkProviders\n        case NETWORK.METIS:\n            return metisNetworkProviders\n        case NETWORK.KLATYN_TESTNET:\n            return klaytnNetworkProviders\n        case NETWORK.FLOW:\n            return flowNetworkProviders\n        case NETWORK.FLOW_TESTNET:\n            return flowTestnetNetworkProviders\n        default:\n            return customNetworkProviders\n    }\n}\n\nexport const networkProviderCredentials = [\n    {\n        label: 'Credential Method',\n        name: 'credentialMethod',\n        type: 'options',\n        options: [\n            {\n                label: 'Alchemy API Key',\n                name: 'alchemyApi',\n                show: {\n                    'networks.networkProvider': [NETWORK_PROVIDER.ALCHEMY]\n                }\n            },\n            {\n                label: 'Infura API Key',\n                name: 'infuraApi',\n                show: {\n                    'networks.networkProvider': [NETWORK_PROVIDER.INFURA]\n                }\n            },\n            {\n                label: 'QuickNode Endpoints',\n                name: 'quickNodeEndpoints',\n                show: {\n                    'networks.networkProvider': [NETWORK_PROVIDER.QUICKNODE]\n                }\n            }\n        ],\n        show: {\n            'networks.networkProvider': [NETWORK_PROVIDER.ALCHEMY, NETWORK_PROVIDER.INFURA, NETWORK_PROVIDER.QUICKNODE]\n        }\n    }\n]\n\n/**\n * URLs\n */\nexport const etherscanAPIs = {\n    [NETWORK.MAINNET]: 'https://api.etherscan.io/api',\n    [NETWORK.GÖRLI]: 'https://api-goerli.etherscan.io/api',\n    [NETWORK.MATIC]: 'https://api.polygonscan.com/api',\n    [NETWORK.MATIC_MUMBAI]: 'https://api-testnet.polygonscan.com/api',\n    [NETWORK.OPTIMISM]: 'https://api-optimistic.etherscan.io/api',\n    [NETWORK.OPTIMISM_GOERLI]: 'https://api-goerli-optimistic.etherscan.io/api',\n    [NETWORK.ARBITRUM]: 'https://api.arbiscan.io/api',\n    [NETWORK.ARBITRUM_GOERLI]: 'https://api-goerli.arbiscan.io/api',\n    [NETWORK.BSC]: 'https://api.bscscan.com/api',\n    [NETWORK.BSC_TESTNET]: 'https://api-testnet.bscscan.com/api',\n    [NETWORK.AVALANCHE]: 'https://api.snowtrace.io/api',\n    [NETWORK.AVALANCHE_TESTNET]: 'https://api-testnet.snowtrace.io/api',\n    [NETWORK.FANTOM]: 'https://api.ftmscan.com/api',\n    [NETWORK.FANTOM_TESTNET]: 'https://api-testnet.ftmscan.com/api',\n    [NETWORK.CRONOS]: 'https://api.cronoscan.com/api',\n    [NETWORK.CRONOS_TESTNET]: 'https://api-testnet.cronoscan.com/api',\n    [NETWORK.GNOSIS]: 'https://api.gnosisscan.io/api',\n    [NETWORK.CELO]: 'https://api.celoscan.io/api',\n    [NETWORK.MOONRIVER]: 'https://api-moonriver.moonscan.io/api',\n    [NETWORK.MOONBEAM]: 'https://api-moonbeam.moonscan.io/api'\n} as INetworkMapping\n\nexport const infuraHTTPAPIs = {\n    [NETWORK.MAINNET]: 'https://mainnet.infura.io/v3/',\n    [NETWORK.GÖRLI]: 'https://goerli.infura.io/v3/',\n    [NETWORK.MATIC]: 'https://polygon-mainnet.infura.io/v3/',\n    [NETWORK.MATIC_MUMBAI]: 'https://polygon-mumbai.infura.io/v3/',\n    [NETWORK.OPTIMISM]: 'https://optimism-mainnet.infura.io/v3/',\n    [NETWORK.OPTIMISM_GOERLI]: 'https://optimism-goerli.infura.io/v3/',\n    [NETWORK.ARBITRUM]: 'https://arbitrum-mainnet.infura.io/v3/',\n    [NETWORK.ARBITRUM_GOERLI]: 'https://arbitrum-goerli.infura.io/v3/'\n} as INetworkMapping\n\nexport const infuraWSSAPIs = {\n    [NETWORK.MAINNET]: 'wss://mainnet.infura.io/ws/v3/',\n    [NETWORK.GÖRLI]: 'wss://goerli.infura.io/ws/v3/',\n    [NETWORK.MATIC]: 'wss://polygon-mainnet.infura.io/ws/v3/',\n    [NETWORK.MATIC_MUMBAI]: 'wss://polygon-mumbai.infura.io/ws/v3/',\n    [NETWORK.OPTIMISM]: 'wss://optimism-mainnet.infura.io/ws/v3/',\n    [NETWORK.OPTIMISM_GOERLI]: 'wss://optimism-goerli.infura.io/ws/v3/',\n    [NETWORK.ARBITRUM]: 'wss://arbitrum-mainnet.infura.io/ws/v3/',\n    [NETWORK.ARBITRUM_GOERLI]: 'wss://arbitrum-goerli.infura.io/ws/v3/'\n} as INetworkMapping\n\nexport const alchemyHTTPAPIs = {\n    [NETWORK.MAINNET]: 'https://eth-mainnet.alchemyapi.io/v2/',\n    [NETWORK.GÖRLI]: 'https://eth-goerli.alchemyapi.io/v2/',\n    [NETWORK.MATIC]: 'https://polygon-mainnet.g.alchemy.com/v2/',\n    [NETWORK.MATIC_MUMBAI]: 'https://polygon-mumbai.g.alchemy.com/v2/',\n    [NETWORK.OPTIMISM]: 'https://opt-mainnet.g.alchemy.com/v2/',\n    [NETWORK.OPTIMISM_GOERLI]: 'https://opt-goerli.g.alchemy.com/v2/',\n    [NETWORK.ARBITRUM]: 'https://arb-mainnet.g.alchemy.com/v2/',\n    [NETWORK.ARBITRUM_GOERLI]: 'https://arb-goerli.g.alchemy.com/v2/',\n    [NETWORK.SOLANA]: 'https://solana-mainnet.g.alchemy.com/v2/',\n    [NETWORK.SOLANA_DEVNET]: 'https://solana-devnet.g.alchemy.com/v2/'\n} as INetworkMapping\n\nexport const alchemyWSSAPIs = {\n    [NETWORK.MAINNET]: 'wss://eth-mainnet.alchemyapi.io/v2/',\n    [NETWORK.GÖRLI]: 'wss://eth-goerli.alchemyapi.io/v2/',\n    [NETWORK.MATIC]: 'wss://polygon-mainnet.g.alchemy.com/v2/',\n    [NETWORK.MATIC_MUMBAI]: 'wss://polygon-mumbai.g.alchemy.com/v2/',\n    [NETWORK.OPTIMISM]: 'wss://opt-mainnet.g.alchemy.com/v2/',\n    [NETWORK.OPTIMISM_GOERLI]: 'wss://opt-goerli.g.alchemy.com/v2/',\n    [NETWORK.ARBITRUM]: 'wss://arb-mainnet.g.alchemy.com/v2/',\n    [NETWORK.ARBITRUM_GOERLI]: 'wss://arb-goerli.g.alchemy.com/v2/',\n    [NETWORK.SOLANA]: 'wss://solana-mainnet.g.alchemy.com/v2/',\n    [NETWORK.SOLANA_DEVNET]: 'wss://solana-devnet.g.alchemy.com/v2/'\n} as INetworkMapping\n\nexport const networkExplorers = {\n    [NETWORK.MAINNET]: 'https://etherscan.io',\n    [NETWORK.GÖRLI]: 'https://goerli.etherscan.io',\n    [NETWORK.MATIC]: 'https://polygonscan.com',\n    [NETWORK.MATIC_MUMBAI]: 'https://mumbai.polygonscan.com',\n    [NETWORK.OPTIMISM]: 'https://optimistic.etherscan.io',\n    [NETWORK.OPTIMISM_GOERLI]: 'https://goerli-optimistic.etherscan.io',\n    [NETWORK.ARBITRUM]: 'https://arbiscan.io',\n    [NETWORK.ARBITRUM_GOERLI]: 'https://goerli-explorer.arbitrum.io',\n    [NETWORK.BSC]: 'https://bscscan.com',\n    [NETWORK.BSC_TESTNET]: 'https://testnet.bscscan.com',\n    [NETWORK.FANTOM]: 'https://ftmscan.com',\n    [NETWORK.FANTOM_TESTNET]: 'https://testnet.ftmscan.com',\n    [NETWORK.CRONOS]: 'https://cronoscan.com',\n    [NETWORK.CRONOS_TESTNET]: 'https://testnet.cronoscan.com',\n    [NETWORK.GNOSIS]: 'https://gnosisscan.io',\n    [NETWORK.CELO]: 'https://celoscan.io',\n    [NETWORK.MOONRIVER]: 'https://moonriver.moonscan.io',\n    [NETWORK.MOONBEAM]: 'https://moonscan.io'\n} as INetworkMapping\n\nexport const openseaExplorers = {\n    [NETWORK.MAINNET]: 'https://opensea.io',\n    [NETWORK.GÖRLI]: 'https://testnets.opensea.io',\n    [NETWORK.MATIC]: 'https://opensea.io/assets/matic',\n    [NETWORK.MATIC_MUMBAI]: 'https://testnets.opensea.io/assets/mumbai'\n} as INetworkMapping\n\nexport const binanceTestnetRPC = [\n    'https://data-seed-prebsc-1-s3.binance.org:8545',\n    'https://data-seed-prebsc-1-s1.binance.org:8545',\n    'https://data-seed-prebsc-2-s2.binance.org:8545'\n] as string[]\n\nexport const binanceMainnetRPC = [\n    'https://bsc-dataseed1.ninicoin.io',\n    'https://bsc-dataseed1.defibit.io',\n    'https://bsc-dataseed.binance.org',\n    'https://bsc.nodereal.io'\n] as string[]\n\nexport const polygonMumbaiRPC = [\n    'https://matic-testnet-archive-rpc.bwarelabs.com',\n    'https://rpc-mumbai.maticvigil.com',\n    'https://matic-mumbai.chainstacklabs.com',\n    'https://rpc-mumbai.matic.today'\n] as string[]\n\nexport const polygonMainnetRPC = ['https://polygon-rpc.com', 'https://rpc-mainnet.matic.quiknode.pro'] as string[]\n\nexport const avalancheMainnetRPC = ['https://api.avax.network/ext/bc/C/rpc', 'https://rpc.ankr.com/avalanche'] as string[]\n\nexport const avalancheTestnetRPC = ['https://api.avax-test.network/ext/bc/C/rpc'] as string[]\n\nexport const gnosisMainnetRPC = ['https://rpc.gnosischain.com'] as string[]\n\nexport const hecoMainnetRPC = ['https://http-mainnet.hecochain.com'] as string[]\n\nexport const fantomMainnetRPC = ['https://rpc.ftm.tools'] as string[]\n\nexport const fantomTestnetRPC = ['https://rpc.testnet.fantom.network'] as string[]\n\nexport const solanaMainnetRPC = ['https://api.mainnet-beta.solana.com'] as string[]\n\nexport const solanaDevnetRPC = ['https://api.devnet.solana.com'] as string[]\n\nexport const solanaTestnetRPC = ['https://api.testnet.solana.com'] as string[]\n\nexport const harmoneyMainnetRPC = ['https://api.harmony.one', 'https://harmony-mainnet.chainstacklabs.com'] as string[]\n\nexport const moonriverMainnetRPC = ['https://rpc.api.moonriver.moonbeam.network'] as string[]\n\nexport const moonbeamMainnetRPC = ['https://rpc.api.moonbeam.network'] as string[]\n\nexport const metisMainnetRPC = ['https://andromeda.metis.io/?owner=1088'] as string[]\n\nexport const klaytnTestnetRPC = ['https://api.baobab.klaytn.net:8651\t'] as string[]\n\nexport const chainIdLookup = {\n    [NETWORK.MAINNET]: CHAIN_ID.MAINNET,\n    [NETWORK.GÖRLI]: CHAIN_ID.GÖRLI,\n    [NETWORK.MATIC]: CHAIN_ID.MATIC,\n    [NETWORK.MATIC_MUMBAI]: CHAIN_ID.MATIC_MUMBAI,\n    [NETWORK.OPTIMISM]: CHAIN_ID.OPT_MAINNET,\n    [NETWORK.OPTIMISM_GOERLI]: CHAIN_ID.OPT_TESTNET_GOERLI,\n    [NETWORK.ARBITRUM]: CHAIN_ID.ARB_MAINNET,\n    [NETWORK.ARBITRUM_GOERLI]: CHAIN_ID.ARB_TESTNET_GOERLI,\n    [NETWORK.ARBITRUM_NOVA]: CHAIN_ID.ARB_NOVA,\n    [NETWORK.BSC]: CHAIN_ID.BINANCE_MAINNET,\n    [NETWORK.BSC_TESTNET]: CHAIN_ID.BINANCE_TESTNET,\n    [NETWORK.CRONOS]: CHAIN_ID.CRONOS_MAINNET,\n    [NETWORK.CRONOS_TESTNET]: CHAIN_ID.CRONOS_TESTNET,\n    [NETWORK.AVALANCHE]: CHAIN_ID.AVALANCHE_MAINNET,\n    [NETWORK.AVALANCHE_TESTNET]: CHAIN_ID.AVALANCHE_TESTNET,\n    [NETWORK.FANTOM]: CHAIN_ID.FANTOM_MAINNET,\n    [NETWORK.FANTOM_TESTNET]: CHAIN_ID.FANTOM_TESTNET,\n    [NETWORK.GNOSIS]: CHAIN_ID.GNOSIS,\n    [NETWORK.CELO]: CHAIN_ID.CELO,\n    [NETWORK.HECO]: CHAIN_ID.HECO,\n    [NETWORK.MOONRIVER]: CHAIN_ID.MOONRIVER,\n    [NETWORK.HECO]: CHAIN_ID.HECO,\n    [NETWORK.HARMONY]: CHAIN_ID.HARMONY,\n    [NETWORK.METIS]: CHAIN_ID.METIS\n} as INetworkMapping\n\nexport const domainIdLookup = {\n    [NETWORK.MAINNET]: DOMAIN_ID.MAINNET,\n    [NETWORK.GÖRLI]: DOMAIN_ID.GÖRLI,\n    [NETWORK.MATIC_MUMBAI]: DOMAIN_ID.MATIC_MUMBAI\n} as INetworkMapping\n\nexport const nativeCurrency = {\n    [NETWORK.MAINNET]: 'ETH',\n    [NETWORK.GÖRLI]: 'ETH',\n    [NETWORK.MATIC]: 'MATIC',\n    [NETWORK.MATIC_MUMBAI]: 'MATIC',\n    [NETWORK.OPTIMISM]: 'ETH',\n    [NETWORK.OPTIMISM_GOERLI]: 'ETH',\n    [NETWORK.ARBITRUM]: 'ETH',\n    [NETWORK.ARBITRUM_GOERLI]: 'ETH',\n    [NETWORK.ARBITRUM_NOVA]: 'ETH',\n    [NETWORK.BSC]: 'BNB',\n    [NETWORK.BSC_TESTNET]: 'BNB',\n    [NETWORK.FANTOM]: 'FTM',\n    [NETWORK.FANTOM_TESTNET]: 'FTM',\n    [NETWORK.AVALANCHE]: 'AVAX',\n    [NETWORK.AVALANCHE_TESTNET]: 'AVAX',\n    [NETWORK.GNOSIS]: 'xDAI',\n    [NETWORK.CELO]: 'CELO',\n    [NETWORK.MOONRIVER]: 'MOVR',\n    [NETWORK.MOONBEAM]: 'GLMR'\n} as INetworkMapping\n\nexport const eventTransferAbi = ['event Transfer(address indexed from, address indexed to, uint value)']\n\nexport const functionTransferAbi = ['function transfer(address to, uint256 amount) external returns (boolean)']\n\nexport const erc1155SingleTransferAbi = [\n    'event TransferSingle(address indexed operator, address indexed from, address indexed to, uint id, uint value)'\n]\nexport const erc1155BatchTransferAbi = [\n    'event TransferSingle(address indexed operator, address indexed from, address indexed to, uint[] id, uint[] value)'\n]\n"
  },
  {
    "path": "packages/components/src/ETHOperations.ts",
    "content": "import { ICommonObject } from '.'\nimport { NETWORK, NETWORK_PROVIDER } from './ChainNetwork'\n\nexport interface IETHOperation {\n    name: string\n    value: string\n    parentGroup?: string\n    description?: string\n    body: ICommonObject\n    method: string\n    providerNetworks: {\n        [key: string]: NETWORK[]\n    }\n    overrideUrl?: string\n    inputParameters?: string\n    exampleParameters?: string\n    outputResponse?: string\n    exampleResponse?: ICommonObject\n}\n\nexport const operationCategoryMapping = {\n    retrievingBlocks: 'Retrieving Blocks',\n    evmExecution: 'EVM/Smart Contract Execution',\n    readingTransactions: 'Reading Transactions',\n    accountInformation: 'Account Information',\n    eventLogs: 'Event Logs',\n    chainInformation: 'Chain Information',\n    txReceiptInformation: 'Transaction Receipt Information',\n    retrievingUncles: 'Retrieving Uncles',\n    filters: 'Filters',\n    readWriteTransactions: 'Reading & Writing Transactions',\n    gettingBlocks: 'Getting Blocks',\n    networkInformation: 'Network Information',\n    slotInformation: 'Slot Information',\n    nodeInformation: 'Node Information',\n    tokenInformation: 'Token Information',\n    networkInflation: 'Network Inflation'\n} as any\n\nexport const alchemySupportedNetworks = [\n    NETWORK.MAINNET,\n    NETWORK.GÖRLI,\n    NETWORK.MATIC,\n    NETWORK.MATIC_MUMBAI,\n    NETWORK.OPTIMISM,\n    NETWORK.OPTIMISM_GOERLI,\n    NETWORK.ARBITRUM,\n    NETWORK.ARBITRUM_GOERLI\n]\n\nexport const infuraSupportedNetworks = [...alchemySupportedNetworks, NETWORK.AVALANCHE, NETWORK.AVALANCHE_TESTNET]\n\nexport const qnSupportedNetworks = [\n    ...infuraSupportedNetworks,\n    NETWORK.ARBITRUM_NOVA,\n    NETWORK.GNOSIS,\n    NETWORK.BSC,\n    NETWORK.BSC_TESTNET,\n    NETWORK.FANTOM,\n    NETWORK.CELO\n]\n\nexport const ethOperations = [\n    {\n        name: 'eth_blockNumber',\n        value: 'eth_blockNumber',\n        parentGroup: 'Retrieving Blocks',\n        description: 'Returns the number of the most recent block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_blockNumber',\n            params: [],\n            id: 0\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0xa1c054'\n        }\n    },\n    {\n        name: 'eth_getBlockByHash',\n        value: 'eth_getBlockByHash',\n        parentGroup: 'Retrieving Blocks',\n        description: 'Returns information about a block by hash.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getBlockByHash',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">DATA</code>, 32 Bytes - Hash of a block.</li>\n            <li><code class=\"inline\">Boolean</code> - If true it returns the full transaction objects, if false it returns only the hashes of the transactions.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xc0f4906fea23cf6f3cce98cb44e8e1449e455b28d684dfa9ff65426495584de6\",\n  true\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                difficulty: '0x2d50ba175407',\n                extraData: '0xe4b883e5bda9e7a59ee4bb99e9b1bc',\n                gasLimit: '0x47e7c4',\n                gasUsed: '0x5208',\n                hash: '0xc0f4906fea23cf6f3cce98cb44e8e1449e455b28d684dfa9ff65426495584de6',\n                logsBloom:\n                    '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',\n                miner: '0x61c808d82a3ac53231750dadc13c777b59310bd9',\n                mixHash: '0xc38853328f753c455edaa4dfc6f62a435e05061beac136c13dbdcd0ff38e5f40',\n                nonce: '0x3b05c6d5524209f1',\n                number: '0x1e8480',\n                parentHash: '0x57ebf07eb9ed1137d41447020a25e51d30a0c272b5896571499c82c33ecb7288',\n                receiptsRoot: '0x84aea4a7aad5c5899bd5cfc7f309cc379009d30179316a2a7baa4a2ea4a438ac',\n                sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',\n                size: '0x28a',\n                stateRoot: '0x96dbad955b166f5119793815c36f11ffa909859bbfeb64b735cca37cbf10bef1',\n                timestamp: '0x57a1118a',\n                totalDifficulty: '0x262c34a6fd1268f6c',\n                transactions: ['0xc55e2b90168af6972193c1f86fa4d7d7b31a29c156665d15b9cd48618b5177ef'],\n                transactionsRoot: '0xb31f174d27b99cdae8e746bd138a01ce60d8dd7b224f7c60845914def05ecc58',\n                uncles: []\n            }\n        }\n    },\n    {\n        name: 'eth_call',\n        value: 'eth_call',\n        parentGroup: 'EVM/Smart Contract Execution',\n        description: 'Executes a new message call immediately without creating a transaction on the block chain.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_call',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code> - The transaction call object</li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n\t\t\t\t<li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n\t\t\t\t<li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.</li>\n\t\t\t\t<li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n\t\t\t\t<li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n\t\t\t\t<li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n\t\t\t</ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\", OR the blockHash (in accordance with EIP-1898)</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"from\": \"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\n    \"to\": \"0xd46e8dd67c5d32be8058bb8eb970870f07244567\",\n    \"gas\": \"0x76c0\",\n    \"gasPrice\": \"0x9184e72a000\",\n    \"value\": \"0x9184e72a\",\n    \"data\": \"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\"\n  }, \n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: '0x'\n        }\n    },\n    {\n        name: 'eth_sendRawTransaction',\n        value: 'eth_sendRawTransaction',\n        parentGroup: 'EVM/Smart Contract Execution',\n        description: 'Submits a pre-signed transaction for broadcast to the Ethereum network.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_sendRawTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">TRANSACTION DATA</code> - The signed transaction data.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xf869018203e882520894f17f52151ebef6c7334fad080c5704d77216b732881bc16d674ec80000801ba02da1c48b670996dcb1f447ef9ef00b33033c48a4fe938f420bec3e56bfd24071a062e0aa78a81bf0290afbc3a9d8e9a068e6d74caa66c5e0fa8a46deaae96b0833\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331'\n        }\n    },\n    {\n        name: 'eth_submitWork',\n        value: 'eth_submitWork',\n        parentGroup: 'EVM/Smart Contract Execution',\n        description: 'Used for submitting a proof-of-work solution.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_submitWork',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">WORK ARRAY 8 Bytes</code> - The nonce found (64 bits)</li>\n            <li><code class=\"inline\">WORK ARRAY 32 Bytes</code> - The header's pow-hash (256 bits)</li>\n\t\t\t<li><code class=\"inline\">WORK ARRAY 32 Bytes</code> - The mix digest (256 bits)</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0x0000000000000001\",\n  \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\n  \"0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: false\n        }\n    },\n    {\n        name: 'eth_getBlockByNumber',\n        value: 'eth_getBlockByNumber',\n        parentGroup: 'Retrieving Blocks',\n        description: 'Returns information about a block by block number.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getBlockByNumber',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer of a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n\t\t\t<li><code class=\"inline\">Boolean</code> - If true it returns the full transaction objects, if false only the hashes of the transactions</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x1b4\",\n  true\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                number: '0x1b4',\n                difficulty: '0x4ea3f27bc',\n                extraData: '0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32',\n                gasLimit: '0x1388',\n                gasUsed: '0x0',\n                hash: '0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae',\n                logsBloom:\n                    '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',\n                miner: '0xbb7b8287f3f0a933474a79eae42cbca977791171',\n                mixHash: '0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843',\n                nonce: '0x689056015818adbe',\n                parentHash: '0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54',\n                receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',\n                sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',\n                size: '0x220',\n                stateRoot: '0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d',\n                timestamp: '0x55ba467c',\n                totalDifficulty: '0x78ed983323d',\n                transactions: [],\n                transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',\n                uncles: []\n            }\n        }\n    },\n    {\n        name: 'eth_getTransactionByHash',\n        value: 'eth_getTransactionByHash',\n        parentGroup: 'Reading Transactions',\n        description:\n            'Returns the information about a transaction requested by transaction hash. In the response object, `blockHash`, `blockNumber`, and `transactionIndex` are `null` when the transaction is pending.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getTransactionByHash',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 32 Bytes - hash of a transaction</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                hash: '0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b',\n                blockHash: '0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2',\n                blockNumber: '0x5daf3b',\n                from: '0xa7d9ddbe1f17865597fbd27ec712455208b6b76d',\n                gas: '0xc350',\n                gasPrice: '0x4a817c800',\n                input: '0x68656c6c6f21',\n                nonce: '0x15',\n                r: '0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea',\n                s: '0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c',\n                to: '0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb',\n                transactionIndex: '0x41',\n                v: '0x25',\n                value: '0xf3dbb76162000'\n            }\n        }\n    },\n    {\n        name: 'eth_getTransactionCount',\n        value: 'eth_getTransactionCount',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns the number of transactions sent from an address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getTransactionCount',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - address.</li>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer of a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xc94770007dda54cF92009BFF0dE90c06F603a09f\",\n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x219'\n        }\n    },\n    {\n        name: 'eth_getTransactionReceipt',\n        value: 'eth_getTransactionReceipt',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns the receipt of a transaction by transaction hash.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getTransactionReceipt',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 32 Bytes - hash of a transaction.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xab059a62e22e230fe0f56d8555340a29b2e9532360368f810595453f6fdd213b\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                transactionHash: '0xab059a62e22e230fe0f56d8555340a29b2e9532360368f810595453f6fdd213b',\n                blockHash: '0x8243343df08b9751f5ca0c5f8c9c0460d8a9b6351066fae0acbd4d3e776de8bb',\n                blockNumber: '0x429d3b',\n                contractAddress: null,\n                cumulativeGasUsed: '0x64b559',\n                from: '0x00b46c2526e227482e2ebb8f4c69e4674d262e75',\n                gasUsed: '0xcaac',\n                logs: [\n                    {\n                        blockHash: '0x8243343df08b9751f5ca0c5f8c9c0460d8a9b6351066fae0acbd4d3e776de8bb',\n                        address: '0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907',\n                        logIndex: '0x56',\n                        data: '0x000000000000000000000000000000000000000000000000000000012a05f200',\n                        removed: false,\n                        topics: [\n                            '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                            '0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75',\n                            '0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078'\n                        ],\n                        blockNumber: '0x429d3b',\n                        transactionIndex: '0xac',\n                        transactionHash: '0xab059a62e22e230fe0f56d8555340a29b2e9532360368f810595453f6fdd213b'\n                    }\n                ],\n                logsBloom:\n                    '0x00000000040000000000000000000000000000000000000000000000000000080000000010000000000000000000000000000000000040000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000010100000000000000000000000000004000000000000200000000000000000000000000000000000000000000',\n                root: '0x3ccba97c7fcc7e1636ce2d44be1a806a8999df26eab80a928205714a878d5114',\n                status: null,\n                to: '0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907',\n                transactionIndex: '0xac'\n            }\n        }\n    },\n    {\n        name: 'eth_getBlockTransactionCountByHash',\n        value: 'eth_getBlockTransactionCountByHash',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns the number of transactions in a block matching the given block hash.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getBlockTransactionCountByHash',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 32 Bytes - hash of a block.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0x8243343df08b9751f5ca0c5f8c9c0460d8a9b6351066fae0acbd4d3e776de8bb\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0xb0'\n        }\n    },\n    {\n        name: 'eth_getBlockTransactionCountByNumber',\n        value: 'eth_getBlockTransactionCountByNumber',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns the number of transactions in a block matching the given block number.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getBlockTransactionCountByNumber',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer of a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n        </ul>`,\n        exampleParameters: `[\n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0xee'\n        }\n    },\n    {\n        name: 'eth_getTransactionByBlockHashAndIndex',\n        value: 'eth_getTransactionByBlockHashAndIndex',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns information about a transaction by block hash and transaction index position.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getTransactionByBlockHashAndIndex',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 32 Bytes - hash of a block.</li>\n            <li><code class=\"inline\">QUANTITY</code> - integer of the transaction index position.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xc0f4906fea23cf6f3cce98cb44e8e1449e455b28d684dfa9ff65426495584de6\",\n  \"0x0\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                blockHash: '0xc0f4906fea23cf6f3cce98cb44e8e1449e455b28d684dfa9ff65426495584de6',\n                blockNumber: '0x1e8480',\n                from: '0x32be343b94f860124dc4fee278fdcbd38c102d88',\n                gas: '0x51615',\n                gasPrice: '0x6fc23ac00',\n                hash: '0xc55e2b90168af6972193c1f86fa4d7d7b31a29c156665d15b9cd48618b5177ef',\n                input: '0x',\n                nonce: '0x1efc5',\n                to: '0x104994f45d9d697ca104e5704a7b77d7fec3537c',\n                transactionIndex: '0x0',\n                value: '0x821878651a4d70000',\n                v: '0x1b',\n                r: '0x51222d91a379452395d0abaff981af4cfcc242f25cfaf947dea8245a477731f9',\n                s: '0x3a997c910b4701cca5d933fb26064ee5af7fe3236ff0ef2b58aa50b25aff8ca5'\n            }\n        }\n    },\n    {\n        name: 'eth_getTransactionByBlockNumberAndIndex',\n        value: 'eth_getTransactionByBlockNumberAndIndex',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns information about a transaction by block number and transaction index position.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getTransactionByBlockNumberAndIndex',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n            <li><code class=\"inline\">QUANTITY</code> - the transaction index position.</li>\n        </ul>`,\n        exampleParameters: `[\n  \"latest\",\n  \"0x0\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                blockHash: '0xf345d6ac2cb6290489915178fef0b2fc7f5a7312dd06773c77532de6740b2b4d',\n                blockNumber: '0xa1c0ff',\n                from: '0x79c384ac9c32e90f00a214c77caac1392ec8cdea',\n                gas: '0xafbe',\n                gasPrice: '0x1270b01800',\n                hash: '0x61b27f711f58ee3090c0976c7e90d2b7eafeb7b889f0db593234f04f8ce07f22',\n                input: '0xa9059cbb0000000000000000000000004b1a99467a284cc690e3237bc69105956816f7620000000000000000000000000000000000000000000000000000001919617600',\n                nonce: '0xa',\n                to: '0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee',\n                transactionIndex: '0x0',\n                value: '0x0',\n                v: '0x1b',\n                r: '0x2123cdbd1130f726ebed55fd2295239778dd4161495d2733f374d7863fc42ab1',\n                s: '0x1f08fd53ff2c3969b88d5e622561d62a799ae68e36f5142689dbfde44bbe1bed'\n            }\n        }\n    },\n\n    {\n        name: 'eth_getBalance',\n        value: 'eth_getBalance',\n        parentGroup: 'Account Information',\n        description: 'Returns the balance of the account of a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getBalance',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - address to check for balance.</li>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xc94770007dda54cF92009BFF0dE90c06F603a09f\",\n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x7c2562030800'\n        }\n    },\n    {\n        name: 'eth_getCode',\n        value: 'eth_getCode',\n        parentGroup: 'Account Information',\n        description: 'Returns code at a given address.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getCode',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - address</li>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907\",\n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100a557806313af40351461012f57806318160ddd1461014e578063313ce5671461017357806370a082311461019c57806375ad319a146101bb5780638da5cb5b146101ee57806395d89b411461021d578063a9059cbb14610230575b34156100a157600080fd5bfe5b005b34156100b057600080fd5b6100b8610252565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156100f45780820151838201526020016100dc565b50505050905090810190601f1680156101215780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013a57600080fd5b6100a3600160a060020a0360043516610289565b341561015957600080fd5b61016161030f565b60405190815260200160405180910390f35b341561017e57600080fd5b610186610315565b60405160ff909116815260200160405180910390f35b34156101a757600080fd5b610161600160a060020a036004351661031a565b34156101c657600080fd5b6101da600160a060020a0360043516610335565b604051901515815260200160405180910390f35b34156101f957600080fd5b61020161038e565b604051600160a060020a03909116815260200160405180910390f35b341561022857600080fd5b6100b861039d565b341561023b57600080fd5b6101da600160a060020a03600435166024356103d4565b60408051908101604052601881527f444f5420416c6c6f636174696f6e20496e64696361746f720000000000000000602082015281565b60005433600160a060020a039081169116146102a457600080fd5b600054600160a060020a0380831691167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405160405180910390a36000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b60015481565b600381565b600160a060020a031660009081526002602052604090205490565b33600160a060020a03811660009081526002602052604081206001015490919060ff16151561036357600080fd5b5050600160a060020a031660009081526002602052604090206001908101805460ff19168217905590565b600054600160a060020a031681565b60408051908101604052600381527f444f540000000000000000000000000000000000000000000000000000000000602082015281565b33600160a060020a03811660009081526002602052604081205490919083908190101561040057600080fd5b33600160a060020a03811660009081526002602052604090206001015460ff16151561042b57600080fd5b85600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8760405190815260200160405180910390a3600160a060020a033381166000908152600260205260408082208054899003905591881681522080548601905560019350505050929150505600a165627a7a72305820228dfae3e67abcdc7f73fb3f83a7d23f45acd853774acad9d2e1ac83b940fbe90029'\n        }\n    },\n    {\n        name: 'eth_getStorageAt',\n        value: 'eth_getStorageAt',\n        parentGroup: 'Account Information',\n        description:\n            \"Returns the value from a storage position at a given address, or in other words, returns the state of the contract's storage, which may not be exposed via the contract's methods.\",\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getStorageAt',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - address of the storage.</li>\n\t\t\t<li><code class=\"inline\">QUANTITY</code> - integer of the position in the storage.</li>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0x295a70b2de5e3953354a6a8344e616ed314d7251\",\n  \"0x0\",\n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: '0x00000000000000000000000000000000000000000000000000000000000004d2'\n        }\n    },\n    {\n        name: 'eth_accounts',\n        value: 'eth_accounts',\n        parentGroup: 'Account Information',\n        description: 'Returns a list of addresses owned by client.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_accounts',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: []\n        }\n    },\n    {\n        name: 'parity_nextNonce',\n        value: 'parity_nextNonce',\n        parentGroup: 'Account Information',\n        description: 'Returns next available nonce for transaction from given account. Includes pending block and transaction queue.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getProof',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">ADDRESS</code> - a string representing the address (20 bytes) to check for transaction count for</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0xc94770007dda54cF92009BFF0dE90c06F603a09f\"\n]`,\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: '0x1a'\n        }\n    },\n    {\n        name: 'eth_getProof',\n        value: 'eth_getProof',\n        parentGroup: 'Account Information',\n        description:\n            'Returns the account and storage values of the specified account including the Merkle-proof. This call can be used to verify that the data you are pulling from is not tampered with.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getProof',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 20 Bytes - address of the account.</li>\n\t\t\t<li><code class=\"inline\">ARRAY</code>, 32 Bytes - array of storage-keys which should be proofed and included.</li>\n            <li><code class=\"inline\">QUANTITY|TAG</code> - a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n        </ul>`,\n        exampleParameters: `[\n  \"0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842\",\n  [\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\"],\n  \"latest\"\n]`,\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: {\n                accountProof: [\n                    '0xf90211a...0701bc80',\n                    '0xf90211a...0d832380',\n                    '0xf90211a...5fb20c80',\n                    '0xf90211a...0675b80',\n                    '0xf90151a0...ca08080'\n                ],\n                balance: '0x0',\n                codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',\n                nonce: '0x0',\n                storageHash: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',\n                storageProof: [\n                    {\n                        key: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',\n                        proof: ['0xf90211a...0701bc80', '0xf90211a...0d832380'],\n                        value: '0x1'\n                    }\n                ]\n            }\n        }\n    },\n    {\n        name: 'eth_getLogs',\n        value: 'eth_getLogs',\n        parentGroup: 'Event Logs',\n        description: 'Returns an array of all logs matching a given filter object.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getLogs',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code> - The filter object</li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">fromBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional, default: \"latest\") Value:</li>\n\t\t\t\t<ul>\n                    <li>Integer block number</li>\n                    <li>\"latest\" for the last mined block</li>\n                    <li>\"pending\", \"earliest\" for not yet mined transactions.</li>\n                </ul>\n                <li><code class=\"inline\">toBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional, default: \"latest\") Value:</li>\n\t\t\t\t<ul>\n                    <li>Integer block number</li>\n                    <li>\"latest\" for the last mined block</li>\n                    <li>\"pending\", \"earliest\" for not yet mined transactions.</li>\n                </ul>\n                <li><code class=\"inline\">address</code>: <code class=\"inline\">DATA|Array</code> 20 Bytes - (optional) Contract address or a list of addresses from which logs should originate.</li>\n\t\t\t\t<li><code class=\"inline\">topics</code>: <code class=\"inline\">Array of DATA</code>  - (optional) Array of 32 Bytes DATA topics.</li>\n\t\t\t\t<ul>\n                    <li>Topics are order-dependent. Each topic can also be an array of DATA with \"or\" options.</li>\n                </ul>\n                <li><code class=\"inline\">blockHash</code>: <code class=\"inline\">DATA</code>, 32 Bytes - (optional) With the addition of EIP-234 (Geth >= v1.8.13 or Parity >= v2.1.0), blockHash is a new filter option which restricts the logs returned to the single block with the 32-byte hash blockHash. Using blockHash is equivalent to fromBlock = toBlock = the block number with hash <code class=\"inline\">blockHash</code>. If blockHash is present in the filter criteria, then neither <code class=\"inline\">fromBlock</code> nor <code class=\"inline\">toBlock</code> are allowed.</li>\n\t\t\t</ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"address\": \"0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907\",\n    \"topics\": [\n        \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\"\n    ],\n    \"blockHash\": \"0x8243343df08b9751f5ca0c5f8c9c0460d8a9b6351066fae0acbd4d3e776de8bb\"\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: [\n                {\n                    address: '0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907',\n                    topics: [\n                        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                        '0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75',\n                        '0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078'\n                    ],\n                    data: '0x000000000000000000000000000000000000000000000000000000012a05f200',\n                    blockNumber: '0x429d3b',\n                    transactionHash: '0xab059a62e22e230fe0f56d8555340a29b2e9532360368f810595453f6fdd213b',\n                    transactionIndex: '0xac',\n                    blockHash: '0x8243343df08b9751f5ca0c5f8c9c0460d8a9b6351066fae0acbd4d3e776de8bb',\n                    logIndex: '0x56',\n                    removed: false\n                }\n            ]\n        }\n    },\n    {\n        name: 'eth_protocolVersion',\n        value: 'eth_protocolVersion',\n        parentGroup: 'Chain Information',\n        description: 'Returns the current ethereum protocol version.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.OPTIMISM, NETWORK.OPTIMISM_GOERLI],\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_protocolVersion',\n            params: [],\n            id: 0\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x40'\n        }\n    },\n    {\n        name: 'eth_gasPrice',\n        value: 'eth_gasPrice',\n        parentGroup: 'Chain Information',\n        description: 'Returns the current price per gas in wei.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_gasPrice',\n            params: [],\n            id: 0\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x98bca5a00'\n        }\n    },\n    {\n        name: 'rollup_gasPrices',\n        value: 'rollup_gasPrices',\n        parentGroup: 'Chain Information',\n        description: 'Returns the L1 and L2 gas prices that are being used by the Sequencer to calculate fees.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: [NETWORK.OPTIMISM, NETWORK.OPTIMISM_GOERLI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'rollup_gasPrices',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                l1gasprice: '0x98bca5a00',\n                l2gasprice: '0x98bca5a00'\n            }\n        }\n    },\n    {\n        name: 'rollup_getInfo',\n        value: 'rollup_getInfo',\n        parentGroup: 'Chain Information',\n        description: 'Returns useful L2-specific information about the current node.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: [NETWORK.OPTIMISM, NETWORK.OPTIMISM_GOERLI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'rollup_getInfo',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                mode: 'sequencer',\n                syncing: true,\n                blocknumber: '0x123',\n                timestamp: '189238123123',\n                queueindex: '0x1',\n                index: '0x1',\n                verifiedindex: '0x1'\n            }\n        }\n    },\n    {\n        name: 'eth_estimateExecutionGas',\n        value: 'eth_estimateExecutionGas',\n        parentGroup: 'Chain Information',\n        description: 'Returns an estimation of gas for a given transaction.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: [NETWORK.OPTIMISM, NETWORK.OPTIMISM_GOERLI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_estimateExecutionGas',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code> - The transaction call object</li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n\t\t\t\t<li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n\t\t\t\t<li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. <code class=\"inline\">eth_estimateGas</code> consumes zero gas, but this parameter may be needed by some executions. NOTE: this parameter has a cap of 550 Million gas per request.</li>\n\t\t\t\t<li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n\t\t\t\t<li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n\t\t\t\t<li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n\t\t\t</ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\"</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"from\": \"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\n    \"to\": \"0xd46e8dd67c5d32be8058bb8eb970870f07244567\",\n    \"gas\": \"0x76c0\",\n    \"gasPrice\": \"0x9184e72a000\",\n    \"value\": \"0x9184e72a\",\n    \"data\": \"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\"\n  }, \n  \"latest\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x98bca5a00'\n        }\n    },\n    {\n        name: 'eth_syncing',\n        value: 'eth_syncing',\n        parentGroup: 'Chain Information',\n        description: 'Returns an object with data about the sync status or false.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_syncing',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: false\n        }\n    },\n    {\n        name: 'eth_mining',\n        value: 'eth_mining',\n        parentGroup: 'Chain Information',\n        description: 'Returns true if the node is mining, otherwise false.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_mining',\n            params: [],\n            id: 0\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: true\n        }\n    },\n    {\n        name: 'eth_estimateGas',\n        value: 'eth_estimateGas',\n        parentGroup: 'Chain Information',\n        description:\n            'Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_estimateGas',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code> - The transaction call object</li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">from</code>: <code class=\"inline\">DATA</code>, 20 Bytes - (optional) The address the transaction is sent from.</li>\n\t\t\t\t<li><code class=\"inline\">to</code>: <code class=\"inline\">DATA</code>, 20 Bytes - The address the transaction is directed to.</li>\n\t\t\t\t<li><code class=\"inline\">gas</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gas provided for the transaction execution. <code class=\"inline\">eth_estimateGas</code> consumes zero gas, but this parameter may be needed by some executions. NOTE: this parameter has a cap of 550 Million gas per request.</li>\n\t\t\t\t<li><code class=\"inline\">gasPrice</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the gasPrice used for each paid gas.</li>\n\t\t\t\t<li><code class=\"inline\">value</code>: <code class=\"inline\">QUANTITY</code> - (optional) Integer of the value sent with this transaction.</li>\n\t\t\t\t<li><code class=\"inline\">data</code>: <code class=\"inline\">DATA</code> - (optional) Hash of the method signature and encoded parameters.</li>\n\t\t\t</ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer block number, or the string \"latest\", \"earliest\" or \"pending\"</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"from\": \"0xb60e8dd61c5d32be8058bb8eb970870f07233155\",\n    \"to\": \"0xd46e8dd67c5d32be8058bb8eb970870f07244567\",\n    \"gas\": \"0x76c0\",\n    \"gasPrice\": \"0x9184e72a000\",\n    \"value\": \"0x9184e72a\",\n    \"data\": \"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675\"\n  }, \n  \"latest\"\n]`,\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: '0x5208'\n        }\n    },\n    {\n        name: 'eth_feeHistory',\n        value: 'eth_feeHistory',\n        parentGroup: 'Chain Information',\n        description: 'Returns a collection of historical gas information',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MAINNET, NETWORK.GÖRLI],\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_feeHistory',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code></li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">OLDESTBLOCK</code> - Lowest number block of the returned range.</li>\n\t\t\t\t<li><code class=\"inline\">BASEFEEPERGAS</code> - An array of block base fees per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-EIP-1559 blocks.</li>\n\t\t\t\t<li><code class=\"inline\">GASUSEDRATIO</code> - An array of block gas used ratios. These are calculated as the ratio of gasUsed and gasLimit.</li>\n\t\t\t\t<li><code class=\"inline\">REWARD</code> - (Optional) An array of effective priority fee per gas data points from a single block. All zeroes are returned if the block is empty.</li>\n\t\t\t</ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  4,\n  \"latest\",\n  [25, 75]\n]`,\n        exampleResponse: {\n            id: '1',\n            jsonrpc: '2.0',\n            result: {\n                oldestBlock: 10762137,\n                reward: [\n                    ['0x4a817c7ee', '0x4a817c7ee'],\n                    ['0x773593f0', '0x773593f5'],\n                    ['0x0', '0x0'],\n                    ['0x773593f5', '0x773bae75']\n                ],\n                baseFeePerGas: ['0x12', '0x10', '0x10', '0xe', '0xd'],\n                gasUsedRatio: [0.026089875, 0.406803, 0, 0.0866665]\n            }\n        }\n    },\n    {\n        name: 'eth_maxPriorityFeePerGas',\n        value: 'eth_maxPriorityFeePerGas',\n        parentGroup: 'Chain Information',\n        description:\n            'Returns a fee per gas that is an estimate of how much you can pay as a priority fee, or \"tip\", to get a transaction included in the current block.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MAINNET, NETWORK.GÖRLI],\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_maxPriorityFeePerGas',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: '0x12a05f1f9'\n        }\n    },\n    {\n        name: 'eth_chainId',\n        value: 'eth_chainId',\n        parentGroup: 'Chain Information',\n        description:\n            'Returns the currently configured chain ID, a value used in replay-protected transaction signing as introduced by EIP-155.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_chainId',\n            params: [],\n            id: 83\n        },\n        exampleResponse: {\n            id: 83,\n            jsonrpc: '2.0',\n            result: '0x3d' // 61\n        }\n    },\n    {\n        name: 'net_version',\n        value: 'net_version',\n        parentGroup: 'Chain Information',\n        description: 'Returns the current network id.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'net_version',\n            params: [],\n            id: 67\n        },\n        exampleResponse: {\n            id: 67,\n            jsonrpc: '2.0',\n            result: '3'\n        }\n    },\n    {\n        name: 'net_listening',\n        value: 'net_listening',\n        parentGroup: 'Chain Information',\n        description: 'Returns true if client is actively listening for network connections.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'net_listening',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: true\n        }\n    },\n    {\n        name: 'net_peerCount',\n        value: 'net_peerCount',\n        parentGroup: 'Chain Information',\n        description: 'Returns the number of peers currently connected to the client.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'net_peerCount',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: '0x64'\n        }\n    },\n    {\n        name: 'eth_getUncleByBlockNumberAndIndex',\n        value: 'eth_getUncleByBlockNumberAndIndex',\n        parentGroup: 'Retrieving Uncles',\n        description: 'Returns information about an uncle of a block by number and uncle index position.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getUncleByBlockNumberAndIndex',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer of a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n\t\t\t<li><code class=\"inline\">QUANTITY</code> - the uncle's index position.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0x29c\",\n  \"0x0\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                difficulty: '0x57f117f5c',\n                extraData: '0x476574682f76312e302e302f77696e646f77732f676f312e342e32',\n                gasLimit: '0x1388',\n                gasUsed: '0x0',\n                hash: '0x932bdf904546a2287a2c9b2ede37925f698a7657484b172d4e5184f80bdd464d',\n                logsBloom:\n                    '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',\n                miner: '0x5bf5e9cf9b456d6591073513de7fd69a9bef04bc',\n                mixHash: '0x4500aa4ee2b3044a155252e35273770edeb2ab6f8cb19ca8e732771484462169',\n                nonce: '0x24732773618192ac',\n                number: '0x299',\n                parentHash: '0xa779859b1ee558258b7008bbabff272280136c5dd3eb3ea3bfa8f6ae03bf91e5',\n                receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',\n                sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',\n                size: '0x21d',\n                stateRoot: '0x2604fbf5183f5360da249b51f1b9f1e0f315d2ff3ffa1a4143ff221ad9ca1fec',\n                timestamp: '0x55ba4827',\n                transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',\n                uncles: []\n            }\n        }\n    },\n    {\n        name: 'eth_getUncleByBlockHashAndIndex',\n        value: 'eth_getUncleByBlockHashAndIndex',\n        parentGroup: 'Retrieving Uncles',\n        description: 'Returns information about an uncle of a block by hash and uncle index position.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getUncleByBlockHashAndIndex',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer of a block number, or the string \"earliest\", \"latest\" or \"pending\"</li>\n\t\t\t<li><code class=\"inline\">QUANTITY</code> - the uncle's index position.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35\",\n  \"0x0\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: {\n                difficulty: '0xbf93da424b943',\n                extraData: '0x65746865726d696e652d657539',\n                gasLimit: '0x7a121d',\n                gasUsed: '0x79ea62',\n                hash: '0x824cce7c7c2ec6874b9fa9a9a898eb5f27cbaf3991dfa81084c3af60d1db618c',\n                logsBloom:\n                    '0x0948432021200401804810002000000000381001001202440000010020000080a016262050e44850268052000400100505022305a64000054004200b0c04110000080c1055c42001054b804940a0401401008a00112d80082113400c10006580140005011a40220020000010001c0a00082300434002000050840010102082801c2000148540201004491814020480080111a0300600000003800640024200109c00202010044000880000106810a1a010000028a0100000422000140011000050a2a44b3080001060800000540c108102102600d000004730404a880100600021080100403000180000062642408b440060590400080101e046f08000000430',\n                miner: '0xea674fdde714fd979de3edf0f56aa9716b898ec8',\n                mixHash: '0x0b15fe0a9aa789c167b0f5ade7b72969d9f2193014cb4e98382254f60ffb2f4a',\n                nonce: '0xa212d6400b89b3f6',\n                number: '0x5bad54',\n                parentHash: '0x05e19fb68d9ec798073808e8b3170875cb327d4b6cde7d6f60fe194677bb26fd',\n                receiptsRoot: '0x90807b32c4aa4610c57289de57fa68ba50ed53f14dd2c25f1862aa049029dcd6',\n                sha3Uncles: '0xf763576c1ea6a8c61a206e16b1a2451bec5cba1c7545d7ff733a1e8c78715569',\n                size: '0x216',\n                stateRoot: '0xebc7a1603bfffe0a14bdb89f898e2f2824abb40f04579beb7b920c56d6e273c9',\n                timestamp: '0x5b54143f',\n                transactionsRoot: '0x7562cba41e067b364b933e7b566fb2444f6954fef3964a5a487d4cd79d97a56c',\n                uncles: []\n            }\n        }\n    },\n    {\n        name: 'eth_getUncleCountByBlockHash',\n        value: 'eth_getUncleCountByBlockHash',\n        parentGroup: 'Retrieving Uncles',\n        description: 'Returns the number of uncles in a block matching the given block hash.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getUncleCountByBlockHash',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code>, 32 Bytes - hash of a block.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x1'\n        }\n    },\n    {\n        name: 'eth_getUncleCountByBlockNumber',\n        value: 'eth_getUncleCountByBlockNumber',\n        parentGroup: 'Retrieving Uncles',\n        description: 'Returns the number of uncles in a block matching the give block number.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getUncleCountByBlockNumber',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY|TAG</code> - integer of a block number, or the string \"latest\", \"earliest\" or \"pending\"</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xe8\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x0'\n        }\n    },\n    {\n        name: 'eth_getFilterChanges',\n        value: 'eth_getFilterChanges',\n        parentGroup: 'Filters',\n        description: 'Polling method for a filter, which returns an array of logs which occurred since last poll.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getFilterChanges',\n            params: [],\n            id: 73\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY</code> - the filter id.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xfe704947a3cd3ca12541458a4321c869\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 73,\n            result: [\n                {\n                    address: '0xb5a5f22694352c15b00323844ad545abb2b11028',\n                    blockHash: '0x99e8663c7b6d8bba3c7627a17d774238eae3e793dee30008debb2699666657de',\n                    blockNumber: '0x5d12ab',\n                    data: '0x0000000000000000000000000000000000000000000000a247d7a2955b61d000',\n                    logIndex: '0x0',\n                    removed: false,\n                    topics: [\n                        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                        '0x000000000000000000000000bdc0afe57b8e9468aa95396da2ab2063e595f37e',\n                        '0x0000000000000000000000007503e090dc2b64a88f034fb45e247cbd82b8741e'\n                    ],\n                    transactionHash: '0xa74c2432c9cf7dbb875a385a2411fd8f13ca9ec12216864b1a1ead3c99de99cd',\n                    transactionIndex: '0x3'\n                },\n                {\n                    address: '0xe38165c9f6deb144afc9c32c206b024817e1496d',\n                    blockHash: '0x99e8663c7b6d8bba3c7627a17d774238eae3e793dee30008debb2699666657de',\n                    blockNumber: '0x5d12ab',\n                    data: '0x0000000000000000000000000000000000000000000000000000000025c6b720',\n                    logIndex: '0x3',\n                    removed: false,\n                    topics: [\n                        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                        '0x00000000000000000000000080e73e47173b2d00b531bf83bc39e710157125c3',\n                        '0x0000000000000000000000008f6cc93795969e5bbbf07c66dfee7d41ad24f1ef'\n                    ],\n                    transactionHash: '0x9e8f1cb1facb9a11a1cf947634053a0b2d557399f926b12127aa10497a2f0153',\n                    transactionIndex: '0x5'\n                }\n            ]\n        }\n    },\n    {\n        name: 'eth_getFilterLogs',\n        value: 'eth_getFilterLogs',\n        parentGroup: 'Filters',\n        description: 'Returns an array of all logs matching filter with given id.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getFilterLogs',\n            params: [],\n            id: 74\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY</code> - the filter id.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xfe704947a3cd3ca12541458a4321c869\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 73,\n            result: [\n                {\n                    address: '0xb5a5f22694352c15b00323844ad545abb2b11028',\n                    blockHash: '0x99e8663c7b6d8bba3c7627a17d774238eae3e793dee30008debb2699666657de',\n                    blockNumber: '0x5d12ab',\n                    data: '0x0000000000000000000000000000000000000000000000a247d7a2955b61d000',\n                    logIndex: '0x0',\n                    removed: false,\n                    topics: [\n                        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                        '0x000000000000000000000000bdc0afe57b8e9468aa95396da2ab2063e595f37e',\n                        '0x0000000000000000000000007503e090dc2b64a88f034fb45e247cbd82b8741e'\n                    ],\n                    transactionHash: '0xa74c2432c9cf7dbb875a385a2411fd8f13ca9ec12216864b1a1ead3c99de99cd',\n                    transactionIndex: '0x3'\n                },\n                {\n                    address: '0xe38165c9f6deb144afc9c32c206b024817e1496d',\n                    blockHash: '0x99e8663c7b6d8bba3c7627a17d774238eae3e793dee30008debb2699666657de',\n                    blockNumber: '0x5d12ab',\n                    data: '0x0000000000000000000000000000000000000000000000000000000025c6b720',\n                    logIndex: '0x3',\n                    removed: false,\n                    topics: [\n                        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                        '0x00000000000000000000000080e73e47173b2d00b531bf83bc39e710157125c3',\n                        '0x0000000000000000000000008f6cc93795969e5bbbf07c66dfee7d41ad24f1ef'\n                    ],\n                    transactionHash: '0x9e8f1cb1facb9a11a1cf947634053a0b2d557399f926b12127aa10497a2f0153',\n                    transactionIndex: '0x5'\n                }\n            ]\n        }\n    },\n    {\n        name: 'eth_newBlockFilter',\n        value: 'eth_newBlockFilter',\n        parentGroup: 'Filters',\n        description: 'Creates a filter in the node, to notify when a new block arrives.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_newBlockFilter',\n            params: [],\n            id: 73\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">QUANTITY</code> - the filter id.</li>\n\t\t</ul>`,\n        exampleParameters: `[\n  \"0xfe704947a3cd3ca12541458a4321c869\"\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0x9fb7f13924bb605fd29f3ddd6d193ece'\n        }\n    },\n    {\n        name: 'eth_newFilter',\n        value: 'eth_newFilter',\n        parentGroup: 'Filters',\n        description: 'Creates a filter object, based on filter options, to notify when the state changes (logs).',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_newFilter',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">Object</code> - The filter object</li>\n\t\t    <ul>\n\t\t\t    <li><code class=\"inline\">fromBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional, default: \"latest\") Value:</li>\n\t\t\t\t<ul>\n                    <li>Integer block number</li>\n                    <li>\"latest\" for the last mined block</li>\n                    <li>\"pending\", \"earliest\" for not yet mined transactions.</li>\n                </ul>\n                <li><code class=\"inline\">toBlock</code>: <code class=\"inline\">QUANTITY|TAG</code> - (optional, default: \"latest\") Value:</li>\n\t\t\t\t<ul>\n                    <li>Integer block number</li>\n                    <li>\"latest\" for the last mined block</li>\n                    <li>\"pending\", \"earliest\" for not yet mined transactions.</li>\n                </ul>\n                <li><code class=\"inline\">address</code>: <code class=\"inline\">DATA|Array</code> 20 Bytes - (optional) Contract address or a list of addresses from which logs should originate.</li>\n\t\t\t\t<li><code class=\"inline\">topics</code>: <code class=\"inline\">Array of DATA</code>  - (optional) Array of 32 Bytes DATA topics.</li>\n\t\t\t\t<ul>\n                    <li>Topics are order-dependent. Each topic can also be an array of DATA with \"or\" options.</li>\n                </ul>\n\t\t\t</ul>\n\t\t</ul>`,\n        exampleParameters: `[\n  {\n    \"fromBlock\": \"0x1\",\n    \"toBlock\": \"0x2\",\n    \"address\": \"0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907\",\n    \"topics\": [\n        \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\"\n    ]\n  }\n]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0xdcc9a819f80efa9e1d215a9d41b2d22e'\n        }\n    },\n    {\n        name: 'eth_newPendingTransactionFilter',\n        value: 'eth_newPendingTransactionFilter',\n        parentGroup: 'Filters',\n        description:\n            'Creates a filter in the node, to notify when new pending transactions arrive. To check if the state has changed, call eth_getFilterChanges.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks,\n            [NETWORK_PROVIDER.QUICKNODE]: qnSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_newPendingTransactionFilter',\n            params: [],\n            id: 73\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: '0xa08914f1caedfcbe814d9fb33e69678d'\n        }\n    },\n    {\n        name: 'eth_uninstallFilter',\n        value: 'eth_uninstallFilter',\n        parentGroup: 'Filters',\n        description: 'Uninstalls a filter with given id.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: alchemySupportedNetworks,\n            [NETWORK_PROVIDER.INFURA]: infuraSupportedNetworks\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_uninstallFilter',\n            params: [],\n            id: 0\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">QUANTITY</code> - The filter id.</li>\n        </ul>`,\n        exampleParameters: `[\"0xfe704947a3cd3ca12541458a4321c869\"]`,\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 0,\n            result: false\n        }\n    },\n    {\n        name: 'qn_broadcastRawTransaction',\n        value: 'qn_broadcastRawTransaction',\n        parentGroup: 'Broadcast Transaction',\n        description: 'Sends a transaction to multiple regions to encourage faster mining.',\n        providerNetworks: {\n            [NETWORK_PROVIDER.QUICKNODE]: [NETWORK.MAINNET, NETWORK.GÖRLI, NETWORK.BSC, NETWORK.BSC_TESTNET]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'qn_broadcastRawTransaction',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n\t\t\t<li><code class=\"inline\">DATA</code> - The signed transaction (typically signed with a library, using your private key)</li>\n        </ul>`,\n        exampleParameters: `[\"0xf8680685051f4d5c00833d090094ac2d0226ade52e6b4ccc97359359f01e34d50352834c4b40802aa004f4410acfc636f5d65e75ca37448fd0b1e90632661ad3ea071594b646e7621fa0186d78ddbeef43bc372fcc650ad4e77e0629206426cd183d751e9ddcc8d5e777\"]`,\n        exampleResponse: {\n            id: 1,\n            jsonrpc: '2.0',\n            result: '0x41ba07c3d8213288869c0c59c3d341074fc63e64359a612a353949de2ed3586a'\n        }\n    }\n] as IETHOperation[]\n\nexport const polygonOperations = [\n    {\n        name: 'bor_getAuthor',\n        value: 'bor_getAuthor',\n        parentGroup: 'Account Information',\n        description: 'Returns address of Author',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI],\n            [NETWORK_PROVIDER.INFURA]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'bor_getAuthor',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li>block number (in hexadecimal format)</li>\n        </ul>`,\n        exampleParameters: '[\"0x1234\"]',\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: '0x5973918275c01f50555d44e92c9d9b353cadad54'\n        }\n    },\n    {\n        name: 'bor_getCurrentValidators',\n        value: 'bor_getCurrentValidators',\n        parentGroup: 'Account Information',\n        description: 'Returns current validators',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI],\n            [NETWORK_PROVIDER.INFURA]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'bor_getCurrentValidators',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: [\n                {\n                    ID: 0,\n                    signer: '0x46a3a41bd932244dd08186e4c19f1a7e48cbcdf4',\n                    power: 1,\n                    accum: -15\n                },\n                {\n                    ID: 0,\n                    signer: '0x6a654ca3bfb5cfb23bf30bafbf96b3b6ec26bb0e',\n                    power: 1,\n                    accum: -21\n                },\n                {\n                    ID: 0,\n                    signer: '0x7c7379531b2aee82e4ca06d4175d13b9cbeafd49',\n                    power: 5,\n                    accum: -8\n                },\n                {\n                    ID: 0,\n                    signer: '0xe77bbfd8ed65720f187efdd109e38d75eaca7385',\n                    power: 2,\n                    accum: 5\n                },\n                {\n                    ID: 0,\n                    signer: '0xf0245f6251bef9447a08766b9da2b07b28ad80b0',\n                    power: 7,\n                    accum: -4\n                }\n            ]\n        }\n    },\n    {\n        name: 'bor_getCurrentProposer',\n        value: 'bor_getCurrentProposer',\n        parentGroup: 'Account Information',\n        description: \"Returns current proposer's address\",\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI],\n            [NETWORK_PROVIDER.INFURA]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'bor_getCurrentProposer',\n            params: [],\n            id: 1\n        },\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: '0xb79fad4ca981472442f53d16365fdf0305ffd8e9'\n        }\n    },\n    {\n        name: 'bor_getRootHash',\n        value: 'bor_getRootHash',\n        parentGroup: 'Account Information',\n        description: 'Returns the root hash given a block range',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI],\n            [NETWORK_PROVIDER.INFURA]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'bor_getRootHash',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">from</code> block number (in int format)</li>\n            <li><code class=\"inline\">to</code> block number (in int format)</li>\n        </ul>`,\n        exampleParameters: '[1000000, 1032767]',\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: '04b073e17b7186ab4daae17c5e2cc2d5a729cffd102cede41ee458a2d5573994'\n        }\n    },\n    {\n        name: 'eth_getSignersAtHash',\n        value: 'eth_getSignersAtHash',\n        parentGroup: 'Account Information',\n        description: 'Returns all signs given a blockhash',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getSignersAtHash',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">blockhash</code></li>\n        </ul>`,\n        exampleParameters: '[\"0x29fa73e3da83ddac98f527254fe37002e052725a88904bac14f03e919e1e2876\"]',\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: [\n                '0x0375b2fc7140977c9c76d45421564e354ed42277',\n                '0x42eefcda06ead475cde3731b8eb138e88cd0bac3',\n                '0x5973918275c01f50555d44e92c9d9b353cadad54',\n                '0x7fcd58c2d53d980b247f1612fdba93e9a76193e6',\n                '0xb702f1c9154ac9c08da247a8e30ee6f2f3373f41',\n                '0xb8bb158b93c94ed35c1970d610d1e2b34e26652c',\n                '0xf84c74dea96df0ec22e11e7c33996c73fcc2d822'\n            ]\n        }\n    },\n    {\n        name: 'eth_getTransactionReceiptsByBlock',\n        value: 'eth_getTransactionReceiptsByBlock',\n        parentGroup: 'Reading Transactions',\n        description: 'Returns all transaction receipts for the given block number or hash',\n        providerNetworks: {\n            [NETWORK_PROVIDER.ALCHEMY]: [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n        },\n        method: 'POST',\n        body: {\n            jsonrpc: '2.0',\n            method: 'eth_getTransactionReceiptsByBlock',\n            params: [],\n            id: 1\n        },\n        inputParameters: `\n        <ul>\n            <li><code class=\"inline\">block_number</code>in hex OR <code class=\"inline\">block_hash</code></li>\n        </ul>`,\n        exampleParameters: '[\"0x989689\"]',\n        exampleResponse: {\n            jsonrpc: '2.0',\n            id: 1,\n            result: [\n                {\n                    blockHash: '0x224c0c3153d6bf1f45f461053aee6cbf71e5b5209685c91d81c288b59c82fb47',\n                    blockNumber: '0x989689',\n                    contractAddress: null,\n                    cumulativeGasUsed: '0x63ee1',\n                    from: '0x7b5fc677cf27a807adf2ebcef72db3b935df6c0a',\n                    gasUsed: '0x63ee1',\n                    logs: [\n                        {\n                            address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',\n                            topics: [\n                                '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925',\n                                '0x0000000000000000000000003d8a89a20aa73fba0f30d080e8120de9f9555724',\n                                '0x000000000000000000000000170f88be2538b8aca98a87c1b186ce5f773cc028'\n                            ],\n                            data: '0x000000000000000000000000000000000000000000000000000000007dd34dc0',\n                            blockNumber: '0x989689',\n                            transactionHash: '0x19a2ba50b19ee8ed28fb902fe8cd0a64b0f7a4fe01281e70e2387fdd00ffd5f0',\n                            transactionIndex: '0x0',\n                            blockHash: '0x224c0c3153d6bf1f45f461053aee6cbf71e5b5209685c91d81c288b59c82fb47',\n                            logIndex: '0x0',\n                            removed: false\n                        },\n                        {\n                            address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',\n                            topics: [\n                                '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',\n                                '0x0000000000000000000000003d8a89a20aa73fba0f30d080e8120de9f9555724',\n                                '0x000000000000000000000000170f88be2538b8aca98a87c1b186ce5f773cc028'\n                            ],\n                            data: '0x000000000000000000000000000000000000000000000000000000007dd34dc0',\n                            blockNumber: '0x989689',\n                            transactionHash: '0x19a2ba50b19ee8ed28fb902fe8cd0a64b0f7a4fe01281e70e2387fdd00ffd5f0',\n                            transactionIndex: '0x0',\n                            blockHash: '0x224c0c3153d6bf1f45f461053aee6cbf71e5b5209685c91d81c288b59c82fb47',\n                            logIndex: '0x1',\n                            removed: false\n                        }\n                    ],\n                    logsBloom:\n                        '0x0400000002000000000100000000000000000000000000001000000000104000000000000000000001000020200000001000800000000000001008014020004000000000001000000000000c000200840000000000000000202100000000000000000000020000000001000000000800000000000800000980100010000000002001000000000000000000000000000000100000020000000000000000000000220000000400000000000000008001000008000000000020000000000000004000000002000000000001000000000000000404000600000000100000100020000010008000002000040000000000000010000000000000008000100002100000',\n                    status: '0x1',\n                    to: '0xd216153c06e857cd7f72665e0af1d7d82172f494',\n                    transactionHash: '0x19a2ba50b19ee8ed28fb902fe8cd0a64b0f7a4fe01281e70e2387fdd00ffd5f0',\n                    transactionIndex: '0x0'\n                }\n            ]\n        }\n    }\n] as IETHOperation[]\n"
  },
  {
    "path": "packages/components/src/Interface.ts",
    "content": "import { Request } from 'express'\nimport { CronJob } from 'cron'\nimport { ObjectId } from 'mongodb'\nimport { CHAIN_ID, DOMAIN_ID, NETWORK } from '.'\n\n/**\n * Databases\n */\nexport interface IWorkflow {\n    _id: ObjectId\n    shortId: string\n    name: string\n    flowData: string\n    deployed: boolean\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IExecution {\n    _id: ObjectId\n    shortId: string\n    workflowShortId: string\n    executionData: string\n    state: ExecutionState\n    createdDate: Date\n    stoppedDate?: Date\n}\n\nexport interface ICredential {\n    _id: ObjectId\n    name: string\n    nodeCredentialName: string\n    credentialData: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IWebhook {\n    _id: ObjectId\n    workflowShortId: string\n    webhookEndpoint: string\n    httpMethod: WebhookMethod\n    webhookId: string\n    nodeId: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IContract {\n    _id: ObjectId\n    name: string\n    abi: string\n    address: string\n    network: string\n    providerCredential: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IWallet {\n    _id: ObjectId\n    name: string\n    address: string\n    network: string\n    providerCredential: string\n    walletCredential: string\n    updatedDate: Date\n    createdDate: Date\n}\n\n/**\n * Types\n */\nexport type ExecutionState = 'INPROGRESS' | 'FINISHED' | 'ERROR' | 'TERMINATED' | 'TIMEOUT'\n\nexport type WebhookMethod = 'GET' | 'POST'\n\nexport type NodeType = 'action' | 'webhook' | 'trigger'\n\nexport type NodeParamsType =\n    | 'asyncOptions'\n    | 'options'\n    | 'string'\n    | 'number'\n    | 'array'\n    | 'boolean'\n    | 'password'\n    | 'json'\n    | 'code'\n    | 'date'\n    | 'file'\n    | 'folder'\n\nexport type DbCollectionName = 'Contract' | 'Webhook' | 'Workflow' | 'Credential' | 'Execution' | 'Wallet'\n\nexport type CommonType = string | number | boolean | undefined | null\n\n/**\n * Others\n */\nexport type IDbCollection = {\n    [key in DbCollectionName]: any[]\n}\n\nexport interface ICommonObject {\n    [key: string]: CommonType | ICommonObject | CommonType[] | ICommonObject[]\n}\n\nexport type INetworkMapping = {\n    [key in NETWORK]: CHAIN_ID | DOMAIN_ID | string\n}\n\nexport interface IAttachment {\n    content: string\n    contentType: string\n    size?: number\n    filename?: string\n}\n\nexport interface INodeOptionsValue {\n    label: string\n    name: string\n    description?: string\n    parentGroup?: string\n    inputParameters?: string\n    exampleParameters?: string\n    outputResponse?: string\n    exampleResponse?: ICommonObject\n    show?: INodeDisplay\n    hide?: INodeDisplay\n    /*\n     * Only used on credentialMethod option to hide registeredCredentials\n     * For example: noAuth\n     */\n    hideRegisteredCredential?: boolean\n}\n\nexport interface INodeParams {\n    label: string\n    name: string\n    type: NodeParamsType\n    default?: CommonType | ICommonObject | ICommonObject[]\n    description?: string\n    options?: Array<INodeOptionsValue>\n    array?: Array<INodeParams>\n    loadMethod?: string\n    loadFromDbCollections?: DbCollectionName[]\n    optional?: boolean | INodeDisplay\n    show?: INodeDisplay\n    hide?: INodeDisplay\n    rows?: number\n    placeholder?: string\n}\n\nexport interface INodeExecutionData {\n    [key: string]: CommonType | CommonType[] | ICommonObject | ICommonObject[]\n}\n\nexport interface IWebhookNodeExecutionData {\n    data: INodeExecutionData\n    response?: any\n}\n\nexport interface INodeDisplay {\n    [key: string]: string[] | string\n}\n\nexport interface INodeProperties {\n    label: string\n    name: string\n    type: NodeType\n    description?: string\n    version: number\n    icon?: string\n    category: string\n    incoming: number\n    outgoing: number\n}\n\nexport interface INode extends INodeProperties {\n    actions?: INodeParams[]\n    credentials?: INodeParams[]\n    networks?: INodeParams[]\n    inputParameters?: INodeParams[]\n    loadMethods?: {\n        [key: string]: (nodeData: INodeData, dbCollection?: IDbCollection, apiKeys?: ICommonObject[]) => Promise<INodeOptionsValue[]>\n    }\n    webhookMethods?: {\n        createWebhook: (nodeData: INodeData, webhookFullUrl: string) => Promise<string | undefined>\n        deleteWebhook: (nodeData: INodeData, webhookId: string) => Promise<boolean>\n    }\n    run?(nodeData: INodeData): Promise<INodeExecutionData[] | null>\n    runTrigger?(nodeData: INodeData): Promise<void>\n    removeTrigger?(nodeData: INodeData): Promise<void>\n    runWebhook?(nodeData: INodeData): Promise<IWebhookNodeExecutionData[] | null>\n}\n\nexport interface INodeData extends INodeProperties {\n    emitEventKey?: string // event emitter key for triggers\n\n    actions?: ICommonObject\n    credentials?: ICommonObject\n    networks?: ICommonObject\n    inputParameters?: ICommonObject\n    outputResponses?: ICommonObject\n\n    loadMethod?: string // method to load async options\n    loadFromDbCollections?: DbCollectionName[] // method to load async options\n\n    req?: Request // For webhook\n    webhookEndpoint?: string // For webhook\n}\n\nexport interface INodeCredential {\n    name: string\n    description?: string\n    version: number\n    credentials: INodeParams[]\n}\n\nexport interface ICronJobs {\n    [key: string]: CronJob[]\n}\n\nexport interface IProviders {\n    [key: string]: {\n        provider: any\n        filter?: any\n    }\n}\n\nexport interface IOAuth2RefreshResponse {\n    access_token: string\n    expires_in: string\n}\n"
  },
  {
    "path": "packages/components/src/abis/WBNB.json",
    "content": "[\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"name\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"string\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"guy\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"approve\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"totalSupply\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"transferFrom\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"withdraw\",\n        \"outputs\": [],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"decimals\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint8\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"address\"\n            }\n        ],\n        \"name\": \"balanceOf\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [],\n        \"name\": \"symbol\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"string\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [\n            {\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"transfer\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": false,\n        \"inputs\": [],\n        \"name\": \"deposit\",\n        \"outputs\": [],\n        \"payable\": true,\n        \"stateMutability\": \"payable\",\n        \"type\": \"function\"\n    },\n    {\n        \"constant\": true,\n        \"inputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"\",\n                \"type\": \"address\"\n            }\n        ],\n        \"name\": \"allowance\",\n        \"outputs\": [\n            {\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"payable\": false,\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"payable\": true,\n        \"stateMutability\": \"payable\",\n        \"type\": \"fallback\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": true,\n                \"name\": \"guy\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Approval\",\n        \"type\": \"event\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": true,\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Transfer\",\n        \"type\": \"event\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"dst\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Deposit\",\n        \"type\": \"event\"\n    },\n    {\n        \"anonymous\": false,\n        \"inputs\": [\n            {\n                \"indexed\": true,\n                \"name\": \"src\",\n                \"type\": \"address\"\n            },\n            {\n                \"indexed\": false,\n                \"name\": \"wad\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"name\": \"Withdrawal\",\n        \"type\": \"event\"\n    }\n]\n"
  },
  {
    "path": "packages/components/src/abis/WETH.json",
    "content": "[\n    {\n        \"inputs\": [\n            {\n                \"internalType\": \"address\",\n                \"name\": \"owner\",\n                \"type\": \"address\"\n            },\n            {\n                \"internalType\": \"address\",\n                \"name\": \"spender\",\n                \"type\": \"address\"\n            }\n        ],\n        \"inputParameters\": [\n            {\n                \"label\": \"Owner Address\",\n                \"name\": \"owner\",\n                \"type\": \"string\",\n                \"show\": {\n                    \"actions.function\": [\"allowance\"]\n                }\n            },\n            {\n                \"label\": \"Spender Address\",\n                \"name\": \"spender\",\n                \"type\": \"string\",\n                \"show\": {\n                    \"actions.function\": [\"allowance\"]\n                }\n            }\n        ],\n        \"name\": \"allowance\",\n        \"label\": \"Get Allowance\",\n        \"description\": \"Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom. This is zero by default.\",\n        \"outputs\": [\n            {\n                \"internalType\": \"uint256\",\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [\n            {\n                \"internalType\": \"address\",\n                \"name\": \"spender\",\n                \"type\": \"address\"\n            },\n            {\n                \"internalType\": \"uint256\",\n                \"name\": \"amount\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"inputParameters\": [\n            {\n                \"label\": \"Spender Address\",\n                \"name\": \"spender\",\n                \"type\": \"string\",\n                \"show\": {\n                    \"actions.function\": [\"approve\"]\n                }\n            },\n            {\n                \"label\": \"Amount\",\n                \"name\": \"amount\",\n                \"type\": \"number\",\n                \"show\": {\n                    \"actions.function\": [\"approve\"]\n                }\n            }\n        ],\n        \"name\": \"approve\",\n        \"label\": \"Approve\",\n        \"description\": \"Sets amount as the allowance of spender over the caller’s tokens.\",\n        \"outputs\": [\n            {\n                \"internalType\": \"bool\",\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [\n            {\n                \"internalType\": \"address\",\n                \"name\": \"account\",\n                \"type\": \"address\"\n            }\n        ],\n        \"inputParameters\": [\n            {\n                \"label\": \"Account Address\",\n                \"name\": \"account\",\n                \"type\": \"string\",\n                \"description\": \"Account address to check for remaining amount\",\n                \"show\": {\n                    \"actions.function\": [\"balanceOf\"]\n                }\n            }\n        ],\n        \"name\": \"balanceOf\",\n        \"label\": \"Get Balance\",\n        \"description\": \"Returns the amount of tokens owned by account\",\n        \"outputs\": [\n            {\n                \"internalType\": \"uint256\",\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [],\n        \"name\": \"decimals\",\n        \"label\": \"Get ERC20 Decimals\",\n        \"description\": \"Returns the decimals of ERC20\",\n        \"outputs\": [\n            {\n                \"internalType\": \"uint8\",\n                \"name\": \"\",\n                \"type\": \"uint8\"\n            }\n        ],\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [],\n        \"name\": \"name\",\n        \"label\": \"Get ERC20 Name\",\n        \"description\": \"Returns the name of ERC20\",\n        \"outputs\": [\n            {\n                \"internalType\": \"string\",\n                \"name\": \"\",\n                \"type\": \"string\"\n            }\n        ],\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [],\n        \"name\": \"symbol\",\n        \"label\": \"Get ERC20 Symbol\",\n        \"description\": \"Returns the symbol of ERC20\",\n        \"outputs\": [\n            {\n                \"internalType\": \"string\",\n                \"name\": \"\",\n                \"type\": \"string\"\n            }\n        ],\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [],\n        \"name\": \"totalSupply\",\n        \"label\": \"Get ERC20 Total Supply\",\n        \"description\": \"Returns the total supply of ERC20\",\n        \"outputs\": [\n            {\n                \"internalType\": \"uint256\",\n                \"name\": \"\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"stateMutability\": \"view\",\n        \"type\": \"function\"\n    },\n    {\n        \"inputs\": [\n            {\n                \"internalType\": \"address\",\n                \"name\": \"from\",\n                \"type\": \"address\"\n            },\n            {\n                \"internalType\": \"address\",\n                \"name\": \"to\",\n                \"type\": \"address\"\n            },\n            {\n                \"internalType\": \"uint256\",\n                \"name\": \"amount\",\n                \"type\": \"uint256\"\n            }\n        ],\n        \"inputParameters\": [\n            {\n                \"label\": \"From Address\",\n                \"name\": \"from\",\n                \"type\": \"string\",\n                \"description\": \"Account address to transfer the token\",\n                \"show\": {\n                    \"actions.function\": [\"transferFrom\"]\n                }\n            },\n            {\n                \"label\": \"To Address\",\n                \"name\": \"to\",\n                \"type\": \"string\",\n                \"description\": \"Account address to receive the token\",\n                \"show\": {\n                    \"actions.function\": [\"transferFrom\"]\n                }\n            },\n            {\n                \"label\": \"Amount\",\n                \"name\": \"amount\",\n                \"type\": \"number\",\n                \"description\": \"Amount of token transfer\",\n                \"show\": {\n                    \"actions.function\": [\"transferFrom\"]\n                }\n            }\n        ],\n        \"name\": \"transferFrom\",\n        \"label\": \"Transfer From\",\n        \"description\": \"Moves amount tokens from sender to recipient using the allowance mechanism. Amount is then deducted from the caller’s allowance.\",\n        \"outputs\": [\n            {\n                \"internalType\": \"bool\",\n                \"name\": \"\",\n                \"type\": \"bool\"\n            }\n        ],\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n    }\n]\n"
  },
  {
    "path": "packages/components/src/index.ts",
    "content": "export * from './Interface'\nexport * from './ETHOperations'\nexport * from './ChainNetwork'\nexport * from './utils'\n"
  },
  {
    "path": "packages/components/src/utils.ts",
    "content": "import axios, { AxiosRequestConfig } from 'axios'\nimport ClientOAuth2 from 'client-oauth2'\nimport FormData from 'form-data'\nimport { ICommonObject, INodeExecutionData, IWebhookNodeExecutionData, IOAuth2RefreshResponse } from './Interface'\nimport { scryptSync, timingSafeEqual } from 'crypto'\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nexport const OAUTH2_REFRESHED = 'oAuth2RefreshedData'\nexport const numberOrExpressionRegex = '^(\\\\d+\\\\.?\\\\d*|{{.*}})$' //return true if string consists only numbers OR expression {{}}\nexport const notEmptyRegex = '(.|\\\\s)*\\\\S(.|\\\\s)*' //return true if string is not empty or blank\n\n/**\n * Return responses as INodeExecutionData\n *\n * @export\n * @param {(ICommonObject | ICommonObject[])} responseData\n * @returns {INodeExecutionData[]}\n */\nexport function returnNodeExecutionData(responseData: ICommonObject | ICommonObject[], oAuth2RefreshedData?: any): INodeExecutionData[] {\n    const returnData: INodeExecutionData[] = []\n\n    if (!Array.isArray(responseData)) {\n        responseData = [responseData]\n    }\n\n    responseData.forEach((data) => {\n        const obj = { data } as ICommonObject\n\n        if (data && data.attachments) {\n            if (Array.isArray(data.attachments) && data.attachments.length) obj.attachments = data.attachments\n            else if (!Array.isArray(data.attachments)) obj.attachments = data.attachments\n        }\n\n        if (data && data.html) obj.html = data.html\n\n        if (oAuth2RefreshedData && Object.keys(oAuth2RefreshedData).length > 0) obj[OAUTH2_REFRESHED] = oAuth2RefreshedData\n\n        returnData.push(obj)\n    })\n\n    return returnData\n}\n\n/**\n * Return responses as IWebhookNodeExecutionData\n *\n * @export\n * @param {(ICommonObject | ICommonObject[])} responseData\n * @param {string} webhookReturnResponse\n * @returns {IWebhookNodeExecutionData[]}\n */\nexport function returnWebhookNodeExecutionData(\n    responseData: ICommonObject | ICommonObject[],\n    webhookReturnResponse?: string\n): IWebhookNodeExecutionData[] {\n    const returnData: IWebhookNodeExecutionData[] = []\n\n    if (!Array.isArray(responseData)) {\n        responseData = [responseData]\n    }\n\n    responseData.forEach((data) => {\n        const returnObj = {\n            data\n        } as IWebhookNodeExecutionData\n\n        if (webhookReturnResponse) returnObj.response = webhookReturnResponse\n\n        returnData.push(returnObj)\n    })\n\n    return returnData\n}\n\n/**\n * Serialize axios query params\n *\n * @export\n * @param {any} params\n * @param {boolean} skipIndex // Set to true if you want same params to be: param=1&param=2 instead of: param[0]=1&param[1]=2\n * @returns {string}\n */\nexport function serializeQueryParams(params: any, skipIndex?: boolean): string {\n    const parts: any[] = []\n\n    const encode = (val: string) => {\n        return encodeURIComponent(val)\n            .replace(/%3A/gi, ':')\n            .replace(/%24/g, '$')\n            .replace(/%2C/gi, ',')\n            .replace(/%20/g, '+')\n            .replace(/%5B/gi, '[')\n            .replace(/%5D/gi, ']')\n    }\n\n    const convertPart = (key: string, val: any) => {\n        if (val instanceof Date) val = val.toISOString()\n        else if (val instanceof Object) val = JSON.stringify(val)\n\n        parts.push(encode(key) + '=' + encode(val))\n    }\n\n    Object.entries(params).forEach(([key, val]) => {\n        if (val === null || typeof val === 'undefined') return\n\n        if (Array.isArray(val)) val.forEach((v, i) => convertPart(`${key}${skipIndex ? '' : `[${i}]`}`, v))\n        else convertPart(key, val)\n    })\n\n    return parts.join('&')\n}\n\n/**\n * Handle error from try catch\n *\n * @export\n * @param {any} error\n * @returns {string}\n */\nexport function handleErrorMessage(error: any): string {\n    let errorMessage = ''\n\n    if (error.message) {\n        errorMessage += error.message + '. '\n    }\n\n    if (error.response && error.response.data) {\n        if (error.response.data.error) {\n            if (typeof error.response.data.error === 'object') errorMessage += JSON.stringify(error.response.data.error) + '. '\n            else if (typeof error.response.data.error === 'string') errorMessage += error.response.data.error + '. '\n        } else if (error.response.data.msg) errorMessage += error.response.data.msg + '. '\n        else if (error.response.data.Message) errorMessage += error.response.data.Message + '. '\n        else if (typeof error.response.data === 'string') errorMessage += error.response.data + '. '\n    }\n\n    if (!errorMessage) errorMessage = 'Unexpected Error.'\n\n    return errorMessage\n}\n\n/**\n * Refresh access_token for oAuth2 apps\n *\n * @export\n * @param {ICommonObject} credentials\n */\nexport async function refreshOAuth2Token(credentials: ICommonObject) {\n    const accessTokenUrl = credentials!.accessTokenUrl as string\n    const authUrl = credentials!.authUrl as string\n    const client_id = credentials!.clientID as string\n    const client_secret = credentials!.clientSecret as string\n    const refreshToken = credentials!.refresh_token as string\n    const accessToken = credentials!.access_token as string\n    const tokenType = credentials!.token_type as string\n\n    return await refreshThroughClient(client_id, client_secret, accessTokenUrl, authUrl, accessToken, refreshToken, tokenType)\n}\n\nconst refreshThroughClient = async (\n    client_id: string,\n    client_secret: string,\n    accessTokenUrl: string,\n    authUrl: string,\n    accessToken: string,\n    refreshToken: string,\n    tokenType: string\n) => {\n    try {\n        const oAuth2Parameters = {\n            clientId: client_id,\n            clientSecret: client_secret,\n            accessTokenUri: accessTokenUrl,\n            authorizationUri: authUrl\n        }\n\n        const oAuthObj = new ClientOAuth2(oAuth2Parameters)\n        const { data } = await oAuthObj.credentials.getToken()\n\n        const tokenInstance = oAuthObj.createToken(accessToken, refreshToken, tokenType, data)\n        const newToken = await tokenInstance.refresh()\n\n        const { access_token, expires_in } = newToken.data\n\n        const returnItem: IOAuth2RefreshResponse = {\n            access_token,\n            expires_in\n        }\n        return returnItem\n    } catch (e) {\n        return await refreshThroughHttpBody(client_id, client_secret, accessTokenUrl, refreshToken)\n    }\n}\n\nconst refreshThroughHttpBody = async (client_id: string, client_secret: string, accessTokenUrl: string, refreshToken: string) => {\n    const method = 'POST'\n    const axiosConfig: AxiosRequestConfig = {\n        method,\n        url: accessTokenUrl,\n        data: {\n            grant_type: 'refresh_token',\n            client_id,\n            client_secret,\n            refresh_token: refreshToken\n        },\n        headers: {\n            'Content-Type': 'application/json; charset=utf-8'\n        }\n    }\n\n    try {\n        const response = await axios(axiosConfig)\n        const refreshedTokenResp = response.data\n\n        const returnItem: IOAuth2RefreshResponse = {\n            access_token: refreshedTokenResp.access_token,\n            expires_in: refreshedTokenResp.expires_in\n        }\n\n        return returnItem\n    } catch (e) {\n        return await refreshThroughHttpHeader(client_id, client_secret, accessTokenUrl, refreshToken)\n    }\n}\n\nconst refreshThroughHttpHeader = async (client_id: string, client_secret: string, accessTokenUrl: string, refreshToken: string) => {\n    const method = 'POST'\n    const formData = new FormData()\n    formData.append('grant_type', 'refresh_token')\n    formData.append('refresh_token', refreshToken)\n\n    const axiosConfig: AxiosRequestConfig = {\n        method,\n        url: accessTokenUrl,\n        data: formData,\n        headers: {\n            ...formData.getHeaders(),\n            Authorization: `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString('base64')}`\n        }\n    }\n\n    try {\n        const response = await axios(axiosConfig)\n        const refreshedTokenResp = response.data\n\n        const returnItem: IOAuth2RefreshResponse = {\n            access_token: refreshedTokenResp.access_token,\n            expires_in: refreshedTokenResp.expires_in\n        }\n\n        return returnItem\n    } catch (e) {\n        throw handleErrorMessage(e)\n    }\n}\n\n/**\n * Returns the path of node modules package\n * @param {string} packageName\n * @returns {string}\n */\nexport const getNodeModulesPackagePath = (packageName: string): string => {\n    const checkPaths = [\n        path.join(__dirname, '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', '..', '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', '..', '..', '..', 'node_modules', packageName)\n    ]\n    for (const checkPath of checkPaths) {\n        if (fs.existsSync(checkPath)) {\n            return checkPath\n        }\n    }\n    return ''\n}\n\n/**\n * Verify valid keys\n * @param {string} storedKey\n * @param {string} suppliedKey\n * @returns {boolean}\n */\nexport const compareKeys = (storedKey: string, suppliedKey: string) => {\n    const [hashedPassword, salt] = storedKey.split('.')\n    const buffer = scryptSync(suppliedKey, salt, 64) as Buffer\n    return timingSafeEqual(Buffer.from(hashedPassword, 'hex'), buffer)\n}\n"
  },
  {
    "path": "packages/components/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"lib\": [\"es2017\"],\n        \"target\": \"es2017\" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,\n        \"experimentalDecorators\": true /* Enable experimental support for TC39 stage 2 draft decorators. */,\n        \"emitDecoratorMetadata\": true /* Emit design-type metadata for decorated declarations in source files. */,\n        \"module\": \"commonjs\" /* Specify what module code is generated. */,\n        \"outDir\": \"./dist/\",\n        \"resolveJsonModule\": true,\n        \"esModuleInterop\": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,\n        \"forceConsistentCasingInFileNames\": true /* Ensure that casing is correct in imports. */,\n        \"strict\": true /* Enable all strict type-checking options. */,\n        \"skipLibCheck\": true /* Skip type checking all .d.ts files. */,\n        \"sourceMap\": true,\n        \"strictPropertyInitialization\": false,\n        \"useUnknownInCatchVariables\": false,\n        \"declaration\": true\n    },\n    \"include\": [\"src\", \"nodes\", \"credentials\"]\n}\n"
  },
  {
    "path": "packages/server/README.md",
    "content": "<!-- markdownlint-disable MD030 -->\n\n# Outerbridge - Automate Web3 and Web2 applications\n\nOuterbridge is a low code/no code workflow automation application, focusing on integrating both on-chain and off-chain applications. The project is licensed under [Apache License Version 2.0](https://github.com/Outerbridgeio/Outerbridge/blob/master/LICENSE.md), source available and free to self-host.\n\n![Outerbridge](https://raw.githubusercontent.com/Outerbridgeio/Outerbridge/master/assets/outerbridge_brand.png)\n\n## ⚡Quick Start\n\n1. Install MongoDB [locally](https://www.mongodb.com/docs/manual/administration/install-community/) OR follow the guide of using MongoDB Atlas [here](https://docs.outerbridge.io/get-started#mongodb-atlas)\n2. Install Outerbridge\n    ```bash\n    npm install -g outerbridge\n    ```\n3. Start Outerbridge\n\n    ```bash\n    npx outerbridge start\n    ```\n\n    If using MongoDB Atlas\n\n    ```bash\n    npx outerbridge start --mongourl=mongodb+srv://<user>:<password>@<your-cluster>.mongodb.net/outerbridge?retryWrites=true&w=majority\n    ```\n\n4. Open [http://localhost:3000](http://localhost:3000)\n\n## 📖 Documentation\n\nOfficial Outerbridge docs can be found under: [https://docs.outerbridge.io](https://docs.outerbridge.io)\n\n## 💻 Cloud Hosted\n\n-   [Cloud Hosted](https://app.outerbridge.io) version of Outerbridge.\n\n## 🌐 Self Host\n\n-   Digital Ocean Droplet: [Setup guide](https://gist.github.com/HenryHengZJ/93210d43d655b4172ee50794ce473b62)\n-   AWS EC2: [Setup guide](https://gist.github.com/HenryHengZJ/627cec19671664a88754c7e383232dc8)\n\n## 🙋 Support\n\nFeel free to ask any questions, raise problems, and request new features in [discussion](https://github.com/Outerbridgeio/Outerbridge/discussions)\n\n## 🙌 Contributing\n\nSee [contributing guide](https://github.com/Outerbridgeio/Outerbridge/blob/master/CONTRIBUTING.md). Reach out to us at [Discord](https://discord.gg/Y9VE4ykPDJ) if you have any questions or issues.\n\n## 📄 License\n\nSource code in this repository is made available under the [Apache License Version 2.0](https://github.com/Outerbridgeio/Outerbridge/blob/master/LICENSE.md).\n"
  },
  {
    "path": "packages/server/babel.config.js",
    "content": "module.exports = {\n    extends: '../../babel.config.js'\n}\n"
  },
  {
    "path": "packages/server/bin/dev",
    "content": "#!/usr/bin/env node\n\nconst oclif = require('@oclif/core')\n\nconst path = require('path')\nconst project = path.join(__dirname, '..', 'tsconfig.json')\n\n// In dev mode -> use ts-node and dev plugins\nprocess.env.NODE_ENV = 'development'\n\nrequire('ts-node').register({ project })\n\n// In dev mode, always show stack traces\noclif.settings.debug = true\n\n// Start the CLI\noclif.run().then(oclif.flush).catch(oclif.Errors.handle)\n"
  },
  {
    "path": "packages/server/bin/dev.cmd",
    "content": "@echo off\n\nnode \"%~dp0\\dev\" %*"
  },
  {
    "path": "packages/server/bin/run",
    "content": "#!/usr/bin/env node\n\nconst oclif = require('@oclif/core')\n\noclif.run().then(require('@oclif/core/flush')).catch(require('@oclif/core/handle'))\n"
  },
  {
    "path": "packages/server/bin/run.cmd",
    "content": "@echo off\n\nnode \"%~dp0\\run\" %*"
  },
  {
    "path": "packages/server/nodemon.json",
    "content": "{\n    \"ignore\": [\"**/*.spec.ts\", \".git\", \"node_modules\"],\n    \"watch\": [\"commands\", \"index.ts\", \"src\"],\n    \"exec\": \"yarn oclif-dev\",\n    \"ext\": \"ts\"\n}\n"
  },
  {
    "path": "packages/server/oauth2.html",
    "content": "<html>\n\t<script>\n\t\t(function messageToUI() {\n\t\t\twindow.opener.postMessage('success', '*');\n\t\t}());\n\t</script>\n\tAccount connected! You may now close the window.\n</html>\n"
  },
  {
    "path": "packages/server/package.json",
    "content": "{\n    \"name\": \"outerbridge\",\n    \"version\": \"1.0.20\",\n    \"description\": \"Outerbridge Server\",\n    \"main\": \"dist/index\",\n    \"types\": \"dist/index.d.ts\",\n    \"bin\": {\n        \"outerbridge\": \"./bin/run\"\n    },\n    \"files\": [\n        \"bin\",\n        \"dist\",\n        \"npm-shrinkwrap.json\",\n        \"oclif.manifest.json\",\n        \"oauth2.html\",\n        \".env\"\n    ],\n    \"oclif\": {\n        \"bin\": \"outerbridge\",\n        \"commands\": \"./dist/commands\"\n    },\n    \"scripts\": {\n        \"build\": \"tsc\",\n        \"start\": \"run-script-os\",\n        \"start:windows\": \"cd bin && run start\",\n        \"start:default\": \"cd bin && ./run start\",\n        \"dev\": \"concurrently \\\"yarn watch\\\" \\\"nodemon\\\"\",\n        \"oclif-dev\": \"run-script-os\",\n        \"oclif-dev:windows\": \"cd bin && dev start\",\n        \"oclif-dev:default\": \"cd bin && ./dev start\",\n        \"postpack\": \"shx rm -f oclif.manifest.json\",\n        \"prepack\": \"yarn build && oclif manifest && oclif readme\",\n        \"typeorm\": \"typeorm-ts-node-commonjs\",\n        \"watch\": \"tsc --watch\",\n        \"version\": \"oclif readme && git add README.md\"\n    },\n    \"keywords\": [],\n    \"homepage\": \"https://outerbridge.io\",\n    \"author\": {\n        \"name\": \"Henry Heng\",\n        \"email\": \"henryheng@outerbridge.io\"\n    },\n    \"engines\": {\n        \"node\": \">=14.7.0\"\n    },\n    \"license\": \"SEE LICENSE IN LICENSE.md\",\n    \"dependencies\": {\n        \"@oclif/core\": \"^1.13.10\",\n        \"axios\": \"^0.27.2\",\n        \"client-oauth2\": \"^4.3.3\",\n        \"cors\": \"^2.8.5\",\n        \"crypto-js\": \"^4.1.1\",\n        \"dotenv\": \"^16.0.0\",\n        \"ethers\": \"^5.6.8\",\n        \"express\": \"^4.17.3\",\n        \"is-localhost-ip\": \"^2.0.0\",\n        \"localtunnel\": \"^2.0.2\",\n        \"moment-timezone\": \"^0.5.34\",\n        \"mongodb\": \"^3.6.0\",\n        \"outerbridge-components\": \"*\",\n        \"outerbridge-ui\": \"*\",\n        \"pg\": \"^8.4.0\",\n        \"reflect-metadata\": \"^0.1.13\",\n        \"socket.io\": \"^4.5.1\",\n        \"typeorm\": \"^0.3.6\"\n    },\n    \"devDependencies\": {\n        \"@types/cors\": \"^2.8.12\",\n        \"@types/crypto-js\": \"^4.1.1\",\n        \"@types/localtunnel\": \"^2.0.1\",\n        \"@types/mongodb\": \"^3.6.0\",\n        \"concurrently\": \"^7.1.0\",\n        \"nodemon\": \"^2.0.15\",\n        \"oclif\": \"^3\",\n        \"run-script-os\": \"^1.1.6\",\n        \"shx\": \"^0.3.3\",\n        \"ts-node\": \"^10.7.0\",\n        \"typescript\": \"^4.8.4\"\n    }\n}\n"
  },
  {
    "path": "packages/server/src/ActiveTestTriggerPool.ts",
    "content": "import { INodeData } from 'outerbridge-components'\nimport { IActiveTestTriggerPool, IComponentNodesPool } from './Interface'\n\n/**\n * This pool is to keep track of active test triggers (event listeners),\n * so we can clear the event listeners whenever user refresh or exit page\n */\nexport class ActiveTestTriggerPool {\n    activeTestTriggers: IActiveTestTriggerPool = {}\n\n    /**\n     * Add to the pool\n     * @param {string} nodeName\n     * @param {INodeData} nodeData\n     */\n    add(nodeName: string, nodeData: INodeData) {\n        this.activeTestTriggers[nodeName] = nodeData\n    }\n\n    /**\n     * Remove all triggers from the pool\n     * @param {IComponentNodesPool} componentNodes\n     */\n    async removeAll(componentNodes: IComponentNodesPool) {\n        const toBeDeleted: string[] = []\n        for (const nodeName in this.activeTestTriggers) {\n            const triggerNodeInstance = componentNodes[nodeName]\n            await triggerNodeInstance.removeTrigger!.call(triggerNodeInstance, this.activeTestTriggers[nodeName])\n            toBeDeleted.push(nodeName)\n        }\n\n        for (const nodeName in toBeDeleted) {\n            delete this.activeTestTriggers[nodeName]\n        }\n    }\n\n    /**\n     * Remove single trigger from the pool\n     * @param {string} nodeName\n     * @param {IComponentNodesPool} componentNodes\n     */\n    async remove(nodeName: string, componentNodes: IComponentNodesPool) {\n        if (Object.prototype.hasOwnProperty.call(this.activeTestTriggers, nodeName)) {\n            const triggerNodeInstance = componentNodes[nodeName]\n            await triggerNodeInstance.removeTrigger!.call(triggerNodeInstance, this.activeTestTriggers[nodeName])\n            delete this.activeTestTriggers[nodeName]\n        }\n    }\n}\n"
  },
  {
    "path": "packages/server/src/ActiveTestWebhookPool.ts",
    "content": "import { INodeData } from 'outerbridge-components'\nimport { IActiveTestWebhookPool, IComponentNodesPool, IReactFlowEdge, IReactFlowNode, WebhookMethod } from './Interface'\n\n/**\n * This pool is to keep track of active test webhooks,\n * so we can clear the webhooks whenever user refresh or exit page\n */\nexport class ActiveTestWebhookPool {\n    activeTestWebhooks: IActiveTestWebhookPool = {}\n\n    /**\n     * Add to the pool\n     * @param {string} webhookEndpoint\n     * @param {WebhookMethod} httpMethod\n     * @param {IReactFlowNode[]} nodes\n     * @param {IReactFlowEdge[]} edges\n     * @param {INodeData} nodeData\n     * @param {string} clientId\n     * @param {boolean} isTestWorkflow\n     * @param {string} webhookId\n     */\n    add(\n        webhookEndpoint: string,\n        httpMethod: WebhookMethod,\n        nodes: IReactFlowNode[],\n        edges: IReactFlowEdge[],\n        nodeData: INodeData,\n        webhookNodeId: string,\n        clientId: string,\n        isTestWorkflow: boolean,\n        webhookId?: string\n    ) {\n        const key = `${webhookEndpoint}_${httpMethod}`\n        this.activeTestWebhooks[key] = {\n            nodes,\n            edges,\n            nodeData,\n            clientId,\n            webhookNodeId,\n            isTestWorkflow,\n            webhookId\n        }\n    }\n\n    /**\n     * Remove all webhooks from the pool\n     * @param {IComponentNodesPool} componentNodes\n     */\n    async removeAll(componentNodes: IComponentNodesPool) {\n        const toBeDeleted: string[] = []\n        for (const key in this.activeTestWebhooks) {\n            const { nodeData, webhookId } = this.activeTestWebhooks[key]\n            const nodeName = nodeData.name\n            const webhookNodeInstance = componentNodes[nodeName]\n\n            // Delete webhook from 3rd party apps\n            if (webhookId) {\n                await webhookNodeInstance.webhookMethods?.deleteWebhook(nodeData, webhookId)\n            }\n\n            toBeDeleted.push(key)\n        }\n\n        for (const key in toBeDeleted) {\n            delete this.activeTestWebhooks[key]\n        }\n    }\n\n    /**\n     * Remove single webhook from the pool\n     * @param {string} testWebhookKey\n     * @param {IComponentNodesPool} componentNodes\n     */\n    async remove(testWebhookKey: string, componentNodes: IComponentNodesPool) {\n        if (Object.prototype.hasOwnProperty.call(this.activeTestWebhooks, testWebhookKey)) {\n            const { nodeData, webhookId } = this.activeTestWebhooks[testWebhookKey]\n            const nodeName = nodeData.name\n            const webhookNodeInstance = componentNodes[nodeName]\n\n            // Delete webhook from 3rd party apps\n            if (webhookId) {\n                await webhookNodeInstance.webhookMethods?.deleteWebhook(nodeData, webhookId)\n            }\n\n            delete this.activeTestWebhooks[testWebhookKey]\n        }\n    }\n}\n"
  },
  {
    "path": "packages/server/src/ChildProcess.ts",
    "content": "import { ICommonObject, INodeData, INodeExecutionData } from 'outerbridge-components'\nimport {\n    IChildProcessMessage,\n    IExploredNode,\n    INodeQueue,\n    IRunWorkflowMessageValue,\n    IVariableDict,\n    IWorkflowExecutedData\n} from './Interface'\nimport { checkOAuth2TokenRefreshed, decryptCredentials } from './utils'\nimport { DataSource } from 'typeorm'\nimport { Workflow } from './entity/Workflow'\nimport { Execution } from './entity/Execution'\nimport { Credential } from './entity/Credential'\nimport { Webhook } from './entity/Webhook'\nimport { Contract } from './entity/Contract'\nimport { Wallet } from './entity/Wallet'\nimport lodash from 'lodash'\n\nexport class ChildProcess {\n    /**\n     * Stop child process after 5 secs timeout\n     */\n    static async stopChildProcess() {\n        setTimeout(() => {\n            process.exit(0)\n        }, 50000)\n    }\n\n    /**\n     * Run the workflow using Breadth First Search Topological Sort\n     * @param {IRunWorkflowMessageValue} messageValue\n     * @return {Promise<void>}\n     */\n    async runWorkflow(messageValue: IRunWorkflowMessageValue): Promise<void> {\n        process.on('SIGTERM', ChildProcess.stopChildProcess)\n        process.on('SIGINT', ChildProcess.stopChildProcess)\n\n        await sendToParentProcess('start', '_')\n\n        const childAppDataSource = await initDB()\n\n        // Create a Queue and add our initial node in it\n        const { startingNodeIds, componentNodes, reactFlowNodes, reactFlowEdges, graph, workflowExecutedData } = messageValue\n\n        const nodeQueue = [] as INodeQueue[]\n        const exploredNode = {} as IExploredNode\n        // In the case of infinite loop, only max 3 loops will be executed\n        const maxLoop = 3\n\n        for (let i = 0; i < startingNodeIds.length; i += 1) {\n            nodeQueue.push({ nodeId: startingNodeIds[i], depth: 0 })\n            exploredNode[startingNodeIds[i]] = { remainingLoop: maxLoop, lastSeenDepth: 0 }\n        }\n\n        while (nodeQueue.length) {\n            const { nodeId, depth } = nodeQueue.shift() as INodeQueue\n            const ignoreNodeIds: string[] = []\n\n            if (!startingNodeIds.includes(nodeId)) {\n                const reactFlowNode = reactFlowNodes.find((nd) => nd.id === nodeId)\n                if (!reactFlowNode || reactFlowNode === undefined) continue\n\n                try {\n                    const nodeInstanceFilePath = componentNodes[reactFlowNode.data.name].filePath\n                    const nodeModule = await import(nodeInstanceFilePath)\n                    const newNodeInstance = new nodeModule.nodeClass()\n\n                    await decryptCredentials(reactFlowNode.data, childAppDataSource)\n\n                    const reactFlowNodeData: INodeData[] = resolveVariables(reactFlowNode.data, workflowExecutedData)\n\n                    let results: INodeExecutionData[] = []\n\n                    for (let i = 0; i < reactFlowNodeData.length; i += 1) {\n                        const result = await newNodeInstance.run!.call(newNodeInstance, reactFlowNodeData[i])\n                        checkOAuth2TokenRefreshed(result, reactFlowNodeData[i], childAppDataSource)\n                        if (result) results.push(...result)\n                    }\n\n                    // Determine which nodes to route next when it comes to ifElse\n                    if (results.length && nodeId.includes('ifElse')) {\n                        let anchorIndex = -1\n                        if (Array.isArray(results) && Object.keys((results as any)[0].data).length === 0) {\n                            anchorIndex = 0\n                        } else if (Array.isArray(results) && Object.keys((results as any)[1].data).length === 0) {\n                            anchorIndex = 1\n                        }\n                        const ifElseEdge = reactFlowEdges.find(\n                            (edg) => edg.source === nodeId && edg.sourceHandle === `${nodeId}-output-${anchorIndex}`\n                        )\n                        if (ifElseEdge) {\n                            ignoreNodeIds.push(ifElseEdge.target)\n                        }\n                    }\n\n                    const newWorkflowExecutedData = {\n                        nodeId,\n                        nodeLabel: reactFlowNode.data.label,\n                        data: results\n                    } as IWorkflowExecutedData\n\n                    workflowExecutedData.push(newWorkflowExecutedData)\n                } catch (e: any) {\n                    // console.error(e);\n                    console.error(e.message)\n                    const newWorkflowExecutedData = {\n                        nodeId,\n                        nodeLabel: reactFlowNode.data.label,\n                        data: [{ error: e.message }]\n                    } as IWorkflowExecutedData\n                    workflowExecutedData.push(newWorkflowExecutedData)\n                    await sendToParentProcess('error', workflowExecutedData)\n                    return\n                }\n            }\n\n            const neighbourNodeIds = graph[nodeId]\n            const nextDepth = depth + 1\n\n            for (let i = 0; i < neighbourNodeIds.length; i += 1) {\n                const neighNodeId = neighbourNodeIds[i]\n\n                if (!ignoreNodeIds.includes(neighNodeId)) {\n                    // If nodeId has been seen, cycle detected\n                    if (Object.prototype.hasOwnProperty.call(exploredNode, neighNodeId)) {\n                        const { remainingLoop, lastSeenDepth } = exploredNode[neighNodeId]\n\n                        if (lastSeenDepth === nextDepth) continue\n\n                        if (remainingLoop === 0) {\n                            break\n                        }\n                        const remainingLoopMinusOne = remainingLoop - 1\n                        exploredNode[neighNodeId] = { remainingLoop: remainingLoopMinusOne, lastSeenDepth: nextDepth }\n                        nodeQueue.push({ nodeId: neighNodeId, depth: nextDepth })\n                    } else {\n                        exploredNode[neighNodeId] = { remainingLoop: maxLoop, lastSeenDepth: nextDepth }\n                        nodeQueue.push({ nodeId: neighNodeId, depth: nextDepth })\n                    }\n                }\n            }\n        }\n        await sendToParentProcess('finish', workflowExecutedData)\n    }\n}\n\n/**\n * Initalize DB in child process\n * @returns {DataSource}\n */\nasync function initDB() {\n    const childAppDataSource = new DataSource({\n        type: 'mongodb',\n        url: process.env.MONGO_URL || `mongodb://${process.env.MONGO_HOST || '0.0.0.0'}:27017/outerbridge`,\n        useNewUrlParser: true,\n        synchronize: true,\n        logging: false,\n        entities: [Workflow, Execution, Credential, Webhook, Contract, Wallet],\n        migrations: [],\n        subscribers: []\n    })\n    return await childAppDataSource.initialize()\n}\n\n/**\n * Get variable value from outputResponses.output\n * @param {string} paramValue\n * @param {IWorkflowExecutedData[]} workflowExecutedData\n * @param {string} key\n * @param {number} loopIndex\n * @returns {string}\n */\nfunction getVariableValue(paramValue: string, workflowExecutedData: IWorkflowExecutedData[], key: string, loopIndex: number): string {\n    let returnVal = paramValue\n    const variableStack = []\n    const variableDict = {} as IVariableDict\n    let startIdx = 0\n    const endIdx = returnVal.length - 1\n\n    while (startIdx < endIdx) {\n        const substr = returnVal.substring(startIdx, startIdx + 2)\n\n        // Store the opening double curly bracket\n        if (substr === '{{') {\n            variableStack.push({ substr, startIdx: startIdx + 2 })\n        }\n\n        // Found the complete variable\n        if (substr === '}}' && variableStack.length > 0 && variableStack[variableStack.length - 1].substr === '{{') {\n            const variableStartIdx = variableStack[variableStack.length - 1].startIdx\n            const variableEndIdx = startIdx\n            const variableFullPath = returnVal.substring(variableStartIdx, variableEndIdx)\n\n            // Split by first occurence of '[' to get just nodeId\n            const [variableNodeId, ...rest] = variableFullPath.split('[')\n            let variablePath = 'data' + '[' + rest.join('[')\n            if (variablePath.includes('$index')) {\n                variablePath = variablePath.split('$index').join(loopIndex.toString())\n            }\n\n            const executedNodeData = workflowExecutedData.find((exec) => exec.nodeId === variableNodeId)\n            if (executedNodeData) {\n                const resolvedVariablePath = getVariableValue(variablePath, workflowExecutedData, key, loopIndex)\n                const variableValue = lodash.get(executedNodeData, resolvedVariablePath)\n                variableDict[`{{${variableFullPath}}}`] = variableValue\n                // For instance: const var1 = \"some var\"\n                if (key === 'code' && typeof variableValue === 'string') variableDict[`{{${variableFullPath}}}`] = `\"${variableValue}\"`\n                if (key === 'code' && typeof variableValue === 'object')\n                    variableDict[`{{${variableFullPath}}}`] = `${JSON.stringify(variableValue)}`\n            }\n            variableStack.pop()\n        }\n        startIdx += 1\n    }\n\n    const variablePaths = Object.keys(variableDict)\n    variablePaths.sort() // Sort by length of variable path because longer path could possibly contains nested variable\n    variablePaths.forEach((path) => {\n        const variableValue = variableDict[path]\n        // Replace all occurence\n        returnVal = returnVal.split(path).join(variableValue)\n    })\n\n    return returnVal\n}\n\n/**\n * Get minimum variable array length from outputResponses.output\n * @param {string} paramValue\n * @param {IReactFlowNode[]} reactFlowNodes\n * @returns {number}\n */\nexport const getVariableLength = (paramValue: string, workflowExecutedData: IWorkflowExecutedData[]): number => {\n    let minLoop = Infinity\n    const variableStack = []\n    let startIdx = 0\n    const endIdx = paramValue.length - 1\n\n    while (startIdx < endIdx) {\n        const substr = paramValue.substring(startIdx, startIdx + 2)\n\n        // Store the opening double curly bracket\n        if (substr === '{{') {\n            variableStack.push({ substr, startIdx: startIdx + 2 })\n        }\n\n        // Found the complete variable\n        if (substr === '}}' && variableStack.length > 0 && variableStack[variableStack.length - 1].substr === '{{') {\n            const variableStartIdx = variableStack[variableStack.length - 1].startIdx\n            const variableEndIdx = startIdx\n            const variableFullPath = paramValue.substring(variableStartIdx, variableEndIdx)\n\n            if (variableFullPath.includes('$index')) {\n                // Split by first occurence of '[' to get just nodeId\n                const [variableNodeId, ...rest] = variableFullPath.split('[')\n                const variablePath = 'data' + '[' + rest.join('[')\n                const [variableArrayPath, ..._] = variablePath.split('[$index]')\n\n                const executedNodeData = workflowExecutedData.find((exec) => exec.nodeId === variableNodeId)\n                if (executedNodeData) {\n                    const variableValue = lodash.get(executedNodeData, variableArrayPath)\n                    if (Array.isArray(variableValue)) minLoop = Math.min(minLoop, variableValue.length)\n                }\n            }\n            variableStack.pop()\n        }\n        startIdx += 1\n    }\n    return minLoop\n}\n\n/**\n * Loop through each inputs and resolve variable if neccessary\n * @param {INodeData} reactFlowNodeData\n * @param {IWorkflowExecutedData[]} workflowExecutedData\n * @returns {INodeData}\n */\nfunction resolveVariables(reactFlowNodeData: INodeData, workflowExecutedData: IWorkflowExecutedData[]): INodeData[] {\n    const flowNodeDataArray: INodeData[] = []\n    const flowNodeData = lodash.cloneDeep(reactFlowNodeData)\n    const types = ['actions', 'networks', 'inputParameters']\n\n    const getMinForLoop = (paramsObj: ICommonObject) => {\n        let minLoop = Infinity\n        for (const key in paramsObj) {\n            const paramValue = paramsObj[key]\n            if (typeof paramValue === 'string' && paramValue.includes('$index')) {\n                // node.data[$index].smtg\n                minLoop = Math.min(minLoop, getVariableLength(paramValue, workflowExecutedData))\n            }\n            if (Array.isArray(paramValue)) {\n                for (let j = 0; j < paramValue.length; j += 1) {\n                    minLoop = Math.min(minLoop, getMinForLoop(paramValue[j] as ICommonObject))\n                }\n            }\n        }\n        return minLoop\n    }\n\n    const getParamValues = (paramsObj: ICommonObject, loopIndex: number) => {\n        for (const key in paramsObj) {\n            const paramValue = paramsObj[key]\n\n            if (typeof paramValue === 'string') {\n                const resolvedValue = getVariableValue(paramValue, workflowExecutedData, key, loopIndex)\n                paramsObj[key] = resolvedValue\n            }\n\n            if (typeof paramValue === 'number') {\n                const paramValueStr = paramValue.toString()\n                const resolvedValue = getVariableValue(paramValueStr, workflowExecutedData, key, loopIndex)\n                paramsObj[key] = resolvedValue\n            }\n\n            if (Array.isArray(paramValue)) {\n                for (let j = 0; j < paramValue.length; j += 1) {\n                    getParamValues(paramValue[j] as ICommonObject, loopIndex)\n                }\n            }\n        }\n    }\n\n    let minLoop = Infinity\n    for (let i = 0; i < types.length; i += 1) {\n        const paramsObj = (flowNodeData as any)[types[i]]\n        minLoop = Math.min(minLoop, getMinForLoop(paramsObj))\n    }\n\n    if (minLoop === Infinity) {\n        for (let i = 0; i < types.length; i += 1) {\n            const paramsObj = (flowNodeData as any)[types[i]]\n            getParamValues(paramsObj, -1)\n        }\n        return [flowNodeData]\n    } else {\n        for (let j = 0; j < minLoop; j += 1) {\n            const clonedFlowNodeData = lodash.cloneDeep(flowNodeData)\n            for (let i = 0; i < types.length; i += 1) {\n                const paramsObj = (clonedFlowNodeData as any)[types[i]]\n                getParamValues(paramsObj, j)\n            }\n            flowNodeDataArray.push(clonedFlowNodeData)\n        }\n        return flowNodeDataArray\n    }\n}\n\n/**\n * Send data back to parent process\n * @param {string} key Key of message\n * @param {*} value Value of message\n * @returns {Promise<void>}\n */\nasync function sendToParentProcess(key: string, value: any): Promise<void> {\n    // tslint:disable-line:no-any\n    return new Promise((resolve, reject) => {\n        process.send!(\n            {\n                key,\n                value\n            },\n            (error: Error) => {\n                if (error) {\n                    return reject(error)\n                }\n                resolve()\n            }\n        )\n    })\n}\n\nconst childProcess = new ChildProcess()\n\nprocess.on('message', async (message: IChildProcessMessage) => {\n    if (message.key === 'start') {\n        await childProcess.runWorkflow(message.value)\n        process.exit()\n    }\n})\n"
  },
  {
    "path": "packages/server/src/CredentialsPool.ts",
    "content": "import { INodeCredential } from 'outerbridge-components'\n\nimport { IComponentCredentialsPool } from './Interface'\n\nimport path from 'path'\nimport { Dirent } from 'fs'\nimport { getNodeModulesPackagePath } from './utils'\nimport { promises } from 'fs'\n\nexport class CredentialsPool {\n    componentCredentials: IComponentCredentialsPool = {}\n\n    /**\n     * Initialize to get all credentials\n     */\n    async initialize() {\n        const packagePath = getNodeModulesPackagePath('outerbridge-components')\n        const credPath = path.join(packagePath, 'dist', 'credentials')\n        const credFiles = await this.getFiles(credPath)\n        return Promise.all(\n            credFiles.map(async (file) => {\n                if (file.endsWith('.js')) {\n                    const credModule = await import(file)\n                    const newCredInstance: INodeCredential = new credModule.credClass()\n                    this.componentCredentials[newCredInstance.name] = newCredInstance\n                }\n            })\n        )\n    }\n\n    /**\n     * Recursive function to get credential files\n     * @param {string} dir\n     * @returns {string[]}\n     */\n    async getFiles(dir: string): Promise<string[]> {\n        const dirents = await promises.readdir(dir, { withFileTypes: true })\n        const files = await Promise.all(\n            dirents.map((dirent: Dirent) => {\n                const res = path.resolve(dir, dirent.name)\n                return dirent.isDirectory() ? this.getFiles(res) : res\n            })\n        )\n        return Array.prototype.concat(...files)\n    }\n}\n"
  },
  {
    "path": "packages/server/src/DataSource.ts",
    "content": "import 'reflect-metadata'\nimport { DataSource } from 'typeorm'\nimport { Workflow } from './entity/Workflow'\nimport { Execution } from './entity/Execution'\nimport { Credential } from './entity/Credential'\nimport { Webhook } from './entity/Webhook'\nimport { Contract } from './entity/Contract'\nimport { Wallet } from './entity/Wallet'\n\nlet appDataSource: DataSource\n\nexport async function init(): Promise<void> {\n    appDataSource = new DataSource({\n        type: 'mongodb',\n        url: process.env.MONGO_URL || `mongodb://${process.env.MONGO_HOST || '0.0.0.0'}:27017/outerbridge`,\n        useNewUrlParser: true,\n        useUnifiedTopology: true,\n        synchronize: true,\n        logging: false,\n        entities: [Workflow, Execution, Credential, Webhook, Contract, Wallet],\n        migrations: [],\n        subscribers: []\n    })\n}\n\nexport function getDataSource(): DataSource {\n    if (appDataSource === undefined) {\n        init()\n    }\n    return appDataSource\n}\n"
  },
  {
    "path": "packages/server/src/DeployedWorkflowPool.ts",
    "content": "import { INodeData, INodeExecutionData, IWebhookNodeExecutionData } from 'outerbridge-components'\nimport {\n    IDeployedWorkflowsPool,\n    IComponentNodesPool,\n    INodeDirectedGraph,\n    ITriggerNode,\n    IChildProcessMessage,\n    IWorkflowExecutedData,\n    ExecutionState,\n    IReactFlowNode,\n    IReactFlowObject,\n    IReactFlowEdge,\n    IRunWorkflowMessageValue,\n    WebhookMethod,\n    IWebhookNode\n} from './Interface'\nimport { DataSource } from 'typeorm'\nimport { join as pathJoin } from 'path'\nimport { fork } from 'child_process'\nimport * as fs from 'fs'\nimport lodash from 'lodash'\nimport { ObjectId } from 'mongodb'\n\nimport { constructGraphs, getStartingNode, decryptCredentials } from './utils'\nimport { Webhook } from './entity/Webhook'\nimport { Execution } from './entity/Execution'\nimport { Workflow } from './entity/Workflow'\nimport { ActiveTestTriggerPool } from './ActiveTestTriggerPool'\nimport { ActiveTestWebhookPool } from './ActiveTestWebhookPool'\n\nexport class DeployedWorkflowPool {\n    deployedWorkflows: IDeployedWorkflowsPool = {}\n    AppDataSource: DataSource\n\n    /**\n     * Initialize to get all deployed workflows\n     * @param {DataSource} AppDataSource\n     */\n    async initialize(AppDataSource: DataSource, componentNodes: IComponentNodesPool) {\n        this.deployedWorkflows = {}\n        this.AppDataSource = AppDataSource\n\n        const workflows = await this.AppDataSource.getMongoRepository(Workflow).find()\n        for (let i = 0; i < workflows.length; i += 1) {\n            const workflow = workflows[i]\n            if (workflow.deployed) {\n                try {\n                    const flowDataString = workflow.flowData\n                    const flowData: IReactFlowObject = JSON.parse(flowDataString)\n                    const reactFlowNodes = flowData.nodes as IReactFlowNode[]\n                    const reactFlowEdges = flowData.edges as IReactFlowEdge[]\n                    const workflowShortId = workflow.shortId\n\n                    const { graph, nodeDependencies } = constructGraphs(reactFlowNodes, reactFlowEdges)\n                    const { faultyNodeLabels, startingNodeIds } = getStartingNode(nodeDependencies, reactFlowNodes)\n\n                    // If there are faulty nodes and deployed is active, update deployed to false\n                    if (faultyNodeLabels.length) {\n                        const body = {\n                            deployed: false\n                        }\n                        const updateWorkflow = new Workflow()\n                        Object.assign(updateWorkflow, body)\n\n                        AppDataSource.getMongoRepository(Workflow).merge(workflow, updateWorkflow)\n                        await AppDataSource.getMongoRepository(Workflow).save(workflow)\n\n                        continue\n                    }\n\n                    await this.add(startingNodeIds, graph, reactFlowNodes, componentNodes, workflowShortId)\n                } catch (e) {\n                    throw new Error(`Error initializing workflow ${workflow.shortId}: ${e}`)\n                }\n            }\n        }\n    }\n\n    /**\n     * Add workflow to deployed workflow pools\n     * @param {string[]} startingNodeIds\n     * @param {INodeDirectedGraph} graph\n     * @param {IReactFlowNode[]} reactFlowNodes\n     * @param {IComponentNodesPool} componentNodes\n     * @param {string} workflowShortId\n     */\n    async add(\n        startingNodeIds: string[],\n        graph: INodeDirectedGraph,\n        reactFlowNodes: IReactFlowNode[],\n        componentNodes: IComponentNodesPool,\n        workflowShortId: string,\n        activeTestTriggerPool?: ActiveTestTriggerPool,\n        activeTestWebhookPool?: ActiveTestWebhookPool\n    ) {\n        for (let i = 0; i < startingNodeIds.length; i += 1) {\n            const startingNodeId = startingNodeIds[i]\n            const startNode = reactFlowNodes.find((nd) => nd.id === startingNodeId)\n            const emitEventKey = `${workflowShortId}_${startingNodeId}`\n\n            if (startNode && startNode.data && startNode.data.type === 'trigger') {\n                const nodeInstance = componentNodes[startNode.data.name]\n                const triggerNodeInstance = nodeInstance as ITriggerNode\n\n                // Remove test trigger\n                if (activeTestTriggerPool) await activeTestTriggerPool.remove(startNode.data.name, componentNodes)\n\n                triggerNodeInstance.on(emitEventKey, async (result: INodeExecutionData[]) => {\n                    await this.startWorkflow(workflowShortId, startNode, startingNodeId, result, componentNodes, startingNodeIds, graph)\n                })\n\n                await decryptCredentials(startNode.data)\n\n                const nodeData = {\n                    ...startNode.data,\n                    emitEventKey,\n                    nodeId: startingNodeId,\n                    workflowShortId\n                } as INodeData\n                await triggerNodeInstance.runTrigger!.call(triggerNodeInstance, nodeData)\n            } else if (startNode && startNode.data && startNode.data.type === 'webhook') {\n                const nodeInstance = componentNodes[startNode.data.name]\n                const webhookNodeInstance = nodeInstance as IWebhookNode\n\n                const newBody = {\n                    nodeId: startingNodeId,\n                    webhookEndpoint: startNode.data.webhookEndpoint,\n                    httpMethod: (startNode.data.inputParameters?.httpMethod as WebhookMethod) || 'POST',\n                    workflowShortId\n                } as any\n\n                // Remove test webhook\n                if (activeTestWebhookPool)\n                    await activeTestWebhookPool.remove(`${newBody.webhookEndpoint}_${newBody.httpMethod}`, componentNodes)\n\n                const foundWebhook = await this.AppDataSource.getMongoRepository(Webhook).findOneBy(newBody)\n\n                // If third party webhook exists, delete and re-create with new tunnel\n                if (foundWebhook && foundWebhook.webhookId && process.env.ENABLE_TUNNEL === 'true') {\n                    if (!process.env.TUNNEL_BASE_URL) {\n                        return\n                    }\n                    await this.AppDataSource.getMongoRepository(Webhook).deleteOne({\n                        _id: new ObjectId(foundWebhook._id)\n                    })\n                    await decryptCredentials(startNode.data)\n                    await webhookNodeInstance.webhookMethods?.deleteWebhook(startNode.data, foundWebhook.webhookId)\n                    const webhookFullUrl = `${process.env.TUNNEL_BASE_URL}api/v1/webhook/${startNode.data.webhookEndpoint}`\n                    const webhookId = await webhookNodeInstance.webhookMethods?.createWebhook.call(\n                        webhookNodeInstance,\n                        startNode.data,\n                        webhookFullUrl\n                    )\n                    if (webhookId !== undefined) {\n                        newBody.webhookId = webhookId\n                    }\n                    const newWebhook = new Webhook()\n                    Object.assign(newWebhook, newBody)\n\n                    const webhook = await this.AppDataSource.getMongoRepository(Webhook).create(newWebhook)\n                    await this.AppDataSource.getMongoRepository(Webhook).save(webhook)\n                }\n\n                if (!foundWebhook) {\n                    if (webhookNodeInstance.webhookMethods?.createWebhook) {\n                        if (!process.env.TUNNEL_BASE_URL) {\n                            return\n                        }\n                        const webhookFullUrl = `${process.env.TUNNEL_BASE_URL}api/v1/webhook/${startNode.data.webhookEndpoint}`\n                        await decryptCredentials(startNode.data)\n                        const webhookId = await webhookNodeInstance.webhookMethods?.createWebhook.call(\n                            webhookNodeInstance,\n                            startNode.data,\n                            webhookFullUrl\n                        )\n                        if (webhookId !== undefined) {\n                            newBody.webhookId = webhookId\n                        }\n                    }\n\n                    const newWebhook = new Webhook()\n                    Object.assign(newWebhook, newBody)\n\n                    const webhook = await this.AppDataSource.getMongoRepository(Webhook).create(newWebhook)\n                    await this.AppDataSource.getMongoRepository(Webhook).save(webhook)\n                }\n            }\n\n            if (Object.prototype.hasOwnProperty.call(this.deployedWorkflows, workflowShortId)) {\n                this.deployedWorkflows[workflowShortId].emitEventKey = emitEventKey\n            } else {\n                this.deployedWorkflows[workflowShortId] = { emitEventKey }\n            }\n        }\n    }\n\n    /**\n     * Start the rest of workflow via child process\n     * @param {string} workflowShortId\n     * @param {IReactFlowNode} startNode\n     * @param {string} startingNodeId\n     * @param {INodeExecutionData[] | IWebhookNodeExecutionData[]} startingNodeExecutedData\n     * @param {IComponentNodesPool} componentNodes\n     * @param {string[]} startingNodeIds\n     * @param {INodeDirectedGraph} graph\n     */\n    async startWorkflow(\n        workflowShortId: string,\n        startNode: IReactFlowNode,\n        startingNodeId: string,\n        startingNodeExecutedData: INodeExecutionData[] | IWebhookNodeExecutionData[],\n        componentNodes: IComponentNodesPool,\n        startingNodeIds: string[],\n        graph: INodeDirectedGraph\n    ) {\n        try {\n            // Fetch latest nodes and edges from DB\n            const { reactFlowNodes, reactFlowEdges } = await this.prepareDataForChildProcess(workflowShortId)\n\n            const controller = new AbortController()\n            const { signal } = controller\n\n            let childpath = pathJoin(__dirname, '..', 'dist', 'ChildProcess.js')\n            if (!fs.existsSync(childpath)) childpath = 'ChildProcess.ts'\n\n            const childProcess = fork(childpath, [], { signal })\n\n            const workflowExecutedData = [\n                {\n                    nodeId: startingNodeId,\n                    nodeLabel: startNode.data.label,\n                    data: startingNodeExecutedData\n                }\n            ] as IWorkflowExecutedData[]\n            const newExecution = await this.addExecution(workflowShortId, workflowExecutedData, controller)\n            const newExecutionShortId = newExecution === undefined ? '' : newExecution.shortId\n\n            // Remove cronJobs and providers to avoid error of converting circular structure to JSON\n            const clonedComponentNodes = lodash.cloneDeep(componentNodes)\n            for (const nodeInstanceName in clonedComponentNodes) {\n                if (Object.prototype.hasOwnProperty.call(clonedComponentNodes[nodeInstanceName], 'providers')) {\n                    delete (clonedComponentNodes[nodeInstanceName] as any)['providers']\n                }\n\n                if (Object.prototype.hasOwnProperty.call(clonedComponentNodes[nodeInstanceName], 'cronJobs')) {\n                    delete (clonedComponentNodes[nodeInstanceName] as any)['cronJobs']\n                }\n            }\n\n            const value = {\n                startingNodeIds,\n                componentNodes: clonedComponentNodes,\n                reactFlowNodes,\n                reactFlowEdges,\n                graph,\n                workflowExecutedData\n            } as IRunWorkflowMessageValue\n            childProcess.send({ key: 'start', value } as IChildProcessMessage)\n\n            let childProcessTimeout: NodeJS.Timeout\n\n            return new Promise((resolve, _) => {\n                childProcess.on('message', async (message: IChildProcessMessage) => {\n                    if (message.key === 'finish') {\n                        let updatedWorkflowExecutedData = message.value as IWorkflowExecutedData[]\n                        updatedWorkflowExecutedData = updatedWorkflowExecutedData.filter((execData) => execData.nodeId !== startingNodeId)\n                        await this.updateExecution(workflowShortId, updatedWorkflowExecutedData, newExecutionShortId, 'FINISHED')\n                        if (childProcessTimeout) {\n                            clearTimeout(childProcessTimeout)\n                        }\n                        resolve(updatedWorkflowExecutedData)\n                    }\n                    if (message.key === 'start') {\n                        if (process.env.EXECUTION_TIMEOUT) {\n                            childProcessTimeout = setTimeout(async () => {\n                                childProcess.kill()\n                                await this.terminateSpecificExecutionAfterTimeout(newExecutionShortId)\n                            }, parseInt(process.env.EXECUTION_TIMEOUT, 10))\n                        }\n                    }\n                    if (message.key === 'error') {\n                        let updatedWorkflowExecutedData = message.value as IWorkflowExecutedData[]\n                        updatedWorkflowExecutedData = updatedWorkflowExecutedData.filter((execData) => execData.nodeId !== startingNodeId)\n                        await this.updateExecution(workflowShortId, updatedWorkflowExecutedData, newExecutionShortId, 'ERROR')\n                        if (childProcessTimeout) {\n                            clearTimeout(childProcessTimeout)\n                        }\n                        resolve(updatedWorkflowExecutedData)\n                    }\n                })\n            })\n        } catch (err) {\n            console.error(err)\n        }\n    }\n\n    /**\n     * Remove workflow from deployed workflow pools by:\n     * 1.) Remove trigger  2.) Trigger abort controller  3.) Delete the object from deployedWorkflowsPool\n     * @param {string[]} startingNodeIds\n     * @param {IReactFlowNode[]} reactFlowNodes\n     * @param {IComponentNodesPool} componentNodes\n     * @param {string} workflowShortId\n     */\n    async remove(\n        startingNodeIds: string[],\n        reactFlowNodes: IReactFlowNode[],\n        componentNodes: IComponentNodesPool,\n        workflowShortId: string\n    ) {\n        for (let i = 0; i < startingNodeIds.length; i += 1) {\n            const startingNodeId = startingNodeIds[i]\n            const startNode = reactFlowNodes.find((nd) => nd.id === startingNodeId)\n            const emitEventKey = `${workflowShortId}_${startingNodeId}`\n\n            if (startNode && startNode.data && startNode.data.type === 'trigger') {\n                const nodeInstance = componentNodes[startNode.data.name]\n                const triggerNodeInstance = nodeInstance as ITriggerNode\n\n                const nodeData = {\n                    ...startNode.data,\n                    emitEventKey,\n                    nodeId: startingNodeId,\n                    workflowShortId\n                } as INodeData\n                await triggerNodeInstance.removeTrigger!.call(triggerNodeInstance, nodeData)\n            } else if (startNode && startNode.data && startNode.data.type === 'webhook') {\n                const nodeInstance = componentNodes[startNode.data.name]\n                const webhookNodeInstance = nodeInstance as IWebhookNode\n\n                const query = {\n                    nodeId: startingNodeId,\n                    webhookEndpoint: startNode.data.webhookEndpoint,\n                    httpMethod: (startNode.data.inputParameters?.httpMethod as WebhookMethod) || 'POST',\n                    workflowShortId\n                } as any\n\n                const webhook = await this.AppDataSource.getMongoRepository(Webhook).findOneBy(query)\n\n                if (webhook && webhook.webhookId) {\n                    await decryptCredentials(startNode.data)\n                    const isWebhookDeleted = await webhookNodeInstance.webhookMethods?.deleteWebhook(startNode.data, webhook.webhookId)\n                    if (isWebhookDeleted) {\n                        query.webhookId = webhook.webhookId\n                    }\n                }\n                await this.AppDataSource.getMongoRepository(Webhook).delete(query)\n            }\n\n            await this.terminateExecutions(workflowShortId)\n\n            if (\n                this.deployedWorkflows[workflowShortId] &&\n                this.deployedWorkflows[workflowShortId].abortController !== undefined &&\n                !this.deployedWorkflows[workflowShortId].abortController?.signal.aborted\n            ) {\n                const workflowAbortController = this.deployedWorkflows[workflowShortId].abortController\n                delete this.deployedWorkflows[workflowShortId]\n                try {\n                    workflowAbortController?.abort()\n                } catch (e) {\n                    // console.error(e);\n                }\n            }\n        }\n    }\n\n    /**\n     * Remove all deployed workflows from pools:\n     * @param {IComponentNodesPool} componentNodes\n     */\n    async removeAll(componentNodes: IComponentNodesPool) {\n        const deployedWorkflowShortIds = Object.keys(this.deployedWorkflows)\n        if (!deployedWorkflowShortIds.length) return\n\n        const workflows = await this.AppDataSource.getMongoRepository(Workflow).findBy({\n            shortId: {\n                $in: deployedWorkflowShortIds\n            }\n        })\n\n        for (let i = 0; i < workflows.length; i += 1) {\n            const workflow = workflows[i]\n            const flowDataString = workflow.flowData\n            const flowData: IReactFlowObject = JSON.parse(flowDataString)\n            const reactFlowNodes = flowData.nodes\n            const reactFlowEdges = flowData.edges\n\n            const { nodeDependencies } = constructGraphs(reactFlowNodes, reactFlowEdges)\n            const { startingNodeIds } = getStartingNode(nodeDependencies, reactFlowNodes)\n\n            await this.remove(startingNodeIds, reactFlowNodes, componentNodes, workflow.shortId)\n        }\n    }\n\n    /**\n     * Get nodes and edges from database\n     * @param {string} workflowShortId\n     */\n    async prepareDataForChildProcess(workflowShortId: string) {\n        const workflow = await this.AppDataSource.getMongoRepository(Workflow).findOneBy({\n            shortId: workflowShortId\n        })\n\n        if (!workflow) {\n            throw new Error(`Workflow ${workflowShortId} not found`)\n        }\n\n        const flowDataString = workflow.flowData\n        const flowData: IReactFlowObject = JSON.parse(flowDataString)\n        const reactFlowNodes = flowData.nodes\n        const reactFlowEdges = flowData.edges\n\n        return { reactFlowNodes, reactFlowEdges }\n    }\n\n    /**\n     * Add results to deployedWorkflows[workflowShortId].workflowExecutedData, and Execution DB\n     * @param {string} workflowShortId\n     * @param {IWorkflowExecutedData[]} workflowExecutedData\n     * @param {AbortController} controller\n     */\n    async addExecution(workflowShortId: string, workflowExecutedData: IWorkflowExecutedData[], controller: AbortController) {\n        if (!Object.prototype.hasOwnProperty.call(this.deployedWorkflows, workflowShortId)) {\n            return\n        }\n\n        this.deployedWorkflows[workflowShortId].workflowExecutedData = workflowExecutedData\n        this.deployedWorkflows[workflowShortId].abortController = controller\n\n        const newExecution = new Execution()\n        const bodyExecution = {\n            workflowShortId,\n            state: 'INPROGRESS' as ExecutionState,\n            executionData: JSON.stringify(this.deployedWorkflows[workflowShortId].workflowExecutedData)\n        }\n        Object.assign(newExecution, bodyExecution)\n\n        const execution = this.AppDataSource.getMongoRepository(Execution).create(newExecution)\n        return await this.AppDataSource.getMongoRepository(Execution).save(execution)\n    }\n\n    /**\n     * Update results to deployedWorkflows[workflowShortId].workflowExecutedData, and Execution DB\n     * @param {string} workflowShortId\n     * @param {IWorkflowExecutedData[]} workflowExecutedData\n     * @param {string} executionShortId\n     * @param {ExecutionState} state\n     */\n    async updateExecution(\n        workflowShortId: string,\n        workflowExecutedData: IWorkflowExecutedData[],\n        executionShortId: string,\n        state: ExecutionState\n    ) {\n        if (!Object.prototype.hasOwnProperty.call(this.deployedWorkflows, workflowShortId)) {\n            return\n        }\n\n        const existingWorkflowExecutedData = this.deployedWorkflows[workflowShortId].workflowExecutedData || []\n        const updateWorkflowExecutedData = existingWorkflowExecutedData.concat(workflowExecutedData)\n        this.deployedWorkflows[workflowShortId].workflowExecutedData = updateWorkflowExecutedData\n\n        const execution = await this.AppDataSource.getMongoRepository(Execution).findOneBy({\n            shortId: executionShortId\n        })\n\n        if (!execution) {\n            throw new Error(`Execution ${executionShortId} not found`)\n        }\n\n        const updateExecution = new Execution()\n        const bodyExecution = {\n            state,\n            executionData: JSON.stringify(this.deployedWorkflows[workflowShortId].workflowExecutedData),\n            stoppedDate: new Date()\n        }\n        Object.assign(updateExecution, bodyExecution)\n\n        this.AppDataSource.getMongoRepository(Execution).merge(execution, updateExecution)\n        await this.AppDataSource.getMongoRepository(Execution).save(execution)\n    }\n\n    /**\n     * Update INPROGRESS execution to TERMINATED\n     * @param {string} workflowShortId\n     */\n    async terminateExecutions(workflowShortId: string) {\n        const executions: Execution[] = await this.AppDataSource.getMongoRepository(Execution)\n            .aggregate([\n                {\n                    $match: { workflowShortId, state: 'INPROGRESS' as ExecutionState }\n                }\n            ])\n            .toArray()\n\n        for (let i = 0; i < executions.length; i += 1) {\n            const execution = executions[i]\n            const body = { state: 'TERMINATED' as ExecutionState, stoppedDate: new Date() }\n            const updateExecution = new Execution()\n            Object.assign(updateExecution, body)\n\n            this.AppDataSource.getMongoRepository(Execution).merge(execution, updateExecution)\n            await this.AppDataSource.getMongoRepository(Execution).save(execution)\n        }\n    }\n\n    /**\n     * Update specific execution to TIMEOUT\n     * @param {string} newExecutionShortId\n     */\n    async terminateSpecificExecutionAfterTimeout(newExecutionShortId: string) {\n        const execution = await this.AppDataSource.getMongoRepository(Execution).findOneBy({\n            shortId: newExecutionShortId\n        })\n\n        if (execution) {\n            const body = { state: 'TIMEOUT' as ExecutionState, stoppedDate: new Date() }\n            const updateExecution = new Execution()\n            Object.assign(updateExecution, body)\n\n            this.AppDataSource.getMongoRepository(Execution).merge(execution, updateExecution)\n            await this.AppDataSource.getMongoRepository(Execution).save(execution)\n        }\n    }\n}\n"
  },
  {
    "path": "packages/server/src/Interface.ts",
    "content": "import { ObjectId } from 'mongodb'\nimport {\n    ICommonObject,\n    INode as INodeComponent,\n    INodeCredential,\n    INodeData,\n    INodeExecutionData,\n    IWebhookNodeExecutionData\n} from 'outerbridge-components'\nimport EventEmitter from 'events'\n\n/**\n * Databases\n */\nexport interface IWorkflow {\n    _id: ObjectId\n    shortId: string\n    name: string\n    flowData: string\n    deployed: boolean\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IExecution {\n    _id: ObjectId\n    shortId: string\n    workflowShortId: string\n    executionData: string\n    state: ExecutionState\n    createdDate: Date\n    stoppedDate?: Date\n}\n\nexport interface ICredential {\n    _id: ObjectId\n    name: string\n    nodeCredentialName: string\n    credentialData: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IWebhook {\n    _id: ObjectId\n    workflowShortId: string\n    webhookEndpoint: string\n    httpMethod: WebhookMethod\n    webhookId: string\n    nodeId: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IContract {\n    _id: ObjectId\n    name: string\n    abi: string\n    address: string\n    network: string\n    providerCredential: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport interface IWallet {\n    _id: ObjectId\n    name: string\n    address: string\n    network: string\n    providerCredential: string\n    walletCredential: string\n    updatedDate: Date\n    createdDate: Date\n}\n\n/**\n * Types\n */\nexport type ExecutionState = 'INPROGRESS' | 'FINISHED' | 'ERROR' | 'TERMINATED' | 'TIMEOUT'\n\nexport type WebhookMethod = 'GET' | 'POST'\n\n/**\n * Others\n */\nexport interface IWorkflowResponse extends IWorkflow {\n    execution: IExecution\n    executionCount: number\n}\n\nexport interface INode extends INodeComponent {\n    filePath: string\n}\n\nexport interface ITriggerNode extends EventEmitter, INodeComponent {\n    filePath: string\n}\n\nexport interface IWebhookNode extends INodeComponent {\n    filePath: string\n}\n\nexport interface IComponentNodesPool {\n    [key: string]: INode | ITriggerNode\n}\n\nexport interface IActiveTestTriggerPool {\n    [key: string]: INodeData\n}\n\nexport interface IActiveTestWebhookPool {\n    [key: string]: {\n        nodes: IReactFlowNode[]\n        edges: IReactFlowEdge[]\n        nodeData: INodeData\n        webhookNodeId: string\n        clientId: string\n        isTestWorkflow: boolean\n        webhookId?: string\n    }\n}\n\nexport interface ICredentialBody {\n    name: string\n    nodeCredentialName: string\n    credentialData: ICredentialDataDecrypted\n}\n\nexport interface ICredentialResponse {\n    _id: ObjectId\n    name: string\n    credentialData: ICredentialDataDecrypted\n    nodeCredentialName: string\n    updatedDate: Date\n    createdDate: Date\n}\n\nexport type ICredentialDataDecrypted = ICommonObject\n\nexport interface IComponentCredentialsPool {\n    [key: string]: INodeCredential\n}\n\nexport interface IWalletResponse extends IWallet {\n    balance: string\n}\n\nexport interface IVariableDict {\n    [key: string]: string\n}\n\nexport interface INodeDependencies {\n    [key: string]: number\n}\n\nexport interface INodeDirectedGraph {\n    [key: string]: string[]\n}\n\nexport interface IWorkflowExecutedData {\n    nodeLabel: string\n    nodeId: string\n    data: INodeExecutionData[] | IWebhookNodeExecutionData[]\n    status?: ExecutionState\n}\n\nexport interface ITestNodeBody {\n    nodeId: string\n    nodes: IReactFlowNode[]\n    edges: IReactFlowEdge[]\n    clientId?: string\n}\n\nexport interface IDeployedWorkflowsPool {\n    [key: string]: {\n        emitEventKey?: string\n        abortController?: AbortController\n        workflowExecutedData?: IWorkflowExecutedData[]\n    }\n}\n\nexport interface IChildProcessMessage {\n    key: string\n    value?: any\n}\n\nexport interface IReactFlowNode {\n    id: string\n    position: {\n        x: number\n        y: number\n    }\n    type: string\n    data: INodeData\n    positionAbsolute: {\n        x: number\n        y: number\n    }\n    z: number\n    handleBounds: {\n        source: any\n        target: any\n    }\n    width: number\n    height: number\n    selected: boolean\n    dragging: boolean\n}\n\nexport interface IReactFlowEdge {\n    source: string\n    sourceHandle: string\n    target: string\n    targetHandle: string\n    type: string\n    id: string\n    data: {\n        label: string\n    }\n}\n\nexport interface IReactFlowObject {\n    nodes: IReactFlowNode[]\n    edges: IReactFlowEdge[]\n    viewport: {\n        x: number\n        y: number\n        zoom: number\n    }\n}\n\nexport interface IRunWorkflowMessageValue {\n    startingNodeIds: string[]\n    componentNodes: IComponentNodesPool\n    reactFlowNodes: IReactFlowNode[]\n    reactFlowEdges: IReactFlowEdge[]\n    graph: INodeDirectedGraph\n    workflowExecutedData: IWorkflowExecutedData[]\n}\n\nexport interface IContractRequestBody {\n    credentials: ICommonObject\n    networks: ICommonObject\n    contractInfo: ICommonObject\n}\n\nexport interface IWalletRequestBody {\n    name: string\n    network: string\n    providerCredential?: string\n    privateKey?: string\n}\n\nexport interface IOAuth2Response {\n    access_token: string\n    token_type: string\n    expires_in: number\n    refresh_token: string\n}\n\nexport interface IExploredNode {\n    [key: string]: {\n        remainingLoop: number\n        lastSeenDepth: number\n    }\n}\n\nexport interface INodeQueue {\n    nodeId: string\n    depth: number\n}\n\nexport type ITestWorkflowBody = ITestNodeBody\n"
  },
  {
    "path": "packages/server/src/NodesPool.ts",
    "content": "import { IComponentNodesPool } from './Interface'\n\nimport path from 'path'\nimport { Dirent } from 'fs'\nimport { getNodeModulesPackagePath } from './utils'\nimport { promises } from 'fs'\n\nexport class NodesPool {\n    componentNodes: IComponentNodesPool = {}\n\n    /**\n     * Initialize to get all nodes\n     */\n    async initialize() {\n        const packagePath = getNodeModulesPackagePath('outerbridge-components')\n        const nodesPath = path.join(packagePath, 'dist', 'nodes')\n        const nodeFiles = await this.getFiles(nodesPath)\n        return Promise.all(\n            nodeFiles.map(async (file) => {\n                if (file.endsWith('.js')) {\n                    const nodeModule = await import(file)\n                    try {\n                        const newNodeInstance = new nodeModule.nodeClass()\n                        newNodeInstance.filePath = file\n                        this.componentNodes[newNodeInstance.name] = newNodeInstance\n\n                        // Replace file icon with absolute path\n                        if (\n                            newNodeInstance.icon &&\n                            (newNodeInstance.icon.endsWith('.svg') ||\n                                newNodeInstance.icon.endsWith('.png') ||\n                                newNodeInstance.icon.endsWith('.jpg'))\n                        ) {\n                            const filePath = file.replace(/\\\\/g, '/').split('/')\n                            filePath.pop()\n                            const nodeIconAbsolutePath = `${filePath.join('/')}/${newNodeInstance.icon}`\n                            this.componentNodes[newNodeInstance.name].icon = nodeIconAbsolutePath\n                        }\n                    } catch (e) {\n                        // console.error(e);\n                    }\n                }\n            })\n        )\n    }\n\n    /**\n     * Recursive function to get node files\n     * @param {string} dir\n     * @returns {string[]}\n     */\n    async getFiles(dir: string): Promise<string[]> {\n        const dirents = await promises.readdir(dir, { withFileTypes: true })\n        const files = await Promise.all(\n            dirents.map((dirent: Dirent) => {\n                const res = path.resolve(dir, dirent.name)\n                return dirent.isDirectory() ? this.getFiles(res) : res\n            })\n        )\n        return Array.prototype.concat(...files)\n    }\n}\n"
  },
  {
    "path": "packages/server/src/commands/start.ts",
    "content": "import { Command, Flags } from '@oclif/core'\nimport path from 'path'\nimport * as Server from '../index'\nimport * as DataSource from '../DataSource'\nimport dotenv from 'dotenv'\n\ndotenv.config({ path: path.join(__dirname, '..', '..', '.env') })\n\nenum EXIT_CODE {\n    SUCCESS = 0,\n    FAILED = 1\n}\nlet processExitCode = EXIT_CODE.SUCCESS\n\nexport default class Start extends Command {\n    static flags = {\n        mongourl: Flags.string()\n    }\n\n    static args = []\n\n    async stopProcess() {\n        console.info('Shutting down Outerbridge...')\n        try {\n            // Shut down the app after timeout if it ever stuck removing pools\n            setTimeout(() => {\n                console.info('Outerbridge was forced to shut down after 30 secs')\n                process.exit(processExitCode)\n            }, 30000)\n\n            // Removing pools\n            const serverApp = Server.getInstance()\n            if (serverApp) await serverApp.stopApp()\n        } catch (error) {\n            console.error('There was an error shutting down Outerbridge...', error)\n        }\n        process.exit(processExitCode)\n    }\n\n    async run(): Promise<void> {\n        process.on('SIGTERM', this.stopProcess)\n        process.on('SIGINT', this.stopProcess)\n\n        // Prevent throw new Error from crashing the app\n        // TODO: Get rid of this and send proper error message to ui\n        process.on('uncaughtException', (err) => {\n            console.error('uncaughtException: ', err)\n        })\n\n        const { flags } = await this.parse(Start)\n        if (flags.mongourl) process.env.MONGO_URL = flags.mongourl\n\n        await (async () => {\n            try {\n                this.log('Starting Outerbridge...')\n                await DataSource.init()\n                await Server.start()\n            } catch (error) {\n                console.error('There was an error starting Outerbridge...', error)\n                processExitCode = EXIT_CODE.FAILED\n                // @ts-ignore\n                process.emit('SIGINT')\n            }\n        })()\n    }\n}\n"
  },
  {
    "path": "packages/server/src/entity/Contract.ts",
    "content": "/* eslint-disable */\nimport { Entity, Column, ObjectIdColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectId } from 'mongodb'\nimport { IContract } from '../Interface'\n\n@Entity()\nexport class Contract implements IContract {\n    @ObjectIdColumn()\n    _id: ObjectId\n\n    @Column()\n    name: string\n\n    @Column()\n    abi: string\n\n    @Column()\n    address: string\n\n    @Column()\n    network: string\n\n    @Column('text', { nullable: true })\n    providerCredential: string\n\n    @CreateDateColumn()\n    createdDate: Date\n\n    @UpdateDateColumn()\n    updatedDate: Date\n}\n"
  },
  {
    "path": "packages/server/src/entity/Credential.ts",
    "content": "/* eslint-disable */\nimport { Entity, Column, ObjectIdColumn, Index, CreateDateColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectId } from 'mongodb'\nimport { ICredential } from '../Interface'\n\n@Entity()\nexport class Credential implements ICredential {\n    @ObjectIdColumn()\n    _id: ObjectId\n\n    @Column()\n    name: string\n\n    @Index()\n    @Column()\n    nodeCredentialName: string\n\n    @Column()\n    credentialData: string\n\n    @CreateDateColumn()\n    createdDate: Date\n\n    @UpdateDateColumn()\n    updatedDate: Date\n}\n"
  },
  {
    "path": "packages/server/src/entity/Execution.ts",
    "content": "/* eslint-disable */\nimport { Entity, Column, ObjectIdColumn, Index, BeforeInsert, CreateDateColumn } from 'typeorm'\nimport { ObjectId } from 'mongodb'\nimport { shortId } from '../utils'\nimport { IExecution, ExecutionState } from '../Interface'\n\n@Entity()\nexport class Execution implements IExecution {\n    @ObjectIdColumn()\n    _id: ObjectId\n\n    @Index()\n    @Column()\n    shortId: string\n\n    @BeforeInsert()\n    beforeInsert() {\n        this.shortId = shortId('E', new Date())\n    }\n\n    @Column()\n    executionData: string\n\n    @Column()\n    state: ExecutionState\n\n    @Column()\n    workflowShortId: string\n\n    @CreateDateColumn()\n    createdDate: Date\n\n    @Column({ nullable: true })\n    stoppedDate: Date\n}\n"
  },
  {
    "path": "packages/server/src/entity/Wallet.ts",
    "content": "/* eslint-disable */\nimport { Entity, Column, ObjectIdColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectId } from 'mongodb'\nimport { IWallet } from '../Interface'\n\n@Entity()\nexport class Wallet implements IWallet {\n    @ObjectIdColumn()\n    _id: ObjectId\n\n    @Column()\n    name: string\n\n    @Column()\n    address: string\n\n    @Column()\n    network: string\n\n    @Column('text', { nullable: true })\n    providerCredential: string\n\n    @Column()\n    walletCredential: string\n\n    @CreateDateColumn()\n    createdDate: Date\n\n    @UpdateDateColumn()\n    updatedDate: Date\n}\n"
  },
  {
    "path": "packages/server/src/entity/Webhook.ts",
    "content": "/* eslint-disable */\nimport { Column, Entity, Index, ObjectIdColumn, CreateDateColumn, UpdateDateColumn, PrimaryColumn } from 'typeorm'\nimport { ObjectId } from 'mongodb'\nimport { IWebhook, WebhookMethod } from '../Interface'\n\n@Entity()\nexport class Webhook implements IWebhook {\n    @ObjectIdColumn()\n    _id: ObjectId\n\n    @Column()\n    nodeId: string\n\n    @Index()\n    @PrimaryColumn()\n    webhookEndpoint: string\n\n    @Index()\n    @PrimaryColumn()\n    httpMethod: WebhookMethod\n\n    @Column()\n    workflowShortId: string\n\n    @Column({ nullable: true })\n    webhookId: string\n\n    @CreateDateColumn()\n    createdDate: Date\n\n    @UpdateDateColumn()\n    updatedDate: Date\n}\n"
  },
  {
    "path": "packages/server/src/entity/Workflow.ts",
    "content": "/* eslint-disable */\nimport { Entity, Column, ObjectIdColumn, Index, BeforeInsert, CreateDateColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectId } from 'mongodb'\nimport { shortId } from '../utils'\nimport { IWorkflow } from '../Interface'\n\n@Entity()\nexport class Workflow implements IWorkflow {\n    @ObjectIdColumn()\n    _id: ObjectId\n\n    @Index()\n    @Column()\n    shortId: string\n\n    @BeforeInsert()\n    beforeInsert() {\n        this.shortId = shortId('W', new Date())\n    }\n\n    @Column()\n    name: string\n\n    @Column()\n    flowData: string\n\n    @Column()\n    deployed: boolean\n\n    @CreateDateColumn()\n    createdDate: Date\n\n    @UpdateDateColumn()\n    updatedDate: Date\n}\n"
  },
  {
    "path": "packages/server/src/index.ts",
    "content": "import express, { Request, Response } from 'express'\nimport path from 'path'\nimport cors from 'cors'\nimport localtunnel from 'localtunnel'\nimport { ObjectId } from 'mongodb'\nimport { Server, Socket } from 'socket.io'\nimport http from 'http'\nimport { ethers } from 'ethers'\nimport ClientOAuth2 from 'client-oauth2'\n\nimport {\n    IComponentCredentialsPool,\n    IComponentNodesPool,\n    IContractRequestBody,\n    ICredentialBody,\n    ICredentialDataDecrypted,\n    ICredentialResponse,\n    IReactFlowEdge,\n    IReactFlowNode,\n    IReactFlowObject,\n    ITestNodeBody,\n    ITestWorkflowBody,\n    ITriggerNode,\n    IWalletRequestBody,\n    IWalletResponse,\n    IWebhookNode,\n    IWorkflowExecutedData,\n    IWorkflowResponse,\n    WebhookMethod\n} from './Interface'\nimport {\n    INodeData,\n    INodeOptionsValue,\n    IDbCollection,\n    etherscanAPIs,\n    nativeCurrency,\n    NETWORK,\n    INodeExecutionData,\n    INode\n} from 'outerbridge-components'\nimport { CredentialsPool } from './CredentialsPool'\nimport { NodesPool } from './NodesPool'\nimport {\n    decryptCredentialData,\n    getEncryptionKey,\n    processWebhook,\n    decryptCredentials,\n    resolveVariables,\n    transformToCredentialEntity,\n    constructGraphsAndGetStartingNodes,\n    encryptCredentialData,\n    getOAuth2HTMLPath,\n    checkOAuth2TokenRefreshed,\n    constructGraphs,\n    testWorkflow,\n    getNodeModulesPackagePath,\n    getRandomSubdomain,\n    getAPIKeys,\n    addAPIKey,\n    deleteAPIKey,\n    updateAPIKey,\n    updateNodeOutput\n} from './utils'\nimport { DeployedWorkflowPool } from './DeployedWorkflowPool'\nimport { ActiveTestTriggerPool } from './ActiveTestTriggerPool'\nimport { ActiveTestWebhookPool } from './ActiveTestWebhookPool'\nimport axios, { AxiosRequestConfig } from 'axios'\n\nimport { Workflow } from './entity/Workflow'\nimport { Execution } from './entity/Execution'\nimport { Credential } from './entity/Credential'\nimport { Webhook } from './entity/Webhook'\nimport { Contract } from './entity/Contract'\nimport { Wallet } from './entity/Wallet'\nimport { getDataSource } from './DataSource'\n\nexport class App {\n    app: express.Application\n    componentNodes: IComponentNodesPool = {}\n    componentCredentials: IComponentCredentialsPool = {}\n    deployedWorkflowsPool: DeployedWorkflowPool\n    activeTestTriggerPool: ActiveTestTriggerPool\n    activeTestWebhookPool: ActiveTestWebhookPool\n    AppDataSource = getDataSource()\n\n    constructor() {\n        this.app = express()\n    }\n\n    async initDatabase() {\n        // Initialize database\n        this.AppDataSource.initialize()\n            .then(async () => {\n                console.info('📦[server]: Data Source has been initialized!')\n\n                // Initialize localtunnel\n                if (process.env.ENABLE_TUNNEL === 'true') {\n                    const subdomain = getRandomSubdomain()\n\n                    const tunnelSettings: localtunnel.TunnelConfig = {\n                        subdomain\n                    }\n\n                    const port = parseInt(process.env.PORT || '', 10) || 3000\n\n                    const createTunnel = (timeout: number): Promise<localtunnel.Tunnel | string> => {\n                        return new Promise(function (resolve, reject) {\n                            localtunnel(port, tunnelSettings).then(resolve, reject)\n                            setTimeout(resolve, timeout, 'TUNNEL_TIMED_OUT')\n                        })\n                    }\n\n                    const newTunnel = await createTunnel(10000)\n\n                    if (typeof newTunnel !== 'string') {\n                        process.env.TUNNEL_BASE_URL = `${newTunnel.url}/`\n                        console.info('🌐[server]: TUNNEL_BASE_URL = ', process.env.TUNNEL_BASE_URL)\n                    }\n                }\n\n                // Initialize node instances\n                const nodesPool = new NodesPool()\n                await nodesPool.initialize()\n                this.componentNodes = nodesPool.componentNodes\n\n                // Initialize credential instances\n                const credsPool = new CredentialsPool()\n                await credsPool.initialize()\n                this.componentCredentials = credsPool.componentCredentials\n\n                // Initialize deployed worklows instances\n                this.deployedWorkflowsPool = new DeployedWorkflowPool()\n                await this.deployedWorkflowsPool.initialize(this.AppDataSource, this.componentNodes)\n\n                // Initialize activeTestTriggerPool instance\n                this.activeTestTriggerPool = new ActiveTestTriggerPool()\n\n                // Initialize activeTestWebhookPool instance\n                this.activeTestWebhookPool = new ActiveTestWebhookPool()\n\n                // Initialize API keys\n                await getAPIKeys()\n            })\n            .catch((err) => {\n                console.error('❌[server]: Error during Data Source initialization:', err)\n            })\n    }\n\n    async config(io: Server) {\n        // Limit is needed to allow sending/receiving base64 encoded string\n        this.app.use(express.json({ limit: '50mb' }))\n        this.app.use(express.urlencoded({ limit: '50mb', extended: true }))\n\n        // Allow access from ui when yarn run dev\n        if (process.env.NODE_ENV !== 'production') {\n            this.app.use(cors({ credentials: true, origin: 'http://localhost:8080' }))\n        }\n\n        // ----------------------------------------\n        // Workflows\n        // ----------------------------------------\n\n        // Get all workflows\n        this.app.get('/api/v1/workflows', async (req: Request, res: Response) => {\n            const workflows: IWorkflowResponse[] = await this.AppDataSource.getMongoRepository(Workflow)\n                .aggregate([\n                    {\n                        $lookup: {\n                            from: 'execution',\n                            localField: 'shortId',\n                            foreignField: 'workflowShortId',\n                            as: 'execution'\n                        }\n                    },\n                    {\n                        $addFields: {\n                            executionCount: {\n                                $size: '$execution'\n                            }\n                        }\n                    }\n                ])\n                .toArray()\n            return res.json(workflows)\n        })\n\n        // Get specific workflow via shortId\n        this.app.get('/api/v1/workflows/:shortId', async (req: Request, res: Response) => {\n            const workflows: IWorkflowResponse[] = await this.AppDataSource.getMongoRepository(Workflow)\n                .aggregate([\n                    {\n                        $match: {\n                            shortId: req.params.shortId\n                        }\n                    },\n                    {\n                        $lookup: {\n                            from: 'execution',\n                            localField: 'shortId',\n                            foreignField: 'workflowShortId',\n                            as: 'execution'\n                        }\n                    },\n                    {\n                        $addFields: {\n                            executionCount: {\n                                $size: '$execution'\n                            }\n                        }\n                    }\n                ])\n                .toArray()\n            if (workflows.length) return res.json(workflows[0])\n            return res.status(404).send(`Workflow ${req.params.shortId} not found`)\n        })\n\n        // Create new workflow\n        this.app.post('/api/v1/workflows', async (req: Request, res: Response) => {\n            const body = req.body\n            const newWorkflow = new Workflow()\n            Object.assign(newWorkflow, body)\n\n            const workflow = await this.AppDataSource.getMongoRepository(Workflow).create(newWorkflow)\n            const results = await this.AppDataSource.getMongoRepository(Workflow).save(workflow)\n            const returnWorkflows: IWorkflowResponse[] = await this.AppDataSource.getMongoRepository(Workflow)\n                .aggregate([\n                    {\n                        $match: {\n                            shortId: results.shortId\n                        }\n                    },\n                    {\n                        $lookup: {\n                            from: 'execution',\n                            localField: 'shortId',\n                            foreignField: 'workflowShortId',\n                            as: 'execution'\n                        }\n                    },\n                    {\n                        $addFields: {\n                            executionCount: {\n                                $size: '$execution'\n                            }\n                        }\n                    }\n                ])\n                .toArray()\n            if (returnWorkflows.length) return res.json(returnWorkflows[0])\n            return res.status(404).send(`Workflow ${results.shortId} not found`)\n        })\n\n        // Update workflow\n        this.app.put('/api/v1/workflows/:shortId', async (req: Request, res: Response) => {\n            const workflow = await this.AppDataSource.getMongoRepository(Workflow).findOneBy({\n                shortId: req.params.shortId\n            })\n\n            if (!workflow) {\n                res.status(404).send(`Workflow ${req.params.shortId} not found`)\n                return\n            }\n\n            // If workflow is deployed, remove from deployedWorkflowsPool, then add it again for new changes to be picked up\n            if (workflow.deployed && workflow.flowData) {\n                try {\n                    const flowDataString = workflow.flowData\n                    const flowData: IReactFlowObject = JSON.parse(flowDataString)\n                    const reactFlowNodes = flowData.nodes as IReactFlowNode[]\n                    const reactFlowEdges = flowData.edges as IReactFlowEdge[]\n                    const workflowShortId = workflow.shortId\n\n                    const response = constructGraphsAndGetStartingNodes(res, reactFlowNodes, reactFlowEdges)\n                    if (response === undefined) return\n\n                    const { startingNodeIds } = response\n\n                    await this.deployedWorkflowsPool.remove(startingNodeIds, reactFlowNodes, this.componentNodes, workflowShortId)\n                } catch (e) {\n                    return res.status(500).send(e)\n                }\n            }\n\n            const body = req.body\n            const updateWorkflow = new Workflow()\n            Object.assign(updateWorkflow, body)\n\n            this.AppDataSource.getMongoRepository(Workflow).merge(workflow, updateWorkflow)\n            const results = await this.AppDataSource.getMongoRepository(Workflow).save(workflow)\n            const returnWorkflows: IWorkflowResponse[] = await this.AppDataSource.getMongoRepository(Workflow)\n                .aggregate([\n                    {\n                        $match: {\n                            shortId: results.shortId\n                        }\n                    },\n                    {\n                        $lookup: {\n                            from: 'execution',\n                            localField: 'shortId',\n                            foreignField: 'workflowShortId',\n                            as: 'execution'\n                        }\n                    },\n                    {\n                        $addFields: {\n                            executionCount: {\n                                $size: '$execution'\n                            }\n                        }\n                    }\n                ])\n                .toArray()\n            if (returnWorkflows.length) {\n                const returnWorkflow = returnWorkflows[0]\n                if (returnWorkflow.deployed && returnWorkflow.flowData) {\n                    try {\n                        const flowData: IReactFlowObject = JSON.parse(returnWorkflow.flowData)\n                        const reactFlowNodes = flowData.nodes as IReactFlowNode[]\n                        const reactFlowEdges = flowData.edges as IReactFlowEdge[]\n                        const workflowShortId = returnWorkflow.shortId\n\n                        const response = constructGraphsAndGetStartingNodes(res, reactFlowNodes, reactFlowEdges)\n                        if (response === undefined) return\n\n                        const { graph, startingNodeIds } = response\n                        await this.deployedWorkflowsPool.add(\n                            startingNodeIds,\n                            graph,\n                            reactFlowNodes,\n                            this.componentNodes,\n                            workflowShortId,\n                            this.activeTestTriggerPool,\n                            this.activeTestWebhookPool\n                        )\n                    } catch (e) {\n                        return res.status(500).send(e)\n                    }\n                }\n                return res.json(returnWorkflow)\n            }\n            return res.status(404).send(`Workflow ${results.shortId} not found`)\n        })\n\n        // Delete workflow via shortId\n        this.app.delete('/api/v1/workflows/:shortId', async (req: Request, res: Response) => {\n            const workflow = await this.AppDataSource.getMongoRepository(Workflow).findOneBy({\n                shortId: req.params.shortId\n            })\n\n            if (!workflow) {\n                res.status(404).send(`Workflow ${req.params.shortId} not found`)\n                return\n            }\n\n            // If workflow is deployed, remove from deployedWorkflowsPool\n            if (workflow.deployed && workflow.flowData) {\n                try {\n                    const flowDataString = workflow.flowData\n                    const flowData: IReactFlowObject = JSON.parse(flowDataString)\n                    const reactFlowNodes = flowData.nodes as IReactFlowNode[]\n                    const reactFlowEdges = flowData.edges as IReactFlowEdge[]\n                    const workflowShortId = workflow.shortId\n\n                    const response = constructGraphsAndGetStartingNodes(res, reactFlowNodes, reactFlowEdges)\n                    if (response === undefined) return\n\n                    const { startingNodeIds } = response\n\n                    await this.deployedWorkflowsPool.remove(startingNodeIds, reactFlowNodes, this.componentNodes, workflowShortId)\n                } catch (e) {\n                    return res.status(500).send(e)\n                }\n            }\n            const results = await this.AppDataSource.getMongoRepository(Workflow).delete({ shortId: req.params.shortId })\n            await this.AppDataSource.getMongoRepository(Webhook).delete({ workflowShortId: req.params.shortId })\n            await this.AppDataSource.getMongoRepository(Execution).delete({ workflowShortId: req.params.shortId })\n            return res.json(results)\n        })\n\n        // Deploy/Halt workflow via shortId\n        this.app.post('/api/v1/workflows/deploy/:shortId', async (req: Request, res: Response) => {\n            const workflow = await this.AppDataSource.getMongoRepository(Workflow).findOneBy({\n                shortId: req.params.shortId\n            })\n\n            if (!workflow) {\n                res.status(404).send(`Workflow ${req.params.shortId} not found`)\n                return\n            }\n\n            try {\n                const flowDataString = workflow.flowData\n                const flowData: IReactFlowObject = JSON.parse(flowDataString)\n                const reactFlowNodes = flowData.nodes as IReactFlowNode[]\n                const reactFlowEdges = flowData.edges as IReactFlowEdge[]\n                const workflowShortId = req.params.shortId\n                const haltDeploy = req.body?.halt\n\n                const response = constructGraphsAndGetStartingNodes(res, reactFlowNodes, reactFlowEdges)\n                if (response === undefined) return\n                const { graph, startingNodeIds } = response\n\n                if (!haltDeploy) {\n                    await this.deployedWorkflowsPool.add(\n                        startingNodeIds,\n                        graph,\n                        reactFlowNodes,\n                        this.componentNodes,\n                        workflowShortId,\n                        this.activeTestTriggerPool,\n                        this.activeTestWebhookPool\n                    )\n                } else {\n                    await this.deployedWorkflowsPool.remove(startingNodeIds, reactFlowNodes, this.componentNodes, workflowShortId)\n                }\n\n                const body = { deployed: haltDeploy ? false : true }\n                const updateWorkflow = new Workflow()\n                Object.assign(updateWorkflow, body)\n\n                this.AppDataSource.getMongoRepository(Workflow).merge(workflow, updateWorkflow)\n                const results = await this.AppDataSource.getMongoRepository(Workflow).save(workflow)\n                const returnWorkflows: IWorkflowResponse[] = await this.AppDataSource.getMongoRepository(Workflow)\n                    .aggregate([\n                        {\n                            $match: {\n                                shortId: results.shortId\n                            }\n                        },\n                        {\n                            $lookup: {\n                                from: 'execution',\n                                localField: 'shortId',\n                                foreignField: 'workflowShortId',\n                                as: 'execution'\n                            }\n                        },\n                        {\n                            $addFields: {\n                                executionCount: {\n                                    $size: '$execution'\n                                }\n                            }\n                        }\n                    ])\n                    .toArray()\n                if (returnWorkflows.length) return res.json(returnWorkflows[0])\n                return res.status(404).send(`Workflow ${results.shortId} not found`)\n            } catch (e) {\n                res.status(500).send(`Workflow ${req.params.shortId} deploy error: ${e}`)\n                return\n            }\n        })\n\n        // Test Workflow from a starting point to end\n        this.app.post('/api/v1/workflows/test/:startingNodeId', async (req: Request, res: Response) => {\n            const body = req.body as ITestWorkflowBody\n            const nodes = body.nodes || []\n            const edges = body.edges || []\n            const clientId = body.clientId || ''\n\n            const { graph } = constructGraphs(nodes, edges)\n            const startingNodeId = req.params.startingNodeId\n\n            const startNode = nodes.find((nd: IReactFlowNode) => nd.id === startingNodeId)\n\n            if (startNode && startNode.data) {\n                let nodeData = startNode.data\n                await decryptCredentials(nodeData)\n                const nodeDataArray = resolveVariables(nodeData, nodes)\n                nodeData = nodeDataArray[0]\n\n                if (!Object.prototype.hasOwnProperty.call(this.componentNodes, nodeData.name)) {\n                    res.status(404).send(`Unable to test workflow from node: ${nodeData.name}`)\n                    return\n                }\n\n                if (nodeData.type === 'trigger') {\n                    const triggerNodeInstance = this.componentNodes[nodeData.name] as ITriggerNode\n                    const emitEventKey = startingNodeId\n                    nodeData.emitEventKey = emitEventKey\n\n                    triggerNodeInstance.once(emitEventKey, async (result: INodeExecutionData[]) => {\n                        await this.activeTestTriggerPool.remove(nodeData.name, this.componentNodes)\n\n                        const newWorkflowExecutedData = {\n                            nodeId: startingNodeId,\n                            nodeLabel: nodeData.label,\n                            data: result,\n                            status: 'FINISHED'\n                        } as IWorkflowExecutedData\n\n                        io.to(clientId).emit('testWorkflowNodeResponse', newWorkflowExecutedData)\n\n                        testWorkflow(startingNodeId, result, nodes, edges, graph, this.componentNodes, clientId, io)\n                    })\n\n                    await triggerNodeInstance.runTrigger!.call(triggerNodeInstance, nodeData)\n                    this.activeTestTriggerPool.add(nodeData.name, nodeData)\n                } else if (nodeData.type === 'webhook') {\n                    const webhookNodeInstance = this.componentNodes[nodeData.name] as IWebhookNode\n                    const newBody = {\n                        webhookEndpoint: nodeData.webhookEndpoint,\n                        httpMethod: (nodeData.inputParameters?.httpMethod as WebhookMethod) || 'POST'\n                    } as any\n\n                    if (webhookNodeInstance.webhookMethods?.createWebhook) {\n                        if (!process.env.TUNNEL_BASE_URL) {\n                            res.status(500).send(`Please enable tunnel by setting ENABLE_TUNNEL to true in env file`)\n                            return\n                        }\n\n                        const webhookFullUrl = `${process.env.TUNNEL_BASE_URL}api/v1/webhook/${nodeData.webhookEndpoint}`\n                        const webhookId = await webhookNodeInstance.webhookMethods?.createWebhook.call(\n                            webhookNodeInstance,\n                            nodeData,\n                            webhookFullUrl\n                        )\n\n                        if (webhookId !== undefined) {\n                            newBody.webhookId = webhookId\n                        }\n                    }\n\n                    this.activeTestWebhookPool.add(\n                        newBody.webhookEndpoint,\n                        newBody.httpMethod,\n                        nodes,\n                        edges,\n                        nodeData,\n                        startingNodeId,\n                        clientId,\n                        true,\n                        newBody?.webhookId\n                    )\n                } else if (nodeData.type === 'action') {\n                    const actionNodeInstance = this.componentNodes[nodeData.name] as INode\n                    const result = await actionNodeInstance.run!.call(actionNodeInstance, nodeData)\n                    checkOAuth2TokenRefreshed(result, nodeData)\n\n                    const newWorkflowExecutedData = {\n                        nodeId: startingNodeId,\n                        nodeLabel: nodeData.label,\n                        data: result,\n                        status: 'FINISHED'\n                    } as IWorkflowExecutedData\n\n                    io.to(clientId).emit('testWorkflowNodeResponse', newWorkflowExecutedData)\n\n                    const reactFlowNodes = nodes\n                    const nodeIndex = reactFlowNodes.findIndex((nd) => nd.id === startingNodeId)\n                    updateNodeOutput(reactFlowNodes, nodeIndex, result || [])\n\n                    testWorkflow(startingNodeId, result || [], reactFlowNodes, edges, graph, this.componentNodes, clientId, io)\n                }\n            }\n        })\n\n        // ----------------------------------------\n        // Execution\n        // ----------------------------------------\n\n        // Get all executions\n        this.app.get('/api/v1/executions', async (req: Request, res: Response) => {\n            const executions = await this.AppDataSource.getMongoRepository(Execution).find()\n            return res.json(executions)\n        })\n\n        // Get specific execution via shortId\n        this.app.get('/api/v1/executions/:shortId', async (req: Request, res: Response) => {\n            const results = await this.AppDataSource.getMongoRepository(Execution).findOneBy({\n                shortId: req.params.shortId\n            })\n            return res.json(results)\n        })\n\n        // Create new execution\n        this.app.post('/api/v1/executions', async (req: Request, res: Response) => {\n            const body = req.body\n            const newExecution = new Execution()\n            Object.assign(newExecution, body)\n\n            const execution = await this.AppDataSource.getMongoRepository(Execution).create(newExecution)\n            const results = await this.AppDataSource.getMongoRepository(Execution).save(execution)\n            return res.json(results)\n        })\n\n        // Update execution\n        this.app.put('/api/v1/executions/:shortId', async (req: Request, res: Response) => {\n            const execution = await this.AppDataSource.getMongoRepository(Execution).findOneBy({\n                shortId: req.params.shortId\n            })\n\n            if (!execution) {\n                res.status(404).send(`Execution ${req.params.shortId} not found`)\n                return\n            }\n\n            const body = req.body\n            const updateExecution = new Execution()\n            Object.assign(updateExecution, body)\n\n            this.AppDataSource.getMongoRepository(Execution).merge(execution, updateExecution)\n            const results = await this.AppDataSource.getMongoRepository(Execution).save(execution)\n            return res.json(results)\n        })\n\n        // Delete execution via shortId\n        this.app.delete('/api/v1/executions/:shortId', async (req: Request, res: Response) => {\n            const results = await this.AppDataSource.getMongoRepository(Execution).delete({ shortId: req.params.shortId })\n            return res.json(results)\n        })\n\n        // ----------------------------------------\n        // Nodes\n        // ----------------------------------------\n\n        // Get all component nodes\n        this.app.get('/api/v1/nodes', (req: Request, res: Response) => {\n            const returnData = []\n            for (const nodeName in this.componentNodes) {\n                // Remove certain node properties to avoid error of Converting circular structure to JSON\n                const clonedNode = JSON.parse(\n                    JSON.stringify(this.componentNodes[nodeName], (key, val) => {\n                        if (key !== 'cronJobs' && key !== 'providers') return val\n                    })\n                )\n                returnData.push(clonedNode)\n            }\n            return res.json(returnData)\n        })\n\n        // Get specific component node via name\n        this.app.get('/api/v1/nodes/:name', (req: Request, res: Response) => {\n            if (Object.prototype.hasOwnProperty.call(this.componentNodes, req.params.name)) {\n                // Remove certain node properties to avoid error of Converting circular structure to JSON\n                const clonedNode = JSON.parse(\n                    JSON.stringify(this.componentNodes[req.params.name], (key, val) => {\n                        if (key !== 'cronJobs' && key !== 'providers') return val\n                    })\n                )\n                return res.json(clonedNode)\n            } else {\n                throw new Error(`Node ${req.params.name} not found`)\n            }\n        })\n\n        // Returns specific component node icon via name\n        this.app.get('/api/v1/node-icon/:name', (req: Request, res: Response) => {\n            if (Object.prototype.hasOwnProperty.call(this.componentNodes, req.params.name)) {\n                const nodeInstance = this.componentNodes[req.params.name]\n                if (nodeInstance.icon === undefined) {\n                    throw new Error(`Node ${req.params.name} icon not found`)\n                }\n\n                if (nodeInstance.icon.endsWith('.svg') || nodeInstance.icon.endsWith('.png') || nodeInstance.icon.endsWith('.jpg')) {\n                    const filepath = nodeInstance.icon\n                    res.sendFile(filepath)\n                } else {\n                    throw new Error(`Node ${req.params.name} icon is missing icon`)\n                }\n            } else {\n                throw new Error(`Node ${req.params.name} not found`)\n            }\n        })\n\n        // Test a node\n        this.app.post('/api/v1/node-test/:name', async (req: Request, res: Response) => {\n            const body: ITestNodeBody = req.body\n            const { nodes, edges, nodeId, clientId } = body\n\n            const node = nodes.find((nd: IReactFlowNode) => nd.id === nodeId)\n\n            if (!node) return res.status(404).send(`Test node ${nodeId} not found`)\n\n            if (Object.prototype.hasOwnProperty.call(this.componentNodes, req.params.name)) {\n                try {\n                    const nodeInstance = this.componentNodes[req.params.name]\n                    const nodeType = nodeInstance.type\n                    const nodeData = node.data\n\n                    await decryptCredentials(nodeData)\n\n                    if (nodeType === 'action') {\n                        let results: INodeExecutionData[] = []\n                        const reactFlowNodeData: INodeData[] = resolveVariables(nodeData, nodes)\n                        for (let i = 0; i < reactFlowNodeData.length; i += 1) {\n                            const result = await nodeInstance.run!.call(nodeInstance, reactFlowNodeData[i])\n                            checkOAuth2TokenRefreshed(result, reactFlowNodeData[i])\n                            if (result) results.push(...result)\n                        }\n                        return res.json(results)\n                    } else if (nodeType === 'trigger') {\n                        const triggerNodeInstance = nodeInstance as ITriggerNode\n                        const emitEventKey = nodeId\n                        nodeData.emitEventKey = emitEventKey\n                        triggerNodeInstance.once(emitEventKey, async (result: INodeExecutionData[]) => {\n                            await this.activeTestTriggerPool.remove(nodeData.name, this.componentNodes)\n                            return res.json(result)\n                        })\n                        await triggerNodeInstance.runTrigger!.call(triggerNodeInstance, nodeData)\n                        this.activeTestTriggerPool.add(req.params.name, nodeData)\n                    } else if (nodeType === 'webhook') {\n                        const webhookNodeInstance = nodeInstance as IWebhookNode\n                        const newBody = {\n                            webhookEndpoint: nodeData.webhookEndpoint,\n                            httpMethod: (nodeData.inputParameters?.httpMethod as WebhookMethod) || 'POST'\n                        } as any\n\n                        if (webhookNodeInstance.webhookMethods?.createWebhook) {\n                            if (!process.env.TUNNEL_BASE_URL) {\n                                res.status(500).send(`Please enable tunnel by setting ENABLE_TUNNEL to true in env file`)\n                                return\n                            }\n\n                            const webhookFullUrl = `${process.env.TUNNEL_BASE_URL}api/v1/webhook/${nodeData.webhookEndpoint}`\n                            const webhookId = await webhookNodeInstance.webhookMethods?.createWebhook.call(\n                                webhookNodeInstance,\n                                nodeData,\n                                webhookFullUrl\n                            )\n\n                            if (webhookId !== undefined) {\n                                newBody.webhookId = webhookId\n                            }\n                        }\n\n                        this.activeTestWebhookPool.add(\n                            newBody.webhookEndpoint,\n                            newBody.httpMethod,\n                            nodes,\n                            edges,\n                            nodeData,\n                            nodeId,\n                            clientId as string,\n                            false,\n                            newBody?.webhookId\n                        )\n\n                        return res.json(newBody)\n                    }\n                } catch (error) {\n                    res.status(500).send(`Node test error: ${error}`)\n                    console.error(error)\n                    return\n                }\n            } else {\n                res.status(404).send(`Node ${req.params.name} not found`)\n                return\n            }\n        })\n\n        // load async options\n        this.app.post('/api/v1/node-load-method/:name', async (req: Request, res: Response) => {\n            const nodeData: INodeData = req.body\n\n            if (Object.prototype.hasOwnProperty.call(this.componentNodes, req.params.name)) {\n                try {\n                    const nodeInstance = this.componentNodes[req.params.name]\n                    const methodName = nodeData.loadMethod || ''\n                    const loadFromDbCollections = nodeData.loadFromDbCollections || []\n                    const dbCollection = {} as IDbCollection\n                    const apiKeys = await getAPIKeys()\n\n                    for (let i = 0; i < loadFromDbCollections.length; i += 1) {\n                        let collection: any\n\n                        if (loadFromDbCollections[i] === 'Contract') collection = Contract\n                        else if (loadFromDbCollections[i] === 'Workflow') collection = Workflow\n                        else if (loadFromDbCollections[i] === 'Webhook') collection = Webhook\n                        else if (loadFromDbCollections[i] === 'Execution') collection = Execution\n                        else if (loadFromDbCollections[i] === 'Credential') collection = Credential\n                        else if (loadFromDbCollections[i] === 'Wallet') collection = Wallet\n\n                        const res = await this.AppDataSource.getMongoRepository(collection).find()\n                        dbCollection[loadFromDbCollections[i]] = res\n                    }\n\n                    await decryptCredentials(nodeData)\n\n                    const returnOptions: INodeOptionsValue[] = await nodeInstance.loadMethods![methodName]!.call(\n                        nodeInstance,\n                        nodeData,\n                        loadFromDbCollections.length ? dbCollection : undefined,\n                        apiKeys\n                    )\n\n                    return res.json(returnOptions)\n                } catch (error) {\n                    return res.json([])\n                }\n            } else {\n                res.status(404).send(`Node ${req.params.name} not found`)\n                return\n            }\n        })\n\n        // ----------------------------------------\n        // Credential\n        // ----------------------------------------\n\n        // Create new credential\n        this.app.post('/api/v1/credentials', async (req: Request, res: Response) => {\n            const body: ICredentialBody = req.body\n\n            const newCredential = await transformToCredentialEntity(body)\n            const credential = await this.AppDataSource.getMongoRepository(Credential).create(newCredential)\n            const results = await this.AppDataSource.getMongoRepository(Credential).save(credential)\n            return res.json(results)\n        })\n\n        // Get node credential via specific nodeCredentialName\n        this.app.get('/api/v1/node-credentials/:nodeCredentialName', (req: Request, res: Response) => {\n            const credentials = []\n\n            for (const credName in this.componentCredentials) {\n                credentials.push(this.componentCredentials[credName])\n            }\n\n            const cred = credentials.find((crd) => crd.name === req.params.nodeCredentialName)\n            if (cred === undefined) {\n                throw new Error(`Credential ${req.params.nodeCredentialName} not found`)\n            }\n            return res.json(cred)\n        })\n\n        // Get list of registered credentials via nodeCredentialName\n        this.app.get('/api/v1/credentials', async (req: Request, res: Response) => {\n            const credentials = await this.AppDataSource.getMongoRepository(Credential).find({\n                // @ts-ignore\n                where: {\n                    nodeCredentialName: { $eq: req.query.nodeCredentialName }\n                }\n            })\n            return res.json(credentials)\n        })\n\n        // Get registered credential via objectId\n        this.app.get('/api/v1/credentials/:id', async (req: Request, res: Response) => {\n            const isEncrypted = req.query.isEncrypted\n\n            const encryptKey = await getEncryptionKey()\n\n            const credential = await this.AppDataSource.getMongoRepository(Credential).findOneBy({\n                _id: new ObjectId(req.params.id)\n            })\n\n            if (!credential) {\n                res.status(404).send(`Credential ${req.params.id} not found`)\n                return\n            }\n\n            if (isEncrypted) {\n                return res.json(credential)\n            } else {\n                // Decrpyt credentialData\n                const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n                const credentialResponse: ICredentialResponse = {\n                    ...credential,\n                    credentialData: decryptedCredentialData\n                }\n                return res.json(credentialResponse)\n            }\n        })\n\n        // Update credential\n        this.app.put('/api/v1/credentials/:id', async (req: Request, res: Response) => {\n            const credential = await this.AppDataSource.getMongoRepository(Credential).findOneBy({\n                _id: new ObjectId(req.params.id)\n            })\n\n            if (!credential) {\n                res.status(404).send(`Credential ${req.params.id} not found`)\n                return\n            }\n\n            const encryptKey = await getEncryptionKey()\n            const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n\n            const body: ICredentialBody = req.body\n            const { credentialData, name, nodeCredentialName } = body\n            const newBody: ICredentialBody = {\n                name: name,\n                nodeCredentialName: nodeCredentialName,\n                credentialData: {\n                    ...decryptedCredentialData,\n                    ...credentialData\n                }\n            }\n            const updateCredential = await transformToCredentialEntity(newBody)\n\n            this.AppDataSource.getMongoRepository(Credential).merge(credential, updateCredential)\n            const results = await this.AppDataSource.getMongoRepository(Credential).save(credential)\n            return res.json(results)\n        })\n\n        // Delete credential via id\n        this.app.delete('/api/v1/credentials/:id', async (req: Request, res: Response) => {\n            const results = await this.AppDataSource.getMongoRepository(Credential).deleteOne({ _id: new ObjectId(req.params.id) })\n            return res.json(results)\n        })\n\n        // ----------------------------------------\n        // Contract\n        // ----------------------------------------\n\n        // Get all contracts\n        this.app.get('/api/v1/contracts', async (req: Request, res: Response) => {\n            const contracts = await this.AppDataSource.getMongoRepository(Contract).find()\n            return res.json(contracts)\n        })\n\n        // Get specific contract via id\n        this.app.get('/api/v1/contracts/:id', async (req: Request, res: Response) => {\n            const results = await this.AppDataSource.getMongoRepository(Contract).findOneBy({\n                _id: new ObjectId(req.params.id)\n            })\n            return res.json(results)\n        })\n\n        // Create new contract\n        this.app.post('/api/v1/contracts', async (req: Request, res: Response) => {\n            try {\n                const body = req.body\n                const newContract = new Contract()\n                Object.assign(newContract, body)\n\n                const contract = await this.AppDataSource.getMongoRepository(Contract).create(newContract)\n                const results = await this.AppDataSource.getMongoRepository(Contract).save(contract)\n                return res.json(results)\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n        })\n\n        // Update contract\n        this.app.put('/api/v1/contracts/:id', async (req: Request, res: Response) => {\n            const contract = await this.AppDataSource.getMongoRepository(Contract).findOneBy({\n                _id: new ObjectId(req.params.id)\n            })\n\n            if (!contract) {\n                res.status(404).send(`Contract with id: ${req.params.id} not found`)\n                return\n            }\n\n            const body = req.body\n            const updateContract = new Contract()\n            Object.assign(updateContract, body)\n\n            this.AppDataSource.getMongoRepository(Contract).merge(contract, updateContract)\n            try {\n                const results = await this.AppDataSource.getMongoRepository(Contract).save(contract)\n                return res.json(results)\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n        })\n\n        // Delete contract via id\n        this.app.delete('/api/v1/contracts/:id', async (req: Request, res: Response) => {\n            const deletQuery = {\n                _id: new ObjectId(req.params.id)\n            } as any\n            const results = await this.AppDataSource.getMongoRepository(Contract).delete(deletQuery)\n            return res.json(results)\n        })\n\n        // Get contract ABI\n        this.app.post('/api/v1/contracts/getabi', async (req: Request, res: Response) => {\n            const body: IContractRequestBody = req.body\n\n            if (body.networks === undefined || body.credentials === undefined || body.contractInfo === undefined) {\n                res.status(500).send(`Missing contract details`)\n                return\n            }\n\n            if (body.credentials && body.credentials.registeredCredential) {\n                // @ts-ignore\n                const credentialId: string = body.credentials.registeredCredential?._id\n\n                const credential = await this.AppDataSource.getMongoRepository(Credential).findOneBy({\n                    _id: new ObjectId(credentialId)\n                })\n                if (!credential) return res.status(404).send(`Credential with id: ${credentialId} not found`)\n\n                const encryptKey = await getEncryptionKey()\n                const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n\n                body.credentials = decryptedCredentialData\n            }\n\n            let url = ''\n            // Get ABI\n            if (body.credentials.apiKey) {\n                url = `${body.networks.uri}?module=contract&action=getabi&address=${body.contractInfo.address}&apikey=${body.credentials.apiKey}`\n            } else {\n                url = `${body.networks.uri}?module=contract&action=getabi&address=${body.contractInfo.address}`\n            }\n\n            // URL is not etherscan.io subdomain\n            if (new URL(url).hostname.split('.').slice(-2).join('.') !== 'etherscan.io') {\n                // How do you want to handle this error?\n                return res.status(403).json({\n                    status: '0',\n                    message: 'NOTOK',\n                    result: 'Please change URL and try again'\n                })\n            }\n\n            const options: AxiosRequestConfig = {\n                method: 'GET',\n                url\n            }\n\n            try {\n                const response = await axios.request(options)\n                return res.json(response.data)\n            } catch (e) {\n                console.error(e)\n                const errorObject = {\n                    status: '0',\n                    message: 'NOTOK',\n                    result: 'Unable to fetch ABI, please use correct API Key for higher rate limit'\n                }\n                return res.json(errorObject)\n            }\n        })\n\n        // ----------------------------------------\n        // Wallets\n        // ----------------------------------------\n\n        // Get all wallets\n        this.app.get('/api/v1/wallets', async (req: Request, res: Response) => {\n            const wallets = await this.AppDataSource.getMongoRepository(Wallet).find()\n            return res.json(wallets)\n        })\n\n        // Get specific wallet via id\n        this.app.get('/api/v1/wallets/:id', async (req: Request, res: Response) => {\n            try {\n                const wallet = await this.AppDataSource.getMongoRepository(Wallet).findOneBy({\n                    _id: new ObjectId(req.params.id)\n                })\n\n                if (!wallet) {\n                    res.status(404).send(`Wallet ${req.params.id} not found`)\n                    return\n                }\n\n                const walletResponse: IWalletResponse = {\n                    ...wallet,\n                    balance: ''\n                }\n\n                // Decrpyt providerCredential\n                const encryptKey = await getEncryptionKey()\n                const providerCredential = JSON.parse(wallet.providerCredential)\n                let decryptedCredentialData: ICredentialDataDecrypted = {}\n\n                if (providerCredential.registeredCredential) {\n                    const credentialId: string = providerCredential.registeredCredential?._id\n\n                    const credential = await this.AppDataSource.getMongoRepository(Credential).findOneBy({\n                        _id: new ObjectId(credentialId)\n                    })\n                    if (!credential) return res.status(404).send(`Credential with id: ${credentialId} not found`)\n\n                    // Decrpyt credentialData\n                    decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n                }\n\n                const credentialMethod = providerCredential.credentialMethod\n                let url = ''\n                const network = wallet.network as NETWORK\n\n                // Get Balance\n                if (decryptedCredentialData.apiKey && credentialMethod !== 'noAuth') {\n                    url = `${etherscanAPIs[network]}?module=account&action=balance&address=${wallet.address}&tag=latest&apikey=${\n                        decryptedCredentialData.apiKey as string\n                    }`\n                } else {\n                    url = `${etherscanAPIs[network]}?module=account&action=balance&address=${wallet.address}&tag=latest`\n                }\n\n                const options: AxiosRequestConfig = {\n                    method: 'GET',\n                    url\n                }\n\n                try {\n                    const response = await axios.request(options)\n                    if (response.data && response.data.result) {\n                        walletResponse.balance = `${ethers.utils.formatEther(ethers.BigNumber.from(response.data.result))} ${\n                            nativeCurrency[wallet.network as NETWORK]\n                        }`\n                    }\n                } catch (e) {\n                    walletResponse.balance = 'Unable to fetch balance, please use correct API Key for higher rate limit'\n                }\n                return res.json(walletResponse)\n            } catch (e) {\n                console.error(e)\n                return res.status(500).send(e)\n            }\n        })\n\n        // Create new wallet\n        this.app.post('/api/v1/wallets', async (req: Request, res: Response) => {\n            try {\n                const body: IWalletRequestBody = req.body\n                const { name, network, providerCredential, privateKey } = body\n\n                const encryptKey = await getEncryptionKey()\n\n                const newBody: any = {\n                    name,\n                    network,\n                    providerCredential\n                }\n\n                let randomWallet: ethers.Wallet\n\n                if (privateKey) randomWallet = new ethers.Wallet(privateKey)\n                else randomWallet = ethers.Wallet.createRandom()\n\n                newBody.address = randomWallet.address\n\n                const walletCredential = {\n                    privateKey: randomWallet.privateKey\n                } as any\n\n                // Imported wallet doesn't have mnemonic and path\n                if (!privateKey) {\n                    walletCredential.mnemonic = randomWallet.mnemonic.phrase\n                    walletCredential.path = randomWallet.mnemonic.path\n                }\n                newBody.walletCredential = encryptCredentialData(walletCredential, encryptKey) as string\n\n                const newWallet = new Wallet()\n                Object.assign(newWallet, newBody)\n\n                const wallet = await this.AppDataSource.getMongoRepository(Wallet).create(newWallet)\n                const results = await this.AppDataSource.getMongoRepository(Wallet).save(wallet)\n                return res.json(results)\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n        })\n\n        // Update wallet\n        this.app.put('/api/v1/wallets/:id', async (req: Request, res: Response) => {\n            const wallet = await this.AppDataSource.getMongoRepository(Wallet).findOneBy({\n                _id: new ObjectId(req.params.id)\n            })\n\n            if (!wallet) {\n                res.status(404).send(`Wallet with id: ${req.params.id} not found`)\n                return\n            }\n\n            const body = req.body\n            const updateWallet = new Wallet()\n            Object.assign(updateWallet, body)\n\n            this.AppDataSource.getMongoRepository(Wallet).merge(wallet, updateWallet)\n            try {\n                const results = await this.AppDataSource.getMongoRepository(Wallet).save(wallet)\n                return res.json(results)\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n        })\n\n        // Delete wallet via id\n        this.app.delete('/api/v1/wallets/:id', async (req: Request, res: Response) => {\n            const deletQuery = {\n                _id: new ObjectId(req.params.id)\n            } as any\n            const results = await this.AppDataSource.getMongoRepository(Wallet).delete(deletQuery)\n            return res.json(results)\n        })\n\n        // Get wallet credentials\n        this.app.get('/api/v1/wallets/credential/:id', async (req: Request, res: Response) => {\n            try {\n                const wallet = await this.AppDataSource.getMongoRepository(Wallet).findOneBy({\n                    _id: new ObjectId(req.params.id)\n                })\n\n                if (!wallet) {\n                    res.status(404).send(`Wallet ${req.params.id} not found`)\n                    return\n                }\n\n                // Decrpyt credentialData\n                const encryptKey = await getEncryptionKey()\n\n                const decryptedCredentialData = decryptCredentialData(wallet.walletCredential, encryptKey)\n\n                return res.json(decryptedCredentialData)\n            } catch (e) {\n                console.error(e)\n                return res.status(500).send(e)\n            }\n        })\n\n        // ----------------------------------------\n        // Active Test Pools\n        // ----------------------------------------\n\n        // Remove active test triggers\n        this.app.post('/api/v1/remove-test-triggers', async (req: Request, res: Response) => {\n            if (this.activeTestTriggerPool) await this.activeTestTriggerPool.removeAll(this.componentNodes)\n            res.status(200).send('success')\n            return\n        })\n\n        // Remove active test webhooks\n        this.app.post('/api/v1/remove-test-webhooks', async (req: Request, res: Response) => {\n            if (this.activeTestWebhookPool) await this.activeTestWebhookPool.removeAll(this.componentNodes)\n            res.status(200).send('success')\n            return\n        })\n\n        // ----------------------------------------\n        // API Keys\n        // ----------------------------------------\n\n        // Get api keys\n        this.app.get('/api/v1/apikey', async (req: Request, res: Response) => {\n            const keys = await getAPIKeys()\n            return res.json(keys)\n        })\n\n        // Add new api key\n        this.app.post('/api/v1/apikey', async (req: Request, res: Response) => {\n            const keys = await addAPIKey(req.body.keyName)\n            return res.json(keys)\n        })\n\n        // Update api key\n        this.app.put('/api/v1/apikey/:id', async (req: Request, res: Response) => {\n            const keys = await updateAPIKey(req.params.id, req.body.keyName)\n            return res.json(keys)\n        })\n\n        // Delete new api key\n        this.app.delete('/api/v1/apikey/:id', async (req: Request, res: Response) => {\n            const keys = await deleteAPIKey(req.params.id)\n            return res.json(keys)\n        })\n\n        // ----------------------------------------\n        // Webhook\n        // ----------------------------------------\n\n        // GET webhook requests\n        this.app.get(`/api/v1/webhook/*`, express.raw(), async (req: Request, res: Response) => {\n            const splitUrl = req.path.split('/api/v1/webhook/')\n            const webhookEndpoint = splitUrl[splitUrl.length - 1]\n            await processWebhook(\n                res,\n                req,\n                this.AppDataSource,\n                webhookEndpoint,\n                'GET',\n                this.componentNodes,\n                io,\n                this.deployedWorkflowsPool,\n                this.activeTestWebhookPool\n            )\n        })\n\n        // POST webhook requests\n        this.app.post(`/api/v1/webhook/*`, express.raw(), async (req: Request, res: Response) => {\n            const splitUrl = req.path.split('/api/v1/webhook/')\n            const webhookEndpoint = splitUrl[splitUrl.length - 1]\n            await processWebhook(\n                res,\n                req,\n                this.AppDataSource,\n                webhookEndpoint,\n                'POST',\n                this.componentNodes,\n                io,\n                this.deployedWorkflowsPool,\n                this.activeTestWebhookPool\n            )\n        })\n\n        this.app.get('/api/v1/get-tunnel-url', (req: Request, res: Response) => {\n            if (!process.env.TUNNEL_BASE_URL) throw new Error(`Tunnel URL not found`)\n            res.send(process.env.TUNNEL_BASE_URL)\n        })\n\n        // ----------------------------------------\n        // OAuth2\n        // ----------------------------------------\n        this.app.get('/api/v1/oauth2', async (req: Request, res: Response) => {\n            if (!req.query.credentialId) return res.status(404).send('Credential not found')\n\n            const credentialId = req.query.credentialId\n\n            const credential = await this.AppDataSource.getMongoRepository(Credential).findOneBy({\n                _id: new ObjectId(credentialId as string)\n            })\n\n            if (!credential) return res.status(404).send(`Credential with Id ${credentialId} not found`)\n\n            // Decrpyt credentialData\n            const encryptKey = await getEncryptionKey()\n\n            const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n\n            const baseURL = req.get('host')\n            const authUrl = decryptedCredentialData.authUrl as string\n            const authorizationURLParameters = decryptedCredentialData.authorizationURLParameters as string\n            const clientID = decryptedCredentialData.clientID as string\n            const scope = decryptedCredentialData.scope as string\n            let scopeArray: any\n            try {\n                scopeArray = scope.replace(/\\s/g, '')\n                scopeArray = JSON.parse(scopeArray)\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n\n            // check if authUrl is valid and is http/https\n            try {\n                const authUrlCheck = new URL(authUrl)\n                if (authUrlCheck.protocol !== 'http:' && authUrlCheck.protocol !== 'https:') {\n                    throw new Error('Invalid URL Protocol')\n                }\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n\n            const serializedScope = scopeArray.join(' ')\n            const redirectUrl = `${req.secure ? 'https' : req.protocol}://${baseURL}/api/v1/oauth2/callback`\n\n            const returnURL = `${authUrl}?${authorizationURLParameters}&client_id=${clientID}&scope=${serializedScope}&redirect_uri=${redirectUrl}&state=${credentialId}`\n\n            res.send(returnURL)\n        })\n\n        this.app.get('/api/v1/oauth2/callback', async (req: Request, res: Response) => {\n            const code = req.query.code\n            if (!code) return res.status(500).send('Unable to retrieve authorization code from oAuth2 callback')\n\n            const credentialId = req.query.state\n            if (!credentialId) return res.status(500).send('Unable to retrieve credentialId from oAuth2 callback')\n\n            const credential = await this.AppDataSource.getMongoRepository(Credential).findOneBy({\n                _id: new ObjectId(credentialId as string)\n            })\n\n            if (!credential) return res.status(404).send(`Credential with Id ${credentialId} not found`)\n\n            // Decrpyt credentialData\n            const encryptKey = await getEncryptionKey()\n            const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n\n            // Get access_token and refresh_token\n            const accessTokenUrl = decryptedCredentialData.accessTokenUrl as string\n            const client_id = decryptedCredentialData.clientID as string\n            const client_secret = decryptedCredentialData.clientSecret as string | undefined\n            const authUrl = decryptedCredentialData.authUrl as string\n            const scope = decryptedCredentialData.scope as string\n            let scopeArray: string[] = []\n            try {\n                scopeArray = JSON.parse(scope.replace(/\\s/g, ''))\n            } catch (e) {\n                return res.status(500).send(e)\n            }\n\n            const baseURL = req.get('host')\n            const redirect_uri = `${req.secure ? 'https' : req.protocol}://${baseURL}/api/v1/oauth2/callback`\n\n            const oAuth2Parameters = {\n                clientId: client_id,\n                clientSecret: client_secret,\n                accessTokenUri: accessTokenUrl,\n                authorizationUri: authUrl,\n                redirectUri: redirect_uri,\n                scopes: scopeArray\n            }\n\n            const oAuthObj = new ClientOAuth2(oAuth2Parameters)\n\n            const queryParameters = req.originalUrl.split('?').splice(1, 1).join('')\n\n            const oauthToken = await oAuthObj.code.getToken(`${oAuth2Parameters.redirectUri}?${queryParameters}`)\n\n            const { access_token, token_type, expires_in, refresh_token } = oauthToken.data\n\n            const body: ICredentialBody = {\n                name: credential.name,\n                nodeCredentialName: credential.nodeCredentialName,\n                credentialData: {\n                    ...decryptedCredentialData,\n                    access_token,\n                    token_type,\n                    expires_in,\n                    refresh_token\n                }\n            }\n\n            const updateCredential = await transformToCredentialEntity(body)\n\n            this.AppDataSource.getMongoRepository(Credential).merge(credential, updateCredential)\n            await this.AppDataSource.getMongoRepository(Credential).save(credential)\n\n            return res.sendFile(getOAuth2HTMLPath())\n        })\n\n        this.app.get('/api/v1/oauth2-redirecturl', async (req: Request, res: Response) => {\n            const baseURL = req.get('host')\n            res.send(`${req.secure ? 'https' : req.protocol}://${baseURL}/api/v1/oauth2/callback`)\n        })\n\n        // ----------------------------------------\n        // Serve UI static\n        // ----------------------------------------\n\n        const packagePath = getNodeModulesPackagePath('outerbridge-ui')\n        const uiBuildPath = path.join(packagePath, 'build')\n        const uiHtmlPath = path.join(packagePath, 'build', 'index.html')\n\n        this.app.use('/', express.static(uiBuildPath))\n\n        // All other requests not handled will return React app\n        this.app.use((req, res) => {\n            res.sendFile(uiHtmlPath)\n        })\n    }\n\n    async stopApp() {\n        try {\n            const removePromises: any[] = []\n\n            // Remove deployed workflows pools\n            if (this.deployedWorkflowsPool) removePromises.push(this.deployedWorkflowsPool.removeAll(this.componentNodes))\n\n            // Remove test trigger pools\n            if (this.activeTestTriggerPool) removePromises.push(this.activeTestTriggerPool.removeAll(this.componentNodes))\n\n            // Remove test webhook pools\n            if (this.activeTestWebhookPool) removePromises.push(this.activeTestWebhookPool.removeAll(this.componentNodes))\n\n            await Promise.all(removePromises)\n        } catch (e) {\n            console.error(`❌[server]: Outerbridge Server shut down error: ${e}`)\n        }\n    }\n}\n\nlet serverApp: App | undefined\n\nexport async function start(): Promise<void> {\n    serverApp = new App()\n\n    const port = parseInt(process.env.PORT || '', 10) || 3000\n    const server = http.createServer(serverApp.app)\n    const io = new Server(server, {\n        cors: {\n            origin: 'http://localhost:8080'\n        }\n    })\n\n    io.on('connection', (socket: Socket) => {\n        console.info('👥[server]: client connected: ', socket.id)\n\n        socket.on('disconnect', (reason) => {\n            console.info('👤[server]: client disconnected = ', reason)\n        })\n    })\n\n    await serverApp.initDatabase()\n    await serverApp.config(io)\n\n    server.listen(port, () => {\n        console.info(`⚡️[server]: Outerbridge Server is listening at ${port}`)\n    })\n}\n\nexport function getInstance(): App | undefined {\n    return serverApp\n}\n"
  },
  {
    "path": "packages/server/src/utils/index.ts",
    "content": "import moment from 'moment'\nimport path from 'path'\nimport fs from 'fs'\nimport { lib, PBKDF2, AES, enc } from 'crypto-js'\nimport { Request, Response } from 'express'\nimport { DataSource } from 'typeorm'\nimport {\n    ICredentialBody,\n    ICredentialDataDecrypted,\n    INodeDependencies,\n    INodeDirectedGraph,\n    IReactFlowEdge,\n    IReactFlowNode,\n    IVariableDict,\n    IReactFlowObject,\n    IWebhookNode,\n    IComponentNodesPool,\n    WebhookMethod,\n    INodeQueue,\n    IExploredNode,\n    IWorkflowExecutedData\n} from '../Interface'\nimport lodash from 'lodash'\nimport { ICommonObject, INodeData, INodeExecutionData, IWallet, OAUTH2_REFRESHED, IOAuth2RefreshResponse } from 'outerbridge-components'\nimport { Workflow } from '../entity/Workflow'\nimport { Credential } from '../entity/Credential'\nimport { Webhook } from '../entity/Webhook'\nimport { DeployedWorkflowPool } from '../DeployedWorkflowPool'\nimport { ObjectId } from 'mongodb'\nimport { ActiveTestWebhookPool } from '../ActiveTestWebhookPool'\nimport { getDataSource } from '../DataSource'\nimport { scryptSync, randomBytes, timingSafeEqual } from 'crypto'\n\nexport enum ShortIdConstants {\n    WORKFLOW_ID_PREFIX = 'W',\n    EXECUTION_ID_PREFIX = 'E'\n}\n\nconst RANDOM_LENGTH = 8\nconst DICTIONARY_1 = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'\nconst DICTIONARY_3 = 'abcdefghijklmnopqrstuvwxyz0123456789'\n\n/**\n * Returns a Short ID\n * Format : WDDMMMYY-[0-1A-Z]*8 , ie:  B10JAN21-2CH9PX8N\n * Where W=Entity Prefix, DD=DAY, MMM=Month, YY=Year, -=Separator (hyphen character), [0-1A-Z]*8 = random part of length 8 by default.\n *\n * @param {string | Date} prefix Identifies the Entity, 'W' for Workflow, 'E' for Execution\n * @param {Date} date The Date the ShortId was created\n * @returns {string} shortId\n */\nexport const shortId = (prefix: 'W' | 'E', date: string | Date): string => {\n    const isValidPrefix = prefix === 'W' || prefix === 'E'\n    const utcCreatedAt = new Date(date)\n    if (!isValidPrefix) throw new Error('Invalid short id prefix, only possible values \"W\" or \"E\".')\n    const DICTIONARY = DICTIONARY_1\n    let randomPart = ''\n    for (let i = 0; i < RANDOM_LENGTH; i++) {\n        randomPart += getRandomCharFromDictionary(DICTIONARY)\n    }\n    const sanitizedDate = formatDateForShortID(utcCreatedAt)\n    return `${prefix}${sanitizedDate}-${randomPart}`\n}\n\n/**\n * Format a date for use in the short id DDMMMYY with no hyphens\n * @param {Date} date\n * @returns {string} the sanitized date as string ie: 10JAN21\n */\nexport const formatDateForShortID = (date: Date): string => {\n    const localDate = moment(date)\n    return localDate.format('DDMMMYY').toUpperCase()\n}\n\nexport const getRandomCharFromDictionary = (dictionary: string) => {\n    const minDec = 0\n    const maxDec = dictionary.length + 1\n    const randDec = Math.floor(Math.random() * (maxDec - minDec) + minDec)\n    return dictionary.charAt(randDec)\n}\n\nexport const getRandomSubdomain = () => {\n    let randomPart = ''\n    for (let i = 0; i < 24; i++) {\n        randomPart += getRandomCharFromDictionary(DICTIONARY_3)\n    }\n    return randomPart\n}\n\n/**\n * Returns the path of node modules package\n * @param {string} packageName\n * @returns {string}\n */\nexport const getNodeModulesPackagePath = (packageName: string): string => {\n    const checkPaths = [\n        path.join(__dirname, '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', '..', '..', 'node_modules', packageName),\n        path.join(__dirname, '..', '..', '..', '..', '..', 'node_modules', packageName)\n    ]\n    for (const checkPath of checkPaths) {\n        if (fs.existsSync(checkPath)) {\n            return checkPath\n        }\n    }\n    return ''\n}\n\n/**\n * Returns the path of encryption key\n * @returns {string}\n */\nexport const getEncryptionKeyPath = (): string => {\n    return path.join(__dirname, '..', '..', 'encryption.key')\n}\n\n/**\n * Generate an encryption key\n * @returns {string}\n */\nexport const generateEncryptKey = (): string => {\n    const salt = lib.WordArray.random(128 / 8)\n    const key256Bits = PBKDF2(process.env.PASSPHRASE || 'MYPASSPHRASE', salt, {\n        keySize: 256 / 32,\n        iterations: 1000\n    })\n    return key256Bits.toString()\n}\n\n/**\n * Returns the encryption key\n * @returns {string}\n */\nexport const getEncryptionKey = async (): Promise<string> => {\n    try {\n        return await fs.promises.readFile(getEncryptionKeyPath(), 'utf8')\n    } catch (error) {\n        const encryptKey = generateEncryptKey()\n        await fs.promises.writeFile(getEncryptionKeyPath(), encryptKey)\n        return encryptKey\n    }\n}\n\n/**\n * Returns the api key path\n * @returns {string}\n */\nexport const getAPIKeyPath = (): string => {\n    return path.join(__dirname, '..', '..', 'api.json')\n}\n\n/**\n * Generate the api key\n * @returns {string}\n */\nexport const generateAPIKey = () => {\n    const buffer = randomBytes(32)\n    return buffer.toString('base64')\n}\n\n/**\n * Generate the secret key\n * @param {string} apiKey\n * @returns {string}\n */\nexport const generateSecretHash = (apiKey: string) => {\n    const salt = randomBytes(8).toString('hex')\n    const buffer = scryptSync(apiKey, salt, 64) as Buffer\n    return `${buffer.toString('hex')}.${salt}`\n}\n\n/**\n * Verify valid keys\n * @param {string} storedKey\n * @param {string} suppliedKey\n * @returns {boolean}\n */\nexport const compareKeys = (storedKey: string, suppliedKey: string) => {\n    const [hashedPassword, salt] = storedKey.split('.')\n    const buffer = scryptSync(suppliedKey, salt, 64) as Buffer\n    return timingSafeEqual(Buffer.from(hashedPassword, 'hex'), buffer)\n}\n\n/**\n * Get API keys\n * @returns {Promise<ICommonObject[]>}\n */\nexport const getAPIKeys = async (): Promise<ICommonObject[]> => {\n    try {\n        const content = await fs.promises.readFile(getAPIKeyPath(), 'utf8')\n        return JSON.parse(content)\n    } catch (error) {\n        const keyName = 'DefaultKey'\n        const apiKey = generateAPIKey()\n        const apiSecret = generateSecretHash(apiKey)\n        const content = [\n            {\n                keyName,\n                apiKey,\n                apiSecret,\n                createdAt: moment().format('DD-MMM-YY'),\n                id: randomBytes(16).toString('hex')\n            }\n        ]\n        await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')\n        return content\n    }\n}\n\n/**\n * Add new API key\n * @param {string} keyName\n * @returns {Promise<ICommonObject[]>}\n */\nexport const addAPIKey = async (keyName: string): Promise<ICommonObject[]> => {\n    const existingAPIKeys = await getAPIKeys()\n    const apiKey = generateAPIKey()\n    const apiSecret = generateSecretHash(apiKey)\n    const content = [\n        ...existingAPIKeys,\n        {\n            keyName,\n            apiKey,\n            apiSecret,\n            createdAt: moment().format('DD-MMM-YY'),\n            id: randomBytes(16).toString('hex')\n        }\n    ]\n    await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')\n    return content\n}\n\n/**\n * Update existing API key\n * @param {string} keyIdToUpdate\n * @param {string} newKeyName\n * @returns {Promise<ICommonObject[]>}\n */\nexport const updateAPIKey = async (keyIdToUpdate: string, newKeyName: string): Promise<ICommonObject[]> => {\n    const existingAPIKeys = await getAPIKeys()\n    const keyIndex = existingAPIKeys.findIndex((key) => key.id === keyIdToUpdate)\n    if (keyIndex < 0) return []\n    existingAPIKeys[keyIndex].keyName = newKeyName\n    await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(existingAPIKeys), 'utf8')\n    return existingAPIKeys\n}\n\n/**\n * Delete API key\n * @param {string} keyIdToDelete\n * @returns {Promise<ICommonObject[]>}\n */\nexport const deleteAPIKey = async (keyIdToDelete: string): Promise<ICommonObject[]> => {\n    const existingAPIKeys = await getAPIKeys()\n    const result = existingAPIKeys.filter((key) => key.id !== keyIdToDelete)\n    await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(result), 'utf8')\n    return result\n}\n\n/**\n * Encrypt credential data\n * @param {ICredentialDataDecrypted} data\n * @param {string} encryptionKey\n * @returns {string}\n */\nexport const encryptCredentialData = (data: ICredentialDataDecrypted, encryptionKey: string): string => {\n    return AES.encrypt(JSON.stringify(data), encryptionKey).toString()\n}\n\n/**\n * Decrypt credential data\n * @param {string} data\n * @param {string} encryptionKey\n * @returns {ICredentialDataDecrypted}\n */\nexport const decryptCredentialData = (data: string, encryptionKey: string): ICredentialDataDecrypted => {\n    const decryptedData = AES.decrypt(data, encryptionKey)\n    try {\n        return JSON.parse(decryptedData.toString(enc.Utf8))\n    } catch (e) {\n        console.error(e)\n        throw new Error('Credentials could not be decrypted.')\n    }\n}\n\n/**\n * Transform ICredentialBody from req to Credential entity\n * @param {ICredentialBody} data\n * @returns {Credential}\n */\nexport const transformToCredentialEntity = async (body: ICredentialBody): Promise<Credential> => {\n    const encryptKey = await getEncryptionKey()\n\n    const credentialBody = {\n        name: body.name,\n        nodeCredentialName: body.nodeCredentialName,\n        credentialData: encryptCredentialData(body.credentialData, encryptKey)\n    }\n\n    const newCredential = new Credential()\n    Object.assign(newCredential, credentialBody)\n\n    return newCredential\n}\n\n/**\n * Returns the path of oauth2 html\n * @returns {string}\n */\nexport const getOAuth2HTMLPath = (): string => {\n    return path.join(__dirname, '..', '..', 'oauth2.html')\n}\n\n/**\n * Construct directed graph and node dependencies score\n * @param {IReactFlowNode[]} reactFlowNodes\n * @param {IReactFlowEdge[]} reactFlowEdges\n */\nexport const constructGraphs = (reactFlowNodes: IReactFlowNode[], reactFlowEdges: IReactFlowEdge[]) => {\n    const nodeDependencies = {} as INodeDependencies\n    const graph = {} as INodeDirectedGraph\n\n    for (let i = 0; i < reactFlowNodes.length; i += 1) {\n        const nodeId = reactFlowNodes[i].id\n        nodeDependencies[nodeId] = 0\n        graph[nodeId] = []\n    }\n\n    for (let i = 0; i < reactFlowEdges.length; i += 1) {\n        const source = reactFlowEdges[i].source\n        const target = reactFlowEdges[i].target\n\n        if (Object.prototype.hasOwnProperty.call(graph, source)) {\n            graph[source].push(target)\n        } else {\n            graph[source] = [target]\n        }\n        nodeDependencies[target] += 1\n    }\n\n    return { graph, nodeDependencies }\n}\n\n/**\n * Get starting node and check if flow is valid\n * @param {INodeDependencies} nodeDependencies\n * @param {IReactFlowNode[]} reactFlowNodes\n */\nexport const getStartingNode = (nodeDependencies: INodeDependencies, reactFlowNodes: IReactFlowNode[]) => {\n    // Find starting node\n    const startingNodeIds = [] as string[]\n    Object.keys(nodeDependencies).forEach((nodeId) => {\n        if (nodeDependencies[nodeId] === 0) {\n            startingNodeIds.push(nodeId)\n        }\n    })\n\n    // Action nodes with 0 dependencies are not valid, must connected to source\n    const faultyNodeLabels = []\n    for (let i = 0; i < startingNodeIds.length; i += 1) {\n        const node = reactFlowNodes.find((nd) => nd.id === startingNodeIds[i])\n\n        if (node && node.data && node.data.type && node.data.type !== 'trigger' && node.data.type !== 'webhook') {\n            faultyNodeLabels.push(node.data.label)\n        }\n    }\n\n    return { faultyNodeLabels, startingNodeIds }\n}\n\n/**\n * Function to get both graphs and starting nodes\n * @param {Response} res\n * @param {IReactFlowNode[]} reactFlowNodes\n * @param {IReactFlowEdge[]} reactFlowEdges\n */\nexport const constructGraphsAndGetStartingNodes = (res: Response, reactFlowNodes: IReactFlowNode[], reactFlowEdges: IReactFlowEdge[]) => {\n    const { graph, nodeDependencies } = constructGraphs(reactFlowNodes, reactFlowEdges)\n    const { faultyNodeLabels, startingNodeIds } = getStartingNode(nodeDependencies, reactFlowNodes)\n    if (faultyNodeLabels.length) {\n        let message = `Action nodes must connected to source. Faulty nodes: `\n        for (let i = 0; i < faultyNodeLabels.length; i += 1) {\n            message += `${faultyNodeLabels[i]}`\n            if (i !== faultyNodeLabels.length - 1) {\n                message += ', '\n            }\n        }\n        res.status(500).send(message)\n        return\n    }\n\n    return { graph, startingNodeIds }\n}\n\n/**\n * Get variable value from outputResponses.output\n * @param {string} paramValue\n * @param {IReactFlowNode[]} reactFlowNodes\n * @param {string} key\n * @param {number} loopIndex\n * @returns {string}\n */\nexport const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowNode[], key: string, loopIndex: number): string => {\n    let returnVal = paramValue\n    const variableStack = []\n    const variableDict = {} as IVariableDict\n    let startIdx = 0\n    const endIdx = returnVal.length - 1\n\n    while (startIdx < endIdx) {\n        const substr = returnVal.substring(startIdx, startIdx + 2)\n\n        // Store the opening double curly bracket\n        if (substr === '{{') {\n            variableStack.push({ substr, startIdx: startIdx + 2 })\n        }\n\n        // Found the complete variable\n        if (substr === '}}' && variableStack.length > 0 && variableStack[variableStack.length - 1].substr === '{{') {\n            const variableStartIdx = variableStack[variableStack.length - 1].startIdx\n            const variableEndIdx = startIdx\n            const variableFullPath = returnVal.substring(variableStartIdx, variableEndIdx)\n\n            // Split by first occurence of '[' to get just nodeId\n            const [variableNodeId, ...rest] = variableFullPath.split('[')\n            let variablePath = 'outputResponses.output' + '[' + rest.join('[')\n            if (variablePath.includes('$index')) {\n                variablePath = variablePath.split('$index').join(loopIndex.toString())\n            }\n\n            const executedNode = reactFlowNodes.find((nd) => nd.id === variableNodeId)\n            if (executedNode) {\n                const resolvedVariablePath = getVariableValue(variablePath, reactFlowNodes, key, loopIndex)\n                const variableValue = lodash.get(executedNode.data, resolvedVariablePath)\n                variableDict[`{{${variableFullPath}}}`] = variableValue\n                // For instance: const var1 = \"some var\"\n                if (key === 'code' && typeof variableValue === 'string') variableDict[`{{${variableFullPath}}}`] = `\"${variableValue}\"`\n                if (key === 'code' && typeof variableValue === 'object')\n                    variableDict[`{{${variableFullPath}}}`] = `${JSON.stringify(variableValue)}`\n            }\n            variableStack.pop()\n        }\n        startIdx += 1\n    }\n\n    const variablePaths = Object.keys(variableDict)\n    variablePaths.sort() // Sort by length of variable path because longer path could possibly contains nested variable\n    variablePaths.forEach((path) => {\n        const variableValue = variableDict[path]\n        // Replace all occurence\n        returnVal = returnVal.split(path).join(variableValue)\n    })\n\n    return returnVal\n}\n\n/**\n * Get minimum variable array length from outputResponses.output\n * @param {string} paramValue\n * @param {IReactFlowNode[]} reactFlowNodes\n * @returns {number}\n */\nexport const getVariableLength = (paramValue: string, reactFlowNodes: IReactFlowNode[]): number => {\n    let minLoop = Infinity\n    const variableStack = []\n    let startIdx = 0\n    const endIdx = paramValue.length - 1\n\n    while (startIdx < endIdx) {\n        const substr = paramValue.substring(startIdx, startIdx + 2)\n\n        // Store the opening double curly bracket\n        if (substr === '{{') {\n            variableStack.push({ substr, startIdx: startIdx + 2 })\n        }\n\n        // Found the complete variable\n        if (substr === '}}' && variableStack.length > 0 && variableStack[variableStack.length - 1].substr === '{{') {\n            const variableStartIdx = variableStack[variableStack.length - 1].startIdx\n            const variableEndIdx = startIdx\n            const variableFullPath = paramValue.substring(variableStartIdx, variableEndIdx)\n\n            if (variableFullPath.includes('$index')) {\n                // Split by first occurence of '[' to get just nodeId\n                const [variableNodeId, ...rest] = variableFullPath.split('[')\n                const variablePath = 'outputResponses.output' + '[' + rest.join('[')\n                const [variableArrayPath, ..._] = variablePath.split('[$index]')\n\n                const executedNode = reactFlowNodes.find((nd) => nd.id === variableNodeId)\n                if (executedNode) {\n                    const variableValue = lodash.get(executedNode.data, variableArrayPath)\n                    if (Array.isArray(variableValue)) minLoop = Math.min(minLoop, variableValue.length)\n                }\n            }\n            variableStack.pop()\n        }\n        startIdx += 1\n    }\n    return minLoop\n}\n\n/**\n * Loop through each inputs and resolve variable if neccessary\n * @param {INodeData} reactFlowNodeData\n * @param {IReactFlowNode[]} reactFlowNodes\n * @returns {INodeData}\n */\nexport const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: IReactFlowNode[]): INodeData[] => {\n    const flowNodeDataArray: INodeData[] = []\n    const flowNodeData = lodash.cloneDeep(reactFlowNodeData)\n    const types = ['actions', 'networks', 'inputParameters']\n\n    const getMinForLoop = (paramsObj: ICommonObject) => {\n        let minLoop = Infinity\n        for (const key in paramsObj) {\n            const paramValue = paramsObj[key]\n            if (typeof paramValue === 'string' && paramValue.includes('$index')) {\n                // node.data[$index].smtg\n                minLoop = Math.min(minLoop, getVariableLength(paramValue, reactFlowNodes))\n            }\n            if (Array.isArray(paramValue)) {\n                for (let j = 0; j < paramValue.length; j += 1) {\n                    minLoop = Math.min(minLoop, getMinForLoop(paramValue[j] as ICommonObject))\n                }\n            }\n        }\n        return minLoop\n    }\n\n    const getParamValues = (paramsObj: ICommonObject, loopIndex: number) => {\n        for (const key in paramsObj) {\n            const paramValue = paramsObj[key]\n\n            if (typeof paramValue === 'string') {\n                const resolvedValue = getVariableValue(paramValue, reactFlowNodes, key, loopIndex)\n                paramsObj[key] = resolvedValue\n            }\n\n            if (typeof paramValue === 'number') {\n                const paramValueStr = paramValue.toString()\n                const resolvedValue = getVariableValue(paramValueStr, reactFlowNodes, key, loopIndex)\n                paramsObj[key] = resolvedValue\n            }\n\n            if (Array.isArray(paramValue)) {\n                for (let j = 0; j < paramValue.length; j += 1) {\n                    getParamValues(paramValue[j] as ICommonObject, loopIndex)\n                }\n            }\n        }\n    }\n\n    let minLoop = Infinity\n    for (let i = 0; i < types.length; i += 1) {\n        const paramsObj = (flowNodeData as any)[types[i]]\n        minLoop = Math.min(minLoop, getMinForLoop(paramsObj))\n    }\n\n    if (minLoop === Infinity) {\n        for (let i = 0; i < types.length; i += 1) {\n            const paramsObj = (flowNodeData as any)[types[i]]\n            getParamValues(paramsObj, -1)\n        }\n        return [flowNodeData]\n    } else {\n        for (let j = 0; j < minLoop; j += 1) {\n            const clonedFlowNodeData = lodash.cloneDeep(flowNodeData)\n            for (let i = 0; i < types.length; i += 1) {\n                const paramsObj = (clonedFlowNodeData as any)[types[i]]\n                getParamValues(paramsObj, j)\n            }\n            flowNodeDataArray.push(clonedFlowNodeData)\n        }\n        return flowNodeDataArray\n    }\n}\n\n/**\n * Decrypt encrypted credentials with encryption key\n * @param {INodeData} nodeData\n */\nexport const decryptCredentials = async (nodeData: INodeData, appDataSource?: DataSource) => {\n    if (!appDataSource) appDataSource = getDataSource()\n\n    if (nodeData.credentials && nodeData.credentials.registeredCredential) {\n        // @ts-ignore\n        const credentialId: string = nodeData.credentials.registeredCredential?._id\n\n        const credential = await appDataSource.getMongoRepository(Credential).findOneBy({\n            _id: new ObjectId(credentialId)\n        })\n        if (!credential) return\n\n        const encryptKey = await getEncryptionKey()\n        const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n\n        nodeData.credentials = { ...nodeData.credentials, ...decryptedCredentialData }\n    }\n    await decryptWalletCredentials(nodeData)\n}\n\n/**\n * Decrypt encrypted wallet credentials with encryption key\n * @param {INodeData} nodeData\n */\nexport const decryptWalletCredentials = async (nodeData: INodeData) => {\n    try {\n        const filters = ['actions', 'credentials', 'networks', 'inputParameters']\n        for (const key in nodeData) {\n            if (filters.includes(key)) {\n                // Iterate object to find wallet\n                for (const paramName in (nodeData as any)[key]) {\n                    if (paramName === 'wallet') {\n                        const walletString = (nodeData as any)[key][paramName]\n                        const walletDetails: IWallet = JSON.parse(walletString)\n                        const walletEncryptedString = walletDetails.walletCredential\n                        const encryptKey = await getEncryptionKey()\n\n                        // Decrpyt credentialData\n                        const decryptedCredentialData = decryptCredentialData(walletEncryptedString, encryptKey)\n                        walletDetails.walletCredential = JSON.stringify(decryptedCredentialData)\n                        ;(nodeData as any)[key][paramName] = JSON.stringify(walletDetails)\n                    }\n                }\n            }\n        }\n    } catch (e) {\n        return\n    }\n}\n\n/**\n * Process webhook\n * @param {Response} res\n * @param {Request} req\n * @param {DataSource} AppDataSource\n * @param {string} webhookEndpoint\n * @param {WebhookMethod} httpMethod\n * @param {IComponentNodesPool} componentNodes\n * @param {any} io\n */\nexport const processWebhook = async (\n    res: Response,\n    req: Request,\n    AppDataSource: DataSource,\n    webhookEndpoint: string,\n    httpMethod: WebhookMethod,\n    componentNodes: IComponentNodesPool,\n    io: any,\n    deployedWorkflowsPool: DeployedWorkflowPool,\n    activeTestWebhooksPool: ActiveTestWebhookPool\n) => {\n    try {\n        // Find if webhook is in activeTestWebhookPool\n        const testWebhookKey = `${webhookEndpoint}_${httpMethod}`\n        if (Object.prototype.hasOwnProperty.call(activeTestWebhooksPool.activeTestWebhooks, testWebhookKey)) {\n            const { nodes, edges, nodeData, clientId, isTestWorkflow, webhookNodeId } =\n                activeTestWebhooksPool.activeTestWebhooks[testWebhookKey]\n            const webhookNodeInstance = componentNodes[nodeData.name] as IWebhookNode\n\n            await decryptCredentials(nodeData)\n\n            if (!isTestWorkflow) {\n                nodeData.req = req\n                const result = await webhookNodeInstance.runWebhook!.call(webhookNodeInstance, nodeData)\n\n                if (result === null) return res.status(200).send('OK!')\n\n                // Emit webhook result\n                io.to(clientId).emit('testWebhookNodeResponse', result)\n\n                // Delete webhook from 3rd party apps and from pool\n                activeTestWebhooksPool.remove(testWebhookKey, componentNodes)\n\n                const webhookResponseCode = (nodeData.inputParameters?.responseCode as number) || 200\n                if (\n                    (nodeData.inputParameters?.returnType as string) === 'lastNodeResponse' ||\n                    nodeData.name === 'chainLinkFunctionWebhook'\n                ) {\n                    const webhookResponseData = result || []\n                    return res.status(webhookResponseCode).json(webhookResponseData)\n                } else {\n                    const webhookResponseData = (nodeData.inputParameters?.responseData as string) || `Webhook ${req.originalUrl} received!`\n                    return res.status(webhookResponseCode).send(webhookResponseData)\n                }\n            } else {\n                nodeData.req = req\n                const result = await webhookNodeInstance.runWebhook!.call(webhookNodeInstance, nodeData)\n\n                if (result === null) return res.status(200).send('OK!')\n\n                const newWorkflowExecutedData = {\n                    nodeId: webhookNodeId,\n                    nodeLabel: nodeData.label,\n                    data: result,\n                    status: 'FINISHED'\n                } as IWorkflowExecutedData\n\n                io.to(clientId).emit('testWorkflowNodeResponse', newWorkflowExecutedData)\n\n                // Delete webhook from 3rd party apps and from pool\n                activeTestWebhooksPool.remove(testWebhookKey, componentNodes)\n\n                const { graph } = constructGraphs(nodes, edges)\n\n                const webhookResponseCode = (nodeData.inputParameters?.responseCode as number) || 200\n                if (\n                    (nodeData.inputParameters?.returnType as string) === 'lastNodeResponse' ||\n                    nodeData.name === 'chainLinkFunctionWebhook'\n                ) {\n                    const lastExecutedResult = await testWorkflow(\n                        webhookNodeId,\n                        result.length ? [{ data: result[0].data }] : [],\n                        nodes,\n                        edges,\n                        graph,\n                        componentNodes,\n                        clientId,\n                        io,\n                        true\n                    )\n                    const webhookResponseData = lastExecutedResult || []\n                    return res.status(webhookResponseCode).json(webhookResponseData)\n                } else {\n                    await testWorkflow(\n                        webhookNodeId,\n                        result.length ? [{ data: result[0].data }] : [],\n                        nodes,\n                        edges,\n                        graph,\n                        componentNodes,\n                        clientId,\n                        io\n                    )\n                    const webhookResponseData = (nodeData.inputParameters?.responseData as string) || `Webhook ${req.originalUrl} received!`\n                    return res.status(webhookResponseCode).send(webhookResponseData)\n                }\n            }\n        } else {\n            const webhook = await AppDataSource.getMongoRepository(Webhook).findOneBy({\n                webhookEndpoint,\n                httpMethod\n            })\n\n            if (!webhook) {\n                res.status(404).send(`Webhook ${req.originalUrl} not found`)\n                return\n            }\n\n            const nodeId = webhook.nodeId\n            const workflowShortId = webhook.workflowShortId\n\n            const workflow = await AppDataSource.getMongoRepository(Workflow).findOneBy({\n                shortId: workflowShortId\n            })\n\n            if (!workflow) {\n                res.status(404).send(`Workflow ${workflowShortId} not found`)\n                return\n            }\n\n            const flowDataString = workflow.flowData\n            const flowData: IReactFlowObject = JSON.parse(flowDataString)\n            const reactFlowNodes = flowData.nodes as IReactFlowNode[]\n            const reactFlowEdges = flowData.edges as IReactFlowEdge[]\n\n            const reactFlowNode = reactFlowNodes.find((nd) => nd.id === nodeId)\n\n            if (!reactFlowNode) {\n                res.status(404).send(`Node ${nodeId} not found`)\n                return\n            }\n\n            const nodeData = reactFlowNode.data\n            const nodeName = nodeData.name\n\n            // Start workflow\n            const { graph, nodeDependencies } = constructGraphs(reactFlowNodes, reactFlowEdges)\n            const { faultyNodeLabels, startingNodeIds } = getStartingNode(nodeDependencies, reactFlowNodes)\n            if (faultyNodeLabels.length) {\n                let message = `Action nodes must connected to source. Faulty nodes: `\n                for (let i = 0; i < faultyNodeLabels.length; i += 1) {\n                    message += `${faultyNodeLabels[i]}`\n                    if (i !== faultyNodeLabels.length - 1) {\n                        message += ', '\n                    }\n                }\n                res.status(500).send(message)\n                return\n            }\n\n            const nodeInstance = componentNodes[nodeName]\n            const webhookNode = nodeInstance as IWebhookNode\n            nodeData.req = req\n            const result = (await webhookNode.runWebhook!.call(webhookNode, nodeData)) || []\n\n            if (result === null) return res.status(200).send('OK!')\n\n            const webhookResponseCode = (nodeData.inputParameters?.responseCode as number) || 200\n\n            /**\n             * Very specific use case for chainLinkFunctionWebhook\n             * This is to prevent workflow from triggering multiple times because\n             * Each oracle node runs the same computation in the Off-chain Reporting protocol, hence webhook will be called multiple times\n             * By storing sessionId, we can keep track if this is the same computation run from oracle node\n             * Info: https://docs.chain.link/chainlink-functions/tutorials/api-post-data\n             */\n            let chainLinkSessionId = ''\n\n            const updateWebhookData = async (chainLinkSessionId: string, data?: any) => {\n                const content: ICommonObject = { sessionId: chainLinkSessionId }\n                if (data) content.data = data\n                const body = { webhookId: JSON.stringify(content) }\n                const updateWebhook = new Webhook()\n                Object.assign(updateWebhook, body)\n\n                AppDataSource.getMongoRepository(Webhook).merge(webhook, updateWebhook)\n                await AppDataSource.getMongoRepository(Webhook).save(webhook)\n            }\n\n            if (\n                nodeData.name === 'chainLinkFunctionWebhook' &&\n                result.length &&\n                result[0].data.headers &&\n                ((result[0].data.headers as any)['cf-session-id'] || (result[0].data.headers as any)['CF-SESSION-ID'])\n            ) {\n                const sessionId = (result[0].data.headers as any)['cf-session-id']\n\n                if (!webhook.webhookId) {\n                    chainLinkSessionId = sessionId\n                    await updateWebhookData(chainLinkSessionId)\n                } else {\n                    const lastSavedSessionId = JSON.parse(webhook.webhookId)?.sessionId\n                    if (lastSavedSessionId !== sessionId) {\n                        chainLinkSessionId = sessionId\n                        await updateWebhookData(chainLinkSessionId)\n                    } else {\n                        const promise = () => {\n                            return new Promise((resolve, reject) => {\n                                let count = 10\n                                const timeout = setInterval(async () => {\n                                    if (count < 0) {\n                                        clearInterval(timeout)\n                                        reject(new Error(`Chainlink Function Webhook Timeout`))\n                                    }\n                                    const webhook = await AppDataSource.getMongoRepository(Webhook).findOneBy({\n                                        webhookEndpoint,\n                                        httpMethod\n                                    })\n                                    if (!webhook) {\n                                        clearInterval(timeout)\n                                        reject(new Error(`Error finding Chainlink Function Webhook`))\n                                    } else {\n                                        const lastSavedData = JSON.parse(webhook.webhookId)?.data\n                                        if (lastSavedData) {\n                                            clearInterval(timeout)\n                                            resolve(lastSavedData)\n                                        }\n                                    }\n                                    count -= 1\n                                }, 1000)\n                            })\n                        }\n                        const responseData = await promise()\n                        return res.status(webhookResponseCode).json(responseData)\n                    }\n                }\n            }\n\n            const workflowExecutedData = (await deployedWorkflowsPool.startWorkflow(\n                workflowShortId,\n                reactFlowNode,\n                reactFlowNode.id,\n                result,\n                componentNodes,\n                startingNodeIds,\n                graph\n            )) as unknown as IWorkflowExecutedData[]\n            if ((nodeData.inputParameters?.returnType as string) === 'lastNodeResponse' || nodeData.name === 'chainLinkFunctionWebhook') {\n                const lastExecutedResult = workflowExecutedData[workflowExecutedData.length - 1]\n                const webhookResponseData = lastExecutedResult?.data || []\n                if (chainLinkSessionId) await updateWebhookData(chainLinkSessionId, webhookResponseData)\n                return res.status(webhookResponseCode).json(webhookResponseData)\n            } else {\n                const webhookResponseData = (nodeData.inputParameters?.responseData as string) || `Webhook ${req.originalUrl} received!`\n                return res.status(webhookResponseCode).send(webhookResponseData)\n            }\n        }\n    } catch (error) {\n        res.status(500).send(`Webhook error: ${error}`)\n        return\n    }\n}\n\n/**\n * Update credential in DB after oAuth2 tokens have been refreshed\n * @param {INodeExecutionData[] | null} result\n * @param {INodeData} nodeData\n * @param {DataSource} appDataSource\n */\nconst updateCredentialAfterOAuth2TokenRefreshed = async (\n    result: INodeExecutionData[] | null,\n    nodeData: INodeData,\n    appDataSource?: DataSource\n) => {\n    if (!result || !result.length) return\n\n    if (!appDataSource) appDataSource = getDataSource()\n\n    let access_token = ''\n    let expires_in = ''\n\n    for (let i = 0; i < result.length; i += 1) {\n        if (Object.prototype.hasOwnProperty.call(result[i], OAUTH2_REFRESHED)) {\n            const refreshData = result[i][OAUTH2_REFRESHED] as unknown as IOAuth2RefreshResponse\n            access_token = refreshData.access_token\n            expires_in = refreshData.expires_in\n            break\n        }\n    }\n\n    result.forEach((el) => {\n        if (el[OAUTH2_REFRESHED]) delete el[OAUTH2_REFRESHED]\n        return el\n    })\n\n    // Update credential\n    if (access_token && expires_in && nodeData.credentials && nodeData.credentials.registeredCredential) {\n        // @ts-ignore\n        const credentialId = nodeData.credentials.registeredCredential._id as string\n        const credential = await appDataSource.getMongoRepository(Credential).findOneBy({\n            _id: new ObjectId(credentialId)\n        })\n\n        if (!credential) return\n\n        const encryptKey = await getEncryptionKey()\n        const decryptedCredentialData = decryptCredentialData(credential.credentialData, encryptKey)\n\n        const body: ICredentialBody = {\n            name: credential.name,\n            nodeCredentialName: credential.nodeCredentialName,\n            credentialData: {\n                ...decryptedCredentialData,\n                access_token,\n                expires_in\n            }\n        }\n        const updateCredential = await transformToCredentialEntity(body)\n\n        appDataSource.getMongoRepository(Credential).merge(credential, updateCredential)\n        await appDataSource.getMongoRepository(Credential).save(credential)\n    }\n}\n\n/**\n * Check if oAuth2 token refreshed\n * @param {INodeExecutionData[] | null} result\n * @param {INodeData} nodeData\n * @param {DataSource} appDataSource\n */\nexport const checkOAuth2TokenRefreshed = (result: INodeExecutionData[] | null, nodeData: INodeData, appDataSource?: DataSource) => {\n    const credentialMethod = nodeData.credentials?.credentialMethod as string\n    if (credentialMethod && credentialMethod.toLowerCase().includes('oauth2')) {\n        updateCredentialAfterOAuth2TokenRefreshed(result, nodeData, appDataSource)\n    }\n}\n\n/**\n * Update reactFlowNodes so that resolveVariables is called, it is getting updated result\n * @param {IReactFlowNode[]} reactFlowNodes\n * @param {number} nodeIndex\n * @param {INodeExecutionData[]} newResult\n */\nexport const updateNodeOutput = (reactFlowNodes: IReactFlowNode[], nodeIndex: number, newResult: INodeExecutionData[] = []) => {\n    if (reactFlowNodes[nodeIndex].data.outputResponses) {\n        reactFlowNodes[nodeIndex].data.outputResponses = {\n            ...reactFlowNodes[nodeIndex].data.outputResponses,\n            output: newResult\n        }\n    } else {\n        reactFlowNodes[nodeIndex].data.outputResponses = {\n            submit: true,\n            needRetest: null,\n            output: newResult\n        }\n    }\n}\n\n/**\n * Test Workflow from starting node to end\n * @param {string} startingNodeId\n * @param {INodeExecutionData[]} startingNodeExecutedData\n * @param {IReactFlowNode[]} reactFlowNodes\n * @param {IReactFlowEdge[]} reactFlowEdges\n * @param {INodeDirectedGraph} graph\n * @param {IComponentNodesPool} componentNodes\n * @param {string} clientId\n * @param {any} io\n */\nexport const testWorkflow = async (\n    startingNodeId: string,\n    startingNodeExecutedData: INodeExecutionData[],\n    reactFlowNodes: IReactFlowNode[],\n    reactFlowEdges: IReactFlowEdge[],\n    graph: INodeDirectedGraph,\n    componentNodes: IComponentNodesPool,\n    clientId: string,\n    io: any,\n    returnLastExecutedResult?: boolean\n) => {\n    // Create a Queue and add our initial node in it\n    const startingNodeIds = [startingNodeId]\n    const startingNodeIndex = reactFlowNodes.findIndex((nd) => nd.id === startingNodeId)\n    updateNodeOutput(reactFlowNodes, startingNodeIndex, startingNodeExecutedData)\n\n    const nodeQueue = [] as INodeQueue[]\n    const exploredNode = {} as IExploredNode\n    // In the case of infinite loop, only max 3 loops will be executed\n    const maxLoop = 3\n\n    // Keep track of last executed result\n    let lastExecutedResult: any\n\n    for (let i = 0; i < startingNodeIds.length; i += 1) {\n        nodeQueue.push({ nodeId: startingNodeIds[i], depth: 0 })\n        exploredNode[startingNodeIds[i]] = { remainingLoop: maxLoop, lastSeenDepth: 0 }\n    }\n\n    while (nodeQueue.length) {\n        const { nodeId, depth } = nodeQueue.shift() as INodeQueue\n        const ignoreNodeIds: string[] = []\n\n        if (!startingNodeIds.includes(nodeId)) {\n            const reactFlowNode = reactFlowNodes.find((nd) => nd.id === nodeId)\n            const nodeIndex = reactFlowNodes.findIndex((nd) => nd.id === nodeId)\n            if (!reactFlowNode || reactFlowNode === undefined || nodeIndex < 0) continue\n\n            try {\n                const nodeInstanceFilePath = componentNodes[reactFlowNode.data.name].filePath\n                const nodeModule = await import(nodeInstanceFilePath)\n                const newNodeInstance = new nodeModule.nodeClass()\n\n                await decryptCredentials(reactFlowNode.data)\n\n                const reactFlowNodeData: INodeData[] = resolveVariables(reactFlowNode.data, reactFlowNodes)\n\n                let results: INodeExecutionData[] = []\n\n                for (let i = 0; i < reactFlowNodeData.length; i += 1) {\n                    const result = await newNodeInstance.run!.call(newNodeInstance, reactFlowNodeData[i])\n                    checkOAuth2TokenRefreshed(result, reactFlowNodeData[i])\n                    if (result) results.push(...result)\n                }\n\n                updateNodeOutput(reactFlowNodes, nodeIndex, results)\n\n                // Determine which nodes to route next when it comes to ifElse\n                if (results.length && nodeId.includes('ifElse')) {\n                    let anchorIndex = -1\n                    if (Array.isArray(results) && Object.keys((results as any)[0].data).length === 0) {\n                        anchorIndex = 0\n                    } else if (Array.isArray(results) && Object.keys((results as any)[1].data).length === 0) {\n                        anchorIndex = 1\n                    }\n                    const ifElseEdge = reactFlowEdges.find(\n                        (edg) => edg.source === nodeId && edg.sourceHandle === `${nodeId}-output-${anchorIndex}`\n                    )\n                    if (ifElseEdge) {\n                        ignoreNodeIds.push(ifElseEdge.target)\n                    }\n                }\n\n                const newWorkflowExecutedData = {\n                    nodeId,\n                    nodeLabel: reactFlowNode.data.label,\n                    data: results,\n                    status: 'FINISHED'\n                } as IWorkflowExecutedData\n\n                lastExecutedResult = results\n\n                io.to(clientId).emit('testWorkflowNodeResponse', newWorkflowExecutedData)\n            } catch (e: any) {\n                console.error(e)\n                const newWorkflowExecutedData = {\n                    nodeId,\n                    nodeLabel: reactFlowNode.data.label,\n                    data: [{ error: e.message }],\n                    status: 'ERROR'\n                } as IWorkflowExecutedData\n\n                lastExecutedResult = [{ error: e.message }]\n\n                io.to(clientId).emit('testWorkflowNodeResponse', newWorkflowExecutedData)\n                return\n            }\n        }\n\n        const neighbourNodeIds = graph[nodeId]\n        const nextDepth = depth + 1\n\n        for (let i = 0; i < neighbourNodeIds.length; i += 1) {\n            const neighNodeId = neighbourNodeIds[i]\n\n            if (!ignoreNodeIds.includes(neighNodeId)) {\n                // If nodeId has been seen, cycle detected\n                if (Object.prototype.hasOwnProperty.call(exploredNode, neighNodeId)) {\n                    const { remainingLoop, lastSeenDepth } = exploredNode[neighNodeId]\n\n                    if (lastSeenDepth === nextDepth) continue\n\n                    if (remainingLoop === 0) {\n                        break\n                    }\n                    const remainingLoopMinusOne = remainingLoop - 1\n                    exploredNode[neighNodeId] = { remainingLoop: remainingLoopMinusOne, lastSeenDepth: nextDepth }\n                    nodeQueue.push({ nodeId: neighNodeId, depth: nextDepth })\n                } else {\n                    exploredNode[neighNodeId] = { remainingLoop: maxLoop, lastSeenDepth: nextDepth }\n                    nodeQueue.push({ nodeId: neighNodeId, depth: nextDepth })\n                }\n            }\n        }\n    }\n    io.to(clientId).emit('testWorkflowNodeFinish')\n\n    if (returnLastExecutedResult) return lastExecutedResult\n}\n"
  },
  {
    "path": "packages/server/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"lib\": [\"es2017\"],\n        \"target\": \"es2017\" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,\n        \"experimentalDecorators\": true /* Enable experimental support for TC39 stage 2 draft decorators. */,\n        \"emitDecoratorMetadata\": true /* Emit design-type metadata for decorated declarations in source files. */,\n        \"module\": \"commonjs\" /* Specify what module code is generated. */,\n        \"outDir\": \"dist\",\n        \"esModuleInterop\": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,\n        \"forceConsistentCasingInFileNames\": true /* Ensure that casing is correct in imports. */,\n        \"strict\": true /* Enable all strict type-checking options. */,\n        \"skipLibCheck\": true /* Skip type checking all .d.ts files. */,\n        \"sourceMap\": true,\n        \"strictPropertyInitialization\": false,\n        \"declaration\": true\n    },\n    \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "packages/ui/.npmignore",
    "content": "/tests\n/src\n/public\n!build\n\nyarn-debug.log*\nyarn-error.log*\n\n.eslintrc\n.prettierignore\n.prettierrc\njsconfig.json\n\n"
  },
  {
    "path": "packages/ui/README.md",
    "content": "<!-- markdownlint-disable MD030 -->\n\n# Outerbridge UI\n\nReact frontend ui for Outerbridge.\n\n![Outerbridge](https://raw.githubusercontent.com/Outerbridgeio/Outerbridge/master/assets/outerbridge_brand.png)\n\nInstall:\n\n```bash\nnpm i outerbridge-ui\n```\n\n## License\n\nSource code in this repository is made available under the [Apache License Version 2.0](https://github.com/Outerbridgeio/Outerbridge/blob/master/LICENSE.md).\n"
  },
  {
    "path": "packages/ui/jsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"esnext\",\n        \"module\": \"commonjs\",\n        \"baseUrl\": \"src\"\n    },\n    \"include\": [\"src/**/*\"],\n    \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "packages/ui/package.json",
    "content": "{\n    \"name\": \"outerbridge-ui\",\n    \"version\": \"1.0.15\",\n    \"license\": \"SEE LICENSE IN LICENSE.md\",\n    \"homepage\": \"https://outerbridge.io\",\n    \"author\": {\n        \"name\": \"Henry Heng\",\n        \"email\": \"henryheng@outerbridge.io\"\n    },\n    \"dependencies\": {\n        \"@emotion/cache\": \"^11.4.0\",\n        \"@emotion/react\": \"^11.10.6\",\n        \"@emotion/styled\": \"^11.10.6\",\n        \"@metamask/jazzicon\": \"^2.0.0\",\n        \"@mui/icons-material\": \"^5.0.3\",\n        \"@mui/material\": \"^5.11.12\",\n        \"@tabler/icons\": \"^1.39.1\",\n        \"clsx\": \"^1.1.1\",\n        \"ethers\": \"^5.6.8\",\n        \"formik\": \"^2.2.6\",\n        \"framer-motion\": \"^4.1.13\",\n        \"history\": \"^5.0.0\",\n        \"html-react-parser\": \"^3.0.4\",\n        \"lodash\": \"^4.17.21\",\n        \"moment\": \"^2.29.3\",\n        \"notistack\": \"^2.0.4\",\n        \"prismjs\": \"^1.28.0\",\n        \"prop-types\": \"^15.7.2\",\n        \"react\": \"^18.2.0\",\n        \"react-datepicker\": \"^4.8.0\",\n        \"react-device-detect\": \"^1.17.0\",\n        \"react-dom\": \"^18.2.0\",\n        \"react-jazzicon\": \"^1.0.4\",\n        \"react-json-view\": \"^1.21.3\",\n        \"react-perfect-scrollbar\": \"^1.5.8\",\n        \"react-redux\": \"^8.0.5\",\n        \"react-router\": \"~6.3.0\",\n        \"react-router-dom\": \"~6.3.0\",\n        \"react-select\": \"^5.3.2\",\n        \"react-simple-code-editor\": \"^0.11.2\",\n        \"reactflow\": \"^11.5.6\",\n        \"redux\": \"^4.0.5\",\n        \"socket.io-client\": \"^4.5.1\",\n        \"yup\": \"^0.32.9\"\n    },\n    \"scripts\": {\n        \"start\": \"react-scripts start\",\n        \"dev\": \"react-scripts start\",\n        \"build\": \"react-scripts build\",\n        \"test\": \"react-scripts test\",\n        \"eject\": \"react-scripts eject\"\n    },\n    \"babel\": {\n        \"presets\": [\n            \"@babel/preset-react\"\n        ]\n    },\n    \"browserslist\": {\n        \"production\": [\n            \">0.2%\",\n            \"not dead\",\n            \"not op_mini all\"\n        ],\n        \"development\": [\n            \"last 1 chrome version\",\n            \"last 1 firefox version\",\n            \"last 1 safari version\"\n        ]\n    },\n    \"devDependencies\": {\n        \"@babel/eslint-parser\": \"^7.15.8\",\n        \"@testing-library/jest-dom\": \"^5.11.10\",\n        \"@testing-library/react\": \"^14.0.0\",\n        \"@testing-library/user-event\": \"^12.8.3\",\n        \"pretty-quick\": \"^3.1.3\",\n        \"react-scripts\": \"^5.0.1\",\n        \"sass\": \"^1.42.1\",\n        \"typescript\": \"^4.8.4\"\n    }\n}\n"
  },
  {
    "path": "packages/ui/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <title>Outerbridge - Automate Web3 and Web2 applications</title>\n  <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n  <!-- Meta Tags-->\n  <meta charset=\"utf-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <meta name=\"theme-color\" content=\"#2296f3\" />\n  <meta name=\"title\" content=\"Outerbridge - Automate Web3 and Web2 applications\" />\n  <meta name=\"description\"\n    content=\"Outerbridge helps you to better integrate Web3 with existing Web2 applications\" />\n  <meta name=\"keywords\"\n    content=\"react, material-ui, reactjs, reactjs, workflow automation, web3, web2, blockchain\" />\n  <meta name=\"author\" content=\"CodedThemes\" />\n  <!-- Open Graph / Facebook -->\n  <meta property=\"og:locale\" content=\"en_US\" />\n  <meta property=\"og:type\" content=\"website\" />\n  <meta property=\"og:url\" content=\"https://outerbridge.io/\" />\n  <meta property=\"og:site_name\" content=\"outerbridge.io\" />\n  <meta property=\"article:publisher\" content=\"https://www.facebook.com/codedthemes\" />\n  <meta property=\"og:title\" content=\"Outerbridge - Automate Web3 and Web2 applications\" />\n  <meta property=\"og:description\"\n    content=\"Outerbridge helps you to better integrate Web3 with existing Web2 applications\" />\n  <meta property=\"og:image\" content=\"https://outerbridge.io/og-image/og-facebook.png\" />\n  <!-- Twitter -->\n  <meta property=\"twitter:card\" content=\"summary_large_image\" />\n  <meta property=\"twitter:url\" content=\"https://outerbridge.io\" />\n  <meta property=\"twitter:title\" content=\"Outerbridge - Automate Web3 and Web2 applications\" />\n  <meta property=\"twitter:description\"\n    content=\"Outerbridge helps you to better integrate Web3 with existing Web2 applications\" />\n  <meta property=\"twitter:image\" content=\"https://outerbridge.io/og-image/og-twitter.png\" />\n  <meta name=\"twitter:creator\" content=\"@codedthemes\" />\n  <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n\n  <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" />\n  <link\n    href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@400;500;600;700&family=Roboto:wght@400;500;700&display=swap\"\n    rel=\"stylesheet\" />\n</head>\n\n<body>\n  <noscript>You need to enable JavaScript to run this app.</noscript>\n  <div id=\"root\"></div>\n  <div id=\"portal\"></div>\n  <!--\n          This HTML file is a template.\n          If you open it directly in the browser, you will see an empty page.\n\n          You can add webfonts, meta tags, or analytics to this file.\n          The build step will place the bundled scripts into the <body> tag.\n\n          To begin the development, run `npm start` or `yarn start`.\n          To create a production bundle, use `npm run build` or `yarn build`.\n   -->  \n</body>\n\n</html>\n"
  },
  {
    "path": "packages/ui/src/App.js",
    "content": "import { useSelector } from 'react-redux'\n\nimport { ThemeProvider } from '@mui/material/styles'\nimport { CssBaseline, StyledEngineProvider } from '@mui/material'\n\n// routing\nimport Routes from 'routes'\n\n// defaultTheme\nimport themes from 'themes'\n\n// project imports\nimport NavigationScroll from 'layout/NavigationScroll'\n\n// ==============================|| APP ||============================== //\n\nconst App = () => {\n    const customization = useSelector((state) => state.customization)\n\n    return (\n        <StyledEngineProvider injectFirst>\n            <ThemeProvider theme={themes(customization)}>\n                <CssBaseline />\n                <NavigationScroll>\n                    <Routes />\n                </NavigationScroll>\n            </ThemeProvider>\n        </StyledEngineProvider>\n    )\n}\n\nexport default App\n"
  },
  {
    "path": "packages/ui/src/api/apikey.js",
    "content": "import client from './client'\n\nconst getAllAPIKeys = () => client.get('/apikey')\n\nconst createNewAPI = (body) => client.post(`/apikey`, body)\n\nconst updateAPI = (id, body) => client.put(`/apikey/${id}`, body)\n\nconst deleteAPI = (id) => client.delete(`/apikey/${id}`)\n\nexport default {\n    getAllAPIKeys,\n    createNewAPI,\n    updateAPI,\n    deleteAPI\n}\n"
  },
  {
    "path": "packages/ui/src/api/client.js",
    "content": "import axios from 'axios'\nimport { baseURL } from 'store/constant'\n\nconst apiClient = axios.create({\n    baseURL: `${baseURL}/api/v1`,\n    headers: {\n        'Content-type': 'application/json'\n    }\n})\n\nexport default apiClient\n"
  },
  {
    "path": "packages/ui/src/api/contracts.js",
    "content": "import client from './client'\n\nconst getAllContracts = () => client.get('/contracts')\n\nconst getSpecificContract = (id) => client.get(`/contracts/${id}`)\n\nconst createNewContract = (body) => client.post(`/contracts`, body) // body: IContract\n\nconst updateContract = (id, body) => client.put(`/contracts/${id}`, body) // body: IContract\n\nconst getContractABI = (body) => client.post(`/contracts/getabi`, body) //body: IContractRequestBody\n\nconst deleteContract = (id) => client.delete(`/contracts/${id}`)\n\nexport default {\n    getAllContracts,\n    getSpecificContract,\n    createNewContract,\n    updateContract,\n    getContractABI,\n    deleteContract\n}\n"
  },
  {
    "path": "packages/ui/src/api/credential.js",
    "content": "import client from './client'\n\nconst getCredentials = (nodeCredentialName) => client.get('/credentials', { params: { nodeCredentialName } })\n\nconst getCredentialParams = (name) => client.get(`/node-credentials/${name}`)\n\nconst getSpecificCredential = (id, isEncrypted) => client.get(`/credentials/${id}`, { params: { isEncrypted } })\n\nconst createNewCredential = (credentialBody) => client.post(`/credentials`, credentialBody) //credentialBody: ICredential\n\nconst updateCredential = (id, credentialBody) => client.put(`/credentials/${id}`, credentialBody) //credentialBody: ICredential\n\nconst deleteCredential = (id) => client.delete(`/credentials/${id}`)\n\nexport default {\n    getCredentials,\n    getCredentialParams,\n    getSpecificCredential,\n    createNewCredential,\n    updateCredential,\n    deleteCredential\n}\n"
  },
  {
    "path": "packages/ui/src/api/executions.js",
    "content": "import client from './client'\n\nconst getAllExecutions = () => client.get('/executions')\n\nconst getSpecificExecution = (shortId) => client.get(`/executions/${shortId}`)\n\nconst createNewExecution = (body) => client.post(`/executions`, body) //body: IExecution\n\nconst updateExecution = (shortId, body) => client.put(`/executions/${shortId}`, body) //body: IExecution\n\nconst deleteExecution = (shortId) => client.delete(`/executions/${shortId}`)\n\nexport default {\n    getAllExecutions,\n    getSpecificExecution,\n    createNewExecution,\n    updateExecution,\n    deleteExecution\n}\n"
  },
  {
    "path": "packages/ui/src/api/nodes.js",
    "content": "import client from './client'\n\nconst getAllNodes = () => client.get('/nodes')\n\nconst getSpecificNode = (name) => client.get(`/nodes/${name}`)\n\nconst testNode = (name, body) => client.post(`/node-test/${name}`, body) //body: ITestNodeBody\n\nconst loadMethodNode = (name, nodeData) => client.post(`/node-load-method/${name}`, nodeData) //nodeData: INodeData\n\nconst removeTestTriggers = () => client.post(`/remove-test-triggers`)\n\nexport default {\n    getAllNodes,\n    getSpecificNode,\n    testNode,\n    loadMethodNode,\n    removeTestTriggers\n}\n"
  },
  {
    "path": "packages/ui/src/api/oauth2.js",
    "content": "import client from './client'\n\nconst geOAuth2PopupURL = (credentialId) => client.get('/oauth2', { params: { credentialId } })\nconst geOAuth2RedirectURL = () => client.get('/oauth2-redirecturl')\n\nexport default {\n    geOAuth2PopupURL,\n    geOAuth2RedirectURL\n}\n"
  },
  {
    "path": "packages/ui/src/api/wallets.js",
    "content": "import client from './client'\n\nconst getAllWallets = () => client.get('/wallets')\n\nconst getSpecificWallet = (id) => client.get(`/wallets/${id}`)\n\nconst createNewWallet = (body) => client.post(`/wallets`, body) //body: IWallet\n\nconst updateWallet = (id, body) => client.put(`/wallets/${id}`, body) //body: IWallet\n\nconst deleteWallet = (id) => client.delete(`/wallets/${id}`)\n\nconst getWalletCredential = (id) => client.get(`/wallets/credential/${id}`)\n\nexport default {\n    getAllWallets,\n    getSpecificWallet,\n    createNewWallet,\n    updateWallet,\n    deleteWallet,\n    getWalletCredential\n}\n"
  },
  {
    "path": "packages/ui/src/api/webhooks.js",
    "content": "import client from './client'\n\nconst deleteAllTestWebhooks = () => client.post(`/remove-test-webhooks`)\nconst getTunnelURL = () => client.get(`/get-tunnel-url`)\n\nexport default {\n    getTunnelURL,\n    deleteAllTestWebhooks\n}\n"
  },
  {
    "path": "packages/ui/src/api/workflows.js",
    "content": "import client from './client'\n\nconst getAllWorkflows = () => client.get('/workflows')\n\nconst getSpecificWorkflow = (shortId) => client.get(`/workflows/${shortId}`)\n\nconst createNewWorkflow = (body) => client.post(`/workflows`, body) //body: IWorkflow\n\nconst updateWorkflow = (shortId, body) => client.put(`/workflows/${shortId}`, body) //body: IWorkflow\n\nconst deployWorkflow = (shortId, body) => client.post(`/workflows/deploy/${shortId}`, body || {}) //body: { halt: boolean }\n\nconst testWorkflow = (startingNodeId, body) => client.post(`/workflows/test/${startingNodeId}`, body) //body: ITestWorkflowBody\n\nconst deleteWorkflow = (shortId) => client.delete(`/workflows/${shortId}`)\n\nexport default {\n    getAllWorkflows,\n    getSpecificWorkflow,\n    createNewWorkflow,\n    updateWorkflow,\n    deployWorkflow,\n    deleteWorkflow,\n    testWorkflow\n}\n"
  },
  {
    "path": "packages/ui/src/assets/scss/_themes-vars.module.scss",
    "content": "// paper & background\n$paper: #ffffff;\n\n// primary\n$primaryLight: #e3f2fd;\n$primaryMain: #2196f3;\n$primaryDark: #1e88e5;\n$primary200: #90caf9;\n$primary800: #1565c0;\n\n// secondary\n$secondaryLight: #ede7f6;\n$secondaryMain: #673ab7;\n$secondaryDark: #5e35b1;\n$secondary200: #b39ddb;\n$secondary800: #4527a0;\n\n// success Colors\n$successLight: #cdf5d8;\n$success200: #69f0ae;\n$successMain: #00e676;\n$successDark: #00c853;\n\n// error\n$errorLight: #f3d2d2;\n$errorMain: #f44336;\n$errorDark: #c62828;\n\n// orange\n$orangeLight: #fbe9e7;\n$orangeMain: #ffab91;\n$orangeDark: #d84315;\n\n// warning\n$warningLight: #fff8e1;\n$warningMain: #ffe57f;\n$warningDark: #ffc107;\n\n// grey\n$grey50: #fafafa;\n$grey100: #f5f5f5;\n$grey200: #eeeeee;\n$grey300: #e0e0e0;\n$grey500: #9e9e9e;\n$grey600: #757575;\n$grey700: #616161;\n$grey900: #212121;\n\n// ==============================|| DARK THEME VARIANTS ||============================== //\n\n// paper & background\n$darkBackground: #191b1f;\n$darkPaper: #191b1f;\n\n// dark 800 & 900\n$darkLevel1: #252525; // level 1\n$darkLevel2: #242424; // level 2\n\n// primary dark\n$darkPrimaryLight: #23262c;\n$darkPrimaryMain: #23262c;\n$darkPrimaryDark: #191b1f;\n$darkPrimary200: #c9d4e9;\n$darkPrimary800: #32353b;\n\n// secondary dark\n$darkSecondaryLight: #454c59;\n$darkSecondaryMain: #7c4dff;\n$darkSecondaryDark: #ffffff;\n$darkSecondary200: #32353b;\n$darkSecondary800: #6200ea;\n\n// text variants\n$darkTextTitle: #d7dcec;\n$darkTextPrimary: #bdc8f0;\n$darkTextSecondary: #8492c4;\n\n// ==============================|| JAVASCRIPT ||============================== //\n\n:export {\n    // paper & background\n    paper: $paper;\n\n    // primary\n    primaryLight: $primaryLight;\n    primary200: $primary200;\n    primaryMain: $primaryMain;\n    primaryDark: $primaryDark;\n    primary800: $primary800;\n\n    // secondary\n    secondaryLight: $secondaryLight;\n    secondary200: $secondary200;\n    secondaryMain: $secondaryMain;\n    secondaryDark: $secondaryDark;\n    secondary800: $secondary800;\n\n    // success\n    successLight: $successLight;\n    success200: $success200;\n    successMain: $successMain;\n    successDark: $successDark;\n\n    // error\n    errorLight: $errorLight;\n    errorMain: $errorMain;\n    errorDark: $errorDark;\n\n    // orange\n    orangeLight: $orangeLight;\n    orangeMain: $orangeMain;\n    orangeDark: $orangeDark;\n\n    // warning\n    warningLight: $warningLight;\n    warningMain: $warningMain;\n    warningDark: $warningDark;\n\n    // grey\n    grey50: $grey50;\n    grey100: $grey100;\n    grey200: $grey200;\n    grey300: $grey300;\n    grey500: $grey500;\n    grey600: $grey600;\n    grey700: $grey700;\n    grey900: $grey900;\n\n    // ==============================|| DARK THEME VARIANTS ||============================== //\n\n    // paper & background\n    darkPaper: $darkPaper;\n    darkBackground: $darkBackground;\n\n    // dark 800 & 900\n    darkLevel1: $darkLevel1;\n    darkLevel2: $darkLevel2;\n\n    // text variants\n    darkTextTitle: $darkTextTitle;\n    darkTextPrimary: $darkTextPrimary;\n    darkTextSecondary: $darkTextSecondary;\n\n    // primary dark\n    darkPrimaryLight: $darkPrimaryLight;\n    darkPrimaryMain: $darkPrimaryMain;\n    darkPrimaryDark: $darkPrimaryDark;\n    darkPrimary200: $darkPrimary200;\n    darkPrimary800: $darkPrimary800;\n\n    // secondary dark\n    darkSecondaryLight: $darkSecondaryLight;\n    darkSecondaryMain: $darkSecondaryMain;\n    darkSecondaryDark: $darkSecondaryDark;\n    darkSecondary200: $darkSecondary200;\n    darkSecondary800: $darkSecondary800;\n}\n"
  },
  {
    "path": "packages/ui/src/assets/scss/style.scss",
    "content": "// color variants\n@import 'themes-vars.module.scss';\n\n// third-party\n@import '~react-perfect-scrollbar/dist/css/styles.css';\n\n// ==============================|| LIGHT BOX ||============================== //\n.fullscreen .react-images__blanket {\n    z-index: 1200;\n}\n\n// ==============================|| PERFECT SCROLLBAR ||============================== //\n\n.scrollbar-container {\n    .ps__rail-y {\n        &:hover > .ps__thumb-y,\n        &:focus > .ps__thumb-y,\n        &.ps--clicking .ps__thumb-y {\n            background-color: $grey500;\n            width: 5px;\n        }\n    }\n    .ps__thumb-y {\n        background-color: $grey500;\n        border-radius: 6px;\n        width: 5px;\n        right: 0;\n    }\n}\n\n.scrollbar-container.ps,\n.scrollbar-container > .ps {\n    &.ps--active-y > .ps__rail-y {\n        width: 5px;\n        background-color: transparent !important;\n        z-index: 999;\n        &:hover,\n        &.ps--clicking {\n            width: 5px;\n            background-color: transparent;\n        }\n    }\n    &.ps--scrolling-y > .ps__rail-y,\n    &.ps--scrolling-x > .ps__rail-x {\n        opacity: 0.4;\n        background-color: transparent;\n    }\n}\n\n// ==============================|| ANIMATION KEYFRAMES ||============================== //\n\n@keyframes wings {\n    50% {\n        transform: translateY(-40px);\n    }\n    100% {\n        transform: translateY(0px);\n    }\n}\n\n@keyframes blink {\n    50% {\n        opacity: 0;\n    }\n    100% {\n        opacity: 1;\n    }\n}\n\n@keyframes bounce {\n    0%,\n    20%,\n    53%,\n    to {\n        animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);\n        transform: translateZ(0);\n    }\n    40%,\n    43% {\n        animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);\n        transform: translate3d(0, -5px, 0);\n    }\n    70% {\n        animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);\n        transform: translate3d(0, -7px, 0);\n    }\n    80% {\n        transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);\n        transform: translateZ(0);\n    }\n    90% {\n        transform: translate3d(0, -2px, 0);\n    }\n}\n\n@keyframes slideY {\n    0%,\n    50%,\n    100% {\n        transform: translateY(0px);\n    }\n    25% {\n        transform: translateY(-10px);\n    }\n    75% {\n        transform: translateY(10px);\n    }\n}\n\n@keyframes slideX {\n    0%,\n    50%,\n    100% {\n        transform: translateX(0px);\n    }\n    25% {\n        transform: translateX(-10px);\n    }\n    75% {\n        transform: translateX(10px);\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/config.js",
    "content": "const config = {\n    // basename: only at build time to set, and Don't add '/' at end off BASENAME for breadcrumbs, also Don't put only '/' use blank('') instead,\n    basename: '',\n    defaultPath: '/workflows',\n    fontFamily: `'Roboto', sans-serif`,\n    borderRadius: 12\n}\n\nexport default config\n"
  },
  {
    "path": "packages/ui/src/hooks/useApi.js",
    "content": "import { useState } from 'react'\n\nexport default (apiFunc) => {\n    const [data, setData] = useState(null)\n    const [error, setError] = useState(null)\n    const [loading, setLoading] = useState(false)\n\n    const request = async (...args) => {\n        setLoading(true)\n        try {\n            const result = await apiFunc(...args)\n            setData(result.data)\n        } catch (err) {\n            setError(err || 'Unexpected Error!')\n        } finally {\n            setLoading(false)\n        }\n    }\n\n    return {\n        data,\n        error,\n        loading,\n        request\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/hooks/useConfirm.js",
    "content": "import { useContext } from 'react'\nimport ConfirmContext from 'store/context/ConfirmContext'\nimport { HIDE_CONFIRM, SHOW_CONFIRM } from 'store/actions'\n\nlet resolveCallback\nconst useConfirm = () => {\n    const [confirmState, dispatch] = useContext(ConfirmContext)\n\n    const closeConfirm = () => {\n        dispatch({\n            type: HIDE_CONFIRM\n        })\n    }\n\n    const onConfirm = () => {\n        closeConfirm()\n        resolveCallback(true)\n    }\n\n    const onCancel = () => {\n        closeConfirm()\n        resolveCallback(false)\n    }\n    const confirm = (confirmPayload) => {\n        dispatch({\n            type: SHOW_CONFIRM,\n            payload: confirmPayload\n        })\n        return new Promise((res) => {\n            resolveCallback = res\n        })\n    }\n\n    return { confirm, onConfirm, onCancel, confirmState }\n}\n\nexport default useConfirm\n"
  },
  {
    "path": "packages/ui/src/hooks/useScriptRef.js",
    "content": "import { useEffect, useRef } from 'react'\n\n// ==============================|| ELEMENT REFERENCE HOOKS  ||============================== //\n\nconst useScriptRef = () => {\n    const scripted = useRef(true)\n\n    useEffect(\n        () => () => {\n            scripted.current = false\n        },\n        []\n    )\n\n    return scripted\n}\n\nexport default useScriptRef\n"
  },
  {
    "path": "packages/ui/src/index.js",
    "content": "import React from 'react'\nimport App from './App'\nimport { store } from 'store'\nimport { createRoot } from 'react-dom/client'\n\n// style + assets\nimport 'assets/scss/style.scss'\n\n// third party\nimport { BrowserRouter } from 'react-router-dom'\nimport { Provider } from 'react-redux'\nimport { SnackbarProvider } from 'notistack'\nimport ConfirmContextProvider from 'store/context/ConfirmContextProvider'\n\nconst container = document.getElementById('root')\nconst root = createRoot(container)\n\nroot.render(\n    <React.StrictMode>\n        <Provider store={store}>\n            <BrowserRouter>\n                <SnackbarProvider>\n                    <ConfirmContextProvider>\n                        <App />\n                    </ConfirmContextProvider>\n                </SnackbarProvider>\n            </BrowserRouter>\n        </Provider>\n    </React.StrictMode>\n)\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/Header/index.js",
    "content": "import PropTypes from 'prop-types'\nimport { useSelector, useDispatch } from 'react-redux'\nimport { useState } from 'react'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Avatar, Box, ButtonBase, Switch } from '@mui/material'\nimport { styled } from '@mui/material/styles'\n\n// project imports\nimport LogoSection from '../LogoSection'\n\n// assets\nimport { IconMenu2 } from '@tabler/icons'\n\n// store\nimport { SET_DARKMODE } from 'store/actions'\n\n// ==============================|| MAIN NAVBAR / HEADER ||============================== //\n\nconst MaterialUISwitch = styled(Switch)(({ theme }) => ({\n    width: 62,\n    height: 34,\n    padding: 7,\n    '& .MuiSwitch-switchBase': {\n        margin: 1,\n        padding: 0,\n        transform: 'translateX(6px)',\n        '&.Mui-checked': {\n            color: '#fff',\n            transform: 'translateX(22px)',\n            '& .MuiSwitch-thumb:before': {\n                backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"20\" width=\"20\" viewBox=\"0 0 20 20\"><path fill=\"${encodeURIComponent(\n                    '#fff'\n                )}\" d=\"M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z\"/></svg>')`\n            },\n            '& + .MuiSwitch-track': {\n                opacity: 1,\n                backgroundColor: theme.palette.mode === 'dark' ? '#8796A5' : '#aab4be'\n            }\n        }\n    },\n    '& .MuiSwitch-thumb': {\n        backgroundColor: theme.palette.mode === 'dark' ? '#003892' : '#001e3c',\n        width: 32,\n        height: 32,\n        '&:before': {\n            content: \"''\",\n            position: 'absolute',\n            width: '100%',\n            height: '100%',\n            left: 0,\n            top: 0,\n            backgroundRepeat: 'no-repeat',\n            backgroundPosition: 'center',\n            backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"20\" width=\"20\" viewBox=\"0 0 20 20\"><path fill=\"${encodeURIComponent(\n                '#fff'\n            )}\" d=\"M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z\"/></svg>')`\n        }\n    },\n    '& .MuiSwitch-track': {\n        opacity: 1,\n        backgroundColor: theme.palette.mode === 'dark' ? '#8796A5' : '#aab4be',\n        borderRadius: 20 / 2\n    }\n}))\n\nconst Header = ({ handleLeftDrawerToggle }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [isDark, setIsDark] = useState(customization.isDarkMode)\n    const dispatch = useDispatch()\n\n    const changeDarkMode = () => {\n        dispatch({ type: SET_DARKMODE, isDarkMode: !isDark })\n        setIsDark((isDark) => !isDark)\n        localStorage.setItem('isDarkMode', !isDark)\n    }\n\n    return (\n        <>\n            {/* logo & toggler button */}\n            <Box\n                sx={{\n                    width: 228,\n                    display: 'flex',\n                    [theme.breakpoints.down('md')]: {\n                        width: 'auto'\n                    }\n                }}\n            >\n                <Box component='span' sx={{ display: { xs: 'none', md: 'block' }, flexGrow: 1 }}>\n                    <LogoSection />\n                </Box>\n                <ButtonBase sx={{ borderRadius: '12px', overflow: 'hidden' }}>\n                    <Avatar\n                        variant='rounded'\n                        sx={{\n                            ...theme.typography.commonAvatar,\n                            ...theme.typography.mediumAvatar,\n                            transition: 'all .2s ease-in-out',\n                            background: theme.palette.secondary.light,\n                            color: theme.palette.secondary.dark,\n                            '&:hover': {\n                                background: theme.palette.secondary.dark,\n                                color: theme.palette.secondary.light\n                            }\n                        }}\n                        onClick={handleLeftDrawerToggle}\n                        color='inherit'\n                    >\n                        <IconMenu2 stroke={1.5} size='1.3rem' />\n                    </Avatar>\n                </ButtonBase>\n            </Box>\n            <Box sx={{ flexGrow: 1 }} />\n            <MaterialUISwitch checked={isDark} onChange={changeDarkMode} />\n        </>\n    )\n}\n\nHeader.propTypes = {\n    handleLeftDrawerToggle: PropTypes.func\n}\n\nexport default Header\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/LogoSection/index.js",
    "content": "import { Link } from 'react-router-dom'\n\n// material-ui\nimport { ButtonBase } from '@mui/material'\n\n// project imports\nimport config from 'config'\nimport Logo from 'ui-component/Logo'\n\n// ==============================|| MAIN LOGO ||============================== //\n\nconst LogoSection = () => (\n    <ButtonBase disableRipple component={Link} to={config.defaultPath}>\n        <Logo />\n    </ButtonBase>\n)\n\nexport default LogoSection\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/Sidebar/MenuList/NavCollapse/index.js",
    "content": "import PropTypes from 'prop-types'\nimport { useState } from 'react'\nimport { useSelector } from 'react-redux'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Collapse, List, ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material'\n\n// project imports\nimport NavItem from '../NavItem'\n\n// assets\nimport FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'\nimport { IconChevronDown, IconChevronUp } from '@tabler/icons'\n\n// ==============================|| SIDEBAR MENU LIST COLLAPSE ITEMS ||============================== //\n\nconst NavCollapse = ({ menu, level }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [open, setOpen] = useState(false)\n    const [selected, setSelected] = useState(null)\n\n    const handleClick = () => {\n        setOpen(!open)\n        setSelected(!selected ? menu.id : null)\n    }\n\n    // menu collapse & item\n    const menus = menu.children?.map((item) => {\n        switch (item.type) {\n            case 'collapse':\n                return <NavCollapse key={item.id} menu={item} level={level + 1} />\n            case 'item':\n                return <NavItem key={item.id} item={item} level={level + 1} />\n            default:\n                return (\n                    <Typography key={item.id} variant='h6' color='error' align='center'>\n                        Menu Items Error\n                    </Typography>\n                )\n        }\n    })\n\n    const Icon = menu.icon\n    const menuIcon = menu.icon ? (\n        <Icon strokeWidth={1.5} size='1.3rem' style={{ marginTop: 'auto', marginBottom: 'auto' }} />\n    ) : (\n        <FiberManualRecordIcon\n            sx={{\n                width: selected === menu.id ? 8 : 6,\n                height: selected === menu.id ? 8 : 6\n            }}\n            fontSize={level > 0 ? 'inherit' : 'medium'}\n        />\n    )\n\n    return (\n        <>\n            <ListItemButton\n                sx={{\n                    borderRadius: `${customization.borderRadius}px`,\n                    mb: 0.5,\n                    alignItems: 'flex-start',\n                    backgroundColor: level > 1 ? 'transparent !important' : 'inherit',\n                    py: level > 1 ? 1 : 1.25,\n                    pl: `${level * 24}px`\n                }}\n                selected={selected === menu.id}\n                onClick={handleClick}\n            >\n                <ListItemIcon sx={{ my: 'auto', minWidth: !menu.icon ? 18 : 36 }}>{menuIcon}</ListItemIcon>\n                <ListItemText\n                    primary={\n                        <Typography variant={selected === menu.id ? 'h5' : 'body1'} color='inherit' sx={{ my: 'auto' }}>\n                            {menu.title}\n                        </Typography>\n                    }\n                    secondary={\n                        menu.caption && (\n                            <Typography variant='caption' sx={{ ...theme.typography.subMenuCaption }} display='block' gutterBottom>\n                                {menu.caption}\n                            </Typography>\n                        )\n                    }\n                />\n                {open ? (\n                    <IconChevronUp stroke={1.5} size='1rem' style={{ marginTop: 'auto', marginBottom: 'auto' }} />\n                ) : (\n                    <IconChevronDown stroke={1.5} size='1rem' style={{ marginTop: 'auto', marginBottom: 'auto' }} />\n                )}\n            </ListItemButton>\n            <Collapse in={open} timeout='auto' unmountOnExit>\n                <List\n                    component='div'\n                    disablePadding\n                    sx={{\n                        position: 'relative',\n                        '&:after': {\n                            content: \"''\",\n                            position: 'absolute',\n                            left: '32px',\n                            top: 0,\n                            height: '100%',\n                            width: '1px',\n                            opacity: 1,\n                            background: theme.palette.primary.light\n                        }\n                    }}\n                >\n                    {menus}\n                </List>\n            </Collapse>\n        </>\n    )\n}\n\nNavCollapse.propTypes = {\n    menu: PropTypes.object,\n    level: PropTypes.number\n}\n\nexport default NavCollapse\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/Sidebar/MenuList/NavGroup/index.js",
    "content": "import PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Divider, List, Typography } from '@mui/material'\n\n// project imports\nimport NavItem from '../NavItem'\nimport NavCollapse from '../NavCollapse'\n\n// ==============================|| SIDEBAR MENU LIST GROUP ||============================== //\n\nconst NavGroup = ({ item }) => {\n    const theme = useTheme()\n\n    // menu list collapse & items\n    const items = item.children?.map((menu) => {\n        switch (menu.type) {\n            case 'collapse':\n                return <NavCollapse key={menu.id} menu={menu} level={1} />\n            case 'item':\n                return <NavItem key={menu.id} item={menu} level={1} navType='MENU' />\n            default:\n                return (\n                    <Typography key={menu.id} variant='h6' color='error' align='center'>\n                        Menu Items Error\n                    </Typography>\n                )\n        }\n    })\n\n    return (\n        <>\n            <List\n                subheader={\n                    item.title && (\n                        <Typography variant='caption' sx={{ ...theme.typography.menuCaption }} display='block' gutterBottom>\n                            {item.title}\n                            {item.caption && (\n                                <Typography variant='caption' sx={{ ...theme.typography.subMenuCaption }} display='block' gutterBottom>\n                                    {item.caption}\n                                </Typography>\n                            )}\n                        </Typography>\n                    )\n                }\n            >\n                {items}\n            </List>\n\n            {/* group divider */}\n            <Divider sx={{ mt: 0.25, mb: 1.25 }} />\n        </>\n    )\n}\n\nNavGroup.propTypes = {\n    item: PropTypes.object\n}\n\nexport default NavGroup\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/Sidebar/MenuList/NavItem/index.js",
    "content": "import PropTypes from 'prop-types'\nimport { forwardRef, useEffect } from 'react'\nimport { Link } from 'react-router-dom'\nimport { useDispatch, useSelector } from 'react-redux'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography, useMediaQuery } from '@mui/material'\n\n// project imports\nimport { MENU_OPEN, SET_MENU } from 'store/actions'\nimport config from 'config'\n\n// assets\nimport FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'\n\n// ==============================|| SIDEBAR MENU LIST ITEMS ||============================== //\n\nconst NavItem = ({ item, level, navType, onClick, onUploadFile }) => {\n    const theme = useTheme()\n    const dispatch = useDispatch()\n    const customization = useSelector((state) => state.customization)\n    const matchesSM = useMediaQuery(theme.breakpoints.down('lg'))\n\n    const Icon = item.icon\n    const itemIcon = item?.icon ? (\n        <Icon stroke={1.5} size='1.3rem' />\n    ) : (\n        <FiberManualRecordIcon\n            sx={{\n                width: customization.isOpen.findIndex((id) => id === item?.id) > -1 ? 8 : 6,\n                height: customization.isOpen.findIndex((id) => id === item?.id) > -1 ? 8 : 6\n            }}\n            fontSize={level > 0 ? 'inherit' : 'medium'}\n        />\n    )\n\n    let itemTarget = '_self'\n    if (item.target) {\n        itemTarget = '_blank'\n    }\n\n    let listItemProps = {\n        component: forwardRef(function ListItemPropsComponent(props, ref) {\n            return <Link ref={ref} {...props} to={`${config.basename}${item.url}`} target={itemTarget} />\n        })\n    }\n    if (item?.external) {\n        listItemProps = { component: 'a', href: item.url, target: itemTarget }\n    }\n    if (item?.id === 'loadWorkflow') {\n        listItemProps.component = 'label'\n    }\n\n    const handleFileUpload = (e) => {\n        if (!e.target.files) return\n\n        const file = e.target.files[0]\n\n        const reader = new FileReader()\n        reader.onload = (evt) => {\n            if (!evt?.target?.result) {\n                return\n            }\n            const { result } = evt.target\n            onUploadFile(result)\n        }\n        reader.readAsText(file)\n    }\n\n    const itemHandler = (id) => {\n        if (navType === 'SETTINGS' && id !== 'loadWorkflow') {\n            onClick(id)\n        } else {\n            dispatch({ type: MENU_OPEN, id })\n            if (matchesSM) dispatch({ type: SET_MENU, opened: false })\n        }\n    }\n\n    // active menu item on page load\n    useEffect(() => {\n        if (navType === 'MENU') {\n            const currentIndex = document.location.pathname\n                .toString()\n                .split('/')\n                .findIndex((id) => id === item.id)\n            if (currentIndex > -1) {\n                dispatch({ type: MENU_OPEN, id: item.id })\n            }\n            if (!document.location.pathname.toString().split('/')[1]) {\n                itemHandler('workflows')\n            }\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [navType])\n\n    return (\n        <ListItemButton\n            {...listItemProps}\n            disabled={item.disabled}\n            sx={{\n                borderRadius: `${customization.borderRadius}px`,\n                mb: 0.5,\n                alignItems: 'flex-start',\n                backgroundColor: level > 1 ? 'transparent !important' : 'inherit',\n                py: level > 1 ? 1 : 1.25,\n                pl: `${level * 24}px`\n            }}\n            selected={customization.isOpen.findIndex((id) => id === item.id) > -1}\n            onClick={() => itemHandler(item.id)}\n        >\n            {item.id === 'loadWorkflow' && <input type='file' hidden accept='.json' onChange={(e) => handleFileUpload(e)} />}\n            <ListItemIcon sx={{ my: 'auto', minWidth: !item?.icon ? 18 : 36 }}>{itemIcon}</ListItemIcon>\n            <ListItemText\n                primary={\n                    <Typography variant={customization.isOpen.findIndex((id) => id === item.id) > -1 ? 'h5' : 'body1'} color='inherit'>\n                        {item.title}\n                    </Typography>\n                }\n                secondary={\n                    item.caption && (\n                        <Typography variant='caption' sx={{ ...theme.typography.subMenuCaption }} display='block' gutterBottom>\n                            {item.caption}\n                        </Typography>\n                    )\n                }\n            />\n            {item.chip && (\n                <Chip\n                    color={item.chip.color}\n                    variant={item.chip.variant}\n                    size={item.chip.size}\n                    label={item.chip.label}\n                    avatar={item.chip.avatar && <Avatar>{item.chip.avatar}</Avatar>}\n                />\n            )}\n        </ListItemButton>\n    )\n}\n\nNavItem.propTypes = {\n    item: PropTypes.object,\n    level: PropTypes.number,\n    navType: PropTypes.string,\n    onClick: PropTypes.func,\n    onUploadFile: PropTypes.func\n}\n\nexport default NavItem\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/Sidebar/MenuList/index.js",
    "content": "// material-ui\nimport { Typography } from '@mui/material'\n\n// project imports\nimport NavGroup from './NavGroup'\nimport menuItem from 'menu-items'\n\n// ==============================|| SIDEBAR MENU LIST ||============================== //\n\nconst MenuList = () => {\n    const navItems = menuItem.items.map((item) => {\n        switch (item.type) {\n            case 'group':\n                return <NavGroup key={item.id} item={item} />\n            default:\n                return (\n                    <Typography key={item.id} variant='h6' color='error' align='center'>\n                        Menu Items Error\n                    </Typography>\n                )\n        }\n    })\n\n    return <>{navItems}</>\n}\n\nexport default MenuList\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/Sidebar/index.js",
    "content": "import PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Box, Drawer, useMediaQuery } from '@mui/material'\n\n// third-party\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport { BrowserView, MobileView } from 'react-device-detect'\n\n// project imports\nimport MenuList from './MenuList'\nimport LogoSection from '../LogoSection'\nimport { drawerWidth } from 'store/constant'\n\n// ==============================|| SIDEBAR DRAWER ||============================== //\n\nconst Sidebar = ({ drawerOpen, drawerToggle, window }) => {\n    const theme = useTheme()\n    const matchUpMd = useMediaQuery(theme.breakpoints.up('md'))\n\n    const drawer = (\n        <>\n            <Box sx={{ display: { xs: 'block', md: 'none' } }}>\n                <Box sx={{ display: 'flex', p: 2, mx: 'auto' }}>\n                    <LogoSection />\n                </Box>\n            </Box>\n            <BrowserView>\n                <PerfectScrollbar\n                    component='div'\n                    style={{\n                        height: !matchUpMd ? 'calc(100vh - 56px)' : 'calc(100vh - 88px)',\n                        paddingLeft: '16px',\n                        paddingRight: '16px'\n                    }}\n                >\n                    <MenuList />\n                </PerfectScrollbar>\n            </BrowserView>\n            <MobileView>\n                <Box sx={{ px: 2 }}>\n                    <MenuList />\n                </Box>\n            </MobileView>\n        </>\n    )\n\n    const container = window !== undefined ? () => window.document.body : undefined\n\n    return (\n        <Box component='nav' sx={{ flexShrink: { md: 0 }, width: matchUpMd ? drawerWidth : 'auto' }} aria-label='mailbox folders'>\n            <Drawer\n                container={container}\n                variant={matchUpMd ? 'persistent' : 'temporary'}\n                anchor='left'\n                open={drawerOpen}\n                onClose={drawerToggle}\n                sx={{\n                    '& .MuiDrawer-paper': {\n                        width: drawerWidth,\n                        background: theme.palette.background.default,\n                        color: theme.palette.text.primary,\n                        borderRight: 'none',\n                        [theme.breakpoints.up('md')]: {\n                            top: '66px'\n                        }\n                    }\n                }}\n                ModalProps={{ keepMounted: true }}\n                color='inherit'\n            >\n                {drawer}\n            </Drawer>\n        </Box>\n    )\n}\n\nSidebar.propTypes = {\n    drawerOpen: PropTypes.bool,\n    drawerToggle: PropTypes.func,\n    window: PropTypes.object\n}\n\nexport default Sidebar\n"
  },
  {
    "path": "packages/ui/src/layout/MainLayout/index.js",
    "content": "import { useEffect } from 'react'\nimport { useDispatch, useSelector } from 'react-redux'\nimport { Outlet } from 'react-router-dom'\n\n// material-ui\nimport { styled, useTheme } from '@mui/material/styles'\nimport { AppBar, Box, CssBaseline, Toolbar, useMediaQuery } from '@mui/material'\n\n// project imports\nimport Header from './Header'\nimport Sidebar from './Sidebar'\nimport { drawerWidth } from 'store/constant'\nimport { SET_MENU } from 'store/actions'\n\n// styles\nconst Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({\n    ...theme.typography.mainContent,\n    ...(!open && {\n        borderBottomLeftRadius: 0,\n        borderBottomRightRadius: 0,\n        transition: theme.transitions.create('margin', {\n            easing: theme.transitions.easing.sharp,\n            duration: theme.transitions.duration.leavingScreen\n        }),\n        [theme.breakpoints.up('md')]: {\n            marginLeft: -(drawerWidth - 20),\n            width: `calc(100% - ${drawerWidth}px)`\n        },\n        [theme.breakpoints.down('md')]: {\n            marginLeft: '20px',\n            width: `calc(100% - ${drawerWidth}px)`,\n            padding: '16px'\n        },\n        [theme.breakpoints.down('sm')]: {\n            marginLeft: '10px',\n            width: `calc(100% - ${drawerWidth}px)`,\n            padding: '16px',\n            marginRight: '10px'\n        }\n    }),\n    ...(open && {\n        transition: theme.transitions.create('margin', {\n            easing: theme.transitions.easing.easeOut,\n            duration: theme.transitions.duration.enteringScreen\n        }),\n        marginLeft: 0,\n        borderBottomLeftRadius: 0,\n        borderBottomRightRadius: 0,\n        width: `calc(100% - ${drawerWidth}px)`,\n        [theme.breakpoints.down('md')]: {\n            marginLeft: '20px'\n        },\n        [theme.breakpoints.down('sm')]: {\n            marginLeft: '10px'\n        }\n    })\n}))\n\n// ==============================|| MAIN LAYOUT ||============================== //\n\nconst MainLayout = () => {\n    const theme = useTheme()\n    const matchDownMd = useMediaQuery(theme.breakpoints.down('lg'))\n\n    // Handle left drawer\n    const leftDrawerOpened = useSelector((state) => state.customization.opened)\n    const dispatch = useDispatch()\n    const handleLeftDrawerToggle = () => {\n        dispatch({ type: SET_MENU, opened: !leftDrawerOpened })\n    }\n\n    useEffect(() => {\n        dispatch({ type: SET_MENU, opened: !matchDownMd })\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [matchDownMd])\n\n    return (\n        <Box sx={{ display: 'flex' }}>\n            <CssBaseline />\n            {/* header */}\n            <AppBar\n                enableColorOnDark\n                position='fixed'\n                color='inherit'\n                elevation={0}\n                sx={{\n                    bgcolor: theme.palette.background.default,\n                    transition: leftDrawerOpened ? theme.transitions.create('width') : 'none'\n                }}\n            >\n                <Toolbar>\n                    <Header handleLeftDrawerToggle={handleLeftDrawerToggle} />\n                </Toolbar>\n            </AppBar>\n            {process.env.REACT_APP_IS_DEMO === 'true' && (\n                <div\n                    style={{\n                        padding: 10,\n                        display: 'flex',\n                        alignItems: 'center',\n                        justifyContent: 'center',\n                        zIndex: 2000,\n                        position: 'fixed',\n                        bottom: 0,\n                        left: 0,\n                        right: 0,\n                        height: 50,\n                        width: '100%',\n                        background: '#d0f4de'\n                    }}\n                >\n                    <h4>\n                        This is a demo version to get an overview of Outerbridge. Beware of credentials you put in as everyone will be able\n                        to see it.\n                    </h4>\n                </div>\n            )}\n\n            {/* drawer */}\n            <Sidebar drawerOpen={leftDrawerOpened} drawerToggle={handleLeftDrawerToggle} />\n\n            {/* main content */}\n            <Main theme={theme} open={leftDrawerOpened}>\n                <Outlet />\n            </Main>\n        </Box>\n    )\n}\n\nexport default MainLayout\n"
  },
  {
    "path": "packages/ui/src/layout/MinimalLayout/index.js",
    "content": "import { Outlet } from 'react-router-dom'\n\n// ==============================|| MINIMAL LAYOUT ||============================== //\n\nconst MinimalLayout = () => (\n    <>\n        <Outlet />\n    </>\n)\n\nexport default MinimalLayout\n"
  },
  {
    "path": "packages/ui/src/layout/NavMotion.js",
    "content": "import PropTypes from 'prop-types'\nimport { motion } from 'framer-motion'\n\n// ==============================|| ANIMATION FOR CONTENT ||============================== //\n\nconst NavMotion = ({ children }) => {\n    const motionVariants = {\n        initial: {\n            opacity: 0,\n            scale: 0.99\n        },\n        in: {\n            opacity: 1,\n            scale: 1\n        },\n        out: {\n            opacity: 0,\n            scale: 1.01\n        }\n    }\n\n    const motionTransition = {\n        type: 'tween',\n        ease: 'anticipate',\n        duration: 0.4\n    }\n\n    return (\n        <motion.div initial='initial' animate='in' exit='out' variants={motionVariants} transition={motionTransition}>\n            {children}\n        </motion.div>\n    )\n}\n\nNavMotion.propTypes = {\n    children: PropTypes.node\n}\n\nexport default NavMotion\n"
  },
  {
    "path": "packages/ui/src/layout/NavigationScroll.js",
    "content": "import PropTypes from 'prop-types'\nimport { useEffect } from 'react'\nimport { useLocation } from 'react-router-dom'\n\n// ==============================|| NAVIGATION SCROLL TO TOP ||============================== //\n\nconst NavigationScroll = ({ children }) => {\n    const location = useLocation()\n    const { pathname } = location\n\n    useEffect(() => {\n        window.scrollTo({\n            top: 0,\n            left: 0,\n            behavior: 'smooth'\n        })\n    }, [pathname])\n\n    return children || null\n}\n\nNavigationScroll.propTypes = {\n    children: PropTypes.node\n}\n\nexport default NavigationScroll\n"
  },
  {
    "path": "packages/ui/src/menu-items/dashboard.js",
    "content": "// assets\nimport { IconHierarchy, IconEditCircle, IconWallet, IconKey } from '@tabler/icons'\n\n// constant\nconst icons = { IconHierarchy, IconEditCircle, IconWallet, IconKey }\n\n// ==============================|| DASHBOARD MENU ITEMS ||============================== //\n\nconst dashboard = {\n    id: 'dashboard',\n    title: '',\n    type: 'group',\n    children: [\n        {\n            id: 'workflows',\n            title: 'Workflows',\n            type: 'item',\n            url: '/workflows',\n            icon: icons.IconHierarchy,\n            breadcrumbs: true\n        },\n        {\n            id: 'contracts',\n            title: 'Contracts',\n            type: 'item',\n            url: '/contracts',\n            icon: icons.IconEditCircle,\n            breadcrumbs: true\n        },\n        {\n            id: 'wallets',\n            title: 'Wallets',\n            type: 'item',\n            url: '/wallets',\n            icon: icons.IconWallet,\n            breadcrumbs: true\n        },\n        {\n            id: 'apikey',\n            title: 'API Keys',\n            type: 'item',\n            url: '/apikey',\n            icon: icons.IconKey,\n            breadcrumbs: true\n        }\n    ]\n}\n\nexport default dashboard\n"
  },
  {
    "path": "packages/ui/src/menu-items/index.js",
    "content": "import dashboard from './dashboard'\n\n// ==============================|| MENU ITEMS ||============================== //\n\nconst menuItems = {\n    items: [dashboard]\n}\n\nexport default menuItems\n"
  },
  {
    "path": "packages/ui/src/menu-items/settings.js",
    "content": "// assets\nimport { IconTrash, IconFileUpload, IconFileExport } from '@tabler/icons'\n\n// constant\nconst icons = { IconTrash, IconFileUpload, IconFileExport }\n\n// ==============================|| SETTINGS MENU ITEMS ||============================== //\n\nconst settings = {\n    id: 'settings',\n    title: '',\n    type: 'group',\n    children: [\n        {\n            id: 'loadWorkflow',\n            title: 'Load Workflow',\n            type: 'item',\n            url: '',\n            icon: icons.IconFileUpload\n        },\n        {\n            id: 'exportWorkflow',\n            title: 'Export Workflow',\n            type: 'item',\n            url: '',\n            icon: icons.IconFileExport\n        },\n        {\n            id: 'deleteWorkflow',\n            title: 'Delete Workflow',\n            type: 'item',\n            url: '',\n            icon: icons.IconTrash\n        }\n    ]\n}\n\nexport default settings\n"
  },
  {
    "path": "packages/ui/src/routes/CanvasRoutes.js",
    "content": "import { lazy } from 'react'\n\n// project imports\nimport Loadable from 'ui-component/Loadable'\nimport MinimalLayout from 'layout/MinimalLayout'\n\n// canvas routing\nconst Canvas = Loadable(lazy(() => import('views/canvas')))\n\n// ==============================|| CANVAS ROUTING ||============================== //\n\nconst CanvasRoutes = {\n    path: '/',\n    element: <MinimalLayout />,\n    children: [\n        {\n            path: '/canvas',\n            element: <Canvas />\n        },\n        {\n            path: '/canvas/:id',\n            element: <Canvas />\n        }\n    ]\n}\n\nexport default CanvasRoutes\n"
  },
  {
    "path": "packages/ui/src/routes/MainRoutes.js",
    "content": "import { lazy } from 'react'\n\n// project imports\nimport MainLayout from 'layout/MainLayout'\nimport Loadable from 'ui-component/Loadable'\n\n// workflows routing\nconst Workflows = Loadable(lazy(() => import('views/workflows')))\n\n// contracts routing\nconst Contracts = Loadable(lazy(() => import('views/contracts')))\n\n// wallets routing\nconst Wallets = Loadable(lazy(() => import('views/wallets')))\n\n// apikey routing\nconst APIKey = Loadable(lazy(() => import('views/apikey')))\n\n// ==============================|| MAIN ROUTING ||============================== //\n\nconst MainRoutes = {\n    path: '/',\n    element: <MainLayout />,\n    children: [\n        {\n            path: '/',\n            element: <Workflows />\n        },\n        {\n            path: '/workflows',\n            element: <Workflows />\n        },\n        {\n            path: '/contracts',\n            element: <Contracts />\n        },\n        {\n            path: '/wallets',\n            element: <Wallets />\n        },\n        {\n            path: '/apikey',\n            element: <APIKey />\n        }\n    ]\n}\n\nexport default MainRoutes\n"
  },
  {
    "path": "packages/ui/src/routes/index.js",
    "content": "import { useRoutes } from 'react-router-dom'\n\n// routes\nimport MainRoutes from './MainRoutes'\nimport CanvasRoutes from './CanvasRoutes'\nimport config from 'config'\n\n// ==============================|| ROUTING RENDER ||============================== //\n\nexport default function ThemeRoutes() {\n    return useRoutes([MainRoutes, CanvasRoutes], config.basename)\n}\n"
  },
  {
    "path": "packages/ui/src/serviceWorker.js",
    "content": "// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n    window.location.hostname === 'localhost' ||\n        // [::1] is the IPv6 localhost address.\n        window.location.hostname === '[::1]' ||\n        // 127.0.0.0/8 are considered localhost for IPv4.\n        window.location.hostname.match(/^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)\n)\n\nfunction registerValidSW(swUrl, config) {\n    navigator.serviceWorker\n        .register(swUrl)\n        .then((registration) => {\n            registration.onupdatefound = () => {\n                const installingWorker = registration.installing\n                if (installingWorker == null) {\n                    return\n                }\n                installingWorker.onstatechange = () => {\n                    if (installingWorker.state === 'installed') {\n                        if (navigator.serviceWorker.controller) {\n                            // At this point, the updated precached content has been fetched,\n                            // but the previous service worker will still serve the older\n                            // content until all client tabs are closed.\n                            console.info(\n                                'New content is available and will be used when all tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n                            )\n\n                            // Execute callback\n                            if (config && config.onUpdate) {\n                                config.onUpdate(registration)\n                            }\n                        } else {\n                            // At this point, everything has been precached.\n                            // It's the perfect time to display a\n                            // \"Content is cached for offline use.\" message.\n                            console.info('Content is cached for offline use.')\n\n                            // Execute callback\n                            if (config && config.onSuccess) {\n                                config.onSuccess(registration)\n                            }\n                        }\n                    }\n                }\n            }\n        })\n        .catch((error) => {\n            console.error('Error during service worker registration:', error)\n        })\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n    // Check if the service worker can be found. If it can't reload the page.\n    fetch(swUrl, {\n        headers: { 'Service-Worker': 'script' }\n    })\n        .then((response) => {\n            // Ensure service worker exists, and that we really are getting a JS file.\n            const contentType = response.headers.get('content-type')\n            if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {\n                // No service worker found. Probably a different app. Reload the page.\n                navigator.serviceWorker.ready.then((registration) => {\n                    registration.unregister().then(() => {\n                        window.location.reload()\n                    })\n                })\n            } else {\n                // Service worker found. Proceed as normal.\n                registerValidSW(swUrl, config)\n            }\n        })\n        .catch(() => {\n            console.info('No internet connection found. App is running in offline mode.')\n        })\n}\n\nexport function register(config) {\n    if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n        // The URL constructor is available in all browsers that support SW.\n        const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)\n        if (publicUrl.origin !== window.location.origin) {\n            // Our service worker won't work if PUBLIC_URL is on a different origin\n            // from what our page is served on. This might happen if a CDN is used to\n            // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n            return\n        }\n\n        window.addEventListener('load', () => {\n            const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`\n\n            if (isLocalhost) {\n                // This is running on localhost. Let's check if a service worker still exists or not.\n                checkValidServiceWorker(swUrl, config)\n\n                // Add some additional logging to localhost, pointing developers to the\n                // service worker/PWA documentation.\n                navigator.serviceWorker.ready.then(() => {\n                    console.info(\n                        'This web app is being served cache-first by a service worker. To learn more, visit https://bit.ly/CRA-PWA'\n                    )\n                })\n            } else {\n                // Is not localhost. Just register service worker\n                registerValidSW(swUrl, config)\n            }\n        })\n    }\n}\n\nexport function unregister() {\n    if ('serviceWorker' in navigator) {\n        navigator.serviceWorker.ready\n            .then((registration) => {\n                registration.unregister()\n            })\n            .catch((error) => {\n                console.error(error.message)\n            })\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/store/actions.js",
    "content": "// action - customization reducer\nexport const SET_MENU = '@customization/SET_MENU'\nexport const MENU_TOGGLE = '@customization/MENU_TOGGLE'\nexport const MENU_OPEN = '@customization/MENU_OPEN'\nexport const SET_FONT_FAMILY = '@customization/SET_FONT_FAMILY'\nexport const SET_BORDER_RADIUS = '@customization/SET_BORDER_RADIUS'\nexport const SET_LAYOUT = '@customization/SET_LAYOUT '\nexport const SET_DARKMODE = '@customization/SET_DARKMODE'\n\n// action - canvas reducer\nexport const REMOVE_EDGE = '@canvas/REMOVE_EDGE'\nexport const SET_DIRTY = '@canvas/SET_DIRTY'\nexport const REMOVE_DIRTY = '@canvas/REMOVE_DIRTY'\nexport const SET_WORKFLOW = '@canvas/SET_WORKFLOW'\n\n// action - notifier reducer\nexport const ENQUEUE_SNACKBAR = 'ENQUEUE_SNACKBAR'\nexport const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR'\nexport const REMOVE_SNACKBAR = 'REMOVE_SNACKBAR'\n\n// action - dialog reducer\nexport const SHOW_CONFIRM = 'SHOW_CONFIRM'\nexport const HIDE_CONFIRM = 'HIDE_CONFIRM'\n\nexport const enqueueSnackbar = (notification) => {\n    const key = notification.options && notification.options.key\n\n    return {\n        type: ENQUEUE_SNACKBAR,\n        notification: {\n            ...notification,\n            key: key || new Date().getTime() + Math.random()\n        }\n    }\n}\n\nexport const closeSnackbar = (key) => ({\n    type: CLOSE_SNACKBAR,\n    dismissAll: !key, // dismiss all if no key has been defined\n    key\n})\n\nexport const removeSnackbar = (key) => ({\n    type: REMOVE_SNACKBAR,\n    key\n})\n"
  },
  {
    "path": "packages/ui/src/store/constant.js",
    "content": "// constant\nexport const gridSpacing = 3\nexport const drawerWidth = 260\nexport const appDrawerWidth = 320\nexport const baseURL = process.env.NODE_ENV === 'production' ? window.location.origin : window.location.origin.replace(':8080', ':3000')\nexport const NETWORK = {\n    MAINNET: 'homestead',\n    GÖRLI: 'goerli',\n    MATIC_MUMBAI: 'maticmum',\n    MATIC: 'matic',\n    OPTIMISM: 'optimism',\n    OPTIMISM_GOERLI: 'optimism-goerli',\n    ARBITRUM: 'arbitrum',\n    ARBITRUM_GOERLI: 'arbitrum-goerli',\n    ARBITRUM_NOVA: 'arbitrum-nova',\n    BSC: 'bsc',\n    BSC_TESTNET: 'bsc-testnet',\n    AVALANCHE: 'avalanche',\n    AVALANCHE_TESTNET: 'avalanche-testnet',\n    FANTOM: 'fantom',\n    FANTOM_TESTNET: 'fantom-testnet',\n    CRONOS: 'cronos',\n    CRONOS_TESTNET: 'cronos-testnet',\n    GNOSIS: 'gnosis',\n    CELO: 'celo',\n    MOONRIVER: 'moonriver',\n    MOONBEAM: 'moonbeam'\n}\nexport const NETWORK_LABEL = {\n    MAINNET: 'Mainnet',\n    GÖRLI: 'Goerli',\n    MATIC_MUMBAI: 'Polygon Mumbai',\n    MATIC: 'Polygon Mainnet',\n    OPTIMISM: 'Optimism Mainnet',\n    OPTIMISM_GOERLI: 'Optimism Goerli',\n    ARBITRUM: 'Arbitrum Mainnet',\n    ARBITRUM_GOERLI: 'Arbitrum Goerli',\n    ARBITRUM_NOVA: 'Arbitrum Nova',\n    BSC: 'Binance Smart Chain Mainnet',\n    BSC_TESTNET: 'Binance Smart Chain Testnet',\n    AVALANCHE: 'Avalanche Mainnet',\n    AVALANCHE_TESTNET: 'Avalanche Testnet',\n    FANTOM: 'Fantom Mainnet',\n    FANTOM_TESTNET: 'Fantom Testnet',\n    CRONOS: 'Cronos Mainnet',\n    CRONOS_TESTNET: 'Cronos Testnet',\n    GNOSIS: 'Gnosis Mainnet',\n    CELO: 'Celo Mainnet',\n    MOONRIVER: 'Moonriver Mainnet',\n    MOONBEAM: 'Moonbeam Mainnet'\n}\nexport const scanAPIs = {\n    [NETWORK.MAINNET]: 'https://api.etherscan.io/api',\n    [NETWORK.GÖRLI]: 'https://api-goerli.etherscan.io/api',\n    [NETWORK.MATIC]: 'https://api.polygonscan.com/api',\n    [NETWORK.MATIC_MUMBAI]: 'https://api-testnet.polygonscan.com/api',\n    [NETWORK.OPTIMISM]: 'https://api-optimistic.etherscan.io/api',\n    [NETWORK.OPTIMISM_GOERLI]: 'https://api-goerli-optimistic.etherscan.io/api',\n    [NETWORK.ARBITRUM]: 'https://api.arbiscan.io/api',\n    [NETWORK.ARBITRUM_GOERLI]: 'https://api-goerli.arbiscan.io/api',\n    [NETWORK.BSC]: 'https://api.bscscan.com/api',\n    [NETWORK.BSC_TESTNET]: 'https://api-testnet.bscscan.com/api',\n    [NETWORK.AVALANCHE]: 'https://api.snowtrace.io/api',\n    [NETWORK.AVALANCHE_TESTNET]: 'https://api-testnet.snowtrace.io/api',\n    [NETWORK.FANTOM]: 'https://api.ftmscan.com/api',\n    [NETWORK.FANTOM_TESTNET]: 'https://api-testnet.ftmscan.com/api',\n    [NETWORK.CRONOS]: 'https://api.cronoscan.com/api',\n    [NETWORK.CRONOS_TESTNET]: 'https://api-testnet.cronoscan.com/api',\n    [NETWORK.GNOSIS]: 'https://api.gnosisscan.io/api',\n    [NETWORK.CELO]: 'https://api.celoscan.io/api',\n    [NETWORK.MOONRIVER]: 'https://api-moonriver.moonscan.io/api',\n    [NETWORK.MOONBEAM]: 'https://api-moonbeam.moonscan.io/api'\n}\nexport const networkExplorers = {\n    [NETWORK.MAINNET]: 'https://etherscan.io',\n    [NETWORK.GÖRLI]: 'https://goerli.etherscan.io',\n    [NETWORK.MATIC]: 'https://polygonscan.com',\n    [NETWORK.MATIC_MUMBAI]: 'https://mumbai.polygonscan.com',\n    [NETWORK.OPTIMISM]: 'https://optimistic.etherscan.io',\n    [NETWORK.OPTIMISM_GOERLI]: 'https://goerli-optimistic.etherscan.io',\n    [NETWORK.ARBITRUM]: 'https://arbiscan.io',\n    [NETWORK.ARBITRUM_GOERLI]: 'https://goerli-explorer.arbitrum.io',\n    [NETWORK.BSC]: 'https://bscscan.com',\n    [NETWORK.BSC_TESTNET]: 'https://testnet.bscscan.com',\n    [NETWORK.FANTOM]: 'https://ftmscan.com',\n    [NETWORK.FANTOM_TESTNET]: 'https://testnet.ftmscan.com',\n    [NETWORK.CRONOS]: 'https://cronoscan.com',\n    [NETWORK.CRONOS_TESTNET]: 'https://testnet.cronoscan.com',\n    [NETWORK.GNOSIS]: 'https://gnosisscan.io',\n    [NETWORK.CELO]: 'https://celoscan.io',\n    [NETWORK.MOONRIVER]: 'https://moonriver.moonscan.io',\n    [NETWORK.MOONBEAM]: 'https://moonscan.io'\n}\nexport const networks = [\n    {\n        label: NETWORK_LABEL.MAINNET,\n        name: NETWORK.MAINNET,\n        uri: scanAPIs[NETWORK.MAINNET],\n        color: '#3c3c3d'\n    },\n    {\n        label: NETWORK_LABEL.GÖRLI,\n        name: NETWORK.GÖRLI,\n        uri: scanAPIs[NETWORK.GÖRLI],\n        color: '#3a86ff'\n    },\n    {\n        label: NETWORK_LABEL.MATIC,\n        name: NETWORK.MATIC,\n        uri: scanAPIs[NETWORK.MATIC],\n        color: '#8247e5'\n    },\n    {\n        label: NETWORK_LABEL.MATIC_MUMBAI,\n        name: NETWORK.MATIC_MUMBAI,\n        uri: scanAPIs[NETWORK.MATIC_MUMBAI],\n        color: '#8247e5'\n    },\n    {\n        label: NETWORK_LABEL.BSC,\n        name: NETWORK.BSC,\n        uri: scanAPIs[NETWORK.BSC],\n        color: '#ffbe0b'\n    },\n    {\n        label: NETWORK_LABEL.BSC_TESTNET,\n        name: NETWORK.BSC_TESTNET,\n        uri: scanAPIs[NETWORK.BSC_TESTNET],\n        color: '#ffbe0b'\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM,\n        name: NETWORK.OPTIMISM,\n        uri: scanAPIs[NETWORK.OPTIMISM],\n        color: '#ef233c'\n    },\n    {\n        label: NETWORK_LABEL.OPTIMISM_GOERLI,\n        name: NETWORK.OPTIMISM_GOERLI,\n        uri: scanAPIs[NETWORK.OPTIMISM_GOERLI],\n        color: '#ef233c'\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM,\n        name: NETWORK.ARBITRUM,\n        uri: scanAPIs[NETWORK.ARBITRUM],\n        color: '#023e8a'\n    },\n    {\n        label: NETWORK_LABEL.ARBITRUM_GOERLI,\n        name: NETWORK.ARBITRUM_GOERLI,\n        uri: scanAPIs[NETWORK.ARBITRUM_GOERLI],\n        color: '#023e8a'\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE,\n        name: NETWORK.AVALANCHE,\n        uri: scanAPIs[NETWORK.AVALANCHE],\n        color: '#e84142'\n    },\n    {\n        label: NETWORK_LABEL.AVALANCHE_TESTNET,\n        name: NETWORK.AVALANCHE_TESTNET,\n        uri: scanAPIs[NETWORK.AVALANCHE_TESTNET],\n        color: '#e84142'\n    },\n    {\n        label: NETWORK_LABEL.FANTOM,\n        name: NETWORK.FANTOM,\n        uri: scanAPIs[NETWORK.FANTOM],\n        color: '#001f68'\n    },\n    {\n        label: NETWORK_LABEL.FANTOM_TESTNET,\n        name: NETWORK.FANTOM_TESTNET,\n        uri: scanAPIs[NETWORK.FANTOM_TESTNET],\n        color: '#001f68'\n    },\n    {\n        label: NETWORK_LABEL.CRONOS,\n        name: NETWORK.CRONOS,\n        uri: scanAPIs[NETWORK.CRONOS],\n        color: '#0c1526'\n    },\n    {\n        label: NETWORK_LABEL.CRONOS_TESTNET,\n        name: NETWORK.CRONOS_TESTNET,\n        uri: scanAPIs[NETWORK.CRONOS_TESTNET],\n        color: '#0c1526'\n    },\n    {\n        label: NETWORK_LABEL.GNOSIS,\n        name: NETWORK.GNOSIS,\n        uri: scanAPIs[NETWORK.GNOSIS],\n        color: '#0d8e74'\n    },\n    {\n        label: NETWORK_LABEL.CELO,\n        name: NETWORK.CELO,\n        uri: scanAPIs[NETWORK.CELO],\n        color: '#35d07f'\n    },\n    {\n        label: NETWORK_LABEL.MOONRIVER,\n        name: NETWORK.MOONRIVER,\n        uri: scanAPIs[NETWORK.MOONRIVER],\n        color: '#fcbf49'\n    },\n    {\n        label: NETWORK_LABEL.MOONBEAM,\n        name: NETWORK.MOONBEAM,\n        uri: scanAPIs[NETWORK.MOONBEAM],\n        color: '#f15bb5'\n    }\n]\n\nexport const network_details = {\n    networks: [\n        {\n            label: 'Select Network',\n            name: 'network',\n            type: 'options',\n            options: networks,\n            default: 'homestead'\n        }\n    ],\n    credentials: [\n        {\n            label: 'API Key (Optional)',\n            name: 'credentialMethod',\n            type: 'options',\n            description: 'Provide an API key to avoid rate limit',\n            options: [\n                {\n                    label: 'No Auth',\n                    name: 'noAuth',\n                    description: 'Use public endpoint without API key',\n                    hideRegisteredCredential: true\n                },\n                {\n                    label: 'Etherscan API',\n                    name: 'etherscanApi',\n                    show: {\n                        'networks.network': [NETWORK.MAINNET, NETWORK.GÖRLI]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.MAINNET]}/apis`\n                },\n                {\n                    label: 'Polygonscan API',\n                    name: 'polygonscanApi',\n                    show: {\n                        'networks.network': [NETWORK.MATIC, NETWORK.MATIC_MUMBAI]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.MATIC]}/apis`\n                },\n                {\n                    label: 'Bscscan API',\n                    name: 'bscscanApi',\n                    show: {\n                        'networks.network': [NETWORK.BSC, NETWORK.BSC_TESTNET]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.BSC]}/apis`\n                },\n                {\n                    label: 'Optimism Etherscan API',\n                    name: 'optimisticEtherscanApi',\n                    show: {\n                        'networks.network': [NETWORK.OPTIMISM, NETWORK.OPTIMISM_GOERLI]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.OPTIMISM]}/apis`\n                },\n                {\n                    label: 'Arbiscan API',\n                    name: 'arbiscanApi',\n                    show: {\n                        'networks.network': [NETWORK.ARBITRUM, NETWORK.ARBITRUM_GOERLI]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.ARBITRUM]}/apis`\n                },\n                {\n                    label: 'FantomScan API',\n                    name: 'fantomscanApi',\n                    show: {\n                        'networks.network': [NETWORK.FANTOM, NETWORK.FANTOM_TESTNET]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.FANTOM]}/apis`\n                },\n                {\n                    label: 'SnowTrace API',\n                    name: 'snowtraceApi',\n                    show: {\n                        'networks.network': [NETWORK.AVALANCHE, NETWORK.AVALANCHE_TESTNET]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.AVALANCHE]}/apis`\n                },\n                {\n                    label: 'CronosScan API',\n                    name: 'cronosscanApi',\n                    show: {\n                        'networks.network': [NETWORK.CRONOS, NETWORK.CRONOS_TESTNET]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.CRONOS]}/apis`\n                },\n                {\n                    label: 'GnosisScan API',\n                    name: 'gnosisscanApi',\n                    show: {\n                        'networks.network': [NETWORK.GNOSIS]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.GNOSIS]}/apis`\n                },\n                {\n                    label: 'CeloScan API',\n                    name: 'celoscanApi',\n                    show: {\n                        'networks.network': [NETWORK.CELO]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.CELO]}/apis`\n                },\n                {\n                    label: 'MoonRiverScan API',\n                    name: 'moonRiverScanApi',\n                    show: {\n                        'networks.network': [NETWORK.MOONRIVER]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.MOONRIVER]}/apis`\n                },\n                {\n                    label: 'MoonbeamScan API',\n                    name: 'moonBeamScanApi',\n                    show: {\n                        'networks.network': [NETWORK.MOONBEAM]\n                    },\n                    description: `Register for a Free API Key from: ${networkExplorers[NETWORK.MOONBEAM]}/apis`\n                }\n            ],\n            default: 'noAuth'\n        }\n    ]\n}\n\nexport const contract_details = {\n    ...network_details,\n    contractInfo: [\n        {\n            label: 'Contract Name',\n            name: 'name',\n            type: 'string',\n            description: 'Name the contract to make it easier to identify it in Outerbridge',\n            default: ''\n        },\n        {\n            label: 'Contract Address',\n            name: 'address',\n            type: 'string',\n            default: ''\n        },\n        {\n            label: 'ABI',\n            name: 'abi',\n            type: 'json',\n            default: '',\n            description: 'ABI will be fetched automatically if address is valid'\n        }\n    ]\n}\n\nexport const wallet_details = {\n    ...network_details,\n    walletInfo: [\n        {\n            label: 'Wallet Name',\n            name: 'name',\n            type: 'string',\n            description: 'Name the wallet to make it easier to identify it in Outerbridge',\n            default: ''\n        }\n    ]\n}\n\nexport const privateKeyField = [\n    {\n        label: 'Private Key',\n        name: 'privateKey',\n        type: 'string',\n        description: 'Private key of wallet to be imported',\n        default: ''\n    }\n]\n"
  },
  {
    "path": "packages/ui/src/store/context/ConfirmContext.js",
    "content": "import React from 'react'\n\nconst ConfirmContext = React.createContext()\n\nexport default ConfirmContext\n"
  },
  {
    "path": "packages/ui/src/store/context/ConfirmContextProvider.js",
    "content": "import { useReducer } from 'react'\nimport PropTypes from 'prop-types'\nimport alertReducer, { initialState } from '../reducers/dialogReducer'\nimport ConfirmContext from './ConfirmContext'\n\nconst ConfirmContextProvider = ({ children }) => {\n    const [state, dispatch] = useReducer(alertReducer, initialState)\n\n    return <ConfirmContext.Provider value={[state, dispatch]}>{children}</ConfirmContext.Provider>\n}\n\nConfirmContextProvider.propTypes = {\n    children: PropTypes.any\n}\n\nexport default ConfirmContextProvider\n"
  },
  {
    "path": "packages/ui/src/store/index.js",
    "content": "import { createStore } from 'redux'\nimport reducer from './reducer'\n\n// ==============================|| REDUX - MAIN STORE ||============================== //\n\nconst store = createStore(reducer)\nconst persister = 'Free'\n\nexport { store, persister }\n"
  },
  {
    "path": "packages/ui/src/store/reducer.js",
    "content": "import { combineReducers } from 'redux'\n\n// reducer import\nimport customizationReducer from './reducers/customizationReducer'\nimport canvasReducer from './reducers/canvasReducer'\nimport notifierReducer from './reducers/notifierReducer'\nimport dialogReducer from './reducers/dialogReducer'\n\n// ==============================|| COMBINE REDUCER ||============================== //\n\nconst reducer = combineReducers({\n    customization: customizationReducer,\n    canvas: canvasReducer,\n    notifier: notifierReducer,\n    dialog: dialogReducer\n})\n\nexport default reducer\n"
  },
  {
    "path": "packages/ui/src/store/reducers/canvasReducer.js",
    "content": "// action - state management\nimport * as actionTypes from '../actions'\n\nexport const initialState = {\n    removeEdgeId: '',\n    isDirty: false,\n    workflow: null\n}\n\n// ==============================|| CANVAS REDUCER ||============================== //\n\nconst canvasReducer = (state = initialState, action) => {\n    switch (action.type) {\n        case actionTypes.REMOVE_EDGE:\n            return {\n                ...state,\n                removeEdgeId: action.edgeId\n            }\n        case actionTypes.SET_DIRTY:\n            return {\n                ...state,\n                isDirty: true\n            }\n        case actionTypes.REMOVE_DIRTY:\n            return {\n                ...state,\n                isDirty: false\n            }\n        case actionTypes.SET_WORKFLOW:\n            return {\n                ...state,\n                workflow: action.workflow\n            }\n        default:\n            return state\n    }\n}\n\nexport default canvasReducer\n"
  },
  {
    "path": "packages/ui/src/store/reducers/customizationReducer.js",
    "content": "// project imports\nimport config from 'config'\n\n// action - state management\nimport * as actionTypes from '../actions'\n\nexport const initialState = {\n    isOpen: [], // for active default menu\n    fontFamily: config.fontFamily,\n    borderRadius: config.borderRadius,\n    opened: true,\n    isHorizontal: localStorage.getItem('isHorizontal') === 'true' ? true : false,\n    isDarkMode: localStorage.getItem('isDarkMode') === 'true' ? true : false\n}\n\n// ==============================|| CUSTOMIZATION REDUCER ||============================== //\n\nconst customizationReducer = (state = initialState, action) => {\n    let id\n    switch (action.type) {\n        case actionTypes.MENU_OPEN:\n            id = action.id\n            return {\n                ...state,\n                isOpen: [id]\n            }\n        case actionTypes.SET_MENU:\n            return {\n                ...state,\n                opened: action.opened\n            }\n        case actionTypes.SET_FONT_FAMILY:\n            return {\n                ...state,\n                fontFamily: action.fontFamily\n            }\n        case actionTypes.SET_BORDER_RADIUS:\n            return {\n                ...state,\n                borderRadius: action.borderRadius\n            }\n        case actionTypes.SET_LAYOUT:\n            return {\n                ...state,\n                isHorizontal: action.isHorizontal\n            }\n        case actionTypes.SET_DARKMODE:\n            return {\n                ...state,\n                isDarkMode: action.isDarkMode\n            }\n        default:\n            return state\n    }\n}\n\nexport default customizationReducer\n"
  },
  {
    "path": "packages/ui/src/store/reducers/dialogReducer.js",
    "content": "import { SHOW_CONFIRM, HIDE_CONFIRM } from '../actions'\n\nexport const initialState = {\n    show: false,\n    title: '',\n    description: '',\n    confirmButtonName: 'OK',\n    cancelButtonName: 'Cancel'\n}\n\nconst alertReducer = (state = initialState, action) => {\n    switch (action.type) {\n        case SHOW_CONFIRM:\n            return {\n                show: true,\n                title: action.payload.title,\n                description: action.payload.description,\n                confirmButtonName: action.payload.confirmButtonName,\n                cancelButtonName: action.payload.cancelButtonName\n            }\n        case HIDE_CONFIRM:\n            return initialState\n        default:\n            return state\n    }\n}\n\nexport default alertReducer\n"
  },
  {
    "path": "packages/ui/src/store/reducers/notifierReducer.js",
    "content": "import { ENQUEUE_SNACKBAR, CLOSE_SNACKBAR, REMOVE_SNACKBAR } from '../actions'\n\nexport const initialState = {\n    notifications: []\n}\n\nconst notifierReducer = (state = initialState, action) => {\n    switch (action.type) {\n        case ENQUEUE_SNACKBAR:\n            return {\n                ...state,\n                notifications: [\n                    ...state.notifications,\n                    {\n                        key: action.key,\n                        ...action.notification\n                    }\n                ]\n            }\n\n        case CLOSE_SNACKBAR:\n            return {\n                ...state,\n                notifications: state.notifications.map((notification) =>\n                    action.dismissAll || notification.key === action.key ? { ...notification, dismissed: true } : { ...notification }\n                )\n            }\n\n        case REMOVE_SNACKBAR:\n            return {\n                ...state,\n                notifications: state.notifications.filter((notification) => notification.key !== action.key)\n            }\n\n        default:\n            return state\n    }\n}\n\nexport default notifierReducer\n"
  },
  {
    "path": "packages/ui/src/themes/compStyleOverride.js",
    "content": "export default function componentStyleOverrides(theme) {\n    const bgColor = theme.colors?.grey50\n    return {\n        MuiButton: {\n            styleOverrides: {\n                root: {\n                    fontWeight: 500,\n                    borderRadius: '4px'\n                }\n            }\n        },\n        MuiSvgIcon: {\n            styleOverrides: {\n                root: {\n                    color: theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit',\n                    background: theme?.customization?.isDarkMode ? theme.colors?.darkPrimaryLight : 'inherit'\n                }\n            }\n        },\n        MuiPaper: {\n            defaultProps: {\n                elevation: 0\n            },\n            styleOverrides: {\n                root: {\n                    backgroundImage: 'none'\n                },\n                rounded: {\n                    borderRadius: `${theme?.customization?.borderRadius}px`\n                }\n            }\n        },\n        MuiCardHeader: {\n            styleOverrides: {\n                root: {\n                    color: theme.colors?.textDark,\n                    padding: '24px'\n                },\n                title: {\n                    fontSize: '1.125rem'\n                }\n            }\n        },\n        MuiCardContent: {\n            styleOverrides: {\n                root: {\n                    padding: '24px'\n                }\n            }\n        },\n        MuiCardActions: {\n            styleOverrides: {\n                root: {\n                    padding: '24px'\n                }\n            }\n        },\n        MuiListItemButton: {\n            styleOverrides: {\n                root: {\n                    color: theme.darkTextPrimary,\n                    paddingTop: '10px',\n                    paddingBottom: '10px',\n                    '&.Mui-selected': {\n                        color: theme.menuSelected,\n                        backgroundColor: theme.menuSelectedBack,\n                        '&:hover': {\n                            backgroundColor: theme.menuSelectedBack\n                        },\n                        '& .MuiListItemIcon-root': {\n                            color: theme.menuSelected\n                        }\n                    },\n                    '&:hover': {\n                        backgroundColor: theme.menuSelectedBack,\n                        color: theme.menuSelected,\n                        '& .MuiListItemIcon-root': {\n                            color: theme.menuSelected\n                        }\n                    }\n                }\n            }\n        },\n        MuiListItemIcon: {\n            styleOverrides: {\n                root: {\n                    color: theme.darkTextPrimary,\n                    minWidth: '36px'\n                }\n            }\n        },\n        MuiListItemText: {\n            styleOverrides: {\n                primary: {\n                    color: theme.textDark\n                }\n            }\n        },\n        MuiInputBase: {\n            styleOverrides: {\n                input: {\n                    color: theme.textDark,\n                    '&::placeholder': {\n                        color: theme.darkTextSecondary,\n                        fontSize: '0.875rem'\n                    }\n                }\n            }\n        },\n        MuiOutlinedInput: {\n            styleOverrides: {\n                root: {\n                    background: theme?.customization?.isDarkMode ? theme.colors?.darkPrimary800 : bgColor,\n                    borderRadius: `${theme?.customization?.borderRadius}px`,\n                    '& .MuiOutlinedInput-notchedOutline': {\n                        borderColor: theme.colors?.grey400\n                    },\n                    '&:hover $notchedOutline': {\n                        borderColor: theme.colors?.primaryLight\n                    },\n                    '&.MuiInputBase-multiline': {\n                        padding: 1\n                    }\n                },\n                input: {\n                    fontWeight: 500,\n                    background: theme?.customization?.isDarkMode ? theme.colors?.darkPrimary800 : bgColor,\n                    padding: '15.5px 14px',\n                    borderRadius: `${theme?.customization?.borderRadius}px`,\n                    '&.MuiInputBase-inputSizeSmall': {\n                        padding: '10px 14px',\n                        '&.MuiInputBase-inputAdornedStart': {\n                            paddingLeft: 0\n                        }\n                    }\n                },\n                inputAdornedStart: {\n                    paddingLeft: 4\n                },\n                notchedOutline: {\n                    borderRadius: `${theme?.customization?.borderRadius}px`\n                }\n            }\n        },\n        MuiSlider: {\n            styleOverrides: {\n                root: {\n                    '&.Mui-disabled': {\n                        color: theme.colors?.grey300\n                    }\n                },\n                mark: {\n                    backgroundColor: theme.paper,\n                    width: '4px'\n                },\n                valueLabel: {\n                    color: theme?.colors?.primaryLight\n                }\n            }\n        },\n        MuiDivider: {\n            styleOverrides: {\n                root: {\n                    borderColor: theme.divider,\n                    opacity: 1\n                }\n            }\n        },\n        MuiAvatar: {\n            styleOverrides: {\n                root: {\n                    color: theme.colors?.primaryDark,\n                    background: theme.colors?.primary200\n                }\n            }\n        },\n        MuiChip: {\n            styleOverrides: {\n                root: {\n                    '&.MuiChip-deletable .MuiChip-deleteIcon': {\n                        color: 'inherit'\n                    }\n                }\n            }\n        },\n        MuiTooltip: {\n            styleOverrides: {\n                tooltip: {\n                    color: theme?.customization?.isDarkMode ? theme.colors?.paper : theme.paper,\n                    background: theme.colors?.grey700\n                }\n            }\n        },\n        MuiAutocomplete: {\n            styleOverrides: {\n                option: {\n                    '&:hover': {\n                        background: theme?.customization?.isDarkMode ? '#233345 !important' : ''\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/themes/index.js",
    "content": "import { createTheme } from '@mui/material/styles'\n\n// assets\nimport colors from 'assets/scss/_themes-vars.module.scss'\n\n// project imports\nimport componentStyleOverrides from './compStyleOverride'\nimport themePalette from './palette'\nimport themeTypography from './typography'\n\n/**\n * Represent theme style and structure as per Material-UI\n * @param {JsonObject} customization customization parameter object\n */\n\nexport const theme = (customization) => {\n    const color = colors\n\n    const themeOption = customization.isDarkMode\n        ? {\n              colors: color,\n              heading: color.paper,\n              paper: color.darkPrimaryLight,\n              backgroundDefault: color.darkPaper,\n              background: color.darkPrimaryLight,\n              darkTextPrimary: color.paper,\n              darkTextSecondary: color.paper,\n              textDark: color.paper,\n              menuSelected: color.darkSecondaryDark,\n              menuSelectedBack: color.darkSecondaryLight,\n              divider: color.darkPaper,\n              customization\n          }\n        : {\n              colors: color,\n              heading: color.grey900,\n              paper: color.paper,\n              backgroundDefault: color.paper,\n              background: color.primaryLight,\n              darkTextPrimary: color.grey700,\n              darkTextSecondary: color.grey500,\n              textDark: color.grey900,\n              menuSelected: color.secondaryDark,\n              menuSelectedBack: color.secondaryLight,\n              divider: color.grey200,\n              customization\n          }\n\n    const themeOptions = {\n        direction: 'ltr',\n        palette: themePalette(themeOption),\n        mixins: {\n            toolbar: {\n                minHeight: '48px',\n                padding: '16px',\n                '@media (min-width: 600px)': {\n                    minHeight: '48px'\n                }\n            }\n        },\n        typography: themeTypography(themeOption)\n    }\n\n    const themes = createTheme(themeOptions)\n    themes.components = componentStyleOverrides(themeOption)\n\n    return themes\n}\n\nexport default theme\n"
  },
  {
    "path": "packages/ui/src/themes/palette.js",
    "content": "/**\n * Color intention that you want to used in your theme\n * @param {JsonObject} theme Theme customization object\n */\n\nexport default function themePalette(theme) {\n    return {\n        mode: theme?.customization?.navType,\n        common: {\n            black: theme.colors?.darkPaper\n        },\n        primary: {\n            light: theme.customization.isDarkMode ? theme.colors?.darkPrimaryLight : theme.colors?.primaryLight,\n            main: theme.colors?.primaryMain,\n            dark: theme.customization.isDarkMode ? theme.colors?.darkPrimaryDark : theme.colors?.primaryDark,\n            200: theme.customization.isDarkMode ? theme.colors?.darkPrimary200 : theme.colors?.primary200,\n            800: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.primary800\n        },\n        secondary: {\n            light: theme.customization.isDarkMode ? theme.colors?.darkSecondaryLight : theme.colors?.secondaryLight,\n            main: theme.customization.isDarkMode ? theme.colors?.darkSecondaryMain : theme.colors?.secondaryMain,\n            dark: theme.customization.isDarkMode ? theme.colors?.darkSecondaryDark : theme.colors?.secondaryDark,\n            200: theme.colors?.secondary200,\n            800: theme.colors?.secondary800\n        },\n        error: {\n            light: theme.colors?.errorLight,\n            main: theme.colors?.errorMain,\n            dark: theme.colors?.errorDark\n        },\n        orange: {\n            light: theme.colors?.orangeLight,\n            main: theme.colors?.orangeMain,\n            dark: theme.colors?.orangeDark\n        },\n        warning: {\n            light: theme.colors?.warningLight,\n            main: theme.colors?.warningMain,\n            dark: theme.colors?.warningDark\n        },\n        success: {\n            light: theme.colors?.successLight,\n            200: theme.colors?.success200,\n            main: theme.colors?.successMain,\n            dark: theme.colors?.successDark\n        },\n        grey: {\n            50: theme.colors?.grey50,\n            100: theme.colors?.grey100,\n            200: theme.colors?.grey200,\n            300: theme.colors?.grey300,\n            500: theme.darkTextSecondary,\n            600: theme.heading,\n            700: theme.darkTextPrimary,\n            900: theme.textDark\n        },\n        dark: {\n            light: theme.colors?.darkTextPrimary,\n            main: theme.colors?.darkLevel1,\n            dark: theme.colors?.darkLevel2,\n            800: theme.colors?.darkBackground,\n            900: theme.colors?.darkPaper\n        },\n        text: {\n            primary: theme.darkTextPrimary,\n            secondary: theme.darkTextSecondary,\n            dark: theme.textDark,\n            hint: theme.colors?.grey100\n        },\n        background: {\n            paper: theme.paper,\n            default: theme.backgroundDefault\n        },\n        card: {\n            main: theme.customization.isDarkMode ? theme.colors?.darkPrimaryMain : theme.colors?.paper,\n            light: theme.customization.isDarkMode ? theme.colors?.darkPrimary200 : theme.colors?.paper,\n            hover: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.paper\n        },\n        asyncSelect: {\n            main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.grey50\n        },\n        canvasHeader: {\n            executionLight: theme.colors?.successLight,\n            executionDark: theme.colors?.successDark,\n            deployLight: theme.colors?.primaryLight,\n            deployDark: theme.colors?.primaryDark,\n            saveLight: theme.colors?.secondaryLight,\n            saveDark: theme.colors?.secondaryDark,\n            settingsLight: theme.colors?.grey300,\n            settingsDark: theme.colors?.grey700\n        },\n        codeEditor: {\n            main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.primaryLight\n        }\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/themes/typography.js",
    "content": "/**\n * Typography used in theme\n * @param {JsonObject} theme theme customization object\n */\n\nexport default function themeTypography(theme) {\n    return {\n        fontFamily: theme?.customization?.fontFamily,\n        h6: {\n            fontWeight: 500,\n            color: theme.heading,\n            fontSize: '0.75rem'\n        },\n        h5: {\n            fontSize: '0.875rem',\n            color: theme.heading,\n            fontWeight: 500\n        },\n        h4: {\n            fontSize: '1rem',\n            color: theme.heading,\n            fontWeight: 600\n        },\n        h3: {\n            fontSize: '1.25rem',\n            color: theme.heading,\n            fontWeight: 600\n        },\n        h2: {\n            fontSize: '1.5rem',\n            color: theme.heading,\n            fontWeight: 700\n        },\n        h1: {\n            fontSize: '2.125rem',\n            color: theme.heading,\n            fontWeight: 700\n        },\n        subtitle1: {\n            fontSize: '0.875rem',\n            fontWeight: 500,\n            color: theme.textDark\n        },\n        subtitle2: {\n            fontSize: '0.75rem',\n            fontWeight: 400,\n            color: theme.darkTextSecondary\n        },\n        caption: {\n            fontSize: '0.75rem',\n            color: theme.darkTextSecondary,\n            fontWeight: 400\n        },\n        body1: {\n            fontSize: '0.875rem',\n            fontWeight: 400,\n            lineHeight: '1.334em'\n        },\n        body2: {\n            letterSpacing: '0em',\n            fontWeight: 400,\n            lineHeight: '1.5em',\n            color: theme.darkTextPrimary\n        },\n        button: {\n            textTransform: 'capitalize'\n        },\n        customInput: {\n            marginTop: 1,\n            marginBottom: 1,\n            '& > label': {\n                top: 23,\n                left: 0,\n                color: theme.grey500,\n                '&[data-shrink=\"false\"]': {\n                    top: 5\n                }\n            },\n            '& > div > input': {\n                padding: '30.5px 14px 11.5px !important'\n            },\n            '& legend': {\n                display: 'none'\n            },\n            '& fieldset': {\n                top: 0\n            }\n        },\n        mainContent: {\n            backgroundColor: theme.background,\n            width: '100%',\n            minHeight: 'calc(100vh - 75px)',\n            flexGrow: 1,\n            padding: '20px',\n            marginTop: '75px',\n            marginRight: '20px',\n            borderRadius: `${theme?.customization?.borderRadius}px`\n        },\n        menuCaption: {\n            fontSize: '0.875rem',\n            fontWeight: 500,\n            color: theme.heading,\n            padding: '6px',\n            textTransform: 'capitalize',\n            marginTop: '10px'\n        },\n        subMenuCaption: {\n            fontSize: '0.6875rem',\n            fontWeight: 500,\n            color: theme.darkTextSecondary,\n            textTransform: 'capitalize'\n        },\n        commonAvatar: {\n            cursor: 'pointer',\n            borderRadius: '8px'\n        },\n        smallAvatar: {\n            width: '22px',\n            height: '22px',\n            fontSize: '1rem'\n        },\n        mediumAvatar: {\n            width: '34px',\n            height: '34px',\n            fontSize: '1.2rem'\n        },\n        largeAvatar: {\n            width: '44px',\n            height: '44px',\n            fontSize: '1.5rem'\n        }\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/ui-component/Loadable.js",
    "content": "import { Suspense } from 'react'\n\n// project imports\nimport Loader from './Loader'\n\n// ==============================|| LOADABLE - LAZY LOADING ||============================== //\n\nconst Loadable = (Component) =>\n    function WithLoader(props) {\n        return (\n            <Suspense fallback={<Loader />}>\n                <Component {...props} />\n            </Suspense>\n        )\n    }\n\nexport default Loadable\n"
  },
  {
    "path": "packages/ui/src/ui-component/Loader.js",
    "content": "// material-ui\nimport LinearProgress from '@mui/material/LinearProgress'\nimport { styled } from '@mui/material/styles'\n\n// styles\nconst LoaderWrapper = styled('div')({\n    position: 'fixed',\n    top: 0,\n    left: 0,\n    zIndex: 1301,\n    width: '100%'\n})\n\n// ==============================|| LOADER ||============================== //\nconst Loader = () => (\n    <LoaderWrapper>\n        <LinearProgress color='primary' />\n    </LoaderWrapper>\n)\n\nexport default Loader\n"
  },
  {
    "path": "packages/ui/src/ui-component/Logo.js",
    "content": "import logo from 'assets/images/outerbridge_brand.png'\nimport logoDark from 'assets/images/outerbridge_brand_white.png'\n\nimport { useSelector } from 'react-redux'\n\n// ==============================|| LOGO ||============================== //\n\nconst Logo = () => {\n    const customization = useSelector((state) => state.customization)\n\n    return (\n        <div style={{ alignItems: 'center', display: 'flex', flexDirection: 'row' }}>\n            <img\n                style={{ objectFit: 'contain', height: 'auto', width: 150 }}\n                src={customization.isDarkMode ? logoDark : logo}\n                alt='Outerbridge'\n            />\n        </div>\n    )\n}\n\nexport default Logo\n"
  },
  {
    "path": "packages/ui/src/ui-component/StyledButton.js",
    "content": "import { styled } from '@mui/material/styles'\nimport { Button } from '@mui/material'\n\nexport const StyledButton = styled(Button)(({ theme, color = 'primary' }) => ({\n    color: 'white',\n    backgroundColor: theme.palette[color].main,\n    '&:hover': {\n        backgroundColor: theme.palette[color].main,\n        backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`\n    }\n}))\n"
  },
  {
    "path": "packages/ui/src/ui-component/StyledFab.js",
    "content": "import { styled } from '@mui/material/styles'\nimport { Fab } from '@mui/material'\n\nexport const StyledFab = styled(Fab)(({ theme, color = 'primary' }) => ({\n    color: 'white',\n    backgroundColor: theme.palette[color].main,\n    '&:hover': {\n        backgroundColor: theme.palette[color].main,\n        backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`\n    }\n}))\n"
  },
  {
    "path": "packages/ui/src/ui-component/TooltipWithParser.js",
    "content": "import { Info } from '@mui/icons-material'\nimport { IconButton, Tooltip } from '@mui/material'\nimport parser from 'html-react-parser'\nimport PropTypes from 'prop-types'\nimport { useSelector } from 'react-redux'\n\nexport const TooltipWithParser = ({ title }) => {\n    const customization = useSelector((state) => state.customization)\n\n    return (\n        <Tooltip title={parser(title)} placement='right'>\n            <div style={{ display: 'flex', alignItems: 'center' }}>\n                <IconButton sx={{ height: 25, width: 25 }}>\n                    <Info\n                        style={{ background: 'transparent', color: customization.isDarkMode ? 'white' : 'inherit', height: 18, width: 18 }}\n                    />\n                </IconButton>\n            </div>\n        </Tooltip>\n    )\n}\n\nTooltipWithParser.propTypes = {\n    title: PropTypes.node\n}\n"
  },
  {
    "path": "packages/ui/src/ui-component/cards/ItemCard.js",
    "content": "import PropTypes from 'prop-types'\n\n// material-ui\nimport { styled, useTheme } from '@mui/material/styles'\nimport { Box, Grid, Chip, Typography } from '@mui/material'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport SkeletonWorkflowCard from 'ui-component/cards/Skeleton/WorkflowCard'\n\n// Const\nimport { networks } from 'store/constant'\n\nimport Jazzicon, { jsNumberForAddress } from 'react-jazzicon'\n\nconst CardWrapper = styled(MainCard)(({ theme }) => ({\n    background: theme.palette.card.main,\n    color: theme.darkTextPrimary,\n    overflow: 'hidden',\n    position: 'relative',\n    boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',\n    cursor: 'pointer',\n    '&:hover': {\n        background: theme.palette.card.hover,\n        boxShadow: '0 2px 14px 0 rgb(32 40 45 / 20%)'\n    }\n}))\n\n// ===========================|| CONTRACT CARD ||=========================== //\n\nconst ItemCard = ({ isLoading, data, images, onClick }) => {\n    const theme = useTheme()\n\n    const chipSX = {\n        height: 24,\n        padding: '0 6px'\n    }\n\n    const activeWorkflowSX = {\n        ...chipSX,\n        color: 'white',\n        backgroundColor: theme.palette.success.dark\n    }\n\n    const getNetworkItem = (network) => {\n        return networks.find((ntw) => ntw.name === network)\n    }\n\n    return (\n        <>\n            {isLoading ? (\n                <SkeletonWorkflowCard />\n            ) : (\n                <CardWrapper border={false} content={false} onClick={onClick}>\n                    <Box sx={{ p: 2.25 }}>\n                        <Grid container direction='column'>\n                            <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>\n                                {data.address && (\n                                    <div style={{ width: 40, height: 40, borderRadius: '50%', backgroundColor: 'white', marginRight: 10 }}>\n                                        <Jazzicon diameter={40} seed={jsNumberForAddress(data.address)} />\n                                    </div>\n                                )}\n                                <Typography sx={{ fontSize: '1.5rem', fontWeight: 500 }}>{data.name}</Typography>\n                            </div>\n                            <Grid sx={{ mt: 1, mb: 1 }} container direction='row'>\n                                <Grid item sx={{ flexGrow: 1 }}>\n                                    {data.address && (\n                                        <Typography\n                                            sx={{\n                                                fontSize: '1rem',\n                                                fontWeight: 500,\n                                                color: theme.palette.secondary[200],\n                                                overflow: 'hidden',\n                                                whiteSpace: 'nowrap',\n                                                textOverflow: 'ellipsis',\n                                                maxWidth: 250\n                                            }}\n                                        >\n                                            {`${data.address.substring(0, 8)}...${data.address.slice(-4)}`}\n                                        </Typography>\n                                    )}\n                                    {data.flowData && (\n                                        <Typography\n                                            sx={{\n                                                fontSize: '1rem',\n                                                fontWeight: 500,\n                                                color: theme.palette.secondary[200]\n                                            }}\n                                        >\n                                            Total Executions: {data.executionCount || '0'}\n                                        </Typography>\n                                    )}\n                                </Grid>\n                                {data.deployed && (\n                                    <Grid item>\n                                        <Chip label='Deployed' sx={activeWorkflowSX} />\n                                    </Grid>\n                                )}\n                            </Grid>\n                            {data.network && (\n                                <Grid item>\n                                    <Chip\n                                        label={getNetworkItem(data.network)?.label || `${data.network} (DEPRECATED)`}\n                                        sx={{\n                                            ...chipSX,\n                                            backgroundColor: getNetworkItem(data.network)?.color || '#666666',\n                                            color: 'white'\n                                        }}\n                                    />\n                                </Grid>\n                            )}\n                            {images && (\n                                <div style={{ display: 'flex', flexDirection: 'row', marginTop: 10 }}>\n                                    {images.map((img) => (\n                                        <div\n                                            key={img}\n                                            style={{\n                                                width: 40,\n                                                height: 40,\n                                                marginRight: 5,\n                                                borderRadius: '50%',\n                                                backgroundColor: 'white'\n                                            }}\n                                        >\n                                            <img\n                                                style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}\n                                                alt=''\n                                                src={img}\n                                            />\n                                        </div>\n                                    ))}\n                                </div>\n                            )}\n                        </Grid>\n                    </Box>\n                </CardWrapper>\n            )}\n        </>\n    )\n}\n\nItemCard.propTypes = {\n    isLoading: PropTypes.bool,\n    data: PropTypes.object,\n    images: PropTypes.array,\n    onClick: PropTypes.func\n}\n\nexport default ItemCard\n"
  },
  {
    "path": "packages/ui/src/ui-component/cards/MainCard.js",
    "content": "import PropTypes from 'prop-types'\nimport { forwardRef } from 'react'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material'\n\n// constant\nconst headerSX = {\n    '& .MuiCardHeader-action': { mr: 0 }\n}\n\n// ==============================|| CUSTOM MAIN CARD ||============================== //\n\nconst MainCard = forwardRef(function MainCard(\n    {\n        border = true,\n        boxShadow,\n        children,\n        content = true,\n        contentClass = '',\n        contentSX = {},\n        darkTitle,\n        secondary,\n        shadow,\n        sx = {},\n        title,\n        ...others\n    },\n    ref\n) {\n    const theme = useTheme()\n\n    return (\n        <Card\n            ref={ref}\n            {...others}\n            sx={{\n                border: border ? '1px solid' : 'none',\n                borderColor: theme.palette.primary[200] + 75,\n                ':hover': {\n                    boxShadow: boxShadow ? shadow || '0 2px 14px 0 rgb(32 40 45 / 8%)' : 'inherit'\n                },\n                ...sx\n            }}\n        >\n            {/* card header and action */}\n            {!darkTitle && title && <CardHeader sx={headerSX} title={title} action={secondary} />}\n            {darkTitle && title && <CardHeader sx={headerSX} title={<Typography variant='h3'>{title}</Typography>} action={secondary} />}\n\n            {/* content & header divider */}\n            {title && <Divider />}\n\n            {/* card content */}\n            {content && (\n                <CardContent sx={contentSX} className={contentClass}>\n                    {children}\n                </CardContent>\n            )}\n            {!content && children}\n        </Card>\n    )\n})\n\nMainCard.propTypes = {\n    border: PropTypes.bool,\n    boxShadow: PropTypes.bool,\n    children: PropTypes.node,\n    content: PropTypes.bool,\n    contentClass: PropTypes.string,\n    contentSX: PropTypes.object,\n    darkTitle: PropTypes.bool,\n    secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),\n    shadow: PropTypes.string,\n    sx: PropTypes.object,\n    title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])\n}\n\nexport default MainCard\n"
  },
  {
    "path": "packages/ui/src/ui-component/cards/Skeleton/WorkflowCard.js",
    "content": "// material-ui\nimport { Card, CardContent, Grid } from '@mui/material'\nimport Skeleton from '@mui/material/Skeleton'\n\n// ==============================|| SKELETON - BRIDGE CARD ||============================== //\n\nconst WorkflowCard = () => (\n    <Card>\n        <CardContent>\n            <Grid container direction='column'>\n                <Grid item>\n                    <Grid container justifyContent='space-between'>\n                        <Grid item>\n                            <Skeleton variant='rectangular' width={44} height={44} />\n                        </Grid>\n                        <Grid item>\n                            <Skeleton variant='rectangular' width={34} height={34} />\n                        </Grid>\n                    </Grid>\n                </Grid>\n                <Grid item>\n                    <Skeleton variant='rectangular' sx={{ my: 2 }} height={40} />\n                </Grid>\n                <Grid item>\n                    <Skeleton variant='rectangular' height={30} />\n                </Grid>\n            </Grid>\n        </CardContent>\n    </Card>\n)\n\nexport default WorkflowCard\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/AttachmentDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport { useState } from 'react'\nimport PropTypes from 'prop-types'\n\nimport { Dialog, DialogContent, DialogTitle, Tabs, Tab, Box, Typography } from '@mui/material'\n\nfunction TabPanel(props) {\n    const { children, value, index, ...other } = props\n    return (\n        <div\n            role='tabpanel'\n            hidden={value !== index}\n            id={`attachment-tabpanel-${index}`}\n            aria-labelledby={`attachment-tab-${index}`}\n            {...other}\n        >\n            {value === index && <Box sx={{ p: 1 }}>{children}</Box>}\n        </div>\n    )\n}\n\nTabPanel.propTypes = {\n    children: PropTypes.node,\n    index: PropTypes.number.isRequired,\n    value: PropTypes.number.isRequired\n}\n\nfunction a11yProps(index) {\n    return {\n        id: `attachment-tab-${index}`,\n        'aria-controls': `attachment-tabpanel-${index}`\n    }\n}\n\nconst AttachmentDialog = ({ show, dialogProps, onCancel }) => {\n    const portalElement = document.getElementById('portal')\n\n    const [value, setValue] = useState(0)\n\n    const handleChange = (event, newValue) => {\n        setValue(newValue)\n    }\n\n    const formatBytes = (bytes, decimals = 2) => {\n        if (bytes === 0) return '0 Bytes'\n\n        const k = 1024\n        const dm = decimals < 0 ? 0 : decimals\n        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']\n\n        const i = Math.floor(Math.log(bytes) / Math.log(k))\n\n        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]\n    }\n\n    const component = show ? (\n        <Dialog\n            open={show}\n            onClose={onCancel}\n            fullWidth\n            maxWidth='lg'\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                <Tabs value={value} onChange={handleChange} aria-label='attachment tabs'>\n                    {dialogProps.executionData.map((execObj, execObjIndex) => (\n                        <Tab key={execObjIndex} label={`Item ${execObjIndex}`} {...a11yProps(execObjIndex)} />\n                    ))}\n                </Tabs>\n                {dialogProps.executionData.map((execObj, execObjIndex) => (\n                    <TabPanel key={execObjIndex} value={value} index={execObjIndex}>\n                        {execObj.attachments &&\n                            execObj.attachments.map((attachment, attchIndex) => (\n                                <div key={attchIndex} style={{ marginBottom: 10 }}>\n                                    <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>\n                                        <Typography sx={{ p: 1 }} variant='h5'>\n                                            {attachment.filename ? attachment.filename : `Attachment ${attchIndex}`} |{' '}\n                                            {attachment.contentType} {attachment.size ? ` | ${formatBytes(attachment.size)}` : ''}\n                                        </Typography>\n                                        <a href={attachment.content} download rel='noopener noreferrer' target='_blank'>\n                                            Download File\n                                        </a>\n                                    </div>\n                                    <embed\n                                        src={attachment.content}\n                                        width='100%'\n                                        height='100%'\n                                        type={attachment.contentType}\n                                        style={{ borderStyle: 'solid', minHeight: '100vh', minWidth: '100vh' }}\n                                    />\n                                </div>\n                            ))}\n                        {!execObj.attachments && <div>No Attachment</div>}\n                    </TabPanel>\n                ))}\n            </DialogContent>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nAttachmentDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func\n}\n\nexport default AttachmentDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/ConfirmDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'\nimport useConfirm from 'hooks/useConfirm'\nimport { StyledButton } from 'ui-component/StyledButton'\n\nconst ConfirmDialog = () => {\n    const { onConfirm, onCancel, confirmState } = useConfirm()\n    const portalElement = document.getElementById('portal')\n\n    const component = confirmState.show ? (\n        <Dialog\n            fullWidth\n            maxWidth='xs'\n            open={confirmState.show}\n            onClose={onCancel}\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {confirmState.title}\n            </DialogTitle>\n            <DialogContent>\n                <DialogContentText sx={{ color: 'black' }} id='alert-dialog-description'>\n                    {confirmState.description}\n                </DialogContentText>\n            </DialogContent>\n            <DialogActions>\n                <Button onClick={onCancel}>{confirmState.cancelButtonName}</Button>\n                <StyledButton variant='contained' onClick={onConfirm}>\n                    {confirmState.confirmButtonName}\n                </StyledButton>\n            </DialogActions>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nexport default ConfirmDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/EditVariableDialog.css",
    "content": ".editor__textarea {\n    outline: 0;\n}\n.editor__textarea::placeholder {\n    color: rgba(120, 120, 120, 0.5);\n}"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/EditVariableDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport { useState, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\nimport {\n    Button,\n    Dialog,\n    DialogActions,\n    DialogContent,\n    IconButton,\n    Box,\n    List,\n    Accordion,\n    AccordionSummary,\n    Typography,\n    AccordionDetails\n} from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport ReactJson from 'react-json-view'\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport { IconArrowsMaximize } from '@tabler/icons'\nimport ExpandDataDialog from './ExpandDataDialog'\nimport { StyledButton } from 'ui-component/StyledButton'\nimport { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'\nimport { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'\n\nimport './EditVariableDialog.css'\n\nconst isPositiveNumeric = (value) => /^\\d+$/.test(value)\n\nconst EditVariableDialog = ({ show, dialogProps, onCancel, onConfirm }) => {\n    const portalElement = document.getElementById('portal')\n\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [inputValue, setInputValue] = useState('')\n    const [input, setInput] = useState(null)\n    const [expanded, setExpanded] = useState(false)\n    const [showExpandDialog, setShowExpandDialog] = useState(false)\n    const [expandDialogProps, setExpandDialogProps] = useState({})\n    const [copiedVariableBody, setCopiedVariableBody] = useState({})\n    const [languageType, setLanguageType] = useState('')\n\n    const handleAccordionChange = (nodeLabel) => (event, isExpanded) => {\n        setExpanded(isExpanded ? nodeLabel : false)\n    }\n\n    const onExpandDialogClicked = (data, node) => {\n        const dialogProp = {\n            title: `Variable Data: ${node.data.label}`,\n            data,\n            node\n        }\n        setExpandDialogProps(dialogProp)\n        setShowExpandDialog(true)\n    }\n\n    const onMouseUp = (e) => {\n        if (e.target && e.target.selectionEnd && e.target.value) {\n            const cursorPosition = e.target.selectionEnd\n            const textBeforeCursorPosition = e.target.value.substring(0, cursorPosition)\n            const textAfterCursorPosition = e.target.value.substring(cursorPosition, e.target.value.length)\n            const body = {\n                textBeforeCursorPosition,\n                textAfterCursorPosition\n            }\n            setCopiedVariableBody(body)\n        } else {\n            setCopiedVariableBody({})\n        }\n    }\n\n    const onClipboardCopy = (e, node) => {\n        const namespaces = e.namespace\n        let returnVariablePath = `${node.id}`\n        for (let i = 0; i < namespaces.length; i += 1) {\n            const namespace = namespaces[i]\n            if (namespace !== 'root') {\n                if (isPositiveNumeric(namespace)) {\n                    if (returnVariablePath.endsWith('.')) {\n                        returnVariablePath = returnVariablePath.substring(0, returnVariablePath.length - 1)\n                    }\n                    returnVariablePath += `[${namespace}]`\n                } else {\n                    returnVariablePath += namespace\n                }\n                if (i !== namespaces.length - 1) {\n                    returnVariablePath += '.'\n                }\n            }\n        }\n        if (copiedVariableBody) {\n            let newInput = ''\n            if (copiedVariableBody.textBeforeCursorPosition === undefined && copiedVariableBody.textAfterCursorPosition === undefined)\n                newInput = `${inputValue}${`{{${returnVariablePath}}}`}`\n            else\n                newInput = `${copiedVariableBody.textBeforeCursorPosition}{{${returnVariablePath}}}${copiedVariableBody.textAfterCursorPosition}`\n            setInputValue(newInput)\n        }\n    }\n\n    const onSave = (value) => {\n        // ArrayInputParameter\n        if (dialogProps.arrayItemBody) {\n            const updateArrayValues = {\n                ...dialogProps.arrayItemBody.arrayItemValues,\n                [dialogProps.arrayItemBody.arrayItemInput.name]: value\n            }\n            const updateInitialValues = dialogProps.arrayItemBody.initialValues\n            updateInitialValues[dialogProps.arrayItemBody.arrayItemIndex] = updateArrayValues\n            const updateValues = {\n                ...dialogProps.values,\n                [dialogProps.input.name]: updateInitialValues\n            }\n            onConfirm(updateValues)\n        } else {\n            // InputParameter\n            const updateValues = {\n                ...dialogProps.values,\n                [dialogProps.input.name]: value,\n                submit: null\n            }\n            onConfirm(updateValues)\n        }\n    }\n\n    // Handle Accordian\n    useEffect(() => {\n        if (dialogProps.values && dialogProps.input) {\n            let inputValues = dialogProps.values\n            let input = dialogProps.input\n            if (dialogProps.arrayItemBody) {\n                inputValues = dialogProps.arrayItemBody.arrayItemValues\n                input = dialogProps.arrayItemBody.arrayItemInput\n            }\n            setInput(input)\n            setInputValue(inputValues[input.name].toString() || '')\n            if (input.type === 'json' || input.type === 'string' || input.type === 'number') setLanguageType('json')\n            if (input.type === 'code') setLanguageType('js')\n        }\n    }, [dialogProps])\n\n    const component = show ? (\n        <Dialog open={show} fullWidth maxWidth='lg' aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>\n            <DialogContent>\n                <div style={{ display: 'flex', flexDirection: 'row' }}>\n                    {input && (input.type === 'json' || input.type === 'string' || input.type === 'number' || input.type === 'code') && (\n                        <div style={{ flex: 1 }}>\n                            <Typography sx={{ mb: 2, ml: 1 }} variant='h4'>\n                                Input\n                            </Typography>\n                            <PerfectScrollbar\n                                style={{\n                                    border: '1px solid',\n                                    borderColor: theme.palette.grey['500'],\n                                    borderRadius: '12px',\n                                    height: '100%',\n                                    maxHeight: 'calc(100vh - 220px)',\n                                    overflowX: 'hidden',\n                                    backgroundColor: 'white'\n                                }}\n                            >\n                                {customization.isDarkMode ? (\n                                    <DarkCodeEditor\n                                        value={inputValue}\n                                        onValueChange={(code) => setInputValue(code)}\n                                        placeholder={input.placeholder}\n                                        type={languageType}\n                                        onMouseUp={(e) => onMouseUp(e)}\n                                        onBlur={(e) => onMouseUp(e)}\n                                        style={{\n                                            fontSize: '0.875rem',\n                                            minHeight: 'calc(100vh - 220px)',\n                                            width: '100%'\n                                        }}\n                                    />\n                                ) : (\n                                    <LightCodeEditor\n                                        value={inputValue}\n                                        onValueChange={(code) => setInputValue(code)}\n                                        placeholder={input.placeholder}\n                                        type={languageType}\n                                        onMouseUp={(e) => onMouseUp(e)}\n                                        onBlur={(e) => onMouseUp(e)}\n                                        style={{\n                                            fontSize: '0.875rem',\n                                            minHeight: 'calc(100vh - 220px)',\n                                            width: '100%'\n                                        }}\n                                    />\n                                )}\n                            </PerfectScrollbar>\n                        </div>\n                    )}\n                    {!dialogProps.hideVariables && (\n                        <div style={{ flex: 1 }}>\n                            <Typography sx={{ mb: 2, ml: 2 }} variant='h4'>\n                                Variables\n                            </Typography>\n                            {dialogProps.availableNodesForVariable.length === 0 && (\n                                <div style={{ padding: 10, marginLeft: 10 }}>\n                                    <span>No Variables. Try connect to other nodes. </span>\n                                </div>\n                            )}\n                            {dialogProps.availableNodesForVariable.length > 0 && (\n                                <PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 220px)', overflowX: 'hidden' }}>\n                                    <Box sx={{ pl: 2, pr: 2 }}>\n                                        <List\n                                            sx={{\n                                                width: '100%',\n                                                py: 0,\n                                                borderRadius: '10px',\n                                                [theme.breakpoints.down('md')]: {\n                                                    maxWidth: 300\n                                                },\n                                                '& .MuiListItemSecondaryAction-root': {\n                                                    top: 22\n                                                },\n                                                '& .MuiDivider-root': {\n                                                    my: 0\n                                                },\n                                                '& .list-container': {\n                                                    pl: 7\n                                                }\n                                            }}\n                                        >\n                                            {dialogProps.availableNodesForVariable.map((node, index) => (\n                                                <Box key={index}>\n                                                    <Accordion\n                                                        expanded={expanded === node.data.label}\n                                                        onChange={handleAccordionChange(node.data.label)}\n                                                    >\n                                                        <AccordionSummary\n                                                            expandIcon={<ExpandMoreIcon />}\n                                                            aria-controls={`${node.data.label}-content`}\n                                                            id={`${node.data.label}-header`}\n                                                        >\n                                                            <Typography variant='h5'>{node.data.label}</Typography>\n                                                        </AccordionSummary>\n                                                        <AccordionDetails>\n                                                            <div style={{ position: 'relative' }}>\n                                                                <ReactJson\n                                                                    theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                                                                    collapsed\n                                                                    style={{ padding: 10, borderRadius: 10 }}\n                                                                    src={\n                                                                        node.data.outputResponses && node.data.outputResponses.output\n                                                                            ? node.data.outputResponses.output\n                                                                            : {}\n                                                                    }\n                                                                    enableClipboard={(e) => onClipboardCopy(e, node)}\n                                                                />\n                                                                <IconButton\n                                                                    size='small'\n                                                                    sx={{\n                                                                        height: 25,\n                                                                        width: 25,\n                                                                        position: 'absolute',\n                                                                        top: 5,\n                                                                        right: 5\n                                                                    }}\n                                                                    title='Expand Variable'\n                                                                    color='primary'\n                                                                    onClick={() =>\n                                                                        onExpandDialogClicked(\n                                                                            node.data.outputResponses && node.data.outputResponses.output\n                                                                                ? node.data.outputResponses.output\n                                                                                : {},\n                                                                            node\n                                                                        )\n                                                                    }\n                                                                >\n                                                                    <IconArrowsMaximize />\n                                                                </IconButton>\n                                                            </div>\n                                                        </AccordionDetails>\n                                                    </Accordion>\n                                                </Box>\n                                            ))}\n                                        </List>\n                                    </Box>\n                                </PerfectScrollbar>\n                            )}\n                        </div>\n                    )}\n                    <ExpandDataDialog\n                        enableClipboard\n                        show={showExpandDialog}\n                        dialogProps={expandDialogProps}\n                        onCancel={() => setShowExpandDialog(false)}\n                        onCopyClick={(e, node) => {\n                            onClipboardCopy(e, node)\n                            setShowExpandDialog(false)\n                        }}\n                    ></ExpandDataDialog>\n                </div>\n            </DialogContent>\n            <DialogActions>\n                <Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>\n                <StyledButton variant='contained' onClick={() => onSave(inputValue)}>\n                    {dialogProps.confirmButtonName}\n                </StyledButton>\n            </DialogActions>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nEditVariableDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onConfirm: PropTypes.func\n}\n\nexport default EditVariableDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/ExpandDataDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\n\nimport { Dialog, DialogContent, DialogTitle } from '@mui/material'\nimport ReactJson from 'react-json-view'\n\n// utils\nimport { copyToClipboard } from 'utils/genericHelper'\n\nconst ExpandDataDialog = ({ show, dialogProps, onCancel, onCopyClick, enableClipboard }) => {\n    const portalElement = document.getElementById('portal')\n    const customization = useSelector((state) => state.customization)\n\n    const component = show ? (\n        <Dialog\n            open={show}\n            fullWidth\n            maxWidth='md'\n            onClose={onCancel}\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                {!enableClipboard && (\n                    <ReactJson\n                        theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                        style={{ padding: 10, borderRadius: 10 }}\n                        src={dialogProps.data}\n                        enableClipboard={(e) => copyToClipboard(e)}\n                    />\n                )}\n                {enableClipboard && (\n                    <ReactJson\n                        theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                        style={{ padding: 10, borderRadius: 10 }}\n                        src={dialogProps.data}\n                        enableClipboard={(e) => onCopyClick(e, dialogProps.node)}\n                    />\n                )}\n            </DialogContent>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nExpandDataDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onCopyClick: PropTypes.func,\n    enableClipboard: PropTypes.bool\n}\n\nexport default ExpandDataDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/HTMLDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport { useState } from 'react'\nimport PropTypes from 'prop-types'\n\nimport { Dialog, DialogContent, DialogTitle, Tabs, Tab, Box } from '@mui/material'\n\nfunction TabPanel(props) {\n    const { children, value, index, ...other } = props\n    return (\n        <div\n            role='tabpanel'\n            hidden={value !== index}\n            id={`attachment-tabpanel-${index}`}\n            aria-labelledby={`attachment-tab-${index}`}\n            {...other}\n        >\n            {value === index && <Box sx={{ p: 1 }}>{children}</Box>}\n        </div>\n    )\n}\n\nTabPanel.propTypes = {\n    children: PropTypes.node,\n    index: PropTypes.number.isRequired,\n    value: PropTypes.number.isRequired\n}\n\nfunction a11yProps(index) {\n    return {\n        id: `attachment-tab-${index}`,\n        'aria-controls': `attachment-tabpanel-${index}`\n    }\n}\n\nconst HTMLDialog = ({ show, dialogProps, onCancel }) => {\n    const portalElement = document.getElementById('portal')\n\n    const [value, setValue] = useState(0)\n\n    const handleChange = (event, newValue) => {\n        setValue(newValue)\n    }\n\n    const component = show ? (\n        <Dialog\n            open={show}\n            onClose={onCancel}\n            fullWidth\n            maxWidth='lg'\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                <Tabs value={value} onChange={handleChange} aria-label='attachment tabs'>\n                    {dialogProps.executionData.map((execObj, execObjIndex) => (\n                        <Tab key={execObjIndex} label={`Item ${execObjIndex}`} {...a11yProps(execObjIndex)} />\n                    ))}\n                </Tabs>\n                {dialogProps.executionData.map((execObj, execObjIndex) => (\n                    <TabPanel key={execObjIndex} value={value} index={execObjIndex}>\n                        {execObj.html && (\n                            <div\n                                style={{ width: '100%', height: '100%', overflow: 'auto' }}\n                                dangerouslySetInnerHTML={{ __html: execObj.html }}\n                            />\n                        )}\n                        {!execObj.html && <div>No HTML</div>}\n                    </TabPanel>\n                ))}\n            </DialogContent>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nHTMLDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func\n}\n\nexport default HTMLDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/SaveWorkflowDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport { useState, useEffect } from 'react'\nimport PropTypes from 'prop-types'\n\nimport { Button, Dialog, DialogActions, DialogContent, OutlinedInput, DialogTitle } from '@mui/material'\nimport { StyledButton } from 'ui-component/StyledButton'\n\nconst SaveWorkflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {\n    const portalElement = document.getElementById('portal')\n\n    const [workflowName, setWorkflowName] = useState('')\n    const [isReadyToSave, setIsReadyToSave] = useState(false)\n\n    useEffect(() => {\n        if (workflowName) setIsReadyToSave(true)\n        else setIsReadyToSave(false)\n    }, [workflowName])\n\n    const component = show ? (\n        <Dialog\n            open={show}\n            fullWidth\n            maxWidth='xs'\n            onClose={onCancel}\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                <OutlinedInput\n                    sx={{ mt: 1 }}\n                    id='workflow-name'\n                    type='text'\n                    fullWidth\n                    placeholder='My New Workflow'\n                    value={workflowName}\n                    onChange={(e) => setWorkflowName(e.target.value)}\n                />\n            </DialogContent>\n            <DialogActions>\n                <Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>\n                <StyledButton disabled={!isReadyToSave} variant='contained' onClick={() => onConfirm(workflowName)}>\n                    {dialogProps.confirmButtonName}\n                </StyledButton>\n            </DialogActions>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nSaveWorkflowDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onConfirm: PropTypes.func\n}\n\nexport default SaveWorkflowDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/dialog/TestWorkflowDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport PropTypes from 'prop-types'\nimport { useSelector } from 'react-redux'\nimport { useState, useEffect } from 'react'\n\nimport {\n    Dialog,\n    DialogContent,\n    DialogTitle,\n    Box,\n    Divider,\n    InputAdornment,\n    List,\n    ListItemButton,\n    ListItem,\n    ListItemAvatar,\n    ListItemText,\n    OutlinedInput,\n    Stack\n} from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// icons\nimport { IconSearch } from '@tabler/icons'\n\n// const\nimport { baseURL } from 'store/constant'\n\nconst TestWorkflowDialog = ({ show, dialogProps, onCancel, onItemClick }) => {\n    const portalElement = document.getElementById('portal')\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [searchValue, setSearchValue] = useState('')\n    const [nodes, setNodes] = useState([])\n\n    const filterSearch = (value) => {\n        setSearchValue(value)\n        setTimeout(() => {\n            if (value) {\n                const returnData = dialogProps.nodes.filter((nd) => nd.data.label.toLowerCase().includes(value.toLowerCase()))\n                setNodes(returnData)\n            } else if (value === '') {\n                setNodes(dialogProps.nodes)\n            }\n        }, 500)\n    }\n\n    useEffect(() => {\n        if (dialogProps.nodes) {\n            setNodes(dialogProps.nodes)\n        }\n    }, [dialogProps])\n\n    const component = show ? (\n        <Dialog\n            open={show}\n            fullWidth\n            maxWidth='md'\n            onClose={onCancel}\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                <Stack>\n                    <span>Select a starting point to test from. Workflow will be executed from the starting point till the end.</span>\n                </Stack>\n                <Box sx={{ p: 2 }}>\n                    <OutlinedInput\n                        sx={{ width: '100%', pr: 1, pl: 2, my: 2 }}\n                        id='input-search-node'\n                        value={searchValue}\n                        onChange={(e) => filterSearch(e.target.value)}\n                        placeholder='Search nodes'\n                        startAdornment={\n                            <InputAdornment position='start'>\n                                <IconSearch stroke={1.5} size='1rem' color={theme.palette.grey[500]} />\n                            </InputAdornment>\n                        }\n                        aria-describedby='search-helper-text'\n                        inputProps={{\n                            'aria-label': 'weight'\n                        }}\n                    />\n                    <Divider />\n                </Box>\n                <div>\n                    <Box sx={{ p: 2 }}>\n                        <List\n                            sx={{\n                                width: '100%',\n                                py: 0,\n                                borderRadius: '10px',\n                                [theme.breakpoints.down('md')]: {\n                                    maxWidth: 300\n                                },\n                                '& .MuiListItemSecondaryAction-root': {\n                                    top: 22\n                                },\n                                '& .MuiDivider-root': {\n                                    my: 0\n                                },\n                                '& .list-container': {\n                                    pl: 7\n                                }\n                            }}\n                        >\n                            {nodes.map((node) => (\n                                <div key={node.id}>\n                                    <ListItemButton\n                                        sx={{ p: 0, borderRadius: `${customization.borderRadius}px` }}\n                                        onClick={() => onItemClick(node.id)}\n                                    >\n                                        <ListItem alignItems='center'>\n                                            <ListItemAvatar>\n                                                <div style={{ width: 50, height: 50, borderRadius: '50%', backgroundColor: 'white' }}>\n                                                    <img\n                                                        style={{ width: '100%', height: '100%', padding: 10, objectFit: 'contain' }}\n                                                        alt={node.data.name}\n                                                        src={`${baseURL}/api/v1/node-icon/${node.data.name}`}\n                                                    />\n                                                </div>\n                                            </ListItemAvatar>\n                                            <ListItemText sx={{ ml: 1 }} primary={node.data.label} secondary={node.data.description} />\n                                        </ListItem>\n                                    </ListItemButton>\n                                    <Divider />\n                                </div>\n                            ))}\n                        </List>\n                    </Box>\n                </div>\n            </DialogContent>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nTestWorkflowDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onItemClick: PropTypes.func\n}\n\nexport default TestWorkflowDialog\n"
  },
  {
    "path": "packages/ui/src/ui-component/editor/DarkCodeEditor.js",
    "content": "import Editor from 'react-simple-code-editor'\nimport { highlight, languages } from 'prismjs/components/prism-core'\nimport 'prismjs/components/prism-clike'\nimport 'prismjs/components/prism-javascript'\nimport 'prismjs/components/prism-json'\nimport 'prismjs/components/prism-markup'\nimport './prism-dark.css'\nimport PropTypes from 'prop-types'\nimport { useTheme } from '@mui/material/styles'\n\nexport const DarkCodeEditor = ({ value, placeholder, type, style, onValueChange, onMouseUp, onBlur }) => {\n    const theme = useTheme()\n\n    return (\n        <Editor\n            value={value}\n            placeholder={placeholder}\n            highlight={(code) => highlight(code, type === 'json' ? languages.json : languages.js)}\n            padding={10}\n            onValueChange={onValueChange}\n            onMouseUp={onMouseUp}\n            onBlur={onBlur}\n            style={{\n                ...style,\n                background: theme.palette.codeEditor.main\n            }}\n            textareaClassName='editor__textarea'\n        />\n    )\n}\n\nDarkCodeEditor.propTypes = {\n    value: PropTypes.string,\n    placeholder: PropTypes.string,\n    type: PropTypes.string,\n    style: PropTypes.object,\n    onValueChange: PropTypes.func,\n    onMouseUp: PropTypes.func,\n    onBlur: PropTypes.func\n}\n"
  },
  {
    "path": "packages/ui/src/ui-component/editor/LightCodeEditor.js",
    "content": "import Editor from 'react-simple-code-editor'\nimport { highlight, languages } from 'prismjs/components/prism-core'\nimport 'prismjs/components/prism-clike'\nimport 'prismjs/components/prism-javascript'\nimport 'prismjs/components/prism-json'\nimport 'prismjs/components/prism-markup'\nimport './prism-light.css'\nimport PropTypes from 'prop-types'\nimport { useTheme } from '@mui/material/styles'\n\nexport const LightCodeEditor = ({ value, placeholder, type, style, onValueChange, onMouseUp, onBlur }) => {\n    const theme = useTheme()\n\n    return (\n        <Editor\n            value={value}\n            placeholder={placeholder}\n            highlight={(code) => highlight(code, type === 'json' ? languages.json : languages.js)}\n            padding={10}\n            onValueChange={onValueChange}\n            onMouseUp={onMouseUp}\n            onBlur={onBlur}\n            style={{\n                ...style,\n                background: theme.palette.card.main\n            }}\n            textareaClassName='editor__textarea'\n        />\n    )\n}\n\nLightCodeEditor.propTypes = {\n    value: PropTypes.string,\n    placeholder: PropTypes.string,\n    type: PropTypes.string,\n    style: PropTypes.object,\n    onValueChange: PropTypes.func,\n    onMouseUp: PropTypes.func,\n    onBlur: PropTypes.func\n}\n"
  },
  {
    "path": "packages/ui/src/ui-component/editor/prism-dark.css",
    "content": "pre[class*='language-'],\ncode[class*='language-'] {\n    color: #d4d4d4;\n    font-size: 13px;\n    text-shadow: none;\n    font-family: Menlo, Monaco, Consolas, 'Andale Mono', 'Ubuntu Mono', 'Courier New', monospace;\n    direction: ltr;\n    text-align: left;\n    white-space: pre;\n    word-spacing: normal;\n    word-break: normal;\n    line-height: 1.5;\n    -moz-tab-size: 4;\n    -o-tab-size: 4;\n    tab-size: 4;\n    -webkit-hyphens: none;\n    -moz-hyphens: none;\n    -ms-hyphens: none;\n    hyphens: none;\n}\n\npre[class*='language-']::selection,\ncode[class*='language-']::selection,\npre[class*='language-'] *::selection,\ncode[class*='language-'] *::selection {\n    text-shadow: none;\n    background: #264f78;\n}\n\n@media print {\n    pre[class*='language-'],\n    code[class*='language-'] {\n        text-shadow: none;\n    }\n}\n\npre[class*='language-'] {\n    padding: 1em;\n    margin: 0.5em 0;\n    overflow: auto;\n    background: #1e1e1e;\n}\n\n:not(pre) > code[class*='language-'] {\n    padding: 0.1em 0.3em;\n    border-radius: 0.3em;\n    color: #db4c69;\n    background: #1e1e1e;\n}\n/*********************************************************\n* Tokens\n*/\n.namespace {\n    opacity: 0.7;\n}\n\n.token.doctype .token.doctype-tag {\n    color: #569cd6;\n}\n\n.token.doctype .token.name {\n    color: #9cdcfe;\n}\n\n.token.comment,\n.token.prolog {\n    color: #6a9955;\n}\n\n.token.punctuation,\n.language-html .language-css .token.punctuation,\n.language-html .language-javascript .token.punctuation {\n    color: #d4d4d4;\n}\n\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.inserted,\n.token.unit {\n    color: #b5cea8;\n}\n\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.deleted {\n    color: #ce9178;\n}\n\n.language-css .token.string.url {\n    text-decoration: underline;\n}\n\n.token.operator,\n.token.entity {\n    color: #d4d4d4;\n}\n\n.token.operator.arrow {\n    color: #569cd6;\n}\n\n.token.atrule {\n    color: #ce9178;\n}\n\n.token.atrule .token.rule {\n    color: #c586c0;\n}\n\n.token.atrule .token.url {\n    color: #9cdcfe;\n}\n\n.token.atrule .token.url .token.function {\n    color: #dcdcaa;\n}\n\n.token.atrule .token.url .token.punctuation {\n    color: #d4d4d4;\n}\n\n.token.keyword {\n    color: #569cd6;\n}\n\n.token.keyword.module,\n.token.keyword.control-flow {\n    color: #c586c0;\n}\n\n.token.function,\n.token.function .token.maybe-class-name {\n    color: #dcdcaa;\n}\n\n.token.regex {\n    color: #d16969;\n}\n\n.token.important {\n    color: #569cd6;\n}\n\n.token.italic {\n    font-style: italic;\n}\n\n.token.constant {\n    color: #9cdcfe;\n}\n\n.token.class-name,\n.token.maybe-class-name {\n    color: #4ec9b0;\n}\n\n.token.console {\n    color: #9cdcfe;\n}\n\n.token.parameter {\n    color: #9cdcfe;\n}\n\n.token.interpolation {\n    color: #9cdcfe;\n}\n\n.token.punctuation.interpolation-punctuation {\n    color: #569cd6;\n}\n\n.token.boolean {\n    color: #569cd6;\n}\n\n.token.property,\n.token.variable,\n.token.imports .token.maybe-class-name,\n.token.exports .token.maybe-class-name {\n    color: #9cdcfe;\n}\n\n.token.selector {\n    color: #d7ba7d;\n}\n\n.token.escape {\n    color: #d7ba7d;\n}\n\n.token.tag {\n    color: #569cd6;\n}\n\n.token.tag .token.punctuation {\n    color: #808080;\n}\n\n.token.cdata {\n    color: #808080;\n}\n\n.token.attr-name {\n    color: #9cdcfe;\n}\n\n.token.attr-value,\n.token.attr-value .token.punctuation {\n    color: #ce9178;\n}\n\n.token.attr-value .token.punctuation.attr-equals {\n    color: #d4d4d4;\n}\n\n.token.entity {\n    color: #569cd6;\n}\n\n.token.namespace {\n    color: #4ec9b0;\n}\n/*********************************************************\n* Language Specific\n*/\n\npre[class*='language-javascript'],\ncode[class*='language-javascript'],\npre[class*='language-jsx'],\ncode[class*='language-jsx'],\npre[class*='language-typescript'],\ncode[class*='language-typescript'],\npre[class*='language-tsx'],\ncode[class*='language-tsx'] {\n    color: #9cdcfe;\n}\n\npre[class*='language-css'],\ncode[class*='language-css'] {\n    color: #ce9178;\n}\n\npre[class*='language-html'],\ncode[class*='language-html'] {\n    color: #d4d4d4;\n}\n\n.language-regex .token.anchor {\n    color: #dcdcaa;\n}\n\n.language-html .token.punctuation {\n    color: #808080;\n}\n/*********************************************************\n* Line highlighting\n*/\npre[class*='language-'] > code[class*='language-'] {\n    position: relative;\n    z-index: 1;\n}\n\n.line-highlight.line-highlight {\n    background: #f7ebc6;\n    box-shadow: inset 5px 0 0 #f7d87c;\n    z-index: 0;\n}\n"
  },
  {
    "path": "packages/ui/src/ui-component/editor/prism-light.css",
    "content": "code[class*='language-'],\npre[class*='language-'] {\n    text-align: left;\n    white-space: pre;\n    word-spacing: normal;\n    word-break: normal;\n    word-wrap: normal;\n    color: #90a4ae;\n    background: #fafafa;\n    font-family: Roboto Mono, monospace;\n    font-size: 1em;\n    line-height: 1.5em;\n\n    -moz-tab-size: 4;\n    -o-tab-size: 4;\n    tab-size: 4;\n\n    -webkit-hyphens: none;\n    -moz-hyphens: none;\n    -ms-hyphens: none;\n    hyphens: none;\n}\n\ncode[class*='language-']::-moz-selection,\npre[class*='language-']::-moz-selection,\ncode[class*='language-'] ::-moz-selection,\npre[class*='language-'] ::-moz-selection {\n    background: #cceae7;\n    color: #263238;\n}\n\ncode[class*='language-']::selection,\npre[class*='language-']::selection,\ncode[class*='language-'] ::selection,\npre[class*='language-'] ::selection {\n    background: #cceae7;\n    color: #263238;\n}\n\n:not(pre) > code[class*='language-'] {\n    white-space: normal;\n    border-radius: 0.2em;\n    padding: 0.1em;\n}\n\npre[class*='language-'] {\n    overflow: auto;\n    position: relative;\n    margin: 0.5em 0;\n    padding: 1.25em 1em;\n}\n\n.language-css > code,\n.language-sass > code,\n.language-scss > code {\n    color: #f76d47;\n}\n\n[class*='language-'] .namespace {\n    opacity: 0.7;\n}\n\n.token.atrule {\n    color: #7c4dff;\n}\n\n.token.attr-name {\n    color: #39adb5;\n}\n\n.token.attr-value {\n    color: #f6a434;\n}\n\n.token.attribute {\n    color: #f6a434;\n}\n\n.token.boolean {\n    color: #7c4dff;\n}\n\n.token.builtin {\n    color: #39adb5;\n}\n\n.token.cdata {\n    color: #39adb5;\n}\n\n.token.char {\n    color: #39adb5;\n}\n\n.token.class {\n    color: #39adb5;\n}\n\n.token.class-name {\n    color: #6182b8;\n}\n\n.token.comment {\n    color: #aabfc9;\n}\n\n.token.constant {\n    color: #7c4dff;\n}\n\n.token.deleted {\n    color: #e53935;\n}\n\n.token.doctype {\n    color: #aabfc9;\n}\n\n.token.entity {\n    color: #e53935;\n}\n\n.token.function {\n    color: #7c4dff;\n}\n\n.token.hexcode {\n    color: #f76d47;\n}\n\n.token.id {\n    color: #7c4dff;\n    font-weight: bold;\n}\n\n.token.important {\n    color: #7c4dff;\n    font-weight: bold;\n}\n\n.token.inserted {\n    color: #39adb5;\n}\n\n.token.keyword {\n    color: #7c4dff;\n}\n\n.token.number {\n    color: #f76d47;\n}\n\n.token.operator {\n    color: #39adb5;\n}\n\n.token.prolog {\n    color: #aabfc9;\n}\n\n.token.property {\n    color: #39adb5;\n}\n\n.token.pseudo-class {\n    color: #f6a434;\n}\n\n.token.pseudo-element {\n    color: #f6a434;\n}\n\n.token.punctuation {\n    color: #39adb5;\n}\n\n.token.regex {\n    color: #6182b8;\n}\n\n.token.selector {\n    color: #e53935;\n}\n\n.token.string {\n    color: #f6a434;\n}\n\n.token.symbol {\n    color: #7c4dff;\n}\n\n.token.tag {\n    color: #e53935;\n}\n\n.token.unit {\n    color: #f76d47;\n}\n\n.token.url {\n    color: #e53935;\n}\n\n.token.variable {\n    color: #e53935;\n}\n"
  },
  {
    "path": "packages/ui/src/ui-component/extended/AnimateButton.js",
    "content": "import PropTypes from 'prop-types'\nimport { forwardRef } from 'react'\n// third-party\nimport { motion, useCycle } from 'framer-motion'\n\n// ==============================|| ANIMATION BUTTON ||============================== //\n\nconst AnimateButton = forwardRef(function AnimateButton({ children, type, direction, offset, scale }, ref) {\n    let offset1\n    let offset2\n    switch (direction) {\n        case 'up':\n        case 'left':\n            offset1 = offset\n            offset2 = 0\n            break\n        case 'right':\n        case 'down':\n        default:\n            offset1 = 0\n            offset2 = offset\n            break\n    }\n\n    const [x, cycleX] = useCycle(offset1, offset2)\n    const [y, cycleY] = useCycle(offset1, offset2)\n\n    switch (type) {\n        case 'rotate':\n            return (\n                <motion.div\n                    ref={ref}\n                    animate={{ rotate: 360 }}\n                    transition={{\n                        repeat: Infinity,\n                        repeatType: 'loop',\n                        duration: 2,\n                        repeatDelay: 0\n                    }}\n                >\n                    {children}\n                </motion.div>\n            )\n        case 'slide':\n            if (direction === 'up' || direction === 'down') {\n                return (\n                    <motion.div\n                        ref={ref}\n                        animate={{ y: y !== undefined ? y : '' }}\n                        onHoverEnd={() => cycleY()}\n                        onHoverStart={() => cycleY()}\n                    >\n                        {children}\n                    </motion.div>\n                )\n            }\n            return (\n                <motion.div ref={ref} animate={{ x: x !== undefined ? x : '' }} onHoverEnd={() => cycleX()} onHoverStart={() => cycleX()}>\n                    {children}\n                </motion.div>\n            )\n\n        case 'scale':\n        default:\n            if (typeof scale === 'number') {\n                scale = {\n                    hover: scale,\n                    tap: scale\n                }\n            }\n            return (\n                <motion.div ref={ref} whileHover={{ scale: scale?.hover }} whileTap={{ scale: scale?.tap }}>\n                    {children}\n                </motion.div>\n            )\n    }\n})\n\nAnimateButton.propTypes = {\n    children: PropTypes.node,\n    offset: PropTypes.number,\n    type: PropTypes.oneOf(['slide', 'scale', 'rotate']),\n    direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),\n    scale: PropTypes.oneOfType([PropTypes.number, PropTypes.object])\n}\n\nAnimateButton.defaultProps = {\n    type: 'scale',\n    offset: 10,\n    direction: 'right',\n    scale: {\n        hover: 1,\n        tap: 0.9\n    }\n}\n\nexport default AnimateButton\n"
  },
  {
    "path": "packages/ui/src/ui-component/extended/Avatar.js",
    "content": "import PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport MuiAvatar from '@mui/material/Avatar'\n\n// ==============================|| AVATAR ||============================== //\n\nconst Avatar = ({ color, outline, size, sx, ...others }) => {\n    const theme = useTheme()\n\n    const colorSX = color && !outline && { color: theme.palette.background.paper, bgcolor: `${color}.main` }\n    const outlineSX = outline && {\n        color: color ? `${color}.main` : `primary.main`,\n        bgcolor: theme.palette.background.paper,\n        border: '2px solid',\n        borderColor: color ? `${color}.main` : `primary.main`\n    }\n    let sizeSX = {}\n    switch (size) {\n        case 'badge':\n            sizeSX = {\n                width: theme.spacing(3.5),\n                height: theme.spacing(3.5)\n            }\n            break\n        case 'xs':\n            sizeSX = {\n                width: theme.spacing(4.25),\n                height: theme.spacing(4.25)\n            }\n            break\n        case 'sm':\n            sizeSX = {\n                width: theme.spacing(5),\n                height: theme.spacing(5)\n            }\n            break\n        case 'lg':\n            sizeSX = {\n                width: theme.spacing(9),\n                height: theme.spacing(9)\n            }\n            break\n        case 'xl':\n            sizeSX = {\n                width: theme.spacing(10.25),\n                height: theme.spacing(10.25)\n            }\n            break\n        case 'md':\n            sizeSX = {\n                width: theme.spacing(7.5),\n                height: theme.spacing(7.5)\n            }\n            break\n        default:\n            sizeSX = {}\n    }\n\n    return <MuiAvatar sx={{ ...colorSX, ...outlineSX, ...sizeSX, ...sx }} {...others} />\n}\n\nAvatar.propTypes = {\n    className: PropTypes.string,\n    color: PropTypes.string,\n    outline: PropTypes.bool,\n    size: PropTypes.string,\n    sx: PropTypes.object\n}\n\nexport default Avatar\n"
  },
  {
    "path": "packages/ui/src/ui-component/extended/Breadcrumbs.js",
    "content": "import PropTypes from 'prop-types'\nimport { useEffect, useState } from 'react'\nimport { Link } from 'react-router-dom'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Box, Card, Divider, Grid, Typography } from '@mui/material'\nimport MuiBreadcrumbs from '@mui/material/Breadcrumbs'\n\n// project imports\nimport config from 'config'\nimport { gridSpacing } from 'store/constant'\n\n// assets\nimport { IconTallymark1 } from '@tabler/icons'\nimport AccountTreeTwoToneIcon from '@mui/icons-material/AccountTreeTwoTone'\nimport HomeIcon from '@mui/icons-material/Home'\nimport HomeTwoToneIcon from '@mui/icons-material/HomeTwoTone'\n\nconst linkSX = {\n    display: 'flex',\n    color: 'grey.900',\n    textDecoration: 'none',\n    alignContent: 'center',\n    alignItems: 'center'\n}\n\n// ==============================|| BREADCRUMBS ||============================== //\n\nconst Breadcrumbs = ({ card, divider, icon, icons, maxItems, navigation, rightAlign, separator, title, titleBottom, ...others }) => {\n    const theme = useTheme()\n\n    const iconStyle = {\n        marginRight: theme.spacing(0.75),\n        marginTop: `-${theme.spacing(0.25)}`,\n        width: '1rem',\n        height: '1rem',\n        color: theme.palette.secondary.main\n    }\n\n    const [main, setMain] = useState()\n    const [item, setItem] = useState()\n\n    // set active item state\n    const getCollapse = (menu) => {\n        if (menu.children) {\n            menu.children.filter((collapse) => {\n                if (collapse.type && collapse.type === 'collapse') {\n                    getCollapse(collapse)\n                } else if (collapse.type && collapse.type === 'item') {\n                    if (document.location.pathname === config.basename + collapse.url) {\n                        setMain(menu)\n                        setItem(collapse)\n                    }\n                }\n                return false\n            })\n        }\n    }\n\n    useEffect(() => {\n        navigation?.items?.map((menu) => {\n            if (menu.type && menu.type === 'group') {\n                getCollapse(menu)\n            }\n            return false\n        })\n    })\n\n    // item separator\n    const SeparatorIcon = separator\n    const separatorIcon = separator ? <SeparatorIcon stroke={1.5} size='1rem' /> : <IconTallymark1 stroke={1.5} size='1rem' />\n\n    let mainContent\n    let itemContent\n    let breadcrumbContent = <Typography />\n    let itemTitle = ''\n    let CollapseIcon\n    let ItemIcon\n\n    // collapse item\n    if (main && main.type === 'collapse') {\n        CollapseIcon = main.icon ? main.icon : AccountTreeTwoToneIcon\n        mainContent = (\n            <Typography component={Link} to='#' variant='subtitle1' sx={linkSX}>\n                {icons && <CollapseIcon style={iconStyle} />}\n                {main.title}\n            </Typography>\n        )\n    }\n\n    // items\n    if (item && item.type === 'item') {\n        itemTitle = item.title\n\n        ItemIcon = item.icon ? item.icon : AccountTreeTwoToneIcon\n        itemContent = (\n            <Typography\n                variant='subtitle1'\n                sx={{\n                    display: 'flex',\n                    textDecoration: 'none',\n                    alignContent: 'center',\n                    alignItems: 'center',\n                    color: 'grey.500'\n                }}\n            >\n                {icons && <ItemIcon style={iconStyle} />}\n                {itemTitle}\n            </Typography>\n        )\n\n        // main\n        if (item.breadcrumbs !== false) {\n            breadcrumbContent = (\n                <Card\n                    sx={{\n                        border: 'none'\n                    }}\n                    {...others}\n                >\n                    <Box sx={{ p: 2, pl: card === false ? 0 : 2 }}>\n                        <Grid\n                            container\n                            direction={rightAlign ? 'row' : 'column'}\n                            justifyContent={rightAlign ? 'space-between' : 'flex-start'}\n                            alignItems={rightAlign ? 'center' : 'flex-start'}\n                            spacing={1}\n                        >\n                            {title && !titleBottom && (\n                                <Grid item>\n                                    <Typography variant='h3' sx={{ fontWeight: 500 }}>\n                                        {item.title}\n                                    </Typography>\n                                </Grid>\n                            )}\n                            <Grid item>\n                                <MuiBreadcrumbs\n                                    sx={{ '& .MuiBreadcrumbs-separator': { width: 16, ml: 1.25, mr: 1.25 } }}\n                                    aria-label='breadcrumb'\n                                    maxItems={maxItems || 8}\n                                    separator={separatorIcon}\n                                >\n                                    <Typography component={Link} to='/' color='inherit' variant='subtitle1' sx={linkSX}>\n                                        {icons && <HomeTwoToneIcon sx={iconStyle} />}\n                                        {icon && <HomeIcon sx={{ ...iconStyle, mr: 0 }} />}\n                                        {!icon && 'Dashboard'}\n                                    </Typography>\n                                    {mainContent}\n                                    {itemContent}\n                                </MuiBreadcrumbs>\n                            </Grid>\n                            {title && titleBottom && (\n                                <Grid item>\n                                    <Typography variant='h3' sx={{ fontWeight: 500 }}>\n                                        {item.title}\n                                    </Typography>\n                                </Grid>\n                            )}\n                        </Grid>\n                    </Box>\n                    {card === false && divider !== false && <Divider sx={{ borderColor: theme.palette.primary.main, mb: gridSpacing }} />}\n                </Card>\n            )\n        }\n    }\n\n    return breadcrumbContent\n}\n\nBreadcrumbs.propTypes = {\n    card: PropTypes.bool,\n    divider: PropTypes.bool,\n    icon: PropTypes.bool,\n    icons: PropTypes.bool,\n    maxItems: PropTypes.number,\n    navigation: PropTypes.object,\n    rightAlign: PropTypes.bool,\n    separator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),\n    title: PropTypes.bool,\n    titleBottom: PropTypes.bool\n}\n\nexport default Breadcrumbs\n"
  },
  {
    "path": "packages/ui/src/ui-component/extended/Transitions.js",
    "content": "import PropTypes from 'prop-types'\nimport { forwardRef } from 'react'\n\n// material-ui\nimport { Collapse, Fade, Box, Grow, Slide, Zoom } from '@mui/material'\n\n// ==============================|| TRANSITIONS ||============================== //\n\nconst Transitions = forwardRef(function Transitions({ children, position, type, direction, ...others }, ref) {\n    let positionSX = {\n        transformOrigin: '0 0 0'\n    }\n\n    switch (position) {\n        case 'top-right':\n            positionSX = {\n                transformOrigin: 'top right'\n            }\n            break\n        case 'top':\n            positionSX = {\n                transformOrigin: 'top'\n            }\n            break\n        case 'bottom-left':\n            positionSX = {\n                transformOrigin: 'bottom left'\n            }\n            break\n        case 'bottom-right':\n            positionSX = {\n                transformOrigin: 'bottom right'\n            }\n            break\n        case 'bottom':\n            positionSX = {\n                transformOrigin: 'bottom'\n            }\n            break\n        case 'top-left':\n        default:\n            positionSX = {\n                transformOrigin: '0 0 0'\n            }\n            break\n    }\n\n    return (\n        <Box ref={ref}>\n            {type === 'grow' && (\n                <Grow {...others}>\n                    <Box sx={positionSX}>{children}</Box>\n                </Grow>\n            )}\n            {type === 'collapse' && (\n                <Collapse {...others} sx={positionSX}>\n                    {children}\n                </Collapse>\n            )}\n            {type === 'fade' && (\n                <Fade\n                    {...others}\n                    timeout={{\n                        appear: 500,\n                        enter: 600,\n                        exit: 400\n                    }}\n                >\n                    <Box sx={positionSX}>{children}</Box>\n                </Fade>\n            )}\n            {type === 'slide' && (\n                <Slide\n                    {...others}\n                    timeout={{\n                        appear: 0,\n                        enter: 400,\n                        exit: 200\n                    }}\n                    direction={direction}\n                >\n                    <Box sx={positionSX}>{children}</Box>\n                </Slide>\n            )}\n            {type === 'zoom' && (\n                <Zoom {...others}>\n                    <Box sx={positionSX}>{children}</Box>\n                </Zoom>\n            )}\n        </Box>\n    )\n})\n\nTransitions.propTypes = {\n    children: PropTypes.node,\n    type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']),\n    position: PropTypes.oneOf(['top-left', 'top-right', 'top', 'bottom-left', 'bottom-right', 'bottom']),\n    direction: PropTypes.oneOf(['up', 'down', 'left', 'right'])\n}\n\nTransitions.defaultProps = {\n    type: 'grow',\n    position: 'top-left',\n    direction: 'up'\n}\n\nexport default Transitions\n"
  },
  {
    "path": "packages/ui/src/utils/genericHelper.js",
    "content": "import lodash from 'lodash'\nimport moment from 'moment'\n\nexport const numberOrExpressionRegex = /^(\\d+\\.?\\d*|{{.*}})$/ //return true if string consists only numbers OR expression {{}}\n\nexport const constructNodeDirectedGraph = (nodes, edges, reverse = false) => {\n    const graph = {}\n    const nodeDependencies = {}\n\n    // Initialize node dependencies and graph\n    for (let i = 0; i < nodes.length; i += 1) {\n        const nodeId = nodes[i].id\n        nodeDependencies[nodeId] = 0\n        graph[nodeId] = []\n    }\n\n    for (let i = 0; i < edges.length; i += 1) {\n        const source = edges[i].source\n        const target = edges[i].target\n\n        if (Object.prototype.hasOwnProperty.call(graph, source)) {\n            graph[source].push(target)\n        } else {\n            graph[source] = [target]\n        }\n\n        if (reverse) {\n            if (Object.prototype.hasOwnProperty.call(graph, target)) {\n                graph[target].push(source)\n            } else {\n                graph[target] = [source]\n            }\n        }\n\n        nodeDependencies[target] += 1\n    }\n\n    return { graph, nodeDependencies }\n}\n\n// Find starting node with 0 dependencies\nexport const findStartingNodeIds = (nodes, nodeDependencies) => {\n    const startingNodeIds = []\n    Object.keys(nodeDependencies).forEach((nodeId) => {\n        if (nodeDependencies[nodeId] === 0) {\n            const node = nodes.find((nd) => nd.id === nodeId)\n            if (node && node.data && node.data.type && (node.data.type === 'trigger' || node.data.type === 'webhook')) {\n                startingNodeIds.push(nodeId)\n            }\n        }\n    })\n\n    return startingNodeIds\n}\n\n// Backtrack function to find all paths from start to target node\nexport const getAllPathsFromStartToTarget = (startNodeId, targetNodeId, graph) => {\n    const paths = []\n    const visitedNodeIds = new Set()\n\n    const DFS = (currentNodeId, endNodeId, tempPath) => {\n        if (currentNodeId === endNodeId) {\n            paths.push(lodash.cloneDeep(tempPath))\n            return\n        }\n\n        const neighbourNodeIds = graph[currentNodeId]\n        visitedNodeIds.add(currentNodeId)\n\n        for (let i = 0; i < neighbourNodeIds.length; i += 1) {\n            const neighNodeId = neighbourNodeIds[i]\n            if (!visitedNodeIds.has(neighNodeId)) {\n                tempPath.push(neighNodeId)\n                DFS(neighNodeId, endNodeId, tempPath)\n                tempPath.pop()\n            }\n        }\n        visitedNodeIds.delete(currentNodeId)\n    }\n\n    DFS(startNodeId, targetNodeId, [startNodeId])\n    return paths\n}\n\n// Breadth First Search to get all connected parent nodes from target\nexport const getAllConnectedNodesFromTarget = (targetNodeId, edges, graph) => {\n    const nodeQueue = []\n    const exploredNodes = []\n\n    nodeQueue.push(targetNodeId)\n    exploredNodes.push(targetNodeId)\n\n    while (nodeQueue.length) {\n        const nodeId = nodeQueue.shift() || ''\n        const parentNodeIds = []\n\n        const inputEdges = edges.filter((edg) => edg.target === nodeId && edg.targetHandle.includes('-input-'))\n        if (inputEdges && inputEdges.length) {\n            for (let j = 0; j < inputEdges.length; j += 1) {\n                parentNodeIds.push(inputEdges[j].source)\n            }\n        }\n\n        const neighbourNodeIds = graph[nodeId]\n\n        for (let i = 0; i < neighbourNodeIds.length; i += 1) {\n            const neighNodeId = neighbourNodeIds[i]\n            if (parentNodeIds.includes(neighNodeId)) {\n                if (!exploredNodes.includes(neighNodeId)) {\n                    exploredNodes.push(neighNodeId)\n                    nodeQueue.push(neighNodeId)\n                }\n            }\n        }\n    }\n    return exploredNodes\n}\n\nexport const getAvailableNodeIdsForVariable = (nodes, edges, targetNodeId) => {\n    const { graph } = constructNodeDirectedGraph(nodes, edges, true)\n    const exploreNodes = getAllConnectedNodesFromTarget(targetNodeId, edges, graph)\n    const setPath = new Set(exploreNodes)\n    setPath.delete(targetNodeId)\n    return [...setPath]\n}\n\nexport const generateWebhookEndpoint = () => {\n    const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'\n    const webhookEndpoint = Array.from({ length: 15 })\n        .map(() => {\n            return characters.charAt(Math.floor(Math.random() * characters.length))\n        })\n        .join('')\n    return webhookEndpoint\n}\n\nexport const getUniqueNodeId = (nodeData, nodes) => {\n    // Get amount of same nodes\n    let totalSameNodes = 0\n    for (let i = 0; i < nodes.length; i += 1) {\n        const node = nodes[i]\n        if (node.data.name === nodeData.name) {\n            totalSameNodes += 1\n        }\n    }\n\n    // Get unique id\n    let nodeId = `${nodeData.name}_${totalSameNodes}`\n    for (let i = 0; i < nodes.length; i += 1) {\n        const node = nodes[i]\n        if (node.id === nodeId) {\n            totalSameNodes += 1\n            nodeId = `${nodeData.name}_${totalSameNodes}`\n        }\n    }\n    return nodeId\n}\n\nconst getUniqueNodeLabel = (nodeData, nodes) => {\n    // Get amount of same nodes\n    let totalSameNodes = 0\n    for (let i = 0; i < nodes.length; i += 1) {\n        const node = nodes[i]\n        if (node.data.name === nodeData.name) {\n            totalSameNodes += 1\n        }\n    }\n\n    // Get unique label\n    let nodeLabel = `${nodeData.label}_${totalSameNodes}`\n    for (let i = 0; i < nodes.length; i += 1) {\n        const node = nodes[i]\n        if (node.data.label === nodeLabel) {\n            totalSameNodes += 1\n            nodeLabel = `${nodeData.label}_${totalSameNodes}`\n        }\n    }\n    return totalSameNodes === 0 ? nodeData.label : nodeLabel\n}\n\nexport const checkIfNodeLabelUnique = (nodeLabel, nodes) => {\n    for (let i = 0; i < nodes.length; i += 1) {\n        const node = nodes[i]\n        if (node.data.label === nodeLabel) {\n            return false\n        }\n    }\n    return true\n}\n\nexport const initializeNodeData = (nodeParams) => {\n    const initialValues = {}\n\n    for (let i = 0; i < nodeParams.length; i += 1) {\n        const input = nodeParams[i]\n\n        // Load from nodeParams default values\n        initialValues[input.name] = input.default || ''\n\n        // Special case for array, always initialize the item if default is not set\n        if (input.type === 'array' && !input.default) {\n            const newObj = {}\n            for (let j = 0; j < input.array.length; j += 1) {\n                newObj[input.array[j].name] = input.array[j].default || ''\n            }\n            initialValues[input.name] = [newObj]\n        }\n    }\n\n    initialValues.submit = null\n\n    return initialValues\n}\n\nexport const addAnchors = (nodeData, nodes, newNodeId) => {\n    const incoming = nodeData.incoming || 0\n    const outgoing = nodeData.outgoing || 0\n\n    const inputAnchors = []\n    for (let i = 0; i < incoming; i += 1) {\n        const newInput = {\n            id: `${newNodeId}-input-${i}`\n        }\n        inputAnchors.push(newInput)\n    }\n\n    const outputAnchors = []\n    for (let i = 0; i < outgoing; i += 1) {\n        const newOutput = {\n            id: `${newNodeId}-output-${i}`\n        }\n        outputAnchors.push(newOutput)\n    }\n\n    nodeData.inputAnchors = inputAnchors\n    nodeData.outputAnchors = outputAnchors\n    nodeData.label = getUniqueNodeLabel(nodeData, nodes)\n\n    if (nodeData.actions) nodeData.actions = initializeNodeData(nodeData.actions)\n    if (nodeData.credentials) nodeData.credentials = initializeNodeData(nodeData.credentials)\n    if (nodeData.networks) nodeData.networks = initializeNodeData(nodeData.networks)\n    if (nodeData.inputParameters) nodeData.inputParameters = initializeNodeData(nodeData.inputParameters)\n\n    return nodeData\n}\n\nexport const getEdgeLabelName = (source) => {\n    const sourceSplit = source.split('-')\n    if (sourceSplit.length && sourceSplit[0].includes('ifElse')) {\n        const outputAnchorsIndex = sourceSplit[sourceSplit.length - 1]\n        return outputAnchorsIndex === '0' ? 'true' : 'false'\n    }\n    return ''\n}\n\nexport const checkMultipleTriggers = (nodes) => {\n    for (let i = 0; i < nodes.length; i += 1) {\n        const node = nodes[i]\n        if (node.data.type === 'webhook' || node.data.type === 'trigger') {\n            return true\n        }\n    }\n    return false\n}\n\nexport const convertDateStringToDateObject = (dateString) => {\n    if (dateString === undefined || !dateString) return undefined\n\n    const date = moment(dateString)\n    if (!date.isValid) return undefined\n\n    // Sat Sep 24 2022 07:30:14\n    return new Date(date.year(), date.month(), date.date(), date.hours(), date.minutes())\n}\n\nexport const getFileName = (fileBase64) => {\n    const splitDataURI = fileBase64.split(',')\n    const filename = splitDataURI[splitDataURI.length - 1].split(':')[1]\n    return filename\n}\n\nexport const getFolderName = (base64ArrayStr) => {\n    try {\n        const base64Array = JSON.parse(base64ArrayStr)\n        const filenames = []\n        for (let i = 0; i < base64Array.length; i += 1) {\n            const fileBase64 = base64Array[i]\n            const splitDataURI = fileBase64.split(',')\n            const filename = splitDataURI[splitDataURI.length - 1].split(':')[1]\n            filenames.push(filename)\n        }\n        return filenames.length ? filenames.join(',') : ''\n    } catch (e) {\n        return ''\n    }\n}\n\nexport const generateExportFlowData = (flowData) => {\n    const nodes = flowData.nodes\n    const edges = flowData.edges\n\n    for (let i = 0; i < nodes.length; i += 1) {\n        nodes[i].selected = false\n        const node = nodes[i]\n        const newNodeData = {\n            label: node.data.label,\n            name: node.data.name,\n            type: node.data.type,\n            inputAnchors: node.data.inputAnchors,\n            outputAnchors: node.data.outputAnchors,\n            selected: false\n        }\n        if (node.data.inputParameters) {\n            newNodeData.inputParameters = { ...node.data.inputParameters, submit: null }\n            if (node.data.inputParameters.wallet) delete newNodeData.inputParameters.wallet\n        }\n        if (node.data.actions) {\n            newNodeData.actions = { ...node.data.actions, submit: null }\n            if (node.data.actions.wallet) delete newNodeData.actions.wallet\n        }\n        if (node.data.networks) {\n            newNodeData.networks = { ...node.data.networks, submit: null }\n            if (node.data.networks.wallet) delete newNodeData.networks.wallet\n        }\n        if (node.data.credentials && node.data.credentials.credentialMethod) {\n            newNodeData.credentials = { credentialMethod: node.data.credentials.credentialMethod, submit: null }\n            if (node.data.credentials.wallet) delete newNodeData.credentials.wallet\n        }\n\n        nodes[i].data = newNodeData\n    }\n    const exportJson = {\n        nodes,\n        edges\n    }\n    return exportJson\n}\n\nconst isHideRegisteredCredential = (params, paramsType, nodeFlowData) => {\n    if (!nodeFlowData[paramsType] || !nodeFlowData[paramsType]['credentialMethod']) return undefined\n    let clonedParams = params\n\n    for (let i = 0; i < clonedParams.length; i += 1) {\n        const input = clonedParams[i]\n        if (input.type === 'options') {\n            const selectedCredentialMethodOption = input.options.find((opt) => opt.name === nodeFlowData[paramsType]['credentialMethod'])\n            if (\n                selectedCredentialMethodOption &&\n                selectedCredentialMethodOption !== undefined &&\n                selectedCredentialMethodOption.hideRegisteredCredential\n            )\n                return true\n        }\n    }\n    return false\n}\n\nexport const handleCredentialParams = (nodeParams, paramsType, reorganizedParams, nodeFlowData) => {\n    if (\n        paramsType === 'credentials' &&\n        nodeParams.find((nPrm) => nPrm.name === 'registeredCredential') === undefined &&\n        nodeParams.find((nPrm) => nPrm.name === 'credentialMethod') !== undefined &&\n        !isHideRegisteredCredential(lodash.cloneDeep(reorganizedParams), paramsType, nodeFlowData)\n    ) {\n        // Add hard-coded registeredCredential params\n        nodeParams.push({\n            name: 'registeredCredential'\n        })\n    } else if (\n        paramsType === 'credentials' &&\n        nodeParams.find((nPrm) => nPrm.name === 'registeredCredential') !== undefined &&\n        nodeParams.find((nPrm) => nPrm.name === 'credentialMethod') !== undefined &&\n        isHideRegisteredCredential(lodash.cloneDeep(reorganizedParams), paramsType, nodeFlowData)\n    ) {\n        // Delete registeredCredential params\n        nodeParams = nodeParams.filter((prm) => prm.name !== 'registeredCredential')\n    } else if (paramsType === 'credentials' && nodeParams.find((nPrm) => nPrm.name === 'credentialMethod') === undefined) {\n        // Delete registeredCredential params\n        nodeParams = nodeParams.filter((prm) => prm.name !== 'registeredCredential')\n    }\n    return nodeParams\n}\n\nexport const copyToClipboard = (e) => {\n    const src = e.src\n    if (Array.isArray(src) || typeof src === 'object') {\n        navigator.clipboard.writeText(JSON.stringify(src, null, '  '))\n    } else {\n        navigator.clipboard.writeText(src)\n    }\n}\n"
  },
  {
    "path": "packages/ui/src/utils/useNotifier.js",
    "content": "import React from 'react'\nimport { useDispatch, useSelector } from 'react-redux'\nimport { useSnackbar } from 'notistack'\nimport { removeSnackbar } from 'store/actions'\n\nlet displayed = []\n\nconst useNotifier = () => {\n    const dispatch = useDispatch()\n    const notifier = useSelector((state) => state.notifier)\n    const { notifications } = notifier\n\n    const { enqueueSnackbar, closeSnackbar } = useSnackbar()\n\n    const storeDisplayed = (id) => {\n        displayed = [...displayed, id]\n    }\n\n    const removeDisplayed = (id) => {\n        displayed = [...displayed.filter((key) => id !== key)]\n    }\n\n    React.useEffect(() => {\n        notifications.forEach(({ key, message, options = {}, dismissed = false }) => {\n            if (dismissed) {\n                // dismiss snackbar using notistack\n                closeSnackbar(key)\n                return\n            }\n\n            // do nothing if snackbar is already displayed\n            if (displayed.includes(key)) return\n\n            // display snackbar using notistack\n            enqueueSnackbar(message, {\n                key,\n                ...options,\n                onClose: (event, reason, myKey) => {\n                    if (options.onClose) {\n                        options.onClose(event, reason, myKey)\n                    }\n                },\n                onExited: (event, myKey) => {\n                    // remove this snackbar from redux store\n                    dispatch(removeSnackbar(myKey))\n                    removeDisplayed(myKey)\n                }\n            })\n\n            // keep track of snackbars that we've displayed\n            storeDisplayed(key)\n        })\n    }, [notifications, closeSnackbar, enqueueSnackbar, dispatch])\n}\n\nexport default useNotifier\n"
  },
  {
    "path": "packages/ui/src/utils/usePrompt.js",
    "content": "import { useCallback, useContext, useEffect } from 'react'\nimport { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'\n\n// https://stackoverflow.com/questions/71572678/react-router-v-6-useprompt-typescript\n\nexport function useBlocker(blocker, when = true) {\n    const { navigator } = useContext(NavigationContext)\n\n    useEffect(() => {\n        if (!when) return\n\n        const unblock = navigator.block((tx) => {\n            const autoUnblockingTx = {\n                ...tx,\n                retry() {\n                    unblock()\n                    tx.retry()\n                }\n            }\n\n            blocker(autoUnblockingTx)\n        })\n\n        return unblock\n    }, [navigator, blocker, when])\n}\n\nexport function usePrompt(message, when = true) {\n    const blocker = useCallback(\n        (tx) => {\n            if (window.confirm(message)) tx.retry()\n        },\n        [message]\n    )\n\n    useBlocker(blocker, when)\n}\n"
  },
  {
    "path": "packages/ui/src/views/apikey/APIKeyDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport PropTypes from 'prop-types'\nimport { useState, useEffect } from 'react'\nimport { useDispatch } from 'react-redux'\nimport { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'\n\nimport { Box, Typography, Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack, IconButton, OutlinedInput } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// Icons\nimport { IconX, IconCopy } from '@tabler/icons'\n\n// API\nimport apikeyApi from 'api/apikey'\n\n// utils\nimport useNotifier from 'utils/useNotifier'\n\nconst APIKeyDialog = ({ show, dialogProps, onCancel, onConfirm }) => {\n    const portalElement = document.getElementById('portal')\n\n    const theme = useTheme()\n    const dispatch = useDispatch()\n\n    // ==============================|| Snackbar ||============================== //\n\n    useNotifier()\n\n    const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))\n    const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))\n\n    const [keyName, setKeyName] = useState('')\n\n    useEffect(() => {\n        if (dialogProps.type === 'EDIT' && dialogProps.key) {\n            setKeyName(dialogProps.key.keyName)\n        } else if (dialogProps.type === 'ADD') {\n            setKeyName('')\n        }\n    }, [dialogProps])\n\n    const addNewKey = async () => {\n        try {\n            const createResp = await apikeyApi.createNewAPI({ keyName })\n            if (createResp.data) {\n                enqueueSnackbar({\n                    message: 'New API key added',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to add new API key: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const saveKey = async () => {\n        try {\n            const saveResp = await apikeyApi.updateAPI(dialogProps.key.id, { keyName })\n            if (saveResp.data) {\n                enqueueSnackbar({\n                    message: 'API Key saved',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to save API key: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const component = show ? (\n        <Dialog\n            fullWidth\n            maxWidth='sm'\n            open={show}\n            onClose={onCancel}\n            aria-labelledby='alert-dialog-title'\n            aria-describedby='alert-dialog-description'\n        >\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                {dialogProps.type === 'EDIT' && (\n                    <Box sx={{ p: 2 }}>\n                        <Typography sx={{ p: 1 }} variant='overline'>\n                            API Key\n                        </Typography>\n                        <Stack direction='row' sx={{ p: 1, mb: 1 }}>\n                            <Typography\n                                sx={{\n                                    p: 1,\n                                    borderRadius: 10,\n                                    backgroundColor: theme.palette.primary.light,\n                                    width: 'max-content',\n                                    height: 'max-content'\n                                }}\n                                variant='h5'\n                            >\n                                {dialogProps.key.apiKey}\n                            </Typography>\n                            <IconButton\n                                title='Copy API Key'\n                                color='primary'\n                                onClick={() => navigator.clipboard.writeText(dialogProps.key.apiKey)}\n                            >\n                                <IconCopy />\n                            </IconButton>\n                        </Stack>\n                    </Box>\n                )}\n\n                <Box sx={{ p: 2 }}>\n                    <Stack sx={{ position: 'relative' }} direction='row'>\n                        <Typography variant='overline'>Key Name</Typography>\n                    </Stack>\n                    <OutlinedInput\n                        id='keyName'\n                        type='string'\n                        fullWidth\n                        placeholder='My New Key'\n                        value={keyName}\n                        name='keyName'\n                        onChange={(e) => setKeyName(e.target.value)}\n                    />\n                </Box>\n            </DialogContent>\n            <DialogActions>\n                <StyledButton variant='contained' onClick={() => (dialogProps.type === 'ADD' ? addNewKey() : saveKey())}>\n                    {dialogProps.confirmButtonName}\n                </StyledButton>\n            </DialogActions>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nAPIKeyDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onConfirm: PropTypes.func\n}\n\nexport default APIKeyDialog\n"
  },
  {
    "path": "packages/ui/src/views/apikey/index.js",
    "content": "import { useEffect, useState } from 'react'\nimport { useDispatch, useSelector } from 'react-redux'\nimport { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'\n\n// material-ui\nimport { Button, Box, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport APIKeyDialog from './APIKeyDialog'\nimport { TooltipWithParser } from 'ui-component/TooltipWithParser'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// API\nimport apiKeyApi from 'api/apikey'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// utils\nimport useNotifier from 'utils/useNotifier'\n\n// Icons\nimport { IconTrash, IconEdit, IconCopy, IconX } from '@tabler/icons'\nimport APIEmptySVG from 'assets/images/api_empty.svg'\n\n// ==============================|| APIKey ||============================== //\n\nconst APIKey = () => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const dispatch = useDispatch()\n    useNotifier()\n\n    const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))\n    const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))\n\n    const [showDialog, setShowDialog] = useState(false)\n    const [dialogProps, setDialogProps] = useState({})\n    const [apiKeys, setAPIKeys] = useState([])\n\n    const getAllAPIKeysApi = useApi(apiKeyApi.getAllAPIKeys)\n\n    const addNew = () => {\n        const dialogProp = {\n            title: 'Add New API Key',\n            type: 'ADD',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Add'\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const edit = (key) => {\n        const dialogProp = {\n            title: 'Edit API Key',\n            type: 'EDIT',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Save',\n            key\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const deleteKey = async (key) => {\n        try {\n            const deleteResp = await apiKeyApi.deleteAPI(key.id)\n            if (deleteResp.data) {\n                enqueueSnackbar({\n                    message: 'API key deleted',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to delete API key: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const onConfirm = () => {\n        setShowDialog(false)\n        getAllAPIKeysApi.request()\n    }\n\n    useEffect(() => {\n        getAllAPIKeysApi.request()\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    useEffect(() => {\n        if (getAllAPIKeysApi.data) {\n            setAPIKeys(getAllAPIKeysApi.data)\n        }\n    }, [getAllAPIKeysApi.data])\n\n    return (\n        <>\n            <MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>\n                <Stack flexDirection='row'>\n                    <h1>API Keys&nbsp;</h1>\n                    <TooltipWithParser title='Include API key as header when turning workflow into HTTP call' />\n                    <Box sx={{ flexGrow: 1 }} />\n\n                    <StyledButton variant='contained' sx={{ color: 'white', mr: 1, height: 37 }} onClick={addNew}>\n                        Add New\n                    </StyledButton>\n                </Stack>\n                {apiKeys.length <= 0 && (\n                    <Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>\n                        <Box sx={{ p: 2, height: 'auto' }}>\n                            <img style={{ objectFit: 'cover', height: '30vh', width: 'auto' }} src={APIEmptySVG} alt='APIEmptySVG' />\n                        </Box>\n                        <div>No API Keys Yet</div>\n                    </Stack>\n                )}\n                {apiKeys.length > 0 && (\n                    <TableContainer component={Paper}>\n                        <Table sx={{ minWidth: 650 }} aria-label='simple table'>\n                            <TableHead>\n                                <TableRow>\n                                    <TableCell>Key Name</TableCell>\n                                    <TableCell>API Key</TableCell>\n                                    <TableCell>Created</TableCell>\n                                    <TableCell> </TableCell>\n                                    <TableCell> </TableCell>\n                                </TableRow>\n                            </TableHead>\n                            <TableBody>\n                                {apiKeys.map((key, index) => (\n                                    <TableRow key={index} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>\n                                        <TableCell component='th' scope='row'>\n                                            {key.keyName}\n                                        </TableCell>\n                                        <TableCell>\n                                            {key.apiKey}\n                                            <IconButton\n                                                title='Copy'\n                                                color='success'\n                                                onClick={() => navigator.clipboard.writeText(key.apiKey)}\n                                            >\n                                                <IconCopy />\n                                            </IconButton>\n                                        </TableCell>\n                                        <TableCell>{key.createdAt}</TableCell>\n                                        <TableCell>\n                                            <IconButton title='Edit' color='primary' onClick={() => edit(key)}>\n                                                <IconEdit />\n                                            </IconButton>\n                                        </TableCell>\n                                        <TableCell>\n                                            <IconButton title='Delete' color='error' onClick={() => deleteKey(key)}>\n                                                <IconTrash />\n                                            </IconButton>\n                                        </TableCell>\n                                    </TableRow>\n                                ))}\n                            </TableBody>\n                        </Table>\n                    </TableContainer>\n                )}\n            </MainCard>\n            <APIKeyDialog\n                show={showDialog}\n                dialogProps={dialogProps}\n                onCancel={() => setShowDialog(false)}\n                onConfirm={onConfirm}\n            ></APIKeyDialog>\n        </>\n    )\n}\n\nexport default APIKey\n"
  },
  {
    "path": "packages/ui/src/views/canvas/AddNodes.js",
    "content": "import { useState, useRef, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport {\n    Accordion,\n    AccordionSummary,\n    AccordionDetails,\n    Box,\n    ClickAwayListener,\n    Divider,\n    InputAdornment,\n    List,\n    ListItemButton,\n    ListItem,\n    ListItemAvatar,\n    ListItemText,\n    OutlinedInput,\n    Paper,\n    Popper,\n    Stack,\n    Typography,\n    Tabs,\n    Tab\n} from '@mui/material'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\n\n// third-party\nimport PerfectScrollbar from 'react-perfect-scrollbar'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport Transitions from 'ui-component/extended/Transitions'\nimport { StyledFab } from 'ui-component/StyledFab'\n\n// icons\nimport { IconPlus, IconSearch, IconMinus } from '@tabler/icons'\n\n// const\nimport { baseURL } from 'store/constant'\n\n// ==============================|| ADD NODES||============================== //\nfunction TabPanel(props) {\n    const { children, value, index, ...other } = props\n    return (\n        <div\n            role='tabpanel'\n            hidden={value !== index}\n            id={`attachment-tabpanel-${index}`}\n            aria-labelledby={`attachment-tab-${index}`}\n            {...other}\n        >\n            {value === index && <Box sx={{ p: 1 }}>{children}</Box>}\n        </div>\n    )\n}\n\nTabPanel.propTypes = {\n    children: PropTypes.node,\n    index: PropTypes.number.isRequired,\n    value: PropTypes.number.isRequired\n}\n\nfunction a11yProps(index) {\n    return {\n        id: `nodes-tab-${index}`,\n        'aria-controls': `nodes-tabpanel-${index}`\n    }\n}\n\nconst AddNodes = ({ nodesData, node }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [searchValue, setSearchValue] = useState('')\n    const [nodes, setNodes] = useState({})\n    const [open, setOpen] = useState(false)\n    const [tabValue, setTabValue] = useState(0)\n    const [categoryExpanded, setCategoryExpanded] = useState({})\n\n    const anchorRef = useRef(null)\n    const prevOpen = useRef(open)\n    const ps = useRef()\n\n    const scrollTop = () => {\n        const curr = ps.current\n        if (curr) {\n            curr.scrollTop = 0\n        }\n    }\n\n    const handleTabChange = (event, newValue) => {\n        setTabValue(newValue)\n        let returnData = []\n        switch (newValue) {\n            case 0:\n                returnData = nodesData\n                break\n            case 1:\n                returnData = nodesData.filter((nd) => nd.type.toLowerCase() === 'trigger')\n                break\n            case 2:\n                returnData = nodesData.filter((nd) => nd.type.toLowerCase() === 'webhook')\n                break\n            case 3:\n                returnData = nodesData.filter((nd) => nd.type.toLowerCase() === 'action')\n                break\n        }\n        groupByCategory(returnData)\n        scrollTop()\n    }\n\n    const filterSearch = (value) => {\n        setSearchValue(value)\n        setTimeout(() => {\n            if (value) {\n                const returnData = nodesData.filter((nd) => nd.name.toLowerCase().includes(value.toLowerCase()))\n                groupByCategory(returnData, true)\n                setTabValue(0)\n                scrollTop()\n            } else if (value === '') {\n                groupByCategory(nodesData)\n                scrollTop()\n            }\n        }, 500)\n    }\n\n    const groupByCategory = (nodes, isFilter) => {\n        const accordianCategories = {}\n        const result = nodes.reduce(function (r, a) {\n            r[a.category] = r[a.category] || []\n            r[a.category].push(a)\n            accordianCategories[a.category] = isFilter ? true : false\n            return r\n        }, Object.create(null))\n        setNodes(result)\n        setCategoryExpanded(accordianCategories)\n    }\n\n    const handleAccordionChange = (category) => (event, isExpanded) => {\n        const accordianCategories = { ...categoryExpanded }\n        accordianCategories[category] = isExpanded\n        setCategoryExpanded(accordianCategories)\n    }\n\n    const handleClose = (event) => {\n        if (anchorRef.current && anchorRef.current.contains(event.target)) {\n            return\n        }\n        setOpen(false)\n    }\n\n    const handleToggle = () => {\n        setOpen((prevOpen) => !prevOpen)\n    }\n\n    const onDragStart = (event, node) => {\n        event.dataTransfer.setData('application/reactflow', JSON.stringify(node))\n        event.dataTransfer.effectAllowed = 'move'\n    }\n\n    useEffect(() => {\n        if (prevOpen.current === true && open === false) {\n            anchorRef.current.focus()\n        }\n\n        prevOpen.current = open\n    }, [open])\n\n    useEffect(() => {\n        if (node) setOpen(false)\n    }, [node])\n\n    useEffect(() => {\n        if (nodesData) groupByCategory(nodesData)\n    }, [nodesData])\n\n    return (\n        <>\n            <StyledFab\n                sx={{ left: 20, top: 20 }}\n                ref={anchorRef}\n                size='small'\n                color='primary'\n                aria-label='add'\n                title='Add Node'\n                onClick={handleToggle}\n            >\n                {open ? <IconMinus /> : <IconPlus />}\n            </StyledFab>\n            <Popper\n                placement='bottom-end'\n                open={open}\n                anchorEl={anchorRef.current}\n                role={undefined}\n                transition\n                disablePortal\n                popperOptions={{\n                    modifiers: [\n                        {\n                            name: 'offset',\n                            options: {\n                                offset: [-40, 14]\n                            }\n                        }\n                    ]\n                }}\n                sx={{ zIndex: 1000 }}\n            >\n                {({ TransitionProps }) => (\n                    <Transitions in={open} {...TransitionProps}>\n                        <Paper>\n                            <ClickAwayListener onClickAway={handleClose}>\n                                <MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>\n                                    <Box sx={{ p: 2 }}>\n                                        <Stack>\n                                            <Typography variant='h4'>Add Nodes</Typography>\n                                        </Stack>\n                                        <OutlinedInput\n                                            sx={{ width: '100%', pr: 1, pl: 2, my: 2 }}\n                                            id='input-search-node'\n                                            value={searchValue}\n                                            onChange={(e) => filterSearch(e.target.value)}\n                                            placeholder='Search nodes'\n                                            startAdornment={\n                                                <InputAdornment position='start'>\n                                                    <IconSearch stroke={1.5} size='1rem' color={theme.palette.grey[500]} />\n                                                </InputAdornment>\n                                            }\n                                            aria-describedby='search-helper-text'\n                                            inputProps={{\n                                                'aria-label': 'weight'\n                                            }}\n                                        />\n                                        <Divider />\n                                    </Box>\n                                    <Tabs variant='fullWidth' value={tabValue} onChange={handleTabChange} aria-label='nodes tabs'>\n                                        <Tab key={0} label='All' {...a11yProps(0)} />\n                                        <Tab key={1} label='Trigger' {...a11yProps(1)} />\n                                        <Tab key={2} label='Webhook' {...a11yProps(2)} />\n                                        <Tab key={3} label='Action' {...a11yProps(3)} />\n                                    </Tabs>\n                                    <PerfectScrollbar\n                                        containerRef={(el) => {\n                                            ps.current = el\n                                        }}\n                                        style={{ height: '100%', maxHeight: 'calc(100vh - 375px)', overflowX: 'hidden' }}\n                                    >\n                                        <Box sx={{ p: 2 }}>\n                                            <List\n                                                sx={{\n                                                    width: '100%',\n                                                    maxWidth: 370,\n                                                    py: 0,\n                                                    borderRadius: '10px',\n                                                    [theme.breakpoints.down('md')]: {\n                                                        maxWidth: 370\n                                                    },\n                                                    '& .MuiListItemSecondaryAction-root': {\n                                                        top: 22\n                                                    },\n                                                    '& .MuiDivider-root': {\n                                                        my: 0\n                                                    },\n                                                    '& .list-container': {\n                                                        pl: 7\n                                                    }\n                                                }}\n                                            >\n                                                {Object.keys(nodes)\n                                                    .sort()\n                                                    .map((category) => (\n                                                        <Accordion\n                                                            expanded={categoryExpanded[category] || false}\n                                                            onChange={handleAccordionChange(category)}\n                                                            key={category}\n                                                        >\n                                                            <AccordionSummary\n                                                                expandIcon={<ExpandMoreIcon />}\n                                                                aria-controls={`nodes-accordian-${category}`}\n                                                                id={`nodes-accordian-header-${category}`}\n                                                            >\n                                                                <Typography variant='h5'>{category}</Typography>\n                                                            </AccordionSummary>\n                                                            <AccordionDetails>\n                                                                {nodes[category].map((node, index) => (\n                                                                    <div\n                                                                        key={node.name}\n                                                                        onDragStart={(event) => onDragStart(event, node)}\n                                                                        draggable\n                                                                    >\n                                                                        <ListItemButton\n                                                                            sx={{\n                                                                                p: 0,\n                                                                                borderRadius: `${customization.borderRadius}px`,\n                                                                                cursor: 'move'\n                                                                            }}\n                                                                        >\n                                                                            <ListItem alignItems='center'>\n                                                                                <ListItemAvatar>\n                                                                                    <div\n                                                                                        style={{\n                                                                                            width: 50,\n                                                                                            height: 50,\n                                                                                            borderRadius: '50%',\n                                                                                            backgroundColor: 'white'\n                                                                                        }}\n                                                                                    >\n                                                                                        <img\n                                                                                            style={{\n                                                                                                width: '100%',\n                                                                                                height: '100%',\n                                                                                                padding: 10,\n                                                                                                objectFit: 'contain'\n                                                                                            }}\n                                                                                            alt={node.name}\n                                                                                            src={`${baseURL}/api/v1/node-icon/${node.name}`}\n                                                                                        />\n                                                                                    </div>\n                                                                                </ListItemAvatar>\n                                                                                <ListItemText\n                                                                                    sx={{ ml: 1 }}\n                                                                                    primary={node.label}\n                                                                                    secondary={node.description}\n                                                                                />\n                                                                            </ListItem>\n                                                                        </ListItemButton>\n                                                                        {index === nodes[category].length - 1 ? null : <Divider />}\n                                                                    </div>\n                                                                ))}\n                                                            </AccordionDetails>\n                                                        </Accordion>\n                                                    ))}\n                                            </List>\n                                        </Box>\n                                    </PerfectScrollbar>\n                                </MainCard>\n                            </ClickAwayListener>\n                        </Paper>\n                    </Transitions>\n                )}\n            </Popper>\n        </>\n    )\n}\n\nAddNodes.propTypes = {\n    nodesData: PropTypes.array,\n    node: PropTypes.object\n}\n\nexport default AddNodes\n"
  },
  {
    "path": "packages/ui/src/views/canvas/ButtonEdge.js",
    "content": "import { getBezierPath, EdgeText } from 'reactflow'\nimport PropTypes from 'prop-types'\nimport { useDispatch } from 'react-redux'\nimport { REMOVE_EDGE } from 'store/actions'\n\nimport './index.css'\n\nconst foreignObjectSize = 40\n\nconst ButtonEdge = ({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, style = {}, data, markerEnd }) => {\n    const [edgePath, edgeCenterX, edgeCenterY] = getBezierPath({\n        sourceX,\n        sourceY,\n        sourcePosition,\n        targetX,\n        targetY,\n        targetPosition\n    })\n\n    const dispatch = useDispatch()\n\n    const onEdgeClick = (evt, id) => {\n        evt.stopPropagation()\n        dispatch({ type: REMOVE_EDGE, edgeId: `${id}:${Date.now()}` })\n    }\n\n    return (\n        <>\n            <path id={id} style={style} className='react-flow__edge-path' d={edgePath} markerEnd={markerEnd} />\n            {data && data.label && (\n                <EdgeText\n                    x={sourceX + 10}\n                    y={sourceY + 10}\n                    label={data.label}\n                    labelStyle={{ fill: 'black' }}\n                    labelBgStyle={{ fill: 'transparent' }}\n                    labelBgPadding={[2, 4]}\n                    labelBgBorderRadius={2}\n                />\n            )}\n            <foreignObject\n                width={foreignObjectSize}\n                height={foreignObjectSize}\n                x={edgeCenterX - foreignObjectSize / 2}\n                y={edgeCenterY - foreignObjectSize / 2}\n                className='edgebutton-foreignobject'\n                requiredExtensions='http://www.w3.org/1999/xhtml'\n            >\n                <div>\n                    <button className='edgebutton' onClick={(event) => onEdgeClick(event, id)}>\n                        ×\n                    </button>\n                </div>\n            </foreignObject>\n        </>\n    )\n}\n\nButtonEdge.propTypes = {\n    id: PropTypes.string,\n    sourceX: PropTypes.number,\n    sourceY: PropTypes.number,\n    targetX: PropTypes.number,\n    targetY: PropTypes.number,\n    sourcePosition: PropTypes.any,\n    targetPosition: PropTypes.any,\n    style: PropTypes.object,\n    data: PropTypes.object,\n    markerEnd: PropTypes.any\n}\n\nexport default ButtonEdge\n"
  },
  {
    "path": "packages/ui/src/views/canvas/CanvasHeader.js",
    "content": "import PropTypes from 'prop-types'\nimport { useNavigate } from 'react-router-dom'\nimport { useSelector } from 'react-redux'\nimport { useEffect, useRef, useState } from 'react'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Avatar, Box, ButtonBase, Typography, Stack, TextField, Chip } from '@mui/material'\n\n// icons\nimport {\n    IconSettings,\n    IconChevronLeft,\n    IconDeviceFloppy,\n    IconRocket,\n    IconPencil,\n    IconCheck,\n    IconX,\n    IconPlayerPause,\n    IconListCheck\n} from '@tabler/icons'\n\n// project imports\nimport Executions from 'views/executions'\nimport Settings from 'views/settings'\nimport SaveWorkflowDialog from 'ui-component/dialog/SaveWorkflowDialog'\n\n// API\nimport workflowsApi from 'api/workflows'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// utils\nimport { generateExportFlowData } from 'utils/genericHelper'\n\n// ==============================|| CANVAS HEADER ||============================== //\n\nconst CanvasHeader = ({ workflow, handleSaveFlow, handleDeployWorkflow, handleStopWorkflow, handleDeleteWorkflow, handleLoadWorkflow }) => {\n    const theme = useTheme()\n    const navigate = useNavigate()\n    const workflowNameRef = useRef()\n    const viewExecutionRef = useRef()\n    const settingsRef = useRef()\n\n    const [isEditingWorkflowName, setEditingWorkflowName] = useState(null)\n    const [workflowName, setWorkflowName] = useState('')\n    const [isExecutionOpen, setExecutionOpen] = useState(false)\n    const [isSettingsOpen, setSettingsOpen] = useState(false)\n    const [workfowDialogOpen, setWorkfowDialogOpen] = useState(false)\n\n    const updateWorkflowApi = useApi(workflowsApi.updateWorkflow)\n    const canvas = useSelector((state) => state.canvas)\n\n    const onSettingsItemClick = (setting) => {\n        setSettingsOpen(false)\n\n        if (setting === 'deleteWorkflow') {\n            handleDeleteWorkflow()\n        } else if (setting === 'exportWorkflow') {\n            try {\n                const flowData = JSON.parse(workflow.flowData)\n                let dataStr = JSON.stringify(generateExportFlowData(flowData))\n                let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr)\n\n                let exportFileDefaultName = `${workflow.name} Workflow.json`\n\n                let linkElement = document.createElement('a')\n                linkElement.setAttribute('href', dataUri)\n                linkElement.setAttribute('download', exportFileDefaultName)\n                linkElement.click()\n            } catch (e) {\n                console.error(e)\n            }\n        }\n    }\n\n    const onUploadFile = (file) => {\n        setSettingsOpen(false)\n        handleLoadWorkflow(file)\n    }\n\n    const submitWorkflowName = () => {\n        if (workflow.shortId) {\n            const updateBody = {\n                name: workflowNameRef.current.value\n            }\n            updateWorkflowApi.request(workflow.shortId, updateBody)\n        }\n    }\n\n    const onSaveWorkflowClick = () => {\n        if (workflow.shortId) handleSaveFlow(workflow.name)\n        else setWorkfowDialogOpen(true)\n    }\n\n    const onConfirmSaveName = (workflowName) => {\n        setWorkfowDialogOpen(false)\n        handleSaveFlow(workflowName)\n    }\n\n    useEffect(() => {\n        if (updateWorkflowApi.data) {\n            setWorkflowName(updateWorkflowApi.data.name)\n        }\n        setEditingWorkflowName(false)\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [updateWorkflowApi.data])\n\n    useEffect(() => {\n        if (workflow) {\n            setWorkflowName(workflow.name)\n        }\n    }, [workflow])\n\n    return (\n        <>\n            <Box>\n                <ButtonBase title='Back' sx={{ borderRadius: '50%' }}>\n                    <Avatar\n                        variant='rounded'\n                        sx={{\n                            ...theme.typography.commonAvatar,\n                            ...theme.typography.mediumAvatar,\n                            transition: 'all .2s ease-in-out',\n                            background: theme.palette.secondary.light,\n                            color: theme.palette.secondary.dark,\n                            '&:hover': {\n                                background: theme.palette.secondary.dark,\n                                color: theme.palette.secondary.light\n                            }\n                        }}\n                        color='inherit'\n                        onClick={() => navigate(-1)}\n                    >\n                        <IconChevronLeft stroke={1.5} size='1.3rem' />\n                    </Avatar>\n                </ButtonBase>\n            </Box>\n            <Box sx={{ flexGrow: 1 }}>\n                {!isEditingWorkflowName && (\n                    <Stack flexDirection='row'>\n                        <Typography\n                            sx={{\n                                fontSize: '1.5rem',\n                                fontWeight: 600,\n                                ml: 2\n                            }}\n                        >\n                            {canvas.isDirty && <strong style={{ color: theme.palette.orange.main }}>*</strong>} {workflowName}\n                        </Typography>\n                        {workflow?.shortId && (\n                            <ButtonBase title='Edit Name' sx={{ borderRadius: '50%' }}>\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.commonAvatar,\n                                        ...theme.typography.mediumAvatar,\n                                        transition: 'all .2s ease-in-out',\n                                        ml: 1,\n                                        background: theme.palette.secondary.light,\n                                        color: theme.palette.secondary.dark,\n                                        '&:hover': {\n                                            background: theme.palette.secondary.dark,\n                                            color: theme.palette.secondary.light\n                                        }\n                                    }}\n                                    color='inherit'\n                                    onClick={() => setEditingWorkflowName(true)}\n                                >\n                                    <IconPencil stroke={1.5} size='1.3rem' />\n                                </Avatar>\n                            </ButtonBase>\n                        )}\n                        {workflow?.deployed && (\n                            <Chip\n                                sx={{\n                                    color: 'white',\n                                    backgroundColor: theme.palette.success.dark,\n                                    ml: 1\n                                }}\n                                label='Deployed'\n                                color='success'\n                            />\n                        )}\n                    </Stack>\n                )}\n                {isEditingWorkflowName && (\n                    <Stack flexDirection='row'>\n                        <TextField\n                            size='small'\n                            inputRef={workflowNameRef}\n                            sx={{\n                                width: '50%',\n                                ml: 2\n                            }}\n                            defaultValue={workflowName}\n                        />\n                        <ButtonBase title='Save Name' sx={{ borderRadius: '50%' }}>\n                            <Avatar\n                                variant='rounded'\n                                sx={{\n                                    ...theme.typography.commonAvatar,\n                                    ...theme.typography.mediumAvatar,\n                                    transition: 'all .2s ease-in-out',\n                                    background: theme.palette.success.light,\n                                    color: theme.palette.success.dark,\n                                    ml: 1,\n                                    '&:hover': {\n                                        background: theme.palette.success.dark,\n                                        color: theme.palette.success.light\n                                    }\n                                }}\n                                color='inherit'\n                                onClick={submitWorkflowName}\n                            >\n                                <IconCheck stroke={1.5} size='1.3rem' />\n                            </Avatar>\n                        </ButtonBase>\n                        <ButtonBase title='Cancel' sx={{ borderRadius: '50%' }}>\n                            <Avatar\n                                variant='rounded'\n                                sx={{\n                                    ...theme.typography.commonAvatar,\n                                    ...theme.typography.mediumAvatar,\n                                    transition: 'all .2s ease-in-out',\n                                    background: theme.palette.error.light,\n                                    color: theme.palette.error.dark,\n                                    ml: 1,\n                                    '&:hover': {\n                                        background: theme.palette.error.dark,\n                                        color: theme.palette.error.light\n                                    }\n                                }}\n                                color='inherit'\n                                onClick={() => setEditingWorkflowName(false)}\n                            >\n                                <IconX stroke={1.5} size='1.3rem' />\n                            </Avatar>\n                        </ButtonBase>\n                    </Stack>\n                )}\n            </Box>\n            <Box>\n                {workflow?.shortId && (\n                    <ButtonBase ref={viewExecutionRef} title='View Executions' sx={{ borderRadius: '50%', mr: 2 }}>\n                        <Avatar\n                            variant='rounded'\n                            sx={{\n                                ...theme.typography.commonAvatar,\n                                ...theme.typography.mediumAvatar,\n                                width: '54px',\n                                transition: 'all .2s ease-in-out',\n                                background: theme.palette.canvasHeader.executionLight,\n                                color: theme.palette.canvasHeader.executionDark,\n                                '&:hover': {\n                                    background: theme.palette.canvasHeader.executionDark,\n                                    color: theme.palette.canvasHeader.executionLight\n                                }\n                            }}\n                            color='inherit'\n                            onClick={() => setExecutionOpen(!isExecutionOpen)}\n                        >\n                            <h6>{workflow?.executionCount}</h6>&nbsp;\n                            <IconListCheck stroke={1.5} size='1.3rem' />\n                        </Avatar>\n                    </ButtonBase>\n                )}\n                {workflow?.shortId && (\n                    <ButtonBase title={workflow?.deployed ? 'Stop Workflow' : 'Deploy Workflow'} sx={{ borderRadius: '50%', mr: 2 }}>\n                        <Avatar\n                            variant='rounded'\n                            sx={{\n                                ...theme.typography.commonAvatar,\n                                ...theme.typography.mediumAvatar,\n                                transition: 'all .2s ease-in-out',\n                                background: theme.palette.canvasHeader.deployLight,\n                                color: theme.palette.canvasHeader.deployDark,\n                                '&:hover': {\n                                    background: theme.palette.canvasHeader.deployDark,\n                                    color: theme.palette.canvasHeader.deployLight\n                                }\n                            }}\n                            color='inherit'\n                            onClick={workflow?.deployed ? handleStopWorkflow : handleDeployWorkflow}\n                        >\n                            {workflow?.deployed ? (\n                                <IconPlayerPause stroke={1.5} size='1.3rem' />\n                            ) : (\n                                <IconRocket stroke={1.5} size='1.3rem' />\n                            )}\n                        </Avatar>\n                    </ButtonBase>\n                )}\n\n                <ButtonBase title='Save Workflow' sx={{ borderRadius: '50%', mr: 2 }}>\n                    <Avatar\n                        variant='rounded'\n                        sx={{\n                            ...theme.typography.commonAvatar,\n                            ...theme.typography.mediumAvatar,\n                            transition: 'all .2s ease-in-out',\n                            background: theme.palette.canvasHeader.saveLight,\n                            color: theme.palette.canvasHeader.saveDark,\n                            '&:hover': {\n                                background: theme.palette.canvasHeader.saveDark,\n                                color: theme.palette.canvasHeader.saveLight\n                            }\n                        }}\n                        color='inherit'\n                        onClick={onSaveWorkflowClick}\n                    >\n                        <IconDeviceFloppy stroke={1.5} size='1.3rem' />\n                    </Avatar>\n                </ButtonBase>\n                <ButtonBase ref={settingsRef} title='Settings' sx={{ borderRadius: '50%' }}>\n                    <Avatar\n                        variant='rounded'\n                        sx={{\n                            ...theme.typography.commonAvatar,\n                            ...theme.typography.mediumAvatar,\n                            transition: 'all .2s ease-in-out',\n                            background: theme.palette.canvasHeader.settingsLight,\n                            color: theme.palette.canvasHeader.settingsDark,\n                            '&:hover': {\n                                background: theme.palette.canvasHeader.settingsDark,\n                                color: theme.palette.canvasHeader.settingsLight\n                            }\n                        }}\n                        onClick={() => setSettingsOpen(!isSettingsOpen)}\n                    >\n                        <IconSettings stroke={1.5} size='1.3rem' />\n                    </Avatar>\n                </ButtonBase>\n            </Box>\n            {workflow?.shortId && (\n                <Executions\n                    workflowShortId={workflow?.shortId}\n                    execution={workflow?.execution}\n                    executionCount={workflow?.executionCount}\n                    isExecutionOpen={isExecutionOpen}\n                    anchorEl={viewExecutionRef.current}\n                />\n            )}\n            <Settings\n                workflow={workflow}\n                isSettingsOpen={isSettingsOpen}\n                anchorEl={settingsRef.current}\n                onSettingsItemClick={onSettingsItemClick}\n                onUploadFile={onUploadFile}\n            />\n            <SaveWorkflowDialog\n                show={workfowDialogOpen}\n                dialogProps={{\n                    title: `Save New Workflow`,\n                    confirmButtonName: 'Save',\n                    cancelButtonName: 'Cancel'\n                }}\n                onCancel={() => setWorkfowDialogOpen(false)}\n                onConfirm={onConfirmSaveName}\n            />\n        </>\n    )\n}\n\nCanvasHeader.propTypes = {\n    workflow: PropTypes.object,\n    handleSaveFlow: PropTypes.func,\n    handleDeployWorkflow: PropTypes.func,\n    handleStopWorkflow: PropTypes.func,\n    handleDeleteWorkflow: PropTypes.func,\n    handleLoadWorkflow: PropTypes.func\n}\n\nexport default CanvasHeader\n"
  },
  {
    "path": "packages/ui/src/views/canvas/CanvasNode.js",
    "content": "import PropTypes from 'prop-types'\nimport { Handle, Position } from 'reactflow'\nimport { useSelector } from 'react-redux'\n// material-ui\nimport { styled, useTheme } from '@mui/material/styles'\nimport { Avatar, Box, Typography } from '@mui/material'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\n\n// icons\nimport { IconCheck, IconExclamationMark } from '@tabler/icons'\n\n// const\nimport { baseURL } from 'store/constant'\n\nconst CardWrapper = styled(MainCard)(({ theme }) => ({\n    background: theme.palette.card.main,\n    color: theme.darkTextPrimary,\n    border: 'solid 1px',\n    width: '200px',\n    height: 'auto',\n    padding: '10px',\n    boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',\n    '&:hover': {\n        background: theme.palette.card.hover,\n        borderColor: theme.palette.primary.main\n    }\n}))\n\nconst handlerPosition = [[['50%']], [['30%'], ['70%']]]\n\n// ===========================|| CANVAS NODE ||=========================== //\n\nconst CanvasNode = ({ data }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n    return (\n        <>\n            <CardWrapper\n                content={false}\n                sx={{\n                    borderColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary\n                }}\n                border={false}\n            >\n                {data && data.outputResponses && data.outputResponses.submit && (\n                    <Avatar\n                        variant='rounded'\n                        sx={{\n                            ...theme.typography.smallAvatar,\n                            borderRadius: '50%',\n                            background: theme.palette.success.dark,\n                            color: 'white',\n                            ml: 2,\n                            position: 'absolute',\n                            top: -10,\n                            right: -10\n                        }}\n                    >\n                        <IconCheck />\n                    </Avatar>\n                )}\n\n                {data && data.outputResponses && data.outputResponses.needRetest && (\n                    <Avatar\n                        variant='rounded'\n                        sx={{\n                            ...theme.typography.smallAvatar,\n                            borderRadius: '50%',\n                            background: theme.palette.warning.dark,\n                            color: 'white',\n                            ml: 2,\n                            position: 'absolute',\n                            top: -10,\n                            right: -10\n                        }}\n                    >\n                        <IconExclamationMark />\n                    </Avatar>\n                )}\n\n                <Box>\n                    {data.inputAnchors.map((inputAnchor, index) => (\n                        <Handle\n                            type='target'\n                            position={customization.isHorizontal ? Position.Top : Position.Left}\n                            key={inputAnchor.id}\n                            id={inputAnchor.id}\n                            style={{\n                                height: 15,\n                                width: 15,\n                                top: customization.isHorizontal ? -7.5 : null,\n                                backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,\n                                left: customization.isHorizontal ? handlerPosition[data.inputAnchors.length - 1][index] : null,\n                                bottom: !customization.isHorizontal ? handlerPosition[data.inputAnchors.length - 1][index] : null\n                            }}\n                        />\n                    ))}\n                    <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>\n                        <Box item style={{ width: 50, marginRight: 10 }}>\n                            <div\n                                style={{\n                                    ...theme.typography.commonAvatar,\n                                    ...theme.typography.largeAvatar,\n                                    borderRadius: '50%',\n                                    backgroundColor: 'white',\n                                    cursor: 'grab'\n                                }}\n                            >\n                                <img\n                                    style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}\n                                    src={`${baseURL}/api/v1/node-icon/${data.name}`}\n                                    alt='Notification'\n                                />\n                            </div>\n                        </Box>\n                        <Box>\n                            <Typography\n                                sx={{\n                                    fontSize: '1rem',\n                                    fontWeight: 500\n                                }}\n                            >\n                                {data.label}\n                            </Typography>\n                        </Box>\n                    </div>\n                    {data.outputAnchors.map((outputAnchor, index) => (\n                        <Handle\n                            type='source'\n                            position={customization.isHorizontal ? Position.Bottom : Position.Right}\n                            key={outputAnchor.id}\n                            id={outputAnchor.id}\n                            style={{\n                                height: 15,\n                                width: 15,\n                                bottom: customization.isHorizontal ? -7.5 : null,\n                                backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,\n                                left: customization.isHorizontal ? handlerPosition[data.outputAnchors.length - 1][index] : null,\n                                top: !customization.isHorizontal ? handlerPosition[data.outputAnchors.length - 1][index] : null\n                            }}\n                        />\n                    ))}\n                </Box>\n            </CardWrapper>\n        </>\n    )\n}\n\nCanvasNode.propTypes = {\n    data: PropTypes.object\n}\n\nexport default CanvasNode\n"
  },
  {
    "path": "packages/ui/src/views/canvas/EditNodes.js",
    "content": "import { useState, useRef, useEffect } from 'react'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport {\n    Accordion,\n    AccordionSummary,\n    AccordionDetails,\n    Box,\n    ClickAwayListener,\n    Divider,\n    Paper,\n    Stack,\n    Popper,\n    Typography,\n    TextField,\n    Avatar\n} from '@mui/material'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\n\n// third-party\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport * as Yup from 'yup'\nimport lodash from 'lodash'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport Transitions from 'ui-component/extended/Transitions'\nimport InputParameters from 'views/inputs/InputParameters'\nimport CredentialInput from 'views/inputs/CredentialInput'\nimport OutputResponses from 'views/output/OutputResponses'\nimport VariableSelector from './VariableSelector'\nimport EditVariableDialog from 'ui-component/dialog/EditVariableDialog'\nimport { StyledFab } from 'ui-component/StyledFab'\n\n// API\nimport nodesApi from 'api/nodes'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// icons\nimport { IconPencil, IconMinus, IconCheck } from '@tabler/icons'\n\n// utils\nimport { getAvailableNodeIdsForVariable, numberOrExpressionRegex, handleCredentialParams } from 'utils/genericHelper'\n\n// ==============================|| EDIT NODES||============================== //\n\nconst EditNodes = ({ node, nodes, edges, workflow, onNodeLabelUpdate, onNodeValuesUpdate }) => {\n    const theme = useTheme()\n\n    const [nodeFlowData, setNodeFlowData] = useState(null)\n    const [nodeLabel, setNodeLabel] = useState('')\n    const [expanded, setExpanded] = useState(false)\n    const [open, setOpen] = useState(false)\n    const [nodeDetails, setNodeDetails] = useState(null)\n    const [nodeParams, setNodeParams] = useState([])\n    const [nodeParamsType, setNodeParamsType] = useState([])\n    const [nodeParamsInitialValues, setNodeParamsInitialValues] = useState({})\n    const [nodeParamsValidation, setNodeParamsValidation] = useState({})\n    const [isVariableSelectorOpen, setVariableSelectorOpen] = useState(false)\n    const [variableBody, setVariableBody] = useState({})\n    const [availableNodesForVariable, setAvailableNodesForVariable] = useState(null)\n    const [isEditVariableDialogOpen, setEditVariableDialog] = useState(false)\n    const [editVariableDialogProps, setEditVariableDialogProps] = useState({})\n\n    const anchorRef = useRef(null)\n    const ps = useRef()\n\n    const getSpecificNodeApi = useApi(nodesApi.getSpecificNode)\n\n    const scrollTop = () => {\n        const curr = ps.current\n        if (curr) {\n            curr.scrollTop = 0\n        }\n    }\n\n    const handleClose = (event) => {\n        if (anchorRef.current && anchorRef.current.contains(event.target)) {\n            return\n        }\n        setOpen(false)\n        setVariableSelectorOpen(false)\n    }\n\n    const handleToggle = () => {\n        setOpen((prevOpen) => !prevOpen)\n        if (open) setVariableSelectorOpen(false)\n    }\n\n    const handleAccordionChange = (paramsType) => (event, isExpanded) => {\n        setExpanded(isExpanded ? paramsType : false)\n        scrollTop()\n    }\n\n    const handleNodeLabelChange = (event) => {\n        setNodeLabel(event.target.value)\n    }\n\n    const saveNodeLabel = () => {\n        onNodeLabelUpdate(nodeLabel)\n    }\n\n    const onEditVariableDialogOpen = (input, values, arrayItemBody) => {\n        const variableNodesIds = getAvailableNodeIdsForVariable(nodes, edges, node.id)\n\n        const nodesForVariable = []\n        for (let i = 0; i < variableNodesIds.length; i += 1) {\n            const nodeId = variableNodesIds[i]\n            const node = nodes.find((nd) => nd.id === nodeId)\n            nodesForVariable.push(node)\n        }\n\n        const dialogProps = {\n            input,\n            values,\n            arrayItemBody,\n            availableNodesForVariable: nodesForVariable,\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Save'\n        }\n\n        setEditVariableDialogProps(dialogProps)\n        setEditVariableDialog(true)\n    }\n\n    const setVariableSelectorState = (variableSelectorState, body) => {\n        setVariableSelectorOpen(variableSelectorState)\n        if (body) {\n            setVariableBody(body)\n            const variableNodesIds = getAvailableNodeIdsForVariable(nodes, edges, node.id)\n\n            const nodesForVariable = []\n            for (let i = 0; i < variableNodesIds.length; i += 1) {\n                const nodeId = variableNodesIds[i]\n                const node = nodes.find((nd) => nd.id === nodeId)\n                nodesForVariable.push(node)\n            }\n            setAvailableNodesForVariable(nodesForVariable)\n        }\n    }\n\n    const paramsChanged = (formParams, paramsType) => {\n        // Because formParams options can be changed due to show hide options,\n        // To avoid that, replace with original details options\n\n        const credentialMethodParam = formParams.find((param) => param.name === 'credentialMethod')\n        const credentialMethodParamIndex = formParams.findIndex((param) => param.name === 'credentialMethod')\n\n        if (credentialMethodParam !== undefined) {\n            const originalParam = nodeDetails[paramsType].find((param) => param.name === 'credentialMethod')\n            if (originalParam !== undefined) {\n                formParams[credentialMethodParamIndex]['options'] = originalParam.options\n            }\n        }\n\n        const updateNodeDetails = {\n            ...nodeDetails,\n            [paramsType]: formParams\n        }\n        setNodeDetails(updateNodeDetails)\n    }\n\n    const valueChanged = (formValues, paramsType) => {\n        const updateNodeFlowData = {\n            ...nodeFlowData,\n            [paramsType]: formValues\n        }\n        // If input parameters change, notify output has to be retest\n        if (nodeFlowData.outputResponses) {\n            const outputResponsesFlowData = nodeFlowData.outputResponses\n            outputResponsesFlowData.submit = null\n            outputResponsesFlowData.needRetest = true\n            updateNodeFlowData.outputResponses = outputResponsesFlowData\n        }\n\n        setNodeFlowData(updateNodeFlowData)\n        onNodeValuesUpdate(updateNodeFlowData)\n    }\n\n    const onVariableSelected = (returnVariablePath) => {\n        if (variableBody) {\n            const path = variableBody.path\n            const paramsType = variableBody.paramsType\n            const newInput = `${variableBody.textBeforeCursorPosition}{{${returnVariablePath}}}${variableBody.textAfterCursorPosition}`\n            const clonedNodeFlowData = lodash.cloneDeep(nodeFlowData)\n            lodash.set(clonedNodeFlowData, path, newInput)\n            valueChanged(clonedNodeFlowData[paramsType], paramsType)\n        }\n    }\n\n    const onSubmit = (formValues, paramsType) => {\n        const updateNodeFlowData = {\n            ...nodeFlowData,\n            [paramsType]: formValues\n        }\n        setNodeFlowData(updateNodeFlowData)\n        onNodeValuesUpdate(updateNodeFlowData)\n\n        const index = nodeParamsType.indexOf(paramsType)\n        if (index >= 0 && index !== nodeParamsType.length - 1) {\n            setExpanded(nodeParamsType[index + 1])\n            scrollTop()\n        }\n    }\n\n    const showHideParameters = (input, displayType, index, toBeDeleteParams) => {\n        const displayOptions = input[displayType]\n        Object.keys(displayOptions).forEach((path) => {\n            const comparisonValue = displayOptions[path]\n            if (path.includes('$index')) {\n                path = path.replace('$index', index)\n            }\n            const groundValue = lodash.get(nodeFlowData, path, '')\n\n            if (Array.isArray(comparisonValue)) {\n                if (displayType === 'show' && !comparisonValue.includes(groundValue)) {\n                    toBeDeleteParams.push(input)\n                }\n                if (displayType === 'hide' && comparisonValue.includes(groundValue)) {\n                    toBeDeleteParams.push(input)\n                }\n            } else if (typeof comparisonValue === 'string') {\n                if (displayType === 'show' && !(comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))) {\n                    toBeDeleteParams.push(input)\n                }\n                if (displayType === 'hide' && (comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))) {\n                    toBeDeleteParams.push(input)\n                }\n            }\n        })\n    }\n\n    const displayParameters = (params, paramsType, arrayIndex) => {\n        const toBeDeleteParams = []\n\n        for (let i = 0; i < params.length; i += 1) {\n            const input = params[i]\n\n            if (input.type === 'array') {\n                const arrayInitialValue = lodash.get(nodeFlowData, `${paramsType}.${input.name}`, [])\n                const inputArray = []\n                for (let j = arrayIndex; j < arrayInitialValue.length; j += 1) {\n                    inputArray.push(displayParameters(input.array || [], paramsType, j))\n                }\n                input.arrayParams = inputArray\n            }\n            if (input.show) {\n                showHideParameters(input, 'show', arrayIndex, toBeDeleteParams)\n            }\n            if (input.hide) {\n                showHideParameters(input, 'hide', arrayIndex, toBeDeleteParams)\n            }\n        }\n\n        let returnParams = params\n        for (let i = 0; i < toBeDeleteParams.length; i += 1) {\n            returnParams = returnParams.filter((prm) => JSON.stringify(prm) !== JSON.stringify(toBeDeleteParams[i]))\n        }\n        return returnParams\n    }\n\n    const showHideOptions = (displayType, index, options) => {\n        let returnOptions = options\n        const toBeDeleteOptions = []\n\n        for (let i = 0; i < returnOptions.length; i += 1) {\n            const option = returnOptions[i]\n            const displayOptions = option[displayType]\n            if (displayOptions) {\n                Object.keys(displayOptions).forEach((path) => {\n                    const comparisonValue = displayOptions[path]\n\n                    if (path.includes('$index')) {\n                        path = path.replace('$index', index)\n                    }\n                    const groundValue = lodash.get(nodeFlowData, path, '')\n\n                    if (Array.isArray(comparisonValue)) {\n                        if (displayType === 'show' && !comparisonValue.includes(groundValue)) {\n                            toBeDeleteOptions.push(option)\n                        }\n                        if (displayType === 'hide' && comparisonValue.includes(groundValue)) {\n                            toBeDeleteOptions.push(option)\n                        }\n                    } else if (typeof comparisonValue === 'string') {\n                        if (displayType === 'show' && !(comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))) {\n                            toBeDeleteOptions.push(option)\n                        }\n                        if (displayType === 'hide' && (comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))) {\n                            toBeDeleteOptions.push(option)\n                        }\n                    }\n                })\n            }\n        }\n\n        for (let i = 0; i < toBeDeleteOptions.length; i += 1) {\n            returnOptions = returnOptions.filter((opt) => JSON.stringify(opt) !== JSON.stringify(toBeDeleteOptions[i]))\n        }\n\n        return returnOptions\n    }\n\n    const displayOptions = (params, paramsType, arrayIndex) => {\n        let clonedParams = params\n\n        for (let i = 0; i < clonedParams.length; i += 1) {\n            const input = clonedParams[i]\n\n            if (input.type === 'array') {\n                const arrayInitialValue = lodash.get(nodeFlowData, `${paramsType}.${input.name}`, [])\n                const inputArray = []\n                for (let j = arrayIndex; j < arrayInitialValue.length; j += 1) {\n                    inputArray.push(displayOptions(input.arrayParams[j] || [], paramsType, j))\n                }\n                input.arrayParams = inputArray\n            }\n\n            if (input.type === 'options') {\n                input.options = showHideOptions('show', arrayIndex, input.options)\n                input.options = showHideOptions('hide', arrayIndex, input.options)\n            }\n        }\n\n        return clonedParams\n    }\n\n    const setYupValidation = (params) => {\n        const validationSchema = {}\n        for (let i = 0; i < params.length; i += 1) {\n            const input = params[i]\n            let inputOptional = input.optional\n\n            if (typeof input.optional === 'object' && input.optional !== null) {\n                const keys = Object.keys(input.optional)\n                inputOptional = true\n                for (let j = 0; j < keys.length; j += 1) {\n                    const path = keys[j]\n                    const comparisonValue = input.optional[path]\n                    const groundValue = lodash.get(nodeFlowData, path, '')\n\n                    if (Array.isArray(comparisonValue)) {\n                        inputOptional = inputOptional && comparisonValue.includes(groundValue)\n                    } else if (typeof comparisonValue === 'string') {\n                        inputOptional = inputOptional && (comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))\n                    }\n                }\n            }\n\n            if (\n                (input.type === 'string' ||\n                    input.type === 'password' ||\n                    input.type === 'date' ||\n                    input.type === 'code' ||\n                    input.type === 'json' ||\n                    input.type === 'file' ||\n                    input.type === 'options' ||\n                    input.type === 'asyncOptions') &&\n                !inputOptional\n            ) {\n                validationSchema[input.name] = Yup.string().required(`${input.label} is required. Type: ${input.type}`)\n            } else if (input.type === 'number' && !inputOptional) {\n                validationSchema[input.name] = Yup.string()\n                    .required(`${input.label} is required. Type: ${input.type}`)\n                    .matches(numberOrExpressionRegex, `${input.label} must be numbers or a variable expression.`)\n            } else if (input.type === 'array' && !inputOptional) {\n                /*\n                ************\n                * Limitation on different object shape within array: https://github.com/jquense/yup/issues/757\n                ************\n                const innerValidationSchema = setYupValidation(input.arrayParams);\n                validationSchema[input.name] = Yup.array(Yup.object(innerValidationSchema)).required(`Must have ${input.label}`).min(1, `Minimum of 1 ${input.label}`);\n                */\n            }\n        }\n        return validationSchema\n    }\n\n    const initializeFormValuesAndParams = (paramsType) => {\n        const initialValues = {}\n\n        const reorganizedParams = displayParameters(nodeDetails[paramsType] || [], paramsType, 0)\n        let nodeParams = displayOptions(lodash.cloneDeep(reorganizedParams), paramsType, 0)\n\n        nodeParams = handleCredentialParams(nodeParams, paramsType, reorganizedParams, nodeFlowData)\n\n        for (let i = 0; i < nodeParams.length; i += 1) {\n            const input = nodeParams[i]\n\n            // Load from nodeFlowData values\n            if (paramsType in nodeFlowData && input.name in nodeFlowData[paramsType]) {\n                initialValues[input.name] = nodeFlowData[paramsType][input.name]\n\n                // Check if option value is still available from the list of options\n                if (input.type === 'options') {\n                    const optionVal = input.options.find((option) => option.name === initialValues[input.name])\n                    if (!optionVal) delete initialValues[input.name]\n                }\n            } else {\n                // Load from nodeParams default values\n                initialValues[input.name] = input.default || ''\n\n                /**\n                 * Special case for array, always initialize the item if default is not set\n                 * Disabling for now\n                if (input.type === 'array' && !input.default) {\n                    const newObj = {}\n                    for (let j = 0; j < input.array.length; j += 1) {\n                        newObj[input.array[j].name] = input.array[j].default || ''\n                    }\n                    initialValues[input.name] = [newObj]\n                }\n                */\n            }\n        }\n\n        initialValues.submit = null\n\n        setNodeParamsInitialValues(initialValues)\n        setNodeParamsValidation(setYupValidation(nodeParams))\n        setNodeParams(nodeParams)\n    }\n\n    // Handle Accordian\n    const prevOpen = useRef(open)\n    useEffect(() => {\n        if (prevOpen.current === true && open === false) {\n            anchorRef.current.focus()\n        }\n\n        prevOpen.current = open\n    }, [open])\n\n    // Get Node Details from API\n    useEffect(() => {\n        if (getSpecificNodeApi.data) {\n            const nodeDetails = getSpecificNodeApi.data\n\n            setNodeDetails(nodeDetails)\n\n            const nodeParamsType = []\n\n            if (nodeDetails.actions) nodeParamsType.push('actions')\n            if (nodeDetails.networks) nodeParamsType.push('networks')\n            if (nodeDetails.credentials) nodeParamsType.push('credentials')\n            if (nodeDetails.inputParameters) nodeParamsType.push('inputParameters')\n            nodeParamsType.push('outputResponses')\n\n            setNodeParamsType(nodeParamsType)\n\n            if (nodeParamsType.length) {\n                setExpanded(nodeParamsType[0])\n                scrollTop()\n            }\n        }\n    }, [getSpecificNodeApi.data])\n\n    // Initialization\n    useEffect(() => {\n        if (node) {\n            setOpen(true)\n            setNodeLabel(node.data.label)\n            setNodeFlowData(node.data)\n            getSpecificNodeApi.request(node.data.name)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [node])\n\n    // Initialize Parameters Initial Values & Validation\n    useEffect(() => {\n        if (nodeDetails && nodeFlowData && expanded) {\n            initializeFormValuesAndParams(expanded)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [nodeDetails, nodeFlowData, expanded])\n\n    return (\n        <>\n            <StyledFab sx={{ left: 40, top: 20 }} ref={anchorRef} size='small' color='secondary' onClick={handleToggle} title='Edit Node'>\n                {open ? <IconMinus /> : <IconPencil />}\n            </StyledFab>\n            <Popper\n                placement='bottom-end'\n                open={open}\n                anchorEl={anchorRef.current}\n                role={undefined}\n                transition\n                disablePortal\n                popperOptions={{\n                    modifiers: [\n                        {\n                            name: 'offset',\n                            options: {\n                                offset: [-80, 14]\n                            }\n                        }\n                    ]\n                }}\n                sx={{ zIndex: 1000 }}\n            >\n                {({ TransitionProps }) => (\n                    <Transitions in={open} {...TransitionProps}>\n                        <Paper>\n                            <ClickAwayListener onClickAway={handleClose}>\n                                <MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>\n                                    <Box sx={{ p: 2 }}>\n                                        <Stack>\n                                            <Typography variant='h4'>Edit Nodes</Typography>\n                                        </Stack>\n                                    </Box>\n                                    <PerfectScrollbar\n                                        containerRef={(el) => {\n                                            ps.current = el\n                                        }}\n                                        style={{ height: '100%', maxHeight: 'calc(100vh - 250px)', overflowX: 'hidden' }}\n                                    >\n                                        {!node && <Box sx={{ p: 2 }}>No data</Box>}\n\n                                        {nodeFlowData && nodeFlowData.label && (\n                                            <Box\n                                                sx={{\n                                                    pl: 4,\n                                                    pr: 4,\n                                                    pt: 2,\n                                                    pb: 2,\n                                                    textAlign: 'center',\n                                                    display: 'flex',\n                                                    flexDirection: 'row',\n                                                    alignItems: 'center'\n                                                }}\n                                            >\n                                                <TextField\n                                                    id={nodeFlowData.name}\n                                                    label='Node Label'\n                                                    variant='outlined'\n                                                    value={nodeLabel}\n                                                    onChange={handleNodeLabelChange}\n                                                    fullWidth\n                                                />\n                                                <StyledFab\n                                                    sx={{\n                                                        minHeight: 10,\n                                                        height: 27,\n                                                        width: 30,\n                                                        ml: 2\n                                                    }}\n                                                    size='small'\n                                                    color='secondary'\n                                                    title='Validate and Save'\n                                                    onClick={saveNodeLabel}\n                                                >\n                                                    <IconCheck />\n                                                </StyledFab>\n                                            </Box>\n                                        )}\n\n                                        {/* actions */}\n                                        {nodeParamsType.includes('actions') && (\n                                            <Box sx={{ p: 2 }}>\n                                                <Accordion expanded={expanded === 'actions'} onChange={handleAccordionChange('actions')}>\n                                                    <AccordionSummary\n                                                        expandIcon={<ExpandMoreIcon />}\n                                                        aria-controls='actions-content'\n                                                        id='actions-header'\n                                                    >\n                                                        <Typography variant='h4'>Actions</Typography>\n                                                        {nodeFlowData && nodeFlowData.actions && nodeFlowData.actions.submit && (\n                                                            <Avatar\n                                                                variant='rounded'\n                                                                sx={{\n                                                                    ...theme.typography.smallAvatar,\n                                                                    borderRadius: '50%',\n                                                                    background: theme.palette.success.dark,\n                                                                    color: 'white',\n                                                                    ml: 2\n                                                                }}\n                                                            >\n                                                                <IconCheck />\n                                                            </Avatar>\n                                                        )}\n                                                    </AccordionSummary>\n                                                    <AccordionDetails>\n                                                        <InputParameters\n                                                            key={node.id} // to reload whenever node changed\n                                                            params={nodeParams}\n                                                            paramsType='actions'\n                                                            initialValues={nodeParamsInitialValues}\n                                                            nodeParamsValidation={nodeParamsValidation}\n                                                            nodeFlowData={nodeFlowData}\n                                                            setVariableSelectorState={setVariableSelectorState}\n                                                            onEditVariableDialogOpen={onEditVariableDialogOpen}\n                                                            valueChanged={valueChanged}\n                                                            onSubmit={onSubmit}\n                                                        />\n                                                    </AccordionDetails>\n                                                </Accordion>\n                                                <Divider />\n                                            </Box>\n                                        )}\n\n                                        {/* networks */}\n                                        {nodeParamsType.includes('networks') && (\n                                            <Box sx={{ p: 2 }}>\n                                                <Accordion expanded={expanded === 'networks'} onChange={handleAccordionChange('networks')}>\n                                                    <AccordionSummary\n                                                        expandIcon={<ExpandMoreIcon />}\n                                                        aria-controls='networks-content'\n                                                        id='networks-header'\n                                                    >\n                                                        <Typography variant='h4'>Networks</Typography>\n                                                        {nodeFlowData && nodeFlowData.networks && nodeFlowData.networks.submit && (\n                                                            <Avatar\n                                                                variant='rounded'\n                                                                sx={{\n                                                                    ...theme.typography.smallAvatar,\n                                                                    borderRadius: '50%',\n                                                                    background: theme.palette.success.dark,\n                                                                    color: 'white',\n                                                                    ml: 2\n                                                                }}\n                                                            >\n                                                                <IconCheck />\n                                                            </Avatar>\n                                                        )}\n                                                    </AccordionSummary>\n                                                    <AccordionDetails>\n                                                        <InputParameters\n                                                            key={node.id} // to reload whenever node changed\n                                                            params={nodeParams}\n                                                            paramsType='networks'\n                                                            initialValues={nodeParamsInitialValues}\n                                                            nodeParamsValidation={nodeParamsValidation}\n                                                            nodeFlowData={nodeFlowData}\n                                                            setVariableSelectorState={setVariableSelectorState}\n                                                            onEditVariableDialogOpen={onEditVariableDialogOpen}\n                                                            valueChanged={valueChanged}\n                                                            onSubmit={onSubmit}\n                                                        />\n                                                    </AccordionDetails>\n                                                </Accordion>\n                                                <Divider />\n                                            </Box>\n                                        )}\n\n                                        {/* credentials */}\n                                        {nodeParamsType.includes('credentials') && (\n                                            <Box sx={{ p: 2 }}>\n                                                <Accordion\n                                                    expanded={expanded === 'credentials'}\n                                                    onChange={handleAccordionChange('credentials')}\n                                                >\n                                                    <AccordionSummary\n                                                        expandIcon={<ExpandMoreIcon />}\n                                                        aria-controls='credentials-content'\n                                                        id='credentials-header'\n                                                    >\n                                                        <Typography variant='h4'>Credentials</Typography>\n                                                        {nodeFlowData && nodeFlowData.credentials && nodeFlowData.credentials.submit && (\n                                                            <Avatar\n                                                                variant='rounded'\n                                                                sx={{\n                                                                    ...theme.typography.smallAvatar,\n                                                                    borderRadius: '50%',\n                                                                    background: theme.palette.success.dark,\n                                                                    color: 'white',\n                                                                    ml: 2\n                                                                }}\n                                                            >\n                                                                <IconCheck />\n                                                            </Avatar>\n                                                        )}\n                                                    </AccordionSummary>\n                                                    <AccordionDetails>\n                                                        <CredentialInput\n                                                            key={node.id} // to reload whenever node changed\n                                                            initialParams={nodeParams}\n                                                            paramsType='credentials'\n                                                            initialValues={nodeParamsInitialValues}\n                                                            initialValidation={nodeParamsValidation}\n                                                            valueChanged={valueChanged}\n                                                            paramsChanged={paramsChanged}\n                                                            onSubmit={onSubmit}\n                                                        />\n                                                    </AccordionDetails>\n                                                </Accordion>\n                                                <Divider />\n                                            </Box>\n                                        )}\n\n                                        {/* inputParameters */}\n                                        {nodeParamsType.includes('inputParameters') && (\n                                            <Box sx={{ p: 2 }}>\n                                                <Accordion\n                                                    expanded={expanded === 'inputParameters'}\n                                                    onChange={handleAccordionChange('inputParameters')}\n                                                >\n                                                    <AccordionSummary\n                                                        expandIcon={<ExpandMoreIcon />}\n                                                        aria-controls='inputParameters-content'\n                                                        id='inputParameters-header'\n                                                    >\n                                                        <Typography variant='h4'>Input Parameters</Typography>\n                                                        {nodeFlowData &&\n                                                            nodeFlowData.inputParameters &&\n                                                            nodeFlowData.inputParameters.submit && (\n                                                                <Avatar\n                                                                    variant='rounded'\n                                                                    sx={{\n                                                                        ...theme.typography.smallAvatar,\n                                                                        borderRadius: '50%',\n                                                                        background: theme.palette.success.dark,\n                                                                        color: 'white',\n                                                                        ml: 2\n                                                                    }}\n                                                                >\n                                                                    <IconCheck />\n                                                                </Avatar>\n                                                            )}\n                                                    </AccordionSummary>\n                                                    <AccordionDetails>\n                                                        <InputParameters\n                                                            key={node.id} // to reload whenever node changed\n                                                            params={nodeParams}\n                                                            paramsType='inputParameters'\n                                                            initialValues={nodeParamsInitialValues}\n                                                            nodeParamsValidation={nodeParamsValidation}\n                                                            nodeFlowData={nodeFlowData}\n                                                            setVariableSelectorState={setVariableSelectorState}\n                                                            onEditVariableDialogOpen={onEditVariableDialogOpen}\n                                                            valueChanged={valueChanged}\n                                                            onSubmit={onSubmit}\n                                                        />\n                                                    </AccordionDetails>\n                                                </Accordion>\n                                                <Divider />\n                                            </Box>\n                                        )}\n\n                                        {/* outputResponses */}\n                                        {nodeDetails && nodeFlowData && (\n                                            <Box sx={{ p: 2 }}>\n                                                <Accordion\n                                                    expanded={expanded === 'outputResponses'}\n                                                    onChange={handleAccordionChange('outputResponses')}\n                                                >\n                                                    <AccordionSummary\n                                                        expandIcon={<ExpandMoreIcon />}\n                                                        aria-controls='outputResponses-content'\n                                                        id='outputResponses-header'\n                                                    >\n                                                        <Typography variant='h4'>Output Responses</Typography>\n                                                        {nodeFlowData &&\n                                                            nodeFlowData.outputResponses &&\n                                                            nodeFlowData.outputResponses.submit && (\n                                                                <Avatar\n                                                                    variant='rounded'\n                                                                    sx={{\n                                                                        ...theme.typography.smallAvatar,\n                                                                        borderRadius: '50%',\n                                                                        background: theme.palette.success.dark,\n                                                                        color: 'white',\n                                                                        ml: 2\n                                                                    }}\n                                                                >\n                                                                    <IconCheck />\n                                                                </Avatar>\n                                                            )}\n                                                    </AccordionSummary>\n                                                    <AccordionDetails>\n                                                        <OutputResponses\n                                                            key={node.id} // to reload whenever node changed\n                                                            nodeId={node.id}\n                                                            nodeParamsType={nodeParamsType}\n                                                            nodeFlowData={nodeFlowData}\n                                                            nodes={nodes}\n                                                            edges={edges}\n                                                            workflow={workflow}\n                                                            onSubmit={onSubmit}\n                                                        />\n                                                    </AccordionDetails>\n                                                </Accordion>\n                                                <Divider />\n                                            </Box>\n                                        )}\n                                    </PerfectScrollbar>\n                                    <VariableSelector\n                                        key={JSON.stringify(availableNodesForVariable)}\n                                        nodes={availableNodesForVariable}\n                                        isVariableSelectorOpen={isVariableSelectorOpen}\n                                        anchorEl={anchorRef.current}\n                                        onVariableSelected={(returnVariablePath) => onVariableSelected(returnVariablePath)}\n                                        handleClose={() => setVariableSelectorOpen(false)}\n                                    />\n                                    <EditVariableDialog\n                                        key={JSON.stringify(editVariableDialogProps)}\n                                        show={isEditVariableDialogOpen}\n                                        dialogProps={editVariableDialogProps}\n                                        onCancel={() => setEditVariableDialog(false)}\n                                        onConfirm={(updateValues) => {\n                                            valueChanged(updateValues, expanded)\n                                            setEditVariableDialog(false)\n                                        }}\n                                    />\n                                </MainCard>\n                            </ClickAwayListener>\n                        </Paper>\n                    </Transitions>\n                )}\n            </Popper>\n        </>\n    )\n}\n\nEditNodes.propTypes = {\n    node: PropTypes.object,\n    nodes: PropTypes.array,\n    edges: PropTypes.array,\n    workflow: PropTypes.object,\n    onNodeLabelUpdate: PropTypes.func,\n    onNodeValuesUpdate: PropTypes.func\n}\n\nexport default EditNodes\n"
  },
  {
    "path": "packages/ui/src/views/canvas/VariableSelector.js",
    "content": "import { useState, useRef, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport { Box, List, Accordion, AccordionSummary, AccordionDetails, Paper, Popper, Stack, Typography, IconButton } from '@mui/material'\n\n// third-party\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport ReactJson from 'react-json-view'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport Transitions from 'ui-component/extended/Transitions'\nimport ExpandDataDialog from 'ui-component/dialog/ExpandDataDialog'\nimport { StyledFab } from 'ui-component/StyledFab'\n\n// icons\nimport { IconX, IconArrowsMaximize } from '@tabler/icons'\n\n// ==============================|| VARIABLE SELECTOR ||============================== //\n\nconst isPositiveNumeric = (value) => /^\\d+$/.test(value)\n\nconst VariableSelector = ({ nodes, isVariableSelectorOpen, anchorEl, onVariableSelected, handleClose }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [expanded, setExpanded] = useState(false)\n    const [open, setOpen] = useState(false)\n    const [showExpandDialog, setShowExpandDialog] = useState(false)\n    const [expandDialogProps, setExpandDialogProps] = useState({})\n\n    const varPrevOpen = useRef(open)\n\n    const handleAccordionChange = (nodeLabel) => (event, isExpanded) => {\n        setExpanded(isExpanded ? nodeLabel : false)\n    }\n\n    const onClipboardCopy = (e, node) => {\n        const namespaces = e.namespace\n        let returnVariablePath = `${node.id}`\n        for (let i = 0; i < namespaces.length; i += 1) {\n            const namespace = namespaces[i]\n            if (namespace !== 'root') {\n                if (isPositiveNumeric(namespace)) {\n                    if (returnVariablePath.endsWith('.')) {\n                        returnVariablePath = returnVariablePath.substring(0, returnVariablePath.length - 1)\n                    }\n                    returnVariablePath += `[${namespace}]`\n                } else {\n                    returnVariablePath += namespace\n                }\n                if (i !== namespaces.length - 1) {\n                    returnVariablePath += '.'\n                }\n            }\n        }\n        onVariableSelected(returnVariablePath)\n    }\n\n    const onExpandDialogClicked = (data, node) => {\n        const dialogProp = {\n            title: `Variable Data: ${node.data.label}`,\n            data,\n            node\n        }\n        setExpandDialogProps(dialogProp)\n        setShowExpandDialog(true)\n    }\n\n    // Handle Accordian\n    useEffect(() => {\n        varPrevOpen.current = open\n    }, [open])\n\n    useEffect(() => {\n        setOpen(isVariableSelectorOpen)\n    }, [isVariableSelectorOpen])\n\n    return (\n        <>\n            <Popper\n                placement='right-start'\n                open={open}\n                role={undefined}\n                transition\n                anchorEl={anchorEl}\n                disablePortal\n                popperOptions={{\n                    modifiers: [\n                        {\n                            name: 'offset',\n                            options: {\n                                offset: [55, 350]\n                            }\n                        }\n                    ]\n                }}\n                sx={{ zIndex: 900, width: 350 }}\n            >\n                {({ TransitionProps }) => (\n                    <Transitions in={open} {...TransitionProps}>\n                        <Paper>\n                            <StyledFab\n                                sx={{\n                                    minHeight: 30,\n                                    height: 30,\n                                    width: 30,\n                                    position: 'absolute',\n                                    right: -10,\n                                    top: -10\n                                }}\n                                color='secondary'\n                                size='small'\n                                onClick={handleClose}\n                            >\n                                <IconX />\n                            </StyledFab>\n                            <MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>\n                                <Box sx={{ p: 2 }}>\n                                    <Stack>\n                                        <Typography variant='h4'>Variable Selector</Typography>\n                                    </Stack>\n                                </Box>\n                                <PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 250px)', overflowX: 'hidden' }}>\n                                    {nodes && nodes.length === 0 && <Box sx={{ p: 2 }}>No variables</Box>}\n\n                                    {nodes && nodes.length > 0 && (\n                                        <Box sx={{ p: 2 }}>\n                                            <List\n                                                sx={{\n                                                    width: '100%',\n                                                    maxWidth: 330,\n                                                    py: 0,\n                                                    borderRadius: '10px',\n                                                    [theme.breakpoints.down('md')]: {\n                                                        maxWidth: 300\n                                                    },\n                                                    '& .MuiListItemSecondaryAction-root': {\n                                                        top: 22\n                                                    },\n                                                    '& .MuiDivider-root': {\n                                                        my: 0\n                                                    },\n                                                    '& .list-container': {\n                                                        pl: 7\n                                                    }\n                                                }}\n                                            >\n                                                {nodes.map((node, index) => (\n                                                    <Box key={index}>\n                                                        <Accordion\n                                                            expanded={expanded === node.data.label}\n                                                            onChange={handleAccordionChange(node.data.label)}\n                                                        >\n                                                            <AccordionSummary\n                                                                expandIcon={<ExpandMoreIcon />}\n                                                                aria-controls={`${node.data.label}-content`}\n                                                                id={`${node.data.label}-header`}\n                                                            >\n                                                                <Typography variant='h5'>{node.data.label}</Typography>\n                                                            </AccordionSummary>\n                                                            <AccordionDetails>\n                                                                <div style={{ position: 'relative' }}>\n                                                                    <ReactJson\n                                                                        theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                                                                        style={{ padding: 10, borderRadius: 10 }}\n                                                                        collapsed\n                                                                        src={\n                                                                            node.data.outputResponses && node.data.outputResponses.output\n                                                                                ? node.data.outputResponses.output\n                                                                                : {}\n                                                                        }\n                                                                        enableClipboard={(e) => onClipboardCopy(e, node)}\n                                                                    />\n                                                                    <IconButton\n                                                                        size='small'\n                                                                        sx={{\n                                                                            height: 25,\n                                                                            width: 25,\n                                                                            position: 'absolute',\n                                                                            top: 5,\n                                                                            right: 5\n                                                                        }}\n                                                                        title='Expand Variable'\n                                                                        color='primary'\n                                                                        onClick={() =>\n                                                                            onExpandDialogClicked(\n                                                                                node.data.outputResponses &&\n                                                                                    node.data.outputResponses.output\n                                                                                    ? node.data.outputResponses.output\n                                                                                    : {},\n                                                                                node\n                                                                            )\n                                                                        }\n                                                                    >\n                                                                        <IconArrowsMaximize />\n                                                                    </IconButton>\n                                                                </div>\n                                                            </AccordionDetails>\n                                                        </Accordion>\n                                                    </Box>\n                                                ))}\n                                            </List>\n                                        </Box>\n                                    )}\n                                </PerfectScrollbar>\n                            </MainCard>\n                        </Paper>\n                    </Transitions>\n                )}\n            </Popper>\n            <ExpandDataDialog\n                enableClipboard\n                show={showExpandDialog}\n                dialogProps={expandDialogProps}\n                onCancel={() => setShowExpandDialog(false)}\n                onCopyClick={(e, node) => {\n                    onClipboardCopy(e, node)\n                    setShowExpandDialog(false)\n                }}\n            ></ExpandDataDialog>\n        </>\n    )\n}\n\nVariableSelector.propTypes = {\n    nodes: PropTypes.array,\n    isVariableSelectorOpen: PropTypes.bool,\n    anchorEl: PropTypes.any,\n    onVariableSelected: PropTypes.func,\n    handleClose: PropTypes.func\n}\n\nexport default VariableSelector\n"
  },
  {
    "path": "packages/ui/src/views/canvas/index.css",
    "content": ".edgebutton {\n    width: 20px;\n    height: 20px;\n    background: #eee;\n    border: 1px solid #fff;\n    cursor: pointer;\n    border-radius: 50%;\n    font-size: 12px;\n    line-height: 1;\n}\n\n.edgebutton:hover {\n    background: #5e35b1;\n    color: #eee;\n    box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0.08);\n}\n\n.edgebutton-foreignobject div {\n    background: transparent;\n    width: 40px;\n    height: 40px;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 40px;\n}\n\n.reactflow-parent-wrapper {\n    display: flex;\n    flex-grow: 1;\n    height: 100%;\n}\n\n.reactflow-parent-wrapper .reactflow-wrapper {\n    flex-grow: 1;\n    height: 100%;\n}\n"
  },
  {
    "path": "packages/ui/src/views/canvas/index.js",
    "content": "import { useEffect, useRef, useState, useCallback } from 'react'\nimport ReactFlow, { addEdge, MiniMap, Controls, Background, useNodesState, useEdgesState } from 'reactflow'\nimport 'reactflow/dist/style.css'\n\nimport { useDispatch, useSelector } from 'react-redux'\nimport { useNavigate } from 'react-router-dom'\nimport { usePrompt } from '../../utils/usePrompt'\nimport {\n    REMOVE_DIRTY,\n    SET_DIRTY,\n    SET_WORKFLOW,\n    enqueueSnackbar as enqueueSnackbarAction,\n    closeSnackbar as closeSnackbarAction\n} from 'store/actions'\n\n// material-ui\nimport { Toolbar, Box, AppBar, Button, Fab, CircularProgress } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// project imports\nimport CanvasNode from './CanvasNode'\nimport ButtonEdge from './ButtonEdge'\nimport CanvasHeader from './CanvasHeader'\nimport AddNodes from './AddNodes'\nimport EditNodes from './EditNodes'\nimport ConfirmDialog from 'ui-component/dialog/ConfirmDialog'\nimport TestWorkflowDialog from 'ui-component/dialog/TestWorkflowDialog'\n\n// API\nimport nodesApi from 'api/nodes'\nimport workflowsApi from 'api/workflows'\nimport webhooksApi from 'api/webhooks'\n\n// Hooks\nimport useApi from 'hooks/useApi'\nimport useConfirm from 'hooks/useConfirm'\n\n// icons\nimport { IconX, IconBolt } from '@tabler/icons'\n\n// third party\nimport socketIOClient from 'socket.io-client'\n\n// utils\nimport {\n    generateWebhookEndpoint,\n    getUniqueNodeId,\n    checkIfNodeLabelUnique,\n    addAnchors,\n    getEdgeLabelName,\n    checkMultipleTriggers\n} from 'utils/genericHelper'\nimport useNotifier from 'utils/useNotifier'\n\n// const\nimport { baseURL } from 'store/constant'\n\nconst nodeTypes = { customNode: CanvasNode }\nconst edgeTypes = { buttonedge: ButtonEdge }\n\n// ==============================|| CANVAS ||============================== //\n\nconst Canvas = () => {\n    const theme = useTheme()\n    const navigate = useNavigate()\n\n    const URLpath = document.location.pathname.toString().split('/')\n    const workflowShortId = URLpath[URLpath.length - 1] && URLpath[URLpath.length - 1].startsWith('W') ? URLpath[URLpath.length - 1] : ''\n\n    const { confirm } = useConfirm()\n\n    const dispatch = useDispatch()\n    const canvas = useSelector((state) => state.canvas)\n    const [canvasDataStore, setCanvasDataStore] = useState(canvas)\n    const [workflow, setWorkflow] = useState(null)\n    const [isTestWorkflowDialogOpen, setTestWorkflowDialogOpen] = useState(false)\n    const [testWorkflowDialogProps, setTestWorkflowDialogProps] = useState({})\n    const [isTestingWorkflow, setIsTestingWorkflow] = useState(false)\n\n    // ==============================|| Snackbar ||============================== //\n\n    useNotifier()\n    const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))\n    const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))\n\n    // ==============================|| ReactFlow ||============================== //\n\n    const [nodes, setNodes, onNodesChange] = useNodesState()\n    const [edges, setEdges, onEdgesChange] = useEdgesState()\n\n    const [rfInstance, setRfInstance] = useState(null)\n    const [selectedNode, setSelectedNode] = useState(null)\n\n    const reactFlowWrapper = useRef(null)\n\n    // ==============================|| Workflow API ||============================== //\n\n    const getNodesApi = useApi(nodesApi.getAllNodes)\n    const removeTestTriggersApi = useApi(nodesApi.removeTestTriggers)\n    const deleteAllTestWebhooksApi = useApi(webhooksApi.deleteAllTestWebhooks)\n    const createNewWorkflowApi = useApi(workflowsApi.createNewWorkflow)\n    const testWorkflowApi = useApi(workflowsApi.testWorkflow)\n    const updateWorkflowApi = useApi(workflowsApi.updateWorkflow)\n    const getSpecificWorkflowApi = useApi(workflowsApi.getSpecificWorkflow)\n\n    // ==============================|| Events & Actions ||============================== //\n\n    const onConnect = (params) => {\n        const newEdge = {\n            ...params,\n            type: 'buttonedge',\n            id: `${params.source}-${params.sourceHandle}-${params.target}-${params.targetHandle}`,\n            data: { label: getEdgeLabelName(params.sourceHandle) }\n        }\n        setEdges((eds) => addEdge(newEdge, eds))\n        setDirty()\n    }\n\n    const handleTestWorkflow = () => {\n        try {\n            if (workflow.deployed) {\n                alert('Testing workflow requires stopping deployed workflow. Please stop deployed workflow first')\n                return\n            }\n            const rfInstanceObject = rfInstance.toObject()\n            const nodes = rfInstanceObject.nodes || []\n            setTestWorkflowDialogOpen(true)\n            setTestWorkflowDialogProps({\n                title: 'Test Workflow',\n                nodes: nodes.filter((nd) => !nd.id.includes('ifElse'))\n            })\n        } catch (e) {\n            console.error(e)\n        }\n    }\n\n    const onStartingPointClick = (startingNodeId) => {\n        try {\n            const socket = socketIOClient(baseURL)\n            const rfInstanceObject = rfInstance.toObject()\n            const nodes = rfInstanceObject.nodes || []\n            const edges = rfInstanceObject.edges || []\n            setTestWorkflowDialogOpen(false)\n\n            socket.on('connect', () => {\n                const clientId = socket.id\n                const node = nodes.find((nd) => nd.id === startingNodeId)\n                const nodeData = node.data\n                const body = {\n                    nodes,\n                    edges,\n                    clientId,\n                    nodeData\n                }\n                testWorkflowApi.request(startingNodeId, body)\n                setNodes((nds) =>\n                    nds.map((node) => {\n                        node.data = {\n                            ...node.data,\n                            outputResponses: {\n                                ...node.data.outputResponses,\n                                submit: null,\n                                needRetest: null\n                            },\n                            selected: false\n                        }\n                        return node\n                    })\n                )\n                setIsTestingWorkflow(true)\n            })\n\n            socket.on('testWorkflowNodeResponse', (value) => {\n                const { nodeId, data, status } = value\n\n                const node = nodes.find((nd) => nd.id === nodeId)\n                if (node) {\n                    const outputValues = {\n                        submit: status === 'FINISHED' ? true : null,\n                        needRetest: status === 'FINISHED' ? null : true,\n                        output: data\n                    }\n                    const nodeData = node.data\n                    nodeData['outputResponses'] = outputValues\n                    setNodes((nds) =>\n                        nds.map((node) => {\n                            if (node.id === nodeId) {\n                                node.data = {\n                                    ...nodeData,\n                                    selected: false\n                                }\n                            }\n                            return node\n                        })\n                    )\n                }\n            })\n\n            socket.on('testWorkflowNodeFinish', () => {\n                setIsTestingWorkflow(false)\n                socket.disconnect()\n            })\n        } catch (e) {\n            console.error(e)\n        }\n    }\n\n    const handleLoadWorkflow = (file) => {\n        try {\n            const flowData = JSON.parse(file)\n            const nodes = flowData.nodes || []\n\n            for (let i = 0; i < nodes.length; i += 1) {\n                const nodeData = nodes[i].data\n                if (nodeData.type === 'webhook') nodeData.webhookEndpoint = generateWebhookEndpoint()\n            }\n\n            setNodes(nodes)\n            setEdges(flowData.edges || [])\n            setDirty()\n        } catch (e) {\n            console.error(e)\n        }\n    }\n\n    const handleDeployWorkflow = async () => {\n        if (rfInstance) {\n            const rfInstanceObject = rfInstance.toObject()\n            const flowData = JSON.stringify(rfInstanceObject)\n\n            try {\n                // Always save workflow first\n                let savedWorkflowResponse\n                if (!workflow.shortId) {\n                    const newWorkflowBody = {\n                        name: workflow.name,\n                        deployed: false,\n                        flowData\n                    }\n                    const response = await workflowsApi.createNewWorkflow(newWorkflowBody)\n                    savedWorkflowResponse = response.data\n                } else {\n                    const updateBody = {\n                        flowData\n                    }\n                    const response = await workflowsApi.updateWorkflow(workflow.shortId, updateBody)\n                    savedWorkflowResponse = response.data\n                }\n\n                dispatch({ type: REMOVE_DIRTY })\n\n                // Then deploy\n                const response = await workflowsApi.deployWorkflow(savedWorkflowResponse.shortId)\n                const deployedWorkflowResponse = response.data\n                dispatch({ type: SET_WORKFLOW, workflow: deployedWorkflowResponse })\n\n                enqueueSnackbar({\n                    message: 'Workflow deployed!',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n            } catch (error) {\n                const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n                enqueueSnackbar({\n                    message: errorData,\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'error',\n                        persist: true,\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n            }\n        }\n    }\n\n    const handleStopWorkflow = async () => {\n        try {\n            const response = await workflowsApi.deployWorkflow(workflow.shortId, { halt: true })\n            const stoppedWorkflowResponse = response.data\n            dispatch({ type: SET_WORKFLOW, workflow: stoppedWorkflowResponse })\n\n            enqueueSnackbar({\n                message: 'Workflow stopped',\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'success',\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: errorData,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n        }\n    }\n\n    const handleDeleteWorkflow = async () => {\n        const confirmPayload = {\n            title: `Delete`,\n            description: `Delete workflow ${workflow.name}?`,\n            confirmButtonName: 'Delete',\n            cancelButtonName: 'Cancel'\n        }\n        const isConfirmed = await confirm(confirmPayload)\n\n        if (isConfirmed) {\n            try {\n                await workflowsApi.deleteWorkflow(workflow.shortId)\n                navigate(-1)\n            } catch (error) {\n                const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n                enqueueSnackbar({\n                    message: errorData,\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'error',\n                        persist: true,\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n            }\n        }\n    }\n\n    const handleSaveFlow = (workflowName) => {\n        if (rfInstance) {\n            setNodes((nds) =>\n                nds.map((node) => {\n                    node.data = {\n                        ...node.data,\n                        selected: false\n                    }\n                    return node\n                })\n            )\n\n            const rfInstanceObject = rfInstance.toObject()\n            const flowData = JSON.stringify(rfInstanceObject)\n\n            if (!workflow.shortId) {\n                const newWorkflowBody = {\n                    name: workflowName,\n                    deployed: false,\n                    flowData\n                }\n                createNewWorkflowApi.request(newWorkflowBody)\n            } else {\n                const updateBody = {\n                    name: workflowName,\n                    flowData\n                }\n                updateWorkflowApi.request(workflow.shortId, updateBody)\n            }\n        }\n    }\n\n    // eslint-disable-next-line\n    const onNodeDoubleClick = useCallback((event, clickedNode) => {\n        setSelectedNode(clickedNode)\n        setNodes((nds) =>\n            nds.map((node) => {\n                if (node.id === clickedNode.id) {\n                    node.data = {\n                        ...node.data,\n                        selected: true\n                    }\n                } else {\n                    node.data = {\n                        ...node.data,\n                        selected: false\n                    }\n                }\n\n                return node\n            })\n        )\n    })\n\n    const onNodeContextMenu = (event, clickedNode) => {\n        event.preventDefault()\n        setSelectedNode(clickedNode)\n        setNodes((nds) =>\n            nds.map((node) => {\n                if (node.id === clickedNode.id) {\n                    node.data = {\n                        ...node.data,\n                        selected: true\n                    }\n                } else {\n                    node.data = {\n                        ...node.data,\n                        selected: false\n                    }\n                }\n                return node\n            })\n        )\n    }\n\n    // eslint-disable-next-line\n    const onNodeLabelUpdate = useCallback((nodeLabel) => {\n        setNodes((nds) =>\n            nds.map((node) => {\n                if (node.id === selectedNode.id) {\n                    if (!checkIfNodeLabelUnique(nodeLabel, rfInstance.getNodes())) {\n                        enqueueSnackbar({\n                            message: 'Duplicated node label',\n                            options: {\n                                key: new Date().getTime() + Math.random(),\n                                variant: 'error',\n                                persist: true,\n                                action: (key) => (\n                                    <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                        <IconX />\n                                    </Button>\n                                )\n                            }\n                        })\n                    } else {\n                        if (node.data.label !== nodeLabel) {\n                            setTimeout(() => setDirty(), 0)\n                        }\n                        node.data = {\n                            ...node.data,\n                            label: nodeLabel\n                        }\n                    }\n                }\n                return node\n            })\n        )\n    })\n\n    // eslint-disable-next-line\n    const onNodeValuesUpdate = useCallback((nodeFlowData) => {\n        setNodes((nds) =>\n            nds.map((node) => {\n                if (node.id === selectedNode.id) {\n                    setTimeout(() => setDirty(), 0)\n                    node.data = {\n                        ...node.data,\n                        ...nodeFlowData,\n                        selected: true\n                    }\n                }\n                return node\n            })\n        )\n    })\n\n    const onDragOver = useCallback((event) => {\n        event.preventDefault()\n        event.dataTransfer.dropEffect = 'move'\n    }, [])\n\n    const onDrop = useCallback(\n        (event) => {\n            event.preventDefault()\n            const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect()\n            let nodeData = event.dataTransfer.getData('application/reactflow')\n\n            // check if the dropped element is valid\n            if (typeof nodeData === 'undefined' || !nodeData) {\n                return\n            }\n\n            nodeData = JSON.parse(nodeData)\n\n            // check if workflow contains multiple triggers/webhooks\n            if ((nodeData.type === 'webhook' || nodeData.type === 'trigger') && checkMultipleTriggers(rfInstance.getNodes())) {\n                enqueueSnackbar({\n                    message: 'Workflow can only contains 1 trigger or webhook node',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'error',\n                        persist: true,\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                return\n            }\n\n            if (nodeData.type === 'webhook') nodeData.webhookEndpoint = generateWebhookEndpoint()\n\n            const position = rfInstance.project({\n                x: event.clientX - reactFlowBounds.left - 100,\n                y: event.clientY - reactFlowBounds.top - 50\n            })\n\n            const newNodeId = getUniqueNodeId(nodeData, rfInstance.getNodes())\n\n            const newNode = {\n                id: newNodeId,\n                position,\n                type: 'customNode',\n                data: addAnchors(nodeData, rfInstance.getNodes(), newNodeId)\n            }\n\n            setSelectedNode(newNode)\n            setNodes((nds) =>\n                nds.concat(newNode).map((node) => {\n                    if (node.id === newNode.id) {\n                        node.data = {\n                            ...node.data,\n                            selected: true\n                        }\n                    } else {\n                        node.data = {\n                            ...node.data,\n                            selected: false\n                        }\n                    }\n\n                    return node\n                })\n            )\n            setTimeout(() => setDirty(), 0)\n        },\n\n        // eslint-disable-next-line\n        [rfInstance]\n    )\n\n    const saveWorkflowSuccess = () => {\n        dispatch({ type: REMOVE_DIRTY })\n        enqueueSnackbar({\n            message: 'Workflow saved',\n            options: {\n                key: new Date().getTime() + Math.random(),\n                variant: 'success',\n                action: (key) => (\n                    <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                        <IconX />\n                    </Button>\n                )\n            }\n        })\n    }\n\n    const errorFailed = (message) => {\n        enqueueSnackbar({\n            message,\n            options: {\n                key: new Date().getTime() + Math.random(),\n                variant: 'error',\n                persist: true,\n                action: (key) => (\n                    <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                        <IconX />\n                    </Button>\n                )\n            }\n        })\n    }\n\n    const setDirty = () => {\n        dispatch({ type: SET_DIRTY })\n    }\n\n    // ==============================|| useEffect ||============================== //\n\n    // Get specific workflow successful\n    useEffect(() => {\n        if (getSpecificWorkflowApi.data) {\n            const workflow = getSpecificWorkflowApi.data\n            const initialFlow = workflow.flowData ? JSON.parse(workflow.flowData) : []\n            setNodes(initialFlow.nodes || [])\n            setEdges(initialFlow.edges || [])\n            dispatch({ type: SET_WORKFLOW, workflow })\n        } else if (getSpecificWorkflowApi.error) {\n            const error = getSpecificWorkflowApi.error\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            errorFailed(`Failed to retrieve workflow: ${errorData}`)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [getSpecificWorkflowApi.data, getSpecificWorkflowApi.error])\n\n    // Create new workflow successful\n    useEffect(() => {\n        if (createNewWorkflowApi.data) {\n            const workflow = createNewWorkflowApi.data\n            dispatch({ type: SET_WORKFLOW, workflow })\n            saveWorkflowSuccess()\n            window.history.replaceState(null, null, `/canvas/${workflow.shortId}`)\n        } else if (createNewWorkflowApi.error) {\n            const error = createNewWorkflowApi.error\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            errorFailed(`Failed to save workflow: ${errorData}`)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [createNewWorkflowApi.data, createNewWorkflowApi.error])\n\n    // Update workflow successful\n    useEffect(() => {\n        if (updateWorkflowApi.data) {\n            dispatch({ type: SET_WORKFLOW, workflow: updateWorkflowApi.data })\n            saveWorkflowSuccess()\n        } else if (updateWorkflowApi.error) {\n            const error = updateWorkflowApi.error\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            errorFailed(`Failed to save workflow: ${errorData}`)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [updateWorkflowApi.data, updateWorkflowApi.error])\n\n    // Test workflow failed\n    useEffect(() => {\n        if (testWorkflowApi.error) {\n            enqueueSnackbar({\n                message: 'Test workflow failed',\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [testWorkflowApi.error])\n\n    // Listen to edge button click remove redux event\n    useEffect(() => {\n        if (rfInstance) {\n            const edges = rfInstance.getEdges()\n            const toRemoveEdgeId = canvasDataStore.removeEdgeId.split(':')[0]\n            setEdges(edges.filter((edge) => edge.id !== toRemoveEdgeId))\n            setDirty()\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [canvasDataStore.removeEdgeId])\n\n    useEffect(() => setWorkflow(canvasDataStore.workflow), [canvasDataStore.workflow])\n\n    // Initialization\n    useEffect(() => {\n        removeTestTriggersApi.request()\n        deleteAllTestWebhooksApi.request()\n\n        if (workflowShortId) {\n            getSpecificWorkflowApi.request(workflowShortId)\n        } else {\n            setNodes([])\n            setEdges([])\n            dispatch({\n                type: SET_WORKFLOW,\n                workflow: {\n                    name: 'Untitled workflow'\n                }\n            })\n        }\n\n        getNodesApi.request()\n\n        // Clear dirty state before leaving and remove any ongoing test triggers and webhooks\n        return () => {\n            removeTestTriggersApi.request()\n            deleteAllTestWebhooksApi.request()\n\n            setTimeout(() => dispatch({ type: REMOVE_DIRTY }), 0)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    useEffect(() => {\n        setCanvasDataStore(canvas)\n    }, [canvas])\n\n    useEffect(() => {\n        function handlePaste(e) {\n            const pasteData = e.clipboardData.getData('text')\n            //TODO: prevent paste event when input focused, temporary fix: catch workflow syntax\n            if (pasteData.includes('{\"nodes\":[') && pasteData.includes('],\"edges\":[')) {\n                handleLoadWorkflow(pasteData)\n            }\n        }\n\n        window.addEventListener('paste', handlePaste)\n\n        return () => {\n            window.removeEventListener('paste', handlePaste)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    usePrompt('You have unsaved changes! Do you want to navigate away?', canvasDataStore.isDirty)\n\n    return (\n        <>\n            <Box>\n                <AppBar\n                    enableColorOnDark\n                    position='fixed'\n                    color='inherit'\n                    elevation={1}\n                    sx={{\n                        bgcolor: theme.palette.background.default\n                    }}\n                >\n                    <Toolbar>\n                        <CanvasHeader\n                            workflow={workflow}\n                            handleSaveFlow={handleSaveFlow}\n                            handleDeployWorkflow={handleDeployWorkflow}\n                            handleStopWorkflow={handleStopWorkflow}\n                            handleDeleteWorkflow={handleDeleteWorkflow}\n                            handleLoadWorkflow={handleLoadWorkflow}\n                        />\n                    </Toolbar>\n                </AppBar>\n                <Box sx={{ marginTop: '70px', height: '90vh', width: '100%' }}>\n                    <div className='reactflow-parent-wrapper'>\n                        <div className='reactflow-wrapper' ref={reactFlowWrapper}>\n                            <ReactFlow\n                                nodes={nodes}\n                                edges={edges}\n                                onNodesChange={onNodesChange}\n                                onNodeDoubleClick={onNodeDoubleClick}\n                                onNodeContextMenu={onNodeContextMenu}\n                                onEdgesChange={onEdgesChange}\n                                onDrop={onDrop}\n                                onDragOver={onDragOver}\n                                onNodeDragStop={setDirty}\n                                nodeTypes={nodeTypes}\n                                edgeTypes={edgeTypes}\n                                onConnect={onConnect}\n                                onInit={setRfInstance}\n                                fitView\n                            >\n                                <MiniMap\n                                    nodeStrokeColor={() => theme.palette.primary.main}\n                                    nodeColor={() => theme.palette.primary.main}\n                                    nodeBorderRadius={2}\n                                />\n                                <Controls\n                                    style={{\n                                        display: 'flex',\n                                        flexDirection: 'row',\n                                        left: '50%',\n                                        transform: 'translate(-50%, -50%)'\n                                    }}\n                                />\n                                <Background color='#aaa' gap={16} />\n                                <AddNodes nodesData={getNodesApi.data} node={selectedNode} />\n                                <EditNodes\n                                    nodes={nodes}\n                                    edges={edges}\n                                    node={selectedNode}\n                                    workflow={workflow}\n                                    onNodeLabelUpdate={onNodeLabelUpdate}\n                                    onNodeValuesUpdate={onNodeValuesUpdate}\n                                />\n                                <Fab\n                                    sx={{ position: 'absolute', right: 20, top: 20 }}\n                                    size='small'\n                                    color='warning'\n                                    aria-label='test'\n                                    title='Test Workflow'\n                                    disabled={isTestingWorkflow}\n                                    onClick={handleTestWorkflow}\n                                >\n                                    {<IconBolt />}\n                                </Fab>\n                                {isTestingWorkflow && (\n                                    <CircularProgress\n                                        size={50}\n                                        sx={{\n                                            color: theme.palette.warning.dark,\n                                            position: 'absolute',\n                                            right: 15,\n                                            top: 15\n                                        }}\n                                    />\n                                )}\n                            </ReactFlow>\n                        </div>\n                    </div>\n                </Box>\n                <ConfirmDialog />\n                <TestWorkflowDialog\n                    show={isTestWorkflowDialogOpen}\n                    dialogProps={testWorkflowDialogProps}\n                    onCancel={() => setTestWorkflowDialogOpen(false)}\n                    onItemClick={onStartingPointClick}\n                />\n            </Box>\n        </>\n    )\n}\n\nexport default Canvas\n"
  },
  {
    "path": "packages/ui/src/views/contracts/ContractDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport PropTypes from 'prop-types'\nimport { useState, useEffect } from 'react'\nimport { useDispatch } from 'react-redux'\nimport { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'\n\nimport {\n    Avatar,\n    Accordion,\n    AccordionSummary,\n    AccordionDetails,\n    Box,\n    Divider,\n    Chip,\n    Typography,\n    Button,\n    Dialog,\n    DialogActions,\n    DialogContent,\n    DialogTitle,\n    Stack,\n    IconButton\n} from '@mui/material'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport { useTheme } from '@mui/material/styles'\n\n// third-party\nimport * as Yup from 'yup'\nimport lodash from 'lodash'\nimport { ethers } from 'ethers'\n\n// project imports\nimport InputParameters from 'views/inputs/InputParameters'\nimport CredentialInput from 'views/inputs/CredentialInput'\nimport EditVariableDialog from 'ui-component/dialog/EditVariableDialog'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// Icons\nimport { IconExclamationMark, IconCheck, IconX, IconArrowUpRightCircle, IconCopy } from '@tabler/icons'\n\n// API\nimport contractsApi from 'api/contracts'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// Const\nimport { contract_details, networks, networkExplorers } from 'store/constant'\n\n// utils\nimport { handleCredentialParams, initializeNodeData } from 'utils/genericHelper'\nimport useNotifier from 'utils/useNotifier'\n\nconst ContractDialog = ({ show, dialogProps, onCancel, onConfirm }) => {\n    const portalElement = document.getElementById('portal')\n\n    const theme = useTheme()\n    const dispatch = useDispatch()\n\n    // ==============================|| Snackbar ||============================== //\n\n    useNotifier()\n    const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))\n    const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))\n\n    const [contractDetails, setContractDetails] = useState(contract_details)\n    const [contractData, setContractData] = useState({})\n    const [contractParams, setContractParams] = useState([])\n    const [contractValues, setContractValues] = useState({})\n    const [contractValidation, setContractValidation] = useState({})\n    const [expanded, setExpanded] = useState(false)\n    const [invalidAddress, setInvalidAddress] = useState(false)\n    const [invalidABI, setInvalidABI] = useState('')\n    const [isReadyToAdd, setIsReadyToAdd] = useState(false)\n    const [isEditVariableDialogOpen, setEditVariableDialog] = useState(false)\n    const [editVariableDialogProps, setEditVariableDialogProps] = useState({})\n    const contractParamsType = ['networks', 'credentials', 'contractInfo']\n\n    const getSpecificContractApi = useApi(contractsApi.getSpecificContract)\n\n    const handleAccordionChange = (expanded) => (event, isExpanded) => {\n        setExpanded(isExpanded ? expanded : false)\n    }\n\n    const reset = () => {\n        setContractData({})\n        setContractParams([])\n        setContractValues({})\n        setContractValidation({})\n        setInvalidAddress(false)\n        setInvalidABI('')\n        setIsReadyToAdd(false)\n        setExpanded(false)\n    }\n\n    const checkIsReadyToAdd = () => {\n        for (let i = 0; i < contractParamsType.length; i += 1) {\n            const paramType = contractParamsType[i]\n            if (!contractData[paramType] || !contractData[paramType].submit) {\n                setIsReadyToAdd(false)\n                return\n            }\n        }\n        setIsReadyToAdd(true)\n    }\n\n    const onEditVariableDialogOpen = (input, values, arrayItemBody) => {\n        const dialogProps = {\n            input,\n            values,\n            arrayItemBody,\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Save',\n            hideVariables: true\n        }\n\n        setEditVariableDialogProps(dialogProps)\n        setEditVariableDialog(true)\n    }\n\n    const addNewContract = async () => {\n        const createNewContractBody = {\n            network: contractData.networks.network,\n            name: contractData.contractInfo.name,\n            abi: contractData.contractInfo.abi,\n            address: contractData.contractInfo.address,\n            providerCredential: JSON.stringify(contractData.credentials)\n        }\n        try {\n            const createResp = await contractsApi.createNewContract(createNewContractBody)\n            if (createResp.data) {\n                enqueueSnackbar({\n                    message: 'New contract added',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to add new contract: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const saveContract = async () => {\n        const saveContractBody = {\n            network: contractData.networks.network,\n            name: contractData.contractInfo.name,\n            abi: contractData.contractInfo.abi,\n            address: contractData.contractInfo.address,\n            providerCredential: JSON.stringify(contractData.credentials)\n        }\n        try {\n            const saveResp = await contractsApi.updateContract(dialogProps.id, saveContractBody)\n            if (saveResp.data) {\n                enqueueSnackbar({\n                    message: 'Contract saved',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to save contract: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const deleteContract = async () => {\n        try {\n            const deleteResp = await contractsApi.deleteContract(dialogProps.id)\n            if (deleteResp.data) {\n                enqueueSnackbar({\n                    message: 'Contract deleted',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to delete contract: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const fetchABI = async (formValues, paramsType) => {\n        const selectedNetwork = networks.find((network) => network.name === contractData.networks.network)\n        if (!selectedNetwork) return\n\n        const body = {\n            ...contractData,\n            networks: {\n                ...contractData.networks,\n                uri: selectedNetwork.uri || ''\n            }\n        }\n\n        const resp = await contractsApi.getContractABI(body)\n        if (!resp.data) {\n            const updateContractData = {\n                ...contractData,\n                [paramsType]: { ...formValues, submit: null }\n            }\n            setContractData(updateContractData)\n            setInvalidABI('Unable to fetch ABI')\n            return\n        } else {\n            const status = resp.data.status\n            if (status === '0') {\n                setInvalidABI('Unable to fetch ABI')\n                return\n            }\n            const abi = resp.data.result\n            setInvalidABI('')\n            return abi === 'Invalid API Key' ? undefined : abi\n        }\n    }\n\n    const valueChanged = (formValues, paramsType) => {\n        const updateContractData = {\n            ...contractData,\n            [paramsType]: formValues\n        }\n\n        const index = contractParamsType.indexOf(paramsType)\n        if (index >= 0 && index !== contractParamsType.length - 1) {\n            for (let i = index + 1; i < contractParamsType.length; i += 1) {\n                const paramType = contractParamsType[i]\n                if (updateContractData[paramType]) updateContractData[paramType].submit = null\n            }\n        }\n\n        setContractData(updateContractData)\n    }\n\n    const paramsChanged = (formParams, paramsType) => {\n        // Because formParams options can be changed due to show hide options,\n        // To avoid that, replace with original details options\n\n        const credentialMethodParam = formParams.find((param) => param.name === 'credentialMethod')\n        const credentialMethodParamIndex = formParams.findIndex((param) => param.name === 'credentialMethod')\n\n        if (credentialMethodParam !== undefined) {\n            const originalParam = contractDetails[paramsType].find((param) => param.name === 'credentialMethod')\n            if (originalParam !== undefined) {\n                formParams[credentialMethodParamIndex]['options'] = originalParam.options\n            }\n        }\n\n        const updateContractDetails = {\n            ...contractDetails,\n            [paramsType]: formParams\n        }\n        setContractDetails(updateContractDetails)\n    }\n\n    const onSubmit = async (formValues, paramsType) => {\n        if (formValues.address) {\n            if (ethers.utils.isAddress(formValues.address)) {\n                setInvalidAddress(false)\n                const abi = await fetchABI(formValues, paramsType)\n                if (abi) {\n                    const updateFormValues = {\n                        submit: true,\n                        ...formValues\n                    }\n                    updateFormValues.abi = abi\n                    const updateContractData = {\n                        ...contractData,\n                        [paramsType]: updateFormValues\n                    }\n                    setContractData(updateContractData)\n                } else {\n                    const updateContractData = {\n                        ...contractData,\n                        [paramsType]: { ...formValues, submit: null }\n                    }\n                    setContractData(updateContractData)\n                }\n            } else {\n                setInvalidAddress(true)\n                const updateContractData = {\n                    ...contractData,\n                    [paramsType]: { ...formValues, submit: null }\n                }\n                setContractData(updateContractData)\n            }\n        } else {\n            const updateContractData = {\n                ...contractData,\n                [paramsType]: formValues\n            }\n            setContractData(updateContractData)\n        }\n\n        const index = contractParamsType.indexOf(paramsType)\n        if (index >= 0 && index !== contractParamsType.length - 1) {\n            setExpanded(contractParamsType[index + 1])\n        }\n    }\n\n    const showHideOptions = (displayType, options) => {\n        let returnOptions = options\n        const toBeDeleteOptions = []\n\n        for (let i = 0; i < returnOptions.length; i += 1) {\n            const option = returnOptions[i]\n            const displayOptions = option[displayType]\n\n            if (displayOptions) {\n                Object.keys(displayOptions).forEach((path) => {\n                    const comparisonValue = displayOptions[path]\n                    const groundValue = lodash.get(contractData, path, '')\n                    if (Array.isArray(comparisonValue)) {\n                        if (displayType === 'show' && !comparisonValue.includes(groundValue)) {\n                            toBeDeleteOptions.push(option)\n                        }\n                        if (displayType === 'hide' && comparisonValue.includes(groundValue)) {\n                            toBeDeleteOptions.push(option)\n                        }\n                    }\n                })\n            }\n        }\n\n        for (let i = 0; i < toBeDeleteOptions.length; i += 1) {\n            returnOptions = returnOptions.filter((opt) => JSON.stringify(opt) !== JSON.stringify(toBeDeleteOptions[i]))\n        }\n\n        return returnOptions\n    }\n\n    const displayOptions = (params) => {\n        let clonedParams = params\n\n        for (let i = 0; i < clonedParams.length; i += 1) {\n            const input = clonedParams[i]\n            if (input.type === 'options') {\n                input.options = showHideOptions('show', input.options)\n                input.options = showHideOptions('hide', input.options)\n            }\n        }\n\n        return clonedParams\n    }\n\n    const setYupValidation = (params) => {\n        const validationSchema = {}\n        for (let i = 0; i < params.length; i += 1) {\n            const input = params[i]\n            if (input.type === 'string' && !input.optional) {\n                validationSchema[input.name] = Yup.string().required(`${input.label} is required. Type: ${input.type}`)\n            } else if (input.type === 'number' && !input.optional) {\n                validationSchema[input.name] = Yup.number().required(`${input.label} is required. Type: ${input.type}`)\n            } else if ((input.type === 'options' || input.type === 'asyncOptions') && !input.optional) {\n                validationSchema[input.name] = Yup.string().required(`${input.label} is required. Type: ${input.type}`)\n            }\n        }\n        return validationSchema\n    }\n\n    const initializeFormValuesAndParams = (paramsType) => {\n        const initialValues = {}\n        let contractParams = displayOptions(lodash.cloneDeep(contractDetails[paramsType] || []))\n        contractParams = handleCredentialParams(contractParams, paramsType, contractDetails[paramsType], contractData)\n\n        for (let i = 0; i < contractParams.length; i += 1) {\n            const input = contractParams[i]\n\n            // Load from contractData values\n            if (paramsType in contractData && input.name in contractData[paramsType]) {\n                initialValues[input.name] = contractData[paramsType][input.name]\n\n                // Check if option value is still available from the list of options\n                if (input.type === 'options') {\n                    const optionVal = input.options.find((option) => option.name === initialValues[input.name])\n                    if (!optionVal) delete initialValues[input.name]\n                }\n            } else {\n                // Load from contractParams default values\n                initialValues[input.name] = input.default || ''\n            }\n        }\n\n        initialValues.submit = null\n\n        setContractValues(initialValues)\n        setContractValidation(setYupValidation(contractParams))\n        setContractParams(contractParams)\n    }\n\n    const transformContractResponse = (contractResponseData, contractDetails) => {\n        const contractData = {\n            networks: {},\n            credentials: {},\n            contractInfo: {}\n        }\n\n        if (contractResponseData) {\n            contractData.networks = { network: contractResponseData.network, submit: true }\n            contractData.contractInfo = { ...contractResponseData, submit: true }\n            if (contractResponseData.providerCredential) {\n                try {\n                    contractData.credentials = JSON.parse(contractResponseData.providerCredential)\n                } catch (e) {\n                    console.error(e)\n                }\n            }\n        } else {\n            contractData.networks = initializeNodeData(contractDetails.networks)\n            contractData.credentials = initializeNodeData(contractDetails.credentials)\n            contractData.contractInfo = initializeNodeData(contractDetails.contractInfo)\n        }\n        return contractData\n    }\n\n    // Get Contract Details from API\n    useEffect(() => {\n        if (getSpecificContractApi.data) {\n            const contractResponseData = getSpecificContractApi.data\n            setContractData(transformContractResponse(contractResponseData))\n            setExpanded('networks')\n        }\n    }, [getSpecificContractApi.data])\n\n    // Initialization\n    useEffect(() => {\n        if (show && dialogProps.type === 'ADD') {\n            reset()\n            setContractData(transformContractResponse(null, contractDetails))\n            setExpanded('networks')\n        } else if (show && dialogProps.type === 'EDIT' && dialogProps.id) {\n            reset()\n            getSpecificContractApi.request(dialogProps.id)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [show, dialogProps])\n\n    // Initialize Parameters Initial Values & Validation\n    useEffect(() => {\n        if (contractDetails && contractData && expanded) {\n            initializeFormValuesAndParams(expanded)\n            checkIsReadyToAdd()\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [contractDetails, contractData, expanded])\n\n    const component = show ? (\n        <Dialog open={show} onClose={onCancel} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                {dialogProps.type === 'ADD' && (\n                    <Chip\n                        sx={{ mb: 1 }}\n                        icon={<IconExclamationMark />}\n                        label='You can only add contract which has been publicly verified'\n                        color='warning'\n                    />\n                )}\n\n                {contractData && contractData.contractInfo && contractData.contractInfo.address && dialogProps.type === 'EDIT' && (\n                    <Box sx={{ p: 2 }}>\n                        <Typography sx={{ p: 1 }} variant='overline'>\n                            ADDRESS\n                        </Typography>\n                        <Stack direction='row' sx={{ p: 1 }}>\n                            <Typography\n                                sx={{\n                                    p: 1,\n                                    borderRadius: 10,\n                                    backgroundColor: theme.palette.primary.light,\n                                    width: 'max-content',\n                                    height: 'max-content'\n                                }}\n                                variant='h5'\n                            >\n                                {contractData.contractInfo.address}\n                            </Typography>\n                            <IconButton\n                                title='Copy Address'\n                                color='primary'\n                                onClick={() => navigator.clipboard.writeText(contractData.contractInfo.address)}\n                            >\n                                <IconCopy />\n                            </IconButton>\n                            <IconButton\n                                title='Open in Block Explorer'\n                                color='primary'\n                                onClick={() =>\n                                    window.open(\n                                        `${networkExplorers[contractData.networks.network]}/address/${contractData.contractInfo.address}`,\n                                        '_blank'\n                                    )\n                                }\n                            >\n                                <IconArrowUpRightCircle />\n                            </IconButton>\n                        </Stack>\n                    </Box>\n                )}\n\n                {/* networks */}\n                <Box sx={{ p: 2 }}>\n                    <Accordion expanded={expanded === 'networks'} onChange={handleAccordionChange('networks')}>\n                        <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls='networks-content' id='networks-header'>\n                            <Typography variant='h4'>Networks</Typography>\n                            {contractData && contractData.networks && contractData.networks.submit && (\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.smallAvatar,\n                                        borderRadius: '50%',\n                                        background: theme.palette.success.dark,\n                                        color: 'white',\n                                        ml: 2\n                                    }}\n                                >\n                                    <IconCheck />\n                                </Avatar>\n                            )}\n                        </AccordionSummary>\n                        <AccordionDetails>\n                            <InputParameters\n                                paramsType='networks'\n                                params={contractParams}\n                                initialValues={contractValues}\n                                nodeParamsValidation={contractValidation}\n                                valueChanged={valueChanged}\n                                onSubmit={onSubmit}\n                                setVariableSelectorState={() => null}\n                                onEditVariableDialogOpen={onEditVariableDialogOpen}\n                            />\n                        </AccordionDetails>\n                    </Accordion>\n                    <Divider />\n                </Box>\n\n                {/* credentials */}\n                <Box sx={{ p: 2 }}>\n                    <Accordion expanded={expanded === 'credentials'} onChange={handleAccordionChange('credentials')}>\n                        <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls='credentials-content' id='credentials-header'>\n                            <Typography variant='h4'>Credentials</Typography>\n                            {contractData && contractData.credentials && contractData.credentials.submit && (\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.smallAvatar,\n                                        borderRadius: '50%',\n                                        background: theme.palette.success.dark,\n                                        color: 'white',\n                                        ml: 2\n                                    }}\n                                >\n                                    <IconCheck />\n                                </Avatar>\n                            )}\n                        </AccordionSummary>\n                        <AccordionDetails>\n                            <CredentialInput\n                                paramsType='credentials'\n                                initialParams={contractParams}\n                                initialValues={contractValues}\n                                initialValidation={contractValidation}\n                                valueChanged={valueChanged}\n                                paramsChanged={paramsChanged}\n                                onSubmit={onSubmit}\n                            />\n                        </AccordionDetails>\n                    </Accordion>\n                    <Divider />\n                </Box>\n\n                {/* contractInfo */}\n                <Box sx={{ p: 2 }}>\n                    <Accordion expanded={expanded === 'contractInfo'} onChange={handleAccordionChange('contractInfo')}>\n                        <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls='contractInfo-content' id='contractInfo-header'>\n                            <Typography variant='h4'>Contract Details</Typography>\n                            {contractData && contractData.contractInfo && contractData.contractInfo.submit && (\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.smallAvatar,\n                                        borderRadius: '50%',\n                                        background: theme.palette.success.dark,\n                                        color: 'white',\n                                        ml: 2\n                                    }}\n                                >\n                                    <IconCheck />\n                                </Avatar>\n                            )}\n                        </AccordionSummary>\n                        <AccordionDetails>\n                            <InputParameters\n                                paramsType='contractInfo'\n                                params={contractParams}\n                                initialValues={contractValues}\n                                nodeParamsValidation={contractValidation}\n                                valueChanged={valueChanged}\n                                onSubmit={onSubmit}\n                                setVariableSelectorState={() => null}\n                                onEditVariableDialogOpen={onEditVariableDialogOpen}\n                            />\n                            {invalidAddress && (\n                                <Chip sx={{ mt: 2, mb: 1 }} icon={<IconX />} label='Invalid Contract Address' color='error' />\n                            )}\n                            {invalidABI && (\n                                <Chip sx={{ mt: 2, mb: 1, ml: invalidAddress ? 2 : 0 }} icon={<IconX />} label={invalidABI} color='error' />\n                            )}\n                        </AccordionDetails>\n                    </Accordion>\n                    <Divider />\n                </Box>\n                <EditVariableDialog\n                    key={JSON.stringify(editVariableDialogProps)}\n                    show={isEditVariableDialogOpen}\n                    dialogProps={editVariableDialogProps}\n                    onCancel={() => setEditVariableDialog(false)}\n                    onConfirm={(updateValues) => {\n                        valueChanged(updateValues, expanded)\n                        setEditVariableDialog(false)\n                    }}\n                />\n            </DialogContent>\n            <DialogActions>\n                <Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>\n                {dialogProps.type === 'EDIT' && (\n                    <StyledButton variant='contained' color='error' onClick={() => deleteContract()}>\n                        Delete\n                    </StyledButton>\n                )}\n                <StyledButton\n                    variant='contained'\n                    disabled={!isReadyToAdd}\n                    onClick={() => (dialogProps.type === 'ADD' ? addNewContract() : saveContract())}\n                >\n                    {dialogProps.confirmButtonName}\n                </StyledButton>\n            </DialogActions>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nContractDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onConfirm: PropTypes.func\n}\n\nexport default ContractDialog\n"
  },
  {
    "path": "packages/ui/src/views/contracts/index.js",
    "content": "import { useEffect, useState } from 'react'\nimport { useSelector } from 'react-redux'\n\n// material-ui\nimport { Grid, Box, Stack } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport ItemCard from 'ui-component/cards/ItemCard'\nimport ContractDialog from './ContractDialog'\nimport ContractEmptySVG from 'assets/images/contract_empty.svg'\nimport { TooltipWithParser } from 'ui-component/TooltipWithParser'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// const\nimport { gridSpacing } from 'store/constant'\n\n// API\nimport contractsApi from 'api/contracts'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// ==============================|| CONTRACTS ||============================== //\n\nconst Contracts = () => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [isLoading, setLoading] = useState(true)\n    const [showDialog, setShowDialog] = useState(false)\n    const [dialogProps, setDialogProps] = useState({})\n\n    const getAllContractsApi = useApi(contractsApi.getAllContracts)\n\n    const addNew = () => {\n        const dialogProp = {\n            title: 'Add New Contract',\n            type: 'ADD',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Add'\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const edit = (id) => {\n        const dialogProp = {\n            title: 'Edit Contract',\n            type: 'EDIT',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Save',\n            id\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const onConfirm = () => {\n        setShowDialog(false)\n        getAllContractsApi.request()\n    }\n\n    useEffect(() => {\n        getAllContractsApi.request()\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    useEffect(() => {\n        setLoading(getAllContractsApi.loading)\n    }, [getAllContractsApi.loading])\n\n    return (\n        <>\n            <MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>\n                <Stack flexDirection='row'>\n                    <h1>Contracts&nbsp;</h1>\n                    <TooltipWithParser title='Verified smart contracts can be imported. Events and functions of smart contracts can be monitored and executed. Read <a target=\"_blank\" href=https://docs.outerbridge.io/how-to-use/contracts>more</a>' />\n                    <Grid sx={{ mb: 1.25 }} container direction='row'>\n                        <Box sx={{ flexGrow: 1 }} />\n                        <Grid item>\n                            <StyledButton variant='contained' onClick={addNew}>\n                                Add New\n                            </StyledButton>\n                        </Grid>\n                    </Grid>\n                </Stack>\n                <Grid container spacing={gridSpacing}>\n                    {!isLoading &&\n                        getAllContractsApi.data &&\n                        getAllContractsApi.data.map((data, index) => (\n                            <Grid key={index} item lg={4} md={6} sm={6} xs={12}>\n                                <ItemCard isLoading={isLoading} onClick={() => edit(data._id)} data={data} />\n                            </Grid>\n                        ))}\n                </Grid>\n                {!isLoading && (!getAllContractsApi.data || getAllContractsApi.data.length === 0) && (\n                    <Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>\n                        <Box sx={{ p: 2, height: 'auto' }}>\n                            <img\n                                style={{ objectFit: 'cover', height: '30vh', width: 'auto' }}\n                                src={ContractEmptySVG}\n                                alt='ContractEmptySVG'\n                            />\n                        </Box>\n                        <div>No Contracts Yet</div>\n                    </Stack>\n                )}\n            </MainCard>\n            <ContractDialog\n                show={showDialog}\n                dialogProps={dialogProps}\n                onCancel={() => setShowDialog(false)}\n                onConfirm={onConfirm}\n            ></ContractDialog>\n        </>\n    )\n}\n\nexport default Contracts\n"
  },
  {
    "path": "packages/ui/src/views/executions/index.js",
    "content": "import { useState, useRef, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { SET_WORKFLOW, enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'\nimport { useDispatch, useSelector } from 'react-redux'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport {\n    Box,\n    List,\n    Accordion,\n    AccordionSummary,\n    AccordionDetails,\n    Paper,\n    Popper,\n    Chip,\n    Stack,\n    Typography,\n    Button,\n    IconButton\n} from '@mui/material'\n\n// third-party\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport moment from 'moment'\nimport ReactJson from 'react-json-view'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport Transitions from 'ui-component/extended/Transitions'\nimport AttachmentDialog from 'ui-component/dialog/AttachmentDialog'\nimport HTMLDialog from 'ui-component/dialog/HTMLDialog'\nimport ExpandDataDialog from 'ui-component/dialog/ExpandDataDialog'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// hooks\nimport useConfirm from 'hooks/useConfirm'\nimport useNotifier from 'utils/useNotifier'\n\n// icon\nimport { IconTrash, IconX, IconArrowsMaximize } from '@tabler/icons'\n\n// API\nimport executionsApi from 'api/executions'\nimport workflowsApi from 'api/workflows'\n\n// utils\nimport { copyToClipboard } from 'utils/genericHelper'\n\n// ==============================|| EXECUTIONS ||============================== //\n\nconst Executions = ({ workflowShortId, execution, executionCount, isExecutionOpen, anchorEl }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [expanded, setExpanded] = useState(false)\n    const [open, setOpen] = useState(false)\n    const [showHTMLDialog, setShowHTMLDialog] = useState(false)\n    const [HTMLDialogProps, setHTMLDialogProps] = useState({})\n    const [showAttachmentDialog, setShowAttachmentDialog] = useState(false)\n    const [attachmentDialogProps, setAttachmentDialogProps] = useState({})\n    const [showExpandDialog, setShowExpandDialog] = useState(false)\n    const [expandDialogProps, setExpandDialogProps] = useState({})\n\n    const dispatch = useDispatch()\n    const varPrevOpen = useRef(open)\n    const { confirm } = useConfirm()\n\n    useNotifier()\n    const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))\n    const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))\n\n    const handleAccordionChange = (executionShortId) => (event, isExpanded) => {\n        setExpanded(isExpanded ? executionShortId : false)\n    }\n\n    const setChipColor = (execState) => {\n        if (execState === 'INPROGRESS') return theme.palette.warning.dark\n        if (execState === 'FINISHED') return theme.palette.success.dark\n        if (execState === 'ERROR') return theme.palette.error.dark\n        if (execState === 'TERMINATED' || execState === 'TIMEOUT') return theme.palette.grey['700']\n        return theme.palette.primary.dark\n    }\n\n    const setChipBgColor = (execState) => {\n        if (execState === 'INPROGRESS') return theme.palette.warning.light\n        if (execState === 'FINISHED') return theme.palette.success.light\n        if (execState === 'ERROR') return theme.palette.error.light\n        if (execState === 'TERMINATED' || execState === 'TIMEOUT') return theme.palette.grey['300']\n        return theme.palette.primary.light\n    }\n\n    const openAttachmentDialog = (executionData) => {\n        const dialogProp = {\n            title: 'Attachments',\n            executionData\n        }\n        setAttachmentDialogProps(dialogProp)\n        setShowAttachmentDialog(true)\n    }\n\n    const openHTMLDialog = (executionData) => {\n        const dialogProp = {\n            title: 'HTML',\n            executionData\n        }\n        setHTMLDialogProps(dialogProp)\n        setShowHTMLDialog(true)\n    }\n\n    const onExpandDialogClicked = (executionData, nodeLabel) => {\n        const dialogProp = {\n            title: `Execution Data: ${nodeLabel}`,\n            data: executionData\n        }\n        setExpandDialogProps(dialogProp)\n        setShowExpandDialog(true)\n    }\n\n    const deleteExecution = async (e, executionShortId) => {\n        e.stopPropagation()\n        const confirmPayload = {\n            title: `Delete`,\n            description: `Delete execution ${executionShortId}?`,\n            confirmButtonName: 'Delete',\n            cancelButtonName: 'Cancel'\n        }\n        const isConfirmed = await confirm(confirmPayload)\n\n        if (isConfirmed) {\n            try {\n                const executionResp = await executionsApi.deleteExecution(executionShortId)\n                if (executionResp.data) {\n                    const workflowResponse = await workflowsApi.getSpecificWorkflow(workflowShortId)\n                    if (workflowResponse.data) dispatch({ type: SET_WORKFLOW, workflow: workflowResponse.data })\n                }\n                enqueueSnackbar({\n                    message: 'Execution deleted!',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n            } catch (error) {\n                const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n                enqueueSnackbar({\n                    message: errorData,\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'error',\n                        persist: true,\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n            }\n        }\n    }\n\n    // Handle Accordian\n    useEffect(() => {\n        varPrevOpen.current = open\n    }, [open])\n\n    useEffect(() => {\n        setOpen(isExecutionOpen)\n    }, [isExecutionOpen])\n\n    return (\n        <>\n            <Popper\n                placement='bottom-end'\n                open={open}\n                anchorEl={anchorEl}\n                role={undefined}\n                transition\n                disablePortal\n                popperOptions={{\n                    modifiers: [\n                        {\n                            name: 'offset',\n                            options: {\n                                offset: [170, 20]\n                            }\n                        }\n                    ]\n                }}\n                sx={{ zIndex: 1000 }}\n            >\n                {({ TransitionProps }) => (\n                    <Transitions in={open} {...TransitionProps}>\n                        <Paper>\n                            <MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>\n                                <Box sx={{ p: 2 }}>\n                                    <Stack>\n                                        <Typography variant='h4'>{executionCount} Executions</Typography>\n                                    </Stack>\n                                </Box>\n                                <PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 250px)', overflowX: 'hidden' }}>\n                                    {executionCount === 0 && execution.length === 0 && <Box sx={{ p: 2 }}>No executions yet</Box>}\n\n                                    {executionCount > 0 && execution.length > 0 && (\n                                        <Box sx={{ p: 2 }}>\n                                            <List\n                                                sx={{\n                                                    width: '100%',\n                                                    maxWidth: 330,\n                                                    py: 0,\n                                                    borderRadius: '10px',\n                                                    [theme.breakpoints.down('md')]: {\n                                                        maxWidth: 300\n                                                    },\n                                                    '& .MuiListItemSecondaryAction-root': {\n                                                        top: 22\n                                                    },\n                                                    '& .MuiDivider-root': {\n                                                        my: 0\n                                                    },\n                                                    '& .list-container': {\n                                                        pl: 7\n                                                    }\n                                                }}\n                                            >\n                                                {execution &&\n                                                    execution.map((exec, index) => (\n                                                        <Box key={index}>\n                                                            <Accordion\n                                                                expanded={expanded === exec.shortId}\n                                                                onChange={handleAccordionChange(exec.shortId)}\n                                                            >\n                                                                <AccordionSummary\n                                                                    expandIcon={<ExpandMoreIcon />}\n                                                                    aria-controls={`${exec.shortId}-content`}\n                                                                    id={`${exec.shortId}-header`}\n                                                                >\n                                                                    <Stack sx={{ p: 1, mr: 1 }} direction='column'>\n                                                                        <Stack sx={{ mb: 1, alignItems: 'center' }} direction='row'>\n                                                                            <Typography variant='h5'>{exec.shortId}</Typography>\n                                                                            {exec.state && (\n                                                                                <Chip\n                                                                                    sx={{\n                                                                                        color: setChipColor(exec.state),\n                                                                                        backgroundColor: setChipBgColor(exec.state),\n                                                                                        ml: 1\n                                                                                    }}\n                                                                                    label={exec.state}\n                                                                                />\n                                                                            )}\n                                                                        </Stack>\n                                                                        <Stack sx={{ mb: -1, alignItems: 'center' }} direction='row'>\n                                                                            <Typography\n                                                                                variant='h6'\n                                                                                sx={{ color: theme.palette.grey['500'] }}\n                                                                            >\n                                                                                {moment(exec.createdDate).format(\n                                                                                    'MMMM Do YYYY, h:mm:ss A z'\n                                                                                )}\n                                                                            </Typography>\n                                                                            <IconButton\n                                                                                size='small'\n                                                                                sx={{ height: 25, width: 25, ml: 1 }}\n                                                                                title='Delete Execution'\n                                                                                color='error'\n                                                                                onClick={(e) => deleteExecution(e, exec.shortId)}\n                                                                            >\n                                                                                <IconTrash />\n                                                                            </IconButton>\n                                                                        </Stack>\n                                                                    </Stack>\n                                                                </AccordionSummary>\n                                                                {JSON.parse(exec.executionData).map((execData, execDataIndex) => (\n                                                                    <AccordionDetails key={execDataIndex}>\n                                                                        <Box\n                                                                            sx={{\n                                                                                p: 2,\n                                                                                backgroundColor: theme.palette.secondary.light,\n                                                                                borderRadius: `15px`,\n                                                                                position: 'relative'\n                                                                            }}\n                                                                            key={execDataIndex}\n                                                                        >\n                                                                            <Typography sx={{ p: 1 }} variant='h5'>\n                                                                                {execData.nodeLabel}\n                                                                            </Typography>\n                                                                            <ReactJson\n                                                                                theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                                                                                style={{ padding: 10, borderRadius: 10 }}\n                                                                                collapsed\n                                                                                src={execData.data}\n                                                                                enableClipboard={(e) => copyToClipboard(e)}\n                                                                            />\n                                                                            <IconButton\n                                                                                size='small'\n                                                                                sx={{\n                                                                                    height: 25,\n                                                                                    width: 25,\n                                                                                    position: 'absolute',\n                                                                                    top: 5,\n                                                                                    right: 5\n                                                                                }}\n                                                                                title='Expand Data'\n                                                                                color='primary'\n                                                                                onClick={() =>\n                                                                                    onExpandDialogClicked(execData.data, execData.nodeLabel)\n                                                                                }\n                                                                            >\n                                                                                <IconArrowsMaximize />\n                                                                            </IconButton>\n                                                                            <div>\n                                                                                {execData.data.map((execObj, execObjIndex) => (\n                                                                                    <div key={execObjIndex}>\n                                                                                        {execObj.html && (\n                                                                                            <Typography sx={{ p: 1, mt: 2 }} variant='h5'>\n                                                                                                HTML\n                                                                                            </Typography>\n                                                                                        )}\n                                                                                        {execObj.html && (\n                                                                                            <div\n                                                                                                style={{\n                                                                                                    width: '100%',\n                                                                                                    height: '100%',\n                                                                                                    maxHeight: 400,\n                                                                                                    overflow: 'auto',\n                                                                                                    backgroundColor:\n                                                                                                        theme.palette.card.main,\n                                                                                                    color: customization.isDarkMode\n                                                                                                        ? 'white'\n                                                                                                        : 'black',\n                                                                                                    borderRadius: 5\n                                                                                                }}\n                                                                                                dangerouslySetInnerHTML={{\n                                                                                                    __html: execObj.html\n                                                                                                }}\n                                                                                            />\n                                                                                        )}\n                                                                                        {execObj.html && (\n                                                                                            <StyledButton\n                                                                                                sx={{ mt: 1 }}\n                                                                                                size='small'\n                                                                                                variant='contained'\n                                                                                                onClick={() =>\n                                                                                                    openHTMLDialog(execData.data)\n                                                                                                }\n                                                                                            >\n                                                                                                View HTML\n                                                                                            </StyledButton>\n                                                                                        )}\n\n                                                                                        {execObj.attachments && (\n                                                                                            <Typography\n                                                                                                sx={{ p: 1, pb: 0, mt: 2 }}\n                                                                                                variant='h5'\n                                                                                            >\n                                                                                                Attachments\n                                                                                            </Typography>\n                                                                                        )}\n                                                                                        {execObj.attachments &&\n                                                                                            execObj.attachments.map(\n                                                                                                (attachment, attchIndex) => (\n                                                                                                    <div key={attchIndex}>\n                                                                                                        <Typography\n                                                                                                            sx={{ p: 1 }}\n                                                                                                            variant='h6'\n                                                                                                        >\n                                                                                                            Item {execObjIndex} |{' '}\n                                                                                                            {attachment.filename\n                                                                                                                ? attachment.filename\n                                                                                                                : `Attachment ${attchIndex}`}\n                                                                                                        </Typography>\n                                                                                                        <embed\n                                                                                                            src={attachment.content}\n                                                                                                            width='100%'\n                                                                                                            height='100%'\n                                                                                                            style={{ borderStyle: 'solid' }}\n                                                                                                            type={attachment.contentType}\n                                                                                                        />\n                                                                                                        <StyledButton\n                                                                                                            size='small'\n                                                                                                            variant='contained'\n                                                                                                            onClick={() =>\n                                                                                                                openAttachmentDialog(\n                                                                                                                    execData.data\n                                                                                                                )\n                                                                                                            }\n                                                                                                        >\n                                                                                                            View Attachment\n                                                                                                        </StyledButton>\n                                                                                                    </div>\n                                                                                                )\n                                                                                            )}\n                                                                                    </div>\n                                                                                ))}\n                                                                            </div>\n                                                                        </Box>\n                                                                    </AccordionDetails>\n                                                                ))}\n                                                            </Accordion>\n                                                        </Box>\n                                                    ))}\n                                            </List>\n                                        </Box>\n                                    )}\n                                </PerfectScrollbar>\n                            </MainCard>\n                        </Paper>\n                    </Transitions>\n                )}\n            </Popper>\n            <AttachmentDialog\n                show={showAttachmentDialog}\n                dialogProps={attachmentDialogProps}\n                onCancel={() => setShowAttachmentDialog(false)}\n            ></AttachmentDialog>\n            <HTMLDialog show={showHTMLDialog} dialogProps={HTMLDialogProps} onCancel={() => setShowHTMLDialog(false)}></HTMLDialog>\n            <ExpandDataDialog\n                show={showExpandDialog}\n                dialogProps={expandDialogProps}\n                onCancel={() => setShowExpandDialog(false)}\n            ></ExpandDataDialog>\n        </>\n    )\n}\n\nExecutions.propTypes = {\n    workflowShortId: PropTypes.string,\n    execution: PropTypes.array,\n    executionCount: PropTypes.number,\n    isExecutionOpen: PropTypes.bool,\n    anchorEl: PropTypes.any\n}\n\nexport default Executions\n"
  },
  {
    "path": "packages/ui/src/views/inputs/ArrayInputParameters.js",
    "content": "import { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\nimport { forwardRef } from 'react'\n\n// material-ui\nimport { Box, Switch, FormControl, OutlinedInput, Popper, TextField, Typography, Stack, Button } from '@mui/material'\nimport Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'\nimport { useTheme, styled } from '@mui/material/styles'\nimport { TooltipWithParser } from '../../ui-component/TooltipWithParser'\nimport { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'\nimport { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'\n\n// icons\nimport { IconX, IconUpload } from '@tabler/icons'\n\n// third party\nimport lodash from 'lodash'\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport DatePicker from 'react-datepicker'\nimport 'react-datepicker/dist/react-datepicker.css'\n\n// utils\nimport { convertDateStringToDateObject, getFileName, getFolderName } from 'utils/genericHelper'\n\n//css\nimport './InputParameters.css'\nimport { StyledFab } from 'ui-component/StyledFab'\n\nconst StyledPopper = styled(Popper)({\n    boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',\n    borderRadius: '10px',\n    [`& .${autocompleteClasses.listbox}`]: {\n        boxSizing: 'border-box',\n        '& ul': {\n            padding: 10,\n            margin: 10\n        }\n    }\n})\n\nconst DateCustomInput = forwardRef(function DateCustomInput({ value, onClick }, ref) {\n    return (\n        <button\n            style={{\n                backgroundColor: '#fafafa',\n                paddingTop: 8,\n                paddingBottom: 8,\n                paddingRight: 12,\n                paddingLeft: 12,\n                borderRadius: 12,\n                width: '100%',\n                height: 50,\n                border: `1px solid #BDBDBD`,\n                cursor: 'pointer',\n                fontWeight: 'bold',\n                textAlign: 'start',\n                color: '#212121',\n                opacity: 0.9\n            }}\n            type='button'\n            onClick={onClick}\n            ref={ref}\n        >\n            {value}\n        </button>\n    )\n})\n\nDateCustomInput.propTypes = {\n    value: PropTypes.string,\n    onClick: PropTypes.func\n}\n\n// ==============================|| ARRAY INPUT PARAMETERS ||============================== //\n\nconst ArrayInputParameters = ({\n    initialValues,\n    arrayParams,\n    paramsType,\n    arrayGroupName,\n    errors,\n    onArrayInputChange,\n    onArrayInputBlur,\n    onArrayItemRemove,\n    onArrayItemMouseUp,\n    onEditVariableDialogOpen\n}) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const processUpdateValues = (inputValue, inputName, values, index) => {\n        const updateArrayValues = {\n            ...values,\n            [inputName]: inputValue\n        }\n        const updateInitialValues = initialValues\n        updateInitialValues[index] = updateArrayValues\n        return updateInitialValues\n    }\n\n    const onInputChange = (inputValue, inputName, values, index) => {\n        const updateInitialValues = processUpdateValues(inputValue, inputName, values, index)\n        onArrayInputChange(updateInitialValues)\n    }\n\n    const onInputBlur = (inputValue, inputName, values, index) => {\n        const updateInitialValues = processUpdateValues(inputValue, inputName, values, index)\n        onArrayInputBlur(updateInitialValues)\n    }\n\n    const onRemoveClick = (index) => {\n        const updateInitialValues = initialValues\n        updateInitialValues.splice(index, 1)\n        onArrayItemRemove(updateInitialValues)\n        onArrayItemMouseUp(false)\n    }\n\n    const onMouseUp = (e, inputName, valueIndex) => {\n        const cursorPosition = e.target.selectionEnd\n        const textBeforeCursorPosition = e.target.value.substring(0, cursorPosition)\n        const textAfterCursorPosition = e.target.value.substring(cursorPosition, e.target.value.length)\n        const path = `${paramsType}.${arrayGroupName}[${valueIndex}].${inputName}`\n        const body = {\n            textBeforeCursorPosition,\n            textAfterCursorPosition,\n            path,\n            paramsType\n        }\n        onArrayItemMouseUp(true, body)\n    }\n\n    const handleFolderUpload = (e, values, inputName, index) => {\n        if (!e.target.files) return\n        const files = e.target.files\n        const reader = new FileReader()\n\n        function readFile(fileIndex, base64Array) {\n            if (fileIndex >= files.length) {\n                onInputChange(JSON.stringify(base64Array), inputName, values, index)\n                return\n            }\n            const file = files[fileIndex]\n            reader.onload = (evt) => {\n                if (!evt?.target?.result) {\n                    return\n                }\n                const { result } = evt.target\n                const value = result + `,filepath:${file.webkitRelativePath}`\n                base64Array.push(value)\n                readFile(fileIndex + 1, lodash.cloneDeep(base64Array))\n            }\n            reader.readAsDataURL(file)\n        }\n        readFile(0, [])\n    }\n\n    const handleFileUpload = (e, onInputChange, values, inputName, index) => {\n        if (!e.target.files) {\n            return\n        }\n\n        const file = e.target.files[0]\n        const { name } = file\n\n        const reader = new FileReader()\n        reader.onload = (evt) => {\n            if (!evt?.target?.result) {\n                return\n            }\n            const { result } = evt.target\n            const value = result + `,filename:${name}`\n            onInputChange(value, inputName, values, index)\n        }\n        reader.readAsDataURL(file)\n    }\n\n    const findMatchingOptions = (options, value) => options.find((option) => option.name === value)\n\n    const getDefaultOptionValue = () => ''\n\n    return (\n        <>\n            {arrayParams.map((_, index) => {\n                const params = arrayParams[index] || []\n                const values = initialValues[index] || {}\n\n                return (\n                    <Box\n                        sx={{\n                            p: 2,\n                            mb: 2,\n                            backgroundColor: theme.palette.secondary.light,\n                            borderRadius: `${customization.borderRadius}px`,\n                            position: 'relative'\n                        }}\n                        key={index}\n                    >\n                        {arrayParams.length > 1 && (\n                            <StyledFab\n                                sx={{\n                                    minHeight: 30,\n                                    height: 30,\n                                    width: 30,\n                                    position: 'absolute',\n                                    right: -10,\n                                    top: -10\n                                }}\n                                color='secondary'\n                                size='small'\n                                onClick={() => onRemoveClick(index)}\n                            >\n                                <IconX />\n                            </StyledFab>\n                        )}\n\n                        {params.map((input, paramIndex) => {\n                            if (input.type === 'file' || input.type === 'folder') {\n                                const inputName = input.name\n\n                                return (\n                                    <FormControl\n                                        key={`${inputName}_${paramIndex}`}\n                                        fullWidth\n                                        sx={{ mb: 1, mt: 1 }}\n                                        error={errors && errors.length > 0 && errors[index] ? Boolean(errors[index][inputName]) : false}\n                                    >\n                                        <Stack direction='row'>\n                                            <Typography variant='overline'>{input.label}</Typography>\n                                            {input.description && <TooltipWithParser title={input.description} />}\n                                        </Stack>\n\n                                        {input.type === 'file' && (\n                                            <span\n                                                style={{\n                                                    fontWeight: 'bold',\n                                                    color: theme.palette.grey['800'],\n                                                    marginBottom: '1rem'\n                                                }}\n                                            >\n                                                {values[inputName] ? getFileName(values[inputName]) : 'Choose a file to upload'}\n                                            </span>\n                                        )}\n\n                                        {input.type === 'folder' && (\n                                            <span\n                                                style={{\n                                                    fontWeight: 'bold',\n                                                    color: theme.palette.grey['800'],\n                                                    marginBottom: '1rem'\n                                                }}\n                                            >\n                                                {values[inputName] ? getFolderName(values[inputName]) : 'Choose a folder to upload'}\n                                            </span>\n                                        )}\n\n                                        <Button\n                                            variant='outlined'\n                                            component='label'\n                                            fullWidth\n                                            startIcon={<IconUpload />}\n                                            sx={{ marginRight: '1rem' }}\n                                        >\n                                            {input.type === 'folder' ? 'Upload Folder' : 'Upload File'}\n                                            {input.type === 'file' && (\n                                                <input type='file' hidden onChange={(e) => handleFileUpload(e, values, inputName, index)} />\n                                            )}\n                                            {input.type === 'folder' && (\n                                                <input\n                                                    type='file'\n                                                    // https://github.com/jsx-eslint/eslint-plugin-react/issues/3454\n                                                    // eslint-disable-next-line react/no-unknown-property\n                                                    directory=''\n                                                    // eslint-disable-next-line react/no-unknown-property\n                                                    webkitdirectory=''\n                                                    hidden\n                                                    onChange={(e) => handleFolderUpload(e, values, inputName, index)}\n                                                />\n                                            )}\n                                        </Button>\n                                    </FormControl>\n                                )\n                            }\n\n                            if (input.type === 'json' || input.type === 'code') {\n                                const inputName = input.name\n\n                                return (\n                                    <FormControl\n                                        key={`${inputName}_${paramIndex}`}\n                                        fullWidth\n                                        sx={{ mb: 1, mt: 1 }}\n                                        error={errors && errors.length > 0 && errors[index] ? Boolean(errors[index][inputName]) : false}\n                                    >\n                                        <Stack sx={{ position: 'relative' }} direction='row'>\n                                            <Typography variant='overline'>{input.label}</Typography>\n                                            {input.description && <TooltipWithParser title={input.description} />}\n                                            <Button\n                                                sx={{\n                                                    position: 'absolute',\n                                                    fontSize: '0.6875rem',\n                                                    right: 0,\n                                                    top: 5,\n                                                    height: 25,\n                                                    width: 'max-content'\n                                                }}\n                                                variant='outlined'\n                                                onClick={() => onEditVariableDialogOpen(input, values, index)}\n                                            >\n                                                Edit Variables\n                                            </Button>\n                                        </Stack>\n                                        <PerfectScrollbar\n                                            style={{\n                                                border: '1px solid',\n                                                borderColor: theme.palette.grey['500'],\n                                                borderRadius: '12px',\n                                                height: '200px',\n                                                maxHeight: '200px',\n                                                overflowX: 'hidden',\n                                                backgroundColor: 'white'\n                                            }}\n                                            onScroll={(e) => e.stopPropagation()}\n                                        >\n                                            {customization.isDarkMode ? (\n                                                <DarkCodeEditor\n                                                    value={values[inputName] || ''}\n                                                    onValueChange={(code) => onInputChange(code, inputName, values, index)}\n                                                    placeholder={input.placeholder}\n                                                    type={input.type}\n                                                    onMouseUp={(e) => onMouseUp(e, inputName, index)}\n                                                    onBlur={(e) => {\n                                                        onInputBlur(e.target.value, inputName, values, index)\n                                                        onMouseUp(e, inputName, index)\n                                                    }}\n                                                    style={{\n                                                        fontSize: '0.875rem',\n                                                        minHeight: '200px',\n                                                        width: '100%'\n                                                    }}\n                                                />\n                                            ) : (\n                                                <LightCodeEditor\n                                                    value={values[inputName] || ''}\n                                                    onValueChange={(code) => onInputChange(code, inputName, values, index)}\n                                                    placeholder={input.placeholder}\n                                                    type={input.type}\n                                                    onMouseUp={(e) => onMouseUp(e, inputName, index)}\n                                                    onBlur={(e) => {\n                                                        onInputBlur(e.target.value, inputName, values, index)\n                                                        onMouseUp(e, inputName, index)\n                                                    }}\n                                                    style={{\n                                                        fontSize: '0.875rem',\n                                                        minHeight: '200px',\n                                                        width: '100%'\n                                                    }}\n                                                />\n                                            )}\n                                        </PerfectScrollbar>\n                                    </FormControl>\n                                )\n                            }\n\n                            if (input.type === 'date') {\n                                const inputName = input.name\n\n                                return (\n                                    <FormControl\n                                        key={`${inputName}_${paramIndex}`}\n                                        fullWidth\n                                        sx={{ mb: 1, mt: 1 }}\n                                        error={errors && errors.length > 0 && errors[index] ? Boolean(errors[index][inputName]) : false}\n                                    >\n                                        <Stack direction='row'>\n                                            <Typography variant='overline'>{input.label}</Typography>\n                                            {input.description && <TooltipWithParser title={input.description} />}\n                                        </Stack>\n                                        <DatePicker\n                                            customInput={<DateCustomInput />}\n                                            selected={convertDateStringToDateObject(values[inputName]) || null}\n                                            showTimeInput\n                                            isClearable\n                                            timeInputLabel='Time:'\n                                            dateFormat='MM/dd/yyyy h:mm aa'\n                                            onChange={(date) => {\n                                                const inputValue = date ? date.toISOString() : null\n                                                onInputChange(inputValue, inputName, values, index)\n                                                onArrayItemMouseUp(false)\n                                            }}\n                                        />\n                                    </FormControl>\n                                )\n                            }\n\n                            if (input.type === 'string' || input.type === 'password' || input.type === 'number') {\n                                const inputName = input.name\n\n                                return (\n                                    <FormControl\n                                        key={`${inputName}_${paramIndex}`}\n                                        fullWidth\n                                        sx={{ mb: 1, mt: 1 }}\n                                        error={errors && errors.length > 0 && errors[index] ? Boolean(errors[index][inputName]) : false}\n                                    >\n                                        <Stack sx={{ position: 'relative' }} direction='row'>\n                                            <Typography variant='overline'>{input.label}</Typography>\n                                            {input.description && <TooltipWithParser title={input.description} />}\n                                            {(input.type === 'string' || input.type === 'number') && (\n                                                <Button\n                                                    sx={{\n                                                        position: 'absolute',\n                                                        fontSize: '0.6875rem',\n                                                        right: 0,\n                                                        top: 5,\n                                                        height: 25,\n                                                        width: 'max-content'\n                                                    }}\n                                                    variant='outlined'\n                                                    onClick={() => onEditVariableDialogOpen(input, values, index)}\n                                                >\n                                                    Edit Variables\n                                                </Button>\n                                            )}\n                                        </Stack>\n                                        <OutlinedInput\n                                            id={inputName}\n                                            type={input.type === 'string' || input.type === 'number' ? 'text' : input.type}\n                                            value={values[inputName] || ''}\n                                            placeholder={input.placeholder}\n                                            name={inputName}\n                                            onBlur={(e) => {\n                                                const inputValue = e.target.value\n                                                onInputBlur(inputValue, inputName, values, index)\n                                                onMouseUp(e, inputName, index)\n                                            }}\n                                            onChange={(e) => {\n                                                const inputValue = e.target.value\n                                                onInputChange(inputValue, inputName, values, index)\n                                            }}\n                                            onMouseUp={(e) => onMouseUp(e, inputName, index)}\n                                        />\n                                    </FormControl>\n                                )\n                            }\n\n                            if (input.type === 'boolean') {\n                                const inputName = input.name\n\n                                return (\n                                    <FormControl\n                                        key={`${inputName}_${paramIndex}`}\n                                        fullWidth\n                                        sx={{ mb: 1, mt: 1 }}\n                                        error={errors && errors.length > 0 && errors[index] ? Boolean(errors[index][inputName]) : false}\n                                    >\n                                        <Stack direction='row'>\n                                            <Typography variant='overline'>{input.label}</Typography>\n                                            {input.description && <TooltipWithParser title={input.description} />}\n                                        </Stack>\n                                        <Switch\n                                            checked={!!values[inputName]}\n                                            onChange={(event) => {\n                                                onInputChange(event.target.checked, inputName, values, index)\n                                            }}\n                                            inputProps={{ 'aria-label': 'controlled' }}\n                                        />\n                                    </FormControl>\n                                )\n                            }\n\n                            if (input.type === 'options') {\n                                const inputName = input.name\n                                const availableOptions = input.options || []\n\n                                return (\n                                    <FormControl key={`${inputName}_${paramIndex}`} fullWidth sx={{ mb: 1, mt: 1 }}>\n                                        <Stack direction='row'>\n                                            <Typography variant='overline'>{input.label}</Typography>\n                                            {input.description && <TooltipWithParser title={input.description} />}\n                                        </Stack>\n                                        <Autocomplete\n                                            id={inputName}\n                                            freeSolo\n                                            onOpen={() => onArrayItemMouseUp(false)}\n                                            options={availableOptions}\n                                            value={findMatchingOptions(availableOptions, values[inputName]) || getDefaultOptionValue()}\n                                            onChange={(e, selection) => {\n                                                const value = selection ? selection.name : ''\n                                                onInputBlur(value, inputName, values, index)\n                                            }}\n                                            PopperComponent={StyledPopper}\n                                            renderInput={(params) => (\n                                                <TextField\n                                                    {...params}\n                                                    value={values[inputName]}\n                                                    error={\n                                                        errors && errors.length > 0 && errors[index]\n                                                            ? Boolean(errors[index][inputName])\n                                                            : false\n                                                    }\n                                                />\n                                            )}\n                                            renderOption={(props, option) => (\n                                                <Box component='li' {...props}>\n                                                    <div style={{ display: 'flex', flexDirection: 'column' }}>\n                                                        <Typography sx={{ p: 1 }} variant='h5'>\n                                                            {option.label}\n                                                        </Typography>\n                                                        {option.description && <Typography sx={{ p: 1 }}>{option.description}</Typography>}\n                                                    </div>\n                                                </Box>\n                                            )}\n                                        />\n                                    </FormControl>\n                                )\n                            }\n                            return null\n                        })}\n                    </Box>\n                )\n            })}\n        </>\n    )\n}\n\nArrayInputParameters.propTypes = {\n    initialValues: PropTypes.array,\n    arrayParams: PropTypes.array,\n    paramsType: PropTypes.string,\n    arrayGroupName: PropTypes.string,\n    errors: PropTypes.array,\n    onArrayInputChange: PropTypes.func,\n    onArrayInputBlur: PropTypes.func,\n    onArrayItemRemove: PropTypes.func,\n    onArrayItemMouseUp: PropTypes.func,\n    onEditVariableDialogOpen: PropTypes.func\n}\n\nexport default ArrayInputParameters\n"
  },
  {
    "path": "packages/ui/src/views/inputs/AsyncSelectWrapper.js",
    "content": "import { useState, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { Typography, Stack } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// project imports\nimport OptionParamsResponse from './OptionParamsResponse'\nimport { TooltipWithParser } from '../../ui-component/TooltipWithParser'\n\n// third party\nimport lodash from 'lodash'\nimport AsyncSelect from 'react-select/async'\nimport axios from 'axios'\n\n// icons\nimport { IconX } from '@tabler/icons'\n\n// Constant\nimport { baseURL } from 'store/constant'\n\n// ==============================|| ASYNC SELECT WRAPPER ||============================== //\n\nconst AsyncSelectWrapper = ({\n    title,\n    description,\n    value,\n    loadMethod,\n    loadFromDbCollections,\n    nodeFlowData,\n    error,\n    onChange,\n    onMenuOpen,\n    onSetError\n}) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const customStyles = {\n        option: (provided, state) => ({\n            ...provided,\n            paddingTop: 15,\n            paddingBottom: 15,\n            paddingLeft: 20,\n            paddingRight: 20,\n            cursor: 'pointer',\n            fontWeight: '500',\n            backgroundColor: customization.isDarkMode\n                ? state.isSelected\n                    ? '#233345'\n                    : theme.palette.primary.light\n                : state.isSelected\n                ? theme.palette.primary.light\n                : '',\n            color: customization.isDarkMode ? 'white' : 'black',\n            '&:hover': {\n                backgroundColor: customization.isDarkMode ? '#233345' : theme.palette.grey['200']\n            }\n        }),\n        control: (provided) => ({\n            ...provided,\n            cursor: 'text',\n            backgroundColor: theme.palette.asyncSelect.main,\n            paddingTop: 8,\n            paddingBottom: 8,\n            paddingRight: 6,\n            paddingLeft: 6,\n            borderRadius: 12,\n            border: customization.isDarkMode ? 'none' : `solid 1px ${theme.palette.grey['400']}`,\n            '&:hover': {\n                borderColor: theme.palette.grey['700']\n            }\n        }),\n        singleValue: (provided) => ({\n            ...provided,\n            color: customization.isDarkMode ? 'white' : 'black',\n            fontWeight: '600'\n        }),\n        menuList: (provided) => ({\n            ...provided,\n            backgroundColor: customization.isDarkMode ? theme.palette.primary.light : '',\n            boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)'\n        })\n    }\n\n    const [asyncOptions, setAsyncOptions] = useState([])\n\n    const getSelectedValue = (value) => asyncOptions.find((option) => option.name === value)\n\n    const getDefaultOptionValue = () => ''\n\n    const formatErrorMessage = (error) => {\n        if (error) return `*${error.replace(/[\"]/g, '')}`\n        return ''\n    }\n\n    const showHideOptions = (options) => {\n        let returnOptions = options\n        const toBeDeleteOptions = []\n        const displayTypes = ['show', 'hide']\n\n        for (let x = 0; x < displayTypes.length; x += 1) {\n            const displayType = displayTypes[x]\n\n            for (let i = 0; i < returnOptions.length; i += 1) {\n                const option = returnOptions[i]\n                const displayOptions = option[displayType]\n\n                if (displayOptions) {\n                    Object.keys(displayOptions).forEach((path) => {\n                        const comparisonValue = displayOptions[path]\n                        const groundValue = lodash.get(nodeFlowData, path, '')\n\n                        if (Array.isArray(comparisonValue)) {\n                            if (displayType === 'show' && !comparisonValue.includes(groundValue)) {\n                                toBeDeleteOptions.push(option)\n                            }\n                            if (displayType === 'hide' && comparisonValue.includes(groundValue)) {\n                                toBeDeleteOptions.push(option)\n                            }\n                        } else if (typeof comparisonValue === 'string') {\n                            if (\n                                displayType === 'show' &&\n                                !(comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))\n                            ) {\n                                toBeDeleteOptions.push(option)\n                            }\n                            if (\n                                displayType === 'hide' &&\n                                (comparisonValue === groundValue || new RegExp(comparisonValue).test(groundValue))\n                            ) {\n                                toBeDeleteOptions.push(option)\n                            }\n                        }\n                    })\n                }\n            }\n        }\n\n        for (let i = 0; i < toBeDeleteOptions.length; i += 1) {\n            returnOptions = returnOptions.filter((opt) => JSON.stringify(opt) !== JSON.stringify(toBeDeleteOptions[i]))\n        }\n\n        return returnOptions\n    }\n\n    const loadOptions = (inputValue, callback) => {\n        axios\n            .post(`${baseURL}/api/v1/node-load-method/${nodeFlowData.name}`, { ...nodeFlowData, loadMethod, loadFromDbCollections })\n            .then((response) => {\n                const data = response.data\n                const filteredOption = (data || []).filter((i) => i.label.toLowerCase().includes(inputValue.toLowerCase()))\n                const options = showHideOptions(filteredOption)\n                setAsyncOptions(options)\n                callback(options)\n            })\n    }\n\n    const formatOptionLabel = ({ label, description }, { context }) => (\n        <>\n            {context === 'menu' && (\n                <div style={{ display: 'flex', flexDirection: 'column' }}>\n                    <div>{label}</div>\n                    {description && <span style={{ fontWeight: 400, paddingTop: 10, paddingBottom: 10 }}>{description}</span>}\n                </div>\n            )}\n            {context === 'value' && (\n                <div style={{ display: 'flex', flexDirection: 'column' }}>\n                    <div>{label}</div>\n                </div>\n            )}\n        </>\n    )\n\n    useEffect(() => () => setAsyncOptions([]), [])\n\n    useEffect(() => {\n        if (value !== undefined) {\n            const selectedOption = asyncOptions.find((option) => option.name === value)\n            if (!selectedOption) {\n                onSetError()\n            }\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [asyncOptions])\n\n    return (\n        <>\n            <Stack direction='row'>\n                <Typography variant='overline'>{title}</Typography>\n                {description && <TooltipWithParser title={description} />}\n            </Stack>\n            <div style={{ position: 'relative' }}>\n                <AsyncSelect\n                    key={JSON.stringify(nodeFlowData)} // to reload async select whenever flowdata changed\n                    styles={customStyles}\n                    value={getSelectedValue(value) || getDefaultOptionValue()}\n                    formatOptionLabel={formatOptionLabel}\n                    getOptionLabel={(option) => option.label}\n                    getOptionValue={(option) => option.name}\n                    loadOptions={loadOptions}\n                    defaultOptions\n                    onChange={onChange}\n                    onMenuOpen={onMenuOpen}\n                />\n                <button\n                    style={{\n                        minHeight: 10,\n                        height: 27,\n                        width: 30,\n                        backgroundColor: theme.palette.asyncSelect.main,\n                        color: customization.isDarkMode ? 'white' : theme.palette.grey['500'],\n                        position: 'absolute',\n                        right: 10,\n                        top: 0,\n                        bottom: 0,\n                        margin: 'auto',\n                        border: 'none',\n                        cursor: 'pointer'\n                    }}\n                    title='Clear Selection'\n                    type='button'\n                    onClick={() => onChange(null)}\n                >\n                    <IconX />\n                </button>\n            </div>\n            {error && <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>{formatErrorMessage(error)}</span>}\n\n            <OptionParamsResponse value={value} options={asyncOptions} />\n        </>\n    )\n}\n\nAsyncSelectWrapper.propTypes = {\n    title: PropTypes.string,\n    description: PropTypes.string,\n    value: PropTypes.string,\n    loadMethod: PropTypes.string,\n    loadFromDbCollections: PropTypes.array,\n    nodeFlowData: PropTypes.object,\n    error: PropTypes.string,\n    onChange: PropTypes.func,\n    onMenuOpen: PropTypes.func,\n    onSetError: PropTypes.func\n}\n\nexport default AsyncSelectWrapper\n"
  },
  {
    "path": "packages/ui/src/views/inputs/CredentialInput.js",
    "content": "import { useState, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { Box, Button, FormControl, Stack, OutlinedInput, Popper, TextField, Typography, IconButton, Switch } from '@mui/material'\nimport Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'\nimport { useTheme, styled } from '@mui/material/styles'\n\n// third party\nimport * as Yup from 'yup'\nimport { Formik } from 'formik'\nimport PerfectScrollbar from 'react-perfect-scrollbar'\n\n// project imports\nimport AnimateButton from 'ui-component/extended/AnimateButton'\nimport { StyledButton } from 'ui-component/StyledButton'\nimport { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'\nimport { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'\n\n// API\nimport credentialApi from 'api/credential'\nimport oauth2Api from 'api/oauth2'\n\n// Hooks\nimport useApi from 'hooks/useApi'\nimport useScriptRef from 'hooks/useScriptRef'\n\n// icons\nimport { IconTrash, IconCopy } from '@tabler/icons'\nimport gLoginLogo from 'assets/images/google-login-white.png'\n\n//css\nimport './InputParameters.css'\n\nimport { TooltipWithParser } from '../../ui-component/TooltipWithParser'\n\nconst StyledPopper = styled(Popper)({\n    boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',\n    borderRadius: '10px',\n    [`& .${autocompleteClasses.listbox}`]: {\n        boxSizing: 'border-box',\n        '& ul': {\n            padding: 10,\n            margin: 10\n        }\n    }\n})\n\nconst ADD_NEW_CREDENTIAL = '+ Add New Credential'\n\n// ==============================|| CREDENTIAL INPUT ||============================== //\n\nconst CredentialInput = ({\n    initialParams,\n    paramsType,\n    initialValues,\n    initialValidation,\n    valueChanged,\n    paramsChanged,\n    onSubmit,\n    ...others\n}) => {\n    const scriptedRef = useScriptRef()\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [credentialValidation, setCredentialValidation] = useState({})\n    const [credentialValues, setCredentialValues] = useState({})\n    const [nodeCredentialName, setNodeCredentialName] = useState('')\n    const [credentialParams, setCredentialParams] = useState([])\n    const [credentialOptions, setCredentialOptions] = useState([])\n    const [oAuth2RedirectURL, setOAuth2RedirectURL] = useState('')\n\n    const getCredentialParamsApi = useApi(credentialApi.getCredentialParams)\n    const getRegisteredCredentialsApi = useApi(credentialApi.getCredentials)\n\n    const onChanged = (values) => {\n        const updateValues = values\n        updateValues.submit = null\n        valueChanged(updateValues, paramsType)\n    }\n\n    const getCredentialRequestBody = (values) => {\n        if (credentialParams.length === 0) throw new Error('Credential params empty')\n\n        const credentialData = {}\n        for (let i = 0; i < credentialParams.length; i += 1) {\n            const credParamName = credentialParams[i].name\n            if (credParamName in values) credentialData[credParamName] = values[credParamName]\n        }\n        delete credentialData.name\n\n        const credBody = {\n            name: values.name,\n            nodeCredentialName: values.credentialMethod,\n            credentialData\n        }\n\n        return credBody\n    }\n\n    const updateYupValidation = (inputName, validationKey) => {\n        const updateValidation = {\n            ...credentialValidation,\n            [inputName]: Yup.object({ [validationKey]: Yup.string().required(`${inputName} is required`) })\n        }\n        setCredentialValidation(updateValidation)\n    }\n\n    const clearCredentialParams = () => {\n        const updateParams = initialParams.filter((item) => credentialParams.every((paramItem) => item.name !== paramItem.name))\n        setCredentialParams([])\n        setOAuth2RedirectURL('')\n\n        paramsChanged(updateParams, paramsType)\n    }\n\n    const clearCredentialParamsValues = (value) => {\n        let updateValues = JSON.parse(JSON.stringify(credentialValues))\n\n        for (let i = 0; i < credentialParams.length; i += 1) {\n            const credParamName = credentialParams[i].name\n            if (credParamName in updateValues) delete updateValues[credParamName]\n        }\n        updateValues = {\n            ...updateValues,\n            registeredCredential: value\n        }\n        valueChanged(updateValues, paramsType)\n    }\n\n    const onDeleteCredential = async (credentialId) => {\n        const response = await credentialApi.deleteCredential(credentialId)\n        if (response.data) {\n            clearCredentialParams()\n            clearCredentialParamsValues('')\n        }\n    }\n\n    const openOAuth2PopUpWindow = (oAuth2PopupURL) => {\n        const windowWidth = 500\n        const windowHeight = 400\n        const left = window.screenX + (window.outerWidth - windowWidth) / 2\n        const top = window.screenY + (window.outerHeight - windowHeight) / 2.5\n        const title = `Connect Credential`\n        const url = oAuth2PopupURL\n        const popup = window.open(url, title, `width=${windowWidth},height=${windowHeight},left=${left},top=${top}`)\n        return popup\n    }\n\n    const findMatchingOptions = (options, value) => options.find((option) => option.name === value)\n\n    const getDefaultOptionValue = () => ''\n\n    // getRegisteredCredentialsApi successful\n    useEffect(() => {\n        if (getRegisteredCredentialsApi.data) {\n            const credentialOptions = []\n            if (getRegisteredCredentialsApi.data.length) {\n                for (let i = 0; i < getRegisteredCredentialsApi.data.length; i += 1) {\n                    credentialOptions.push({\n                        _id: getRegisteredCredentialsApi.data[i]._id,\n                        name: getRegisteredCredentialsApi.data[i].name\n                    })\n                }\n            }\n            credentialOptions.push({\n                name: ADD_NEW_CREDENTIAL\n            })\n            setCredentialOptions(credentialOptions)\n            if (initialParams.find((prm) => prm.name === 'registeredCredential')) {\n                updateYupValidation('registeredCredential', 'name')\n            }\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [getRegisteredCredentialsApi.data])\n\n    // getCredentialParamsApi successful\n    useEffect(() => {\n        if (getCredentialParamsApi.data) {\n            const newCredentialParams = getCredentialParamsApi.data.credentials\n\n            const credentialNameParam = {\n                label: 'Credential Name',\n                name: 'name',\n                type: 'string',\n                default: ''\n            }\n\n            newCredentialParams.unshift(credentialNameParam)\n\n            setCredentialParams(newCredentialParams)\n\n            const updateParams = initialParams\n\n            for (let i = 0; i < newCredentialParams.length; i += 1) {\n                const credParamName = newCredentialParams[i].name\n                if (initialParams.find((prm) => prm.name === credParamName) === undefined) {\n                    updateParams.push(newCredentialParams[i])\n                }\n            }\n            paramsChanged(updateParams, paramsType)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [getCredentialParamsApi.data])\n\n    // Initialize values\n    useEffect(() => {\n        setCredentialValues(initialValues)\n        if (initialValues && initialValues.credentialMethod) {\n            getRegisteredCredentialsApi.request(initialValues.credentialMethod)\n            setNodeCredentialName(initialValues.credentialMethod)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [initialValues])\n\n    // Initialize validation\n    useEffect(() => {\n        setCredentialValidation(initialValidation)\n    }, [initialValidation])\n\n    return (\n        <>\n            <Box sx={{ width: 400 }}>\n                <Formik\n                    enableReinitialize\n                    initialValues={credentialValues}\n                    validationSchema={Yup.object().shape(credentialValidation)}\n                    onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {\n                        try {\n                            if (scriptedRef.current) {\n                                const isAddNewCredential =\n                                    values && values.registeredCredential && values.registeredCredential.name === ADD_NEW_CREDENTIAL\n\n                                if (!isAddNewCredential && (credentialParams.length === 0 || !values.credentialMethod)) {\n                                    onSubmit(values.credentialMethod ? { ...values, submit: true } : { submit: true }, paramsType)\n                                    setStatus({ success: true })\n                                    setSubmitting(false)\n                                } else {\n                                    const body = getCredentialRequestBody(values)\n                                    let response\n                                    if (isAddNewCredential) {\n                                        response = await credentialApi.createNewCredential(body)\n                                    } else {\n                                        response = await credentialApi.updateCredential(values.registeredCredential._id, body)\n                                    }\n                                    if (response.data) {\n                                        // Open oAuth2 window\n                                        if (values.credentialMethod.toLowerCase().includes('oauth2')) {\n                                            const oAuth2PopupURL = await oauth2Api.geOAuth2PopupURL(response.data._id)\n                                            const popUpWindow = openOAuth2PopUpWindow(oAuth2PopupURL.data)\n\n                                            const oAuth2Completed = async (event) => {\n                                                if (event.data === 'success') {\n                                                    window.removeEventListener('message', oAuth2Completed, false)\n\n                                                    const submitValues = {\n                                                        credentialMethod: values.credentialMethod,\n                                                        registeredCredential: {\n                                                            _id: response.data._id,\n                                                            name: response.data.name\n                                                        },\n                                                        submit: true\n                                                    }\n                                                    clearCredentialParams()\n                                                    onSubmit(submitValues, paramsType)\n                                                    setStatus({ success: true })\n                                                    setSubmitting(false)\n\n                                                    if (popUpWindow) {\n                                                        popUpWindow.close()\n                                                    }\n                                                }\n                                            }\n                                            window.addEventListener('message', oAuth2Completed, false)\n                                            return\n                                        }\n\n                                        const submitValues = {\n                                            credentialMethod: values.credentialMethod,\n                                            registeredCredential: {\n                                                _id: response.data._id,\n                                                name: response.data.name\n                                            },\n                                            submit: true\n                                        }\n                                        clearCredentialParams()\n                                        onSubmit(submitValues, paramsType)\n                                        setStatus({ success: true })\n                                        setSubmitting(false)\n                                    } else {\n                                        throw new Error(response)\n                                    }\n                                }\n                            }\n                        } catch (err) {\n                            console.error(err)\n                            if (scriptedRef.current) {\n                                setStatus({ success: false })\n                                setErrors({ submit: err.message })\n                                setSubmitting(false)\n                            }\n                        }\n                    }}\n                >\n                    {({ errors, handleBlur, handleChange, handleSubmit, setFieldValue, isSubmitting, values }) => (\n                        <form noValidate onSubmit={handleSubmit} {...others}>\n                            {initialParams.map((input) => {\n                                if (input.type === 'options') {\n                                    const inputName = input.name\n                                    const availableOptions = input.options || []\n\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }}>\n                                            <Stack direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                            </Stack>\n                                            <Autocomplete\n                                                id={inputName}\n                                                freeSolo\n                                                options={availableOptions}\n                                                value={findMatchingOptions(availableOptions, values[inputName]) || getDefaultOptionValue()}\n                                                onChange={(e, selection) => {\n                                                    const value = selection ? selection.name : ''\n                                                    setFieldValue(inputName, value)\n                                                    const overwriteValues = {\n                                                        [inputName]: value\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                    clearCredentialParams()\n                                                    if (selection) {\n                                                        getRegisteredCredentialsApi.request(value)\n                                                        setNodeCredentialName(value)\n                                                    } else {\n                                                        setCredentialOptions([])\n                                                    }\n                                                }}\n                                                onBlur={handleBlur}\n                                                PopperComponent={StyledPopper}\n                                                renderInput={(params) => (\n                                                    <TextField {...params} value={values[inputName]} error={Boolean(errors[inputName])} />\n                                                )}\n                                                renderOption={(props, option) => (\n                                                    <Box component='li' {...props}>\n                                                        <div style={{ display: 'flex', flexDirection: 'column' }}>\n                                                            <Typography sx={{ p: 1 }} variant='h5'>\n                                                                {option.label}\n                                                            </Typography>\n                                                            {option.description && (\n                                                                <Typography sx={{ p: 1 }}>{option.description}</Typography>\n                                                            )}\n                                                        </div>\n                                                    </Box>\n                                                )}\n                                            />\n                                            {errors[inputName] && (\n                                                <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                    *{errors[inputName]}\n                                                </span>\n                                            )}\n                                        </FormControl>\n                                    )\n                                }\n                                return null\n                            })}\n\n                            {initialParams.find((prm) => prm.name === 'registeredCredential') && (\n                                <FormControl fullWidth sx={{ mb: 1, mt: 1 }}>\n                                    <Stack direction='row'>\n                                        <Typography variant='overline'>Registered Credential</Typography>\n                                        <TooltipWithParser title='Select previously registered credential OR add new credential' />\n                                    </Stack>\n                                    <Autocomplete\n                                        id='registered-credential'\n                                        freeSolo\n                                        options={credentialOptions}\n                                        value={values.registeredCredential && values.credentialMethod ? values.registeredCredential : ' '}\n                                        getOptionLabel={(option) => option.name || ' '}\n                                        onChange={async (e, selectedCredential) => {\n                                            setFieldValue(\n                                                'registeredCredential',\n                                                selectedCredential !== null ? selectedCredential : initialValues.registeredCredential\n                                            )\n                                            const overwriteValues = {\n                                                ...values,\n                                                registeredCredential: selectedCredential\n                                            }\n                                            onChanged(overwriteValues)\n                                            if (selectedCredential) {\n                                                if (selectedCredential.name !== ADD_NEW_CREDENTIAL) {\n                                                    const resp = await credentialApi.getSpecificCredential(selectedCredential._id)\n                                                    if (resp.data) {\n                                                        const updateValues = {\n                                                            ...overwriteValues,\n                                                            ...resp.data.credentialData,\n                                                            name: resp.data.name\n                                                        }\n                                                        valueChanged(updateValues, paramsType)\n                                                    }\n                                                } else {\n                                                    clearCredentialParamsValues(selectedCredential)\n                                                }\n                                                getCredentialParamsApi.request(nodeCredentialName)\n                                                if (values.credentialMethod.toLowerCase().includes('oauth2')) {\n                                                    const redirectURLResp = await oauth2Api.geOAuth2RedirectURL()\n                                                    if (redirectURLResp.data) setOAuth2RedirectURL(redirectURLResp.data)\n                                                }\n                                            }\n                                        }}\n                                        onInputChange={(e, value) => {\n                                            if (!value) {\n                                                clearCredentialParams()\n                                                clearCredentialParamsValues('')\n                                            }\n                                        }}\n                                        onBlur={handleBlur}\n                                        PopperComponent={StyledPopper}\n                                        renderInput={(params) => (\n                                            <TextField\n                                                {...params}\n                                                value={values.registeredCredential}\n                                                error={Boolean(errors.registeredCredential)}\n                                            />\n                                        )}\n                                        renderOption={(props, option) => (\n                                            <Box component='li' {...props}>\n                                                <div style={{ display: 'flex', flexDirection: 'column' }}>\n                                                    <Typography sx={{ p: 1 }} variant='h5'>\n                                                        {option.name}\n                                                    </Typography>\n                                                </div>\n                                            </Box>\n                                        )}\n                                    />\n                                    {errors.registeredCredential && (\n                                        <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                            *Registered Credential is required\n                                        </span>\n                                    )}\n                                </FormControl>\n                            )}\n\n                            {values && values.registeredCredential && values.registeredCredential._id && (\n                                <Button\n                                    sx={{ mb: 2 }}\n                                    size='small'\n                                    variant='outlined'\n                                    startIcon={<IconTrash size={15} />}\n                                    onClick={() => onDeleteCredential(values.registeredCredential._id)}\n                                >\n                                    Delete Credential\n                                </Button>\n                            )}\n\n                            {oAuth2RedirectURL && (\n                                <div>\n                                    <Typography variant='overline'>OAuth2 Redirect URL</Typography>\n                                    <Stack direction='row'>\n                                        <Typography\n                                            sx={{\n                                                p: 1,\n                                                borderRadius: 10,\n                                                backgroundColor: theme.palette.primary.light,\n                                                width: 'max-content',\n                                                height: 'max-content'\n                                            }}\n                                            variant='h5'\n                                        >\n                                            {oAuth2RedirectURL}\n                                        </Typography>\n                                        <IconButton\n                                            title='Copy URL'\n                                            color='primary'\n                                            onClick={() => navigator.clipboard.writeText(oAuth2RedirectURL)}\n                                        >\n                                            <IconCopy />\n                                        </IconButton>\n                                    </Stack>\n                                </div>\n                            )}\n\n                            {values.credentialMethod &&\n                                credentialParams.map((input) => {\n                                    if (input.type === 'json') {\n                                        const inputName = input.name\n\n                                        return (\n                                            <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                                <Stack direction='row'>\n                                                    <Typography variant='overline'>{input.label}</Typography>\n                                                    {input.description && <TooltipWithParser title={input.description} />}\n                                                </Stack>\n                                                <PerfectScrollbar\n                                                    style={{\n                                                        border: '1px solid',\n                                                        borderColor: theme.palette.grey['500'],\n                                                        borderRadius: '12px',\n                                                        height: '200px',\n                                                        maxHeight: '200px',\n                                                        overflowX: 'hidden',\n                                                        backgroundColor: 'white'\n                                                    }}\n                                                    onScroll={(e) => e.stopPropagation()}\n                                                >\n                                                    {customization.isDarkMode ? (\n                                                        <DarkCodeEditor\n                                                            value={values[inputName] || ''}\n                                                            onValueChange={(code) => {\n                                                                setFieldValue(inputName, code)\n                                                            }}\n                                                            placeholder={input.placeholder}\n                                                            type={input.type}\n                                                            onBlur={(e) => {\n                                                                const overwriteValues = {\n                                                                    ...values,\n                                                                    [inputName]: e.target.value\n                                                                }\n                                                                onChanged(overwriteValues)\n                                                            }}\n                                                            style={{\n                                                                fontSize: '0.875rem',\n                                                                minHeight: '200px',\n                                                                width: '100%'\n                                                            }}\n                                                        />\n                                                    ) : (\n                                                        <LightCodeEditor\n                                                            value={values[inputName] || ''}\n                                                            onValueChange={(code) => {\n                                                                setFieldValue(inputName, code)\n                                                            }}\n                                                            placeholder={input.placeholder}\n                                                            type='json'\n                                                            onBlur={(e) => {\n                                                                const overwriteValues = {\n                                                                    ...values,\n                                                                    [inputName]: e.target.value\n                                                                }\n                                                                onChanged(overwriteValues)\n                                                            }}\n                                                            style={{\n                                                                fontSize: '0.875rem',\n                                                                minHeight: '200px',\n                                                                width: '100%'\n                                                            }}\n                                                        />\n                                                    )}\n                                                </PerfectScrollbar>\n                                                {errors[inputName] && (\n                                                    <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                        *{errors[inputName]}\n                                                    </span>\n                                                )}\n                                            </FormControl>\n                                        )\n                                    }\n\n                                    if (input.type === 'string' || input.type === 'password' || input.type === 'number') {\n                                        const inputName = input.name\n\n                                        return (\n                                            <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                                <Stack direction='row'>\n                                                    <Typography variant='overline'>{input.label}</Typography>\n                                                    {input.description && <TooltipWithParser title={input.description} />}\n                                                </Stack>\n                                                <OutlinedInput\n                                                    id={inputName}\n                                                    type={input.type === 'string' || input.type === 'number' ? 'text' : input.type}\n                                                    value={values[inputName] || ''}\n                                                    placeholder={input.placeholder}\n                                                    name={inputName}\n                                                    onBlur={(e) => {\n                                                        handleBlur(e)\n                                                        onChanged(values)\n                                                    }}\n                                                    onChange={handleChange}\n                                                />\n                                                {errors[inputName] && (\n                                                    <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                        *{errors[inputName]}\n                                                    </span>\n                                                )}\n                                            </FormControl>\n                                        )\n                                    }\n\n                                    if (input.type === 'boolean') {\n                                        const inputName = input.name\n\n                                        return (\n                                            <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                                <Stack direction='row'>\n                                                    <Typography variant='overline'>{input.label}</Typography>\n                                                    {input.description && <TooltipWithParser title={input.description} />}\n                                                </Stack>\n                                                <Switch\n                                                    checked={!!values[inputName]}\n                                                    onChange={(event) => {\n                                                        setFieldValue(inputName, event.target.checked)\n                                                        const overwriteValues = {\n                                                            ...values,\n                                                            [inputName]: event.target.checked\n                                                        }\n                                                        onChanged(overwriteValues)\n                                                    }}\n                                                    inputProps={{ 'aria-label': 'controlled' }}\n                                                />\n                                            </FormControl>\n                                        )\n                                    }\n\n                                    if (input.type === 'options') {\n                                        const inputName = input.name\n                                        const availableOptions = input.options || []\n\n                                        return (\n                                            <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }}>\n                                                <Stack direction='row'>\n                                                    <Typography variant='overline'>{input.label}</Typography>\n                                                    {input.description && <TooltipWithParser title={input.description} />}\n                                                </Stack>\n                                                <Autocomplete\n                                                    id={inputName}\n                                                    freeSolo\n                                                    options={availableOptions}\n                                                    value={\n                                                        findMatchingOptions(availableOptions, values[inputName]) || getDefaultOptionValue()\n                                                    }\n                                                    onChange={(e, selection) => {\n                                                        const value = selection ? selection.name : ''\n                                                        setFieldValue(inputName, value)\n                                                        const overwriteValues = {\n                                                            ...values,\n                                                            [inputName]: value\n                                                        }\n                                                        onChanged(overwriteValues)\n                                                    }}\n                                                    onBlur={handleBlur}\n                                                    PopperComponent={StyledPopper}\n                                                    renderInput={(params) => (\n                                                        <TextField\n                                                            {...params}\n                                                            value={values[inputName]}\n                                                            error={Boolean(errors[inputName])}\n                                                        />\n                                                    )}\n                                                    renderOption={(props, option) => (\n                                                        <Box component='li' {...props}>\n                                                            <div style={{ display: 'flex', flexDirection: 'column' }}>\n                                                                <Typography sx={{ p: 1 }} variant='h5'>\n                                                                    {option.label}\n                                                                </Typography>\n                                                                {option.description && (\n                                                                    <Typography sx={{ p: 1 }}>{option.description}</Typography>\n                                                                )}\n                                                            </div>\n                                                        </Box>\n                                                    )}\n                                                />\n                                                {errors[inputName] && (\n                                                    <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                        *{errors[inputName]}\n                                                    </span>\n                                                )}\n                                            </FormControl>\n                                        )\n                                    }\n                                    return null\n                                })}\n\n                            <Box sx={{ mt: 2 }}>\n                                {!(values.credentialMethod || '').toLowerCase().includes('google') && (\n                                    <AnimateButton>\n                                        <StyledButton\n                                            disableElevation\n                                            disabled={isSubmitting || Object.keys(errors).length > 0}\n                                            fullWidth\n                                            size='large'\n                                            type='submit'\n                                            variant='contained'\n                                            color='secondary'\n                                        >\n                                            {values &&\n                                            values.registeredCredential &&\n                                            (values.registeredCredential.name === ADD_NEW_CREDENTIAL || credentialParams.length)\n                                                ? 'Save and Continue'\n                                                : 'Continue'}\n                                        </StyledButton>\n                                    </AnimateButton>\n                                )}\n                                {(values.credentialMethod || '').toLowerCase().includes('google') && (\n                                    <StyledButton\n                                        disabled={isSubmitting || Object.keys(errors).length > 0}\n                                        fullWidth\n                                        size='large'\n                                        type='submit'\n                                        variant='contained'\n                                        color='secondary'\n                                        sx={{ p: 0, margin: 0 }}\n                                    >\n                                        <div\n                                            style={{\n                                                alignItems: 'center',\n                                                display: 'flex',\n                                                width: '100%',\n                                                height: 50,\n                                                background: 'white'\n                                            }}\n                                        >\n                                            <img\n                                                style={{ objectFit: 'contain', height: '100%', width: '100%', padding: 7 }}\n                                                src={gLoginLogo}\n                                                alt='Google Login'\n                                            />\n                                        </div>\n                                    </StyledButton>\n                                )}\n                            </Box>\n                        </form>\n                    )}\n                </Formik>\n            </Box>\n        </>\n    )\n}\n\nCredentialInput.propTypes = {\n    initialParams: PropTypes.array,\n    paramsType: PropTypes.string,\n    initialValues: PropTypes.object,\n    initialValidation: PropTypes.object,\n    valueChanged: PropTypes.func,\n    paramsChanged: PropTypes.func,\n    onSubmit: PropTypes.func\n}\n\nexport default CredentialInput\n"
  },
  {
    "path": "packages/ui/src/views/inputs/InputParameters.css",
    "content": ".editor__textarea {\n    outline: 0;\n}\n.editor__textarea::placeholder {\n    color: rgba(120, 120, 120, 0.5);\n}"
  },
  {
    "path": "packages/ui/src/views/inputs/InputParameters.js",
    "content": "import PropTypes from 'prop-types'\nimport { forwardRef } from 'react'\nimport { useSelector } from 'react-redux'\n\n// material-ui\nimport { Box, Stack, Button, FormControl, OutlinedInput, Popper, TextField, Typography, Switch } from '@mui/material'\nimport Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'\nimport { styled, useTheme } from '@mui/material/styles'\n\n// third party\nimport lodash from 'lodash'\nimport * as Yup from 'yup'\nimport { Formik } from 'formik'\nimport PerfectScrollbar from 'react-perfect-scrollbar'\nimport DatePicker from 'react-datepicker'\nimport 'react-datepicker/dist/react-datepicker.css'\n\n// project imports\nimport useScriptRef from 'hooks/useScriptRef'\nimport AnimateButton from 'ui-component/extended/AnimateButton'\nimport ArrayInputParameters from './ArrayInputParameters'\nimport OptionParamsResponse from './OptionParamsResponse'\nimport AsyncSelectWrapper from './AsyncSelectWrapper'\nimport { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'\nimport { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'\nimport { TooltipWithParser } from '../../ui-component/TooltipWithParser'\nimport { StyledButton } from '../../ui-component/StyledButton'\n\n// icons\nimport { IconPlus, IconUpload } from '@tabler/icons'\n\n// utils\nimport { convertDateStringToDateObject, getFileName, getFolderName } from 'utils/genericHelper'\n\n//css\nimport './InputParameters.css'\n\nconst StyledPopper = styled(Popper)({\n    boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',\n    borderRadius: '10px',\n    [`& .${autocompleteClasses.listbox}`]: {\n        boxSizing: 'border-box',\n        '& ul': {\n            padding: 10,\n            margin: 10,\n            backgroundColor: 'red'\n        }\n    }\n})\n\nconst DateCustomInput = forwardRef(function DateCustomInput({ isDarkMode, value, onClick }, ref) {\n    return (\n        <button\n            style={{\n                backgroundColor: isDarkMode ? '#32353b' : '#fafafa',\n                paddingTop: 8,\n                paddingBottom: 8,\n                paddingRight: 12,\n                paddingLeft: 12,\n                borderRadius: 12,\n                width: '100%',\n                height: 50,\n                border: isDarkMode ? 'none' : `1px solid #BDBDBD`,\n                cursor: 'pointer',\n                fontWeight: 'bold',\n                textAlign: 'start',\n                color: isDarkMode ? '#ffffff' : '#212121',\n                opacity: 0.9\n            }}\n            type='button'\n            onClick={onClick}\n            ref={ref}\n        >\n            {value}\n        </button>\n    )\n})\n\nDateCustomInput.propTypes = {\n    isDarkMode: PropTypes.bool,\n    value: PropTypes.string,\n    onClick: PropTypes.func\n}\n\n// ==============================|| INPUT PARAMETERS ||============================== //\n\nconst InputParameters = ({\n    params,\n    paramsType,\n    initialValues,\n    nodeParamsValidation,\n    nodeFlowData,\n    valueChanged,\n    onSubmit,\n    setVariableSelectorState,\n    onEditVariableDialogOpen,\n    ...others\n}) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n    const scriptedRef = useScriptRef()\n\n    const onChanged = (values) => {\n        const updateValues = values\n        updateValues.submit = null\n        valueChanged(updateValues, paramsType)\n    }\n\n    const onMouseUp = (e, inputName) => {\n        const cursorPosition = e.target.selectionEnd\n        const textBeforeCursorPosition = e.target.value.substring(0, cursorPosition)\n        const textAfterCursorPosition = e.target.value.substring(cursorPosition, e.target.value.length)\n        const path = `${paramsType}.${inputName}`\n        const body = {\n            textBeforeCursorPosition,\n            textAfterCursorPosition,\n            path,\n            paramsType\n        }\n        setVariableSelectorState(true, body)\n    }\n\n    const onAddArrayItem = (values, arrayItems, arrayName) => {\n        const updateValues = {\n            ...values,\n            [arrayName]: arrayItems\n        }\n        valueChanged(updateValues, paramsType)\n    }\n\n    const handleFolderUpload = (e, setFieldValue, values, inputName) => {\n        setVariableSelectorState(false)\n        if (!e.target.files) return\n        const files = e.target.files\n        const reader = new FileReader()\n\n        function readFile(fileIndex, base64Array) {\n            if (fileIndex >= files.length) {\n                setFieldValue(inputName, JSON.stringify(base64Array))\n                const overwriteValues = {\n                    ...values,\n                    [inputName]: JSON.stringify(base64Array)\n                }\n                onChanged(overwriteValues)\n                return\n            }\n            const file = files[fileIndex]\n            reader.onload = (evt) => {\n                if (!evt?.target?.result) {\n                    return\n                }\n                const { result } = evt.target\n                const value = result + `,filepath:${file.webkitRelativePath}`\n                base64Array.push(value)\n                readFile(fileIndex + 1, lodash.cloneDeep(base64Array))\n            }\n            reader.readAsDataURL(file)\n        }\n        readFile(0, [])\n    }\n\n    const handleFileUpload = (e, setFieldValue, values, inputName) => {\n        setVariableSelectorState(false)\n        if (!e.target.files) return\n\n        const file = e.target.files[0]\n        const { name } = file\n\n        const reader = new FileReader()\n        reader.onload = (evt) => {\n            if (!evt?.target?.result) {\n                return\n            }\n            const { result } = evt.target\n\n            const value = result + `,filename:${name}`\n            setFieldValue(inputName, value)\n            const overwriteValues = {\n                ...values,\n                [inputName]: value\n            }\n            onChanged(overwriteValues)\n        }\n        reader.readAsDataURL(file)\n    }\n\n    const findMatchingOptions = (options = [], value) => options.find((option) => option.name === value)\n\n    const getDefaultOptionValue = () => ''\n\n    return (\n        <>\n            <Box sx={{ width: 400 }}>\n                <Formik\n                    enableReinitialize\n                    initialValues={initialValues}\n                    validationSchema={Yup.object().shape(nodeParamsValidation)}\n                    onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {\n                        setVariableSelectorState(false)\n                        try {\n                            if (scriptedRef.current) {\n                                values.submit = true\n                                setStatus({ success: true })\n                                setSubmitting(false)\n                                onSubmit(values, paramsType)\n                            }\n                        } catch (err) {\n                            console.error(err)\n                            if (scriptedRef.current) {\n                                setStatus({ success: false })\n                                setErrors({ submit: err.message })\n                                setSubmitting(false)\n                            }\n                        }\n                    }}\n                >\n                    {({ errors, handleBlur, handleChange, handleSubmit, setFieldValue, isSubmitting, values }) => (\n                        <form noValidate onSubmit={handleSubmit} {...others}>\n                            {params.map((input) => {\n                                if (input.type === 'file' || input.type === 'folder') {\n                                    const inputName = input.name\n\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                            <Stack direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                            </Stack>\n\n                                            {input.type === 'file' && (\n                                                <span\n                                                    style={{\n                                                        fontWeight: 'bold',\n                                                        color: theme.palette.grey['800'],\n                                                        marginBottom: '1rem'\n                                                    }}\n                                                >\n                                                    {values[inputName] ? getFileName(values[inputName]) : 'Choose a file to upload'}\n                                                </span>\n                                            )}\n\n                                            {input.type === 'folder' && (\n                                                <span\n                                                    style={{\n                                                        fontWeight: 'bold',\n                                                        color: theme.palette.grey['800'],\n                                                        marginBottom: '1rem'\n                                                    }}\n                                                >\n                                                    {values[inputName] ? getFolderName(values[inputName]) : 'Choose a folder to upload'}\n                                                </span>\n                                            )}\n\n                                            <Button\n                                                variant='outlined'\n                                                component='label'\n                                                fullWidth\n                                                startIcon={<IconUpload />}\n                                                sx={{ marginRight: '1rem' }}\n                                            >\n                                                {input.type === 'folder' ? 'Upload Folder' : 'Upload File'}\n                                                {input.type === 'file' && (\n                                                    <input\n                                                        type='file'\n                                                        hidden\n                                                        onChange={(e) => handleFileUpload(e, setFieldValue, values, inputName)}\n                                                    />\n                                                )}\n                                                {input.type === 'folder' && (\n                                                    <input\n                                                        type='file'\n                                                        // https://github.com/jsx-eslint/eslint-plugin-react/issues/3454\n                                                        // eslint-disable-next-line react/no-unknown-property\n                                                        directory=''\n                                                        // eslint-disable-next-line react/no-unknown-property\n                                                        webkitdirectory=''\n                                                        hidden\n                                                        onChange={(e) => handleFolderUpload(e, setFieldValue, values, inputName)}\n                                                    />\n                                                )}\n                                            </Button>\n                                            {errors[inputName] && (\n                                                <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                    *{errors[inputName]}\n                                                </span>\n                                            )}\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'json' || input.type === 'code') {\n                                    const inputName = input.name\n\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                            <Stack sx={{ position: 'relative' }} direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                                <Button\n                                                    sx={{\n                                                        position: 'absolute',\n                                                        fontSize: '0.6875rem',\n                                                        right: 0,\n                                                        top: 5,\n                                                        height: 25,\n                                                        width: 'max-content'\n                                                    }}\n                                                    variant='outlined'\n                                                    onClick={() => onEditVariableDialogOpen(input, values)}\n                                                >\n                                                    Edit Variables\n                                                </Button>\n                                            </Stack>\n                                            <PerfectScrollbar\n                                                style={{\n                                                    border: '1px solid',\n                                                    borderColor: theme.palette.grey['500'],\n                                                    borderRadius: '12px',\n                                                    height: '200px',\n                                                    maxHeight: '200px',\n                                                    overflowX: 'hidden',\n                                                    backgroundColor: 'white'\n                                                }}\n                                                onScroll={(e) => e.stopPropagation()}\n                                            >\n                                                {customization.isDarkMode ? (\n                                                    <DarkCodeEditor\n                                                        value={values[inputName] || ''}\n                                                        onValueChange={(code) => {\n                                                            setFieldValue(inputName, code)\n                                                        }}\n                                                        placeholder={input.placeholder}\n                                                        type={input.type}\n                                                        onMouseUp={(e) => onMouseUp(e, inputName)}\n                                                        onBlur={(e) => {\n                                                            const overwriteValues = {\n                                                                ...values,\n                                                                [inputName]: e.target.value\n                                                            }\n                                                            onChanged(overwriteValues)\n                                                            onMouseUp(e, inputName)\n                                                        }}\n                                                        style={{\n                                                            fontSize: '0.875rem',\n                                                            minHeight: '200px',\n                                                            width: '100%'\n                                                        }}\n                                                    />\n                                                ) : (\n                                                    <LightCodeEditor\n                                                        value={values[inputName] || ''}\n                                                        onValueChange={(code) => {\n                                                            setFieldValue(inputName, code)\n                                                        }}\n                                                        placeholder={input.placeholder}\n                                                        type={input.type}\n                                                        onMouseUp={(e) => onMouseUp(e, inputName)}\n                                                        onBlur={(e) => {\n                                                            const overwriteValues = {\n                                                                ...values,\n                                                                [inputName]: e.target.value\n                                                            }\n                                                            onChanged(overwriteValues)\n                                                            onMouseUp(e, inputName)\n                                                        }}\n                                                        style={{\n                                                            fontSize: '0.875rem',\n                                                            minHeight: '200px',\n                                                            width: '100%'\n                                                        }}\n                                                    />\n                                                )}\n                                            </PerfectScrollbar>\n                                            {errors[inputName] && (\n                                                <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                    *{errors[inputName]}\n                                                </span>\n                                            )}\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'date') {\n                                    const inputName = input.name\n\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                            <Stack direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                            </Stack>\n                                            <DatePicker\n                                                customInput={<DateCustomInput isDarkMode={customization.isDarkMode} />}\n                                                selected={convertDateStringToDateObject(values[inputName]) || null}\n                                                showTimeInput\n                                                isClearable\n                                                timeInputLabel='Time:'\n                                                dateFormat='MM/dd/yyyy h:mm aa'\n                                                onChange={(date) => {\n                                                    const value = date ? date.toISOString() : null\n                                                    setVariableSelectorState(false)\n                                                    setFieldValue(inputName, value)\n                                                    const overwriteValues = {\n                                                        ...values,\n                                                        [inputName]: value\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                }}\n                                            />\n                                            {errors[inputName] && (\n                                                <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                    *{errors[inputName]}\n                                                </span>\n                                            )}\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'string' || input.type === 'password' || input.type === 'number') {\n                                    const inputName = input.name\n\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                            <Stack sx={{ position: 'relative' }} direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                                {(input.type === 'string' || input.type === 'number') && (\n                                                    <Button\n                                                        sx={{\n                                                            position: 'absolute',\n                                                            fontSize: '0.6875rem',\n                                                            right: 0,\n                                                            top: 5,\n                                                            height: 25,\n                                                            width: 'max-content'\n                                                        }}\n                                                        variant='outlined'\n                                                        onClick={() => onEditVariableDialogOpen(input, values)}\n                                                    >\n                                                        Edit Variables\n                                                    </Button>\n                                                )}\n                                            </Stack>\n                                            <OutlinedInput\n                                                id={inputName}\n                                                type={input.type === 'string' || input.type === 'number' ? 'text' : input.type}\n                                                placeholder={input.placeholder}\n                                                multiline={!!input.rows}\n                                                maxRows={input.rows || 0}\n                                                minRows={input.rows || 0}\n                                                value={values[inputName] || ''}\n                                                name={inputName}\n                                                onBlur={(e) => {\n                                                    handleBlur(e)\n                                                    onChanged(values)\n                                                    onMouseUp(e, inputName)\n                                                }}\n                                                onMouseUp={(e) => onMouseUp(e, inputName)}\n                                                onChange={handleChange}\n                                            />\n                                            {errors[inputName] && (\n                                                <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                    *{errors[inputName]}\n                                                </span>\n                                            )}\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'boolean') {\n                                    const inputName = input.name\n\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }} error={Boolean(errors[inputName])}>\n                                            <Stack direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                            </Stack>\n                                            <Switch\n                                                checked={!!values[inputName]}\n                                                onChange={(event) => {\n                                                    setVariableSelectorState(false)\n                                                    setFieldValue(inputName, event.target.checked)\n                                                    const overwriteValues = {\n                                                        ...values,\n                                                        [inputName]: event.target.checked\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                }}\n                                                inputProps={{ 'aria-label': 'controlled' }}\n                                            />\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'asyncOptions') {\n                                    const inputName = input.name\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }}>\n                                            <AsyncSelectWrapper\n                                                title={input.label}\n                                                description={input.description}\n                                                value={values[inputName]}\n                                                loadMethod={input.loadMethod}\n                                                loadFromDbCollections={input.loadFromDbCollections || []}\n                                                nodeFlowData={nodeFlowData}\n                                                error={JSON.stringify(errors[inputName])}\n                                                onChange={(selection) => {\n                                                    const value = selection ? selection.name : ''\n                                                    setFieldValue(inputName, value)\n                                                    const overwriteValues = {\n                                                        ...values,\n                                                        [inputName]: value\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                }}\n                                                onMenuOpen={() => setVariableSelectorState(false)}\n                                                onSetError={() => {\n                                                    const value = ''\n                                                    setFieldValue(inputName, value)\n                                                }}\n                                            />\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'options') {\n                                    const inputName = input.name\n                                    return (\n                                        <FormControl key={inputName} fullWidth sx={{ mb: 1, mt: 1 }}>\n                                            <Stack direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                            </Stack>\n                                            <Autocomplete\n                                                id={inputName}\n                                                freeSolo\n                                                onOpen={() => setVariableSelectorState(false)}\n                                                options={input.options || []}\n                                                value={findMatchingOptions(input.options, values[inputName]) || getDefaultOptionValue()}\n                                                onChange={(e, selection) => {\n                                                    const value = selection ? selection.name : ''\n                                                    setFieldValue(inputName, value)\n                                                    const overwriteValues = {\n                                                        ...values,\n                                                        [inputName]: value\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                }}\n                                                onInputChange={(e, value) => {\n                                                    if (!value) setFieldValue(inputName, '')\n                                                }}\n                                                onBlur={handleBlur}\n                                                PopperComponent={StyledPopper}\n                                                renderInput={(params) => (\n                                                    <TextField {...params} value={values[inputName]} error={Boolean(errors[inputName])} />\n                                                )}\n                                                renderOption={(props, option) => (\n                                                    <Box component='li' {...props}>\n                                                        <div style={{ display: 'flex', flexDirection: 'column' }}>\n                                                            <Typography sx={{ p: 1 }} variant='h5'>\n                                                                {option.label}\n                                                            </Typography>\n                                                            {option.description && (\n                                                                <Typography sx={{ p: 1, color: customization.isDarkMode ? '#9e9e9e' : '' }}>\n                                                                    {option.description}\n                                                                </Typography>\n                                                            )}\n                                                        </div>\n                                                    </Box>\n                                                )}\n                                            />\n                                            {errors[inputName] && (\n                                                <span style={{ color: 'red', fontSize: '0.7rem', fontStyle: 'italic' }}>\n                                                    *{errors[inputName]}\n                                                </span>\n                                            )}\n                                            <OptionParamsResponse value={values[inputName]} options={input.options || []} />\n                                        </FormControl>\n                                    )\n                                }\n\n                                if (input.type === 'array') {\n                                    const arrayParamItems = input.arrayParams\n                                    const templateArray = input.array\n                                    const inputName = input.name\n                                    const arrayItemsValues = values[inputName] || []\n\n                                    return (\n                                        <Stack sx={{ mt: 1 }} key={inputName}>\n                                            <Stack direction='row'>\n                                                <Typography variant='overline'>{input.label}</Typography>\n                                                {input.description && <TooltipWithParser title={input.description} />}\n                                            </Stack>\n                                            <ArrayInputParameters\n                                                initialValues={arrayItemsValues}\n                                                arrayParams={arrayParamItems}\n                                                paramsType={paramsType}\n                                                arrayGroupName={inputName}\n                                                errors={errors[inputName] ? errors[inputName] : []}\n                                                onArrayInputChange={(updateInitialValues) => {\n                                                    setFieldValue(inputName, updateInitialValues)\n                                                }}\n                                                onArrayInputBlur={(updateInitialValues) => {\n                                                    setFieldValue(inputName, updateInitialValues)\n                                                    const overwriteValues = {\n                                                        ...values,\n                                                        [inputName]: updateInitialValues\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                }}\n                                                onArrayItemRemove={(updateInitialValues) => {\n                                                    setFieldValue(inputName, updateInitialValues)\n                                                    const overwriteValues = {\n                                                        ...values,\n                                                        [inputName]: updateInitialValues\n                                                    }\n                                                    onChanged(overwriteValues)\n                                                }}\n                                                onArrayItemMouseUp={(variableState, body) => {\n                                                    if (body) setVariableSelectorState(variableState, body)\n                                                    else setVariableSelectorState(variableState)\n                                                }}\n                                                onEditVariableDialogOpen={(arrayItemInput, arrayItemValues, arrayItemIndex) => {\n                                                    const arrayItemBody = {\n                                                        arrayItemInput,\n                                                        arrayItemValues,\n                                                        arrayItemIndex,\n                                                        initialValues: arrayItemsValues\n                                                    }\n                                                    onEditVariableDialogOpen(input, values, arrayItemBody)\n                                                }}\n                                            />\n                                            <Box key={inputName} sx={{ mb: 2 }}>\n                                                <AnimateButton>\n                                                    <Button\n                                                        disableElevation\n                                                        fullWidth\n                                                        size='small'\n                                                        variant='outlined'\n                                                        color='secondary'\n                                                        startIcon={<IconPlus />}\n                                                        onClick={() => {\n                                                            setVariableSelectorState(false)\n                                                            let newObj = {}\n                                                            if (input.default && input.default.length) {\n                                                                newObj = input.default[0]\n                                                            } else {\n                                                                for (let i = 0; i < templateArray.length; i += 1) {\n                                                                    newObj[templateArray[i].name] = templateArray[i].default || ''\n                                                                }\n                                                            }\n                                                            arrayItemsValues.push(newObj)\n                                                            onAddArrayItem(values, arrayItemsValues, inputName)\n                                                        }}\n                                                    >\n                                                        Add {input.label}\n                                                    </Button>\n                                                </AnimateButton>\n                                            </Box>\n                                        </Stack>\n                                    )\n                                }\n                                return null\n                            })}\n\n                            <Box sx={{ mt: 2 }}>\n                                <AnimateButton>\n                                    <StyledButton\n                                        disableElevation\n                                        disabled={isSubmitting || Object.keys(errors).length > 0}\n                                        fullWidth\n                                        size='large'\n                                        type='submit'\n                                        variant='contained'\n                                        color='secondary'\n                                    >\n                                        Continue\n                                    </StyledButton>\n                                </AnimateButton>\n                            </Box>\n                        </form>\n                    )}\n                </Formik>\n            </Box>\n        </>\n    )\n}\n\nInputParameters.propTypes = {\n    params: PropTypes.array,\n    paramsType: PropTypes.string,\n    initialValues: PropTypes.object,\n    nodeParamsValidation: PropTypes.object,\n    nodeFlowData: PropTypes.object,\n    valueChanged: PropTypes.func,\n    onSubmit: PropTypes.func,\n    setVariableSelectorState: PropTypes.func,\n    onEditVariableDialogOpen: PropTypes.func\n}\n\nexport default InputParameters\n"
  },
  {
    "path": "packages/ui/src/views/inputs/OptionParamsResponse.css",
    "content": ".params ul {\n    line-height: 1.75em;\n}\n    \n.params li {\n    margin-bottom: 1em;\n}\n\n.inline {\n    background-color: white;\n    color: #4527a0;\n    font-weight: 600;\n    padding: 0.2em;\n    padding-left: 0.4em;\n    padding-right: 0.4em;\n    border-radius: 0.5em;\n}"
  },
  {
    "path": "packages/ui/src/views/inputs/OptionParamsResponse.js",
    "content": "import PropTypes from 'prop-types'\nimport { useSelector } from 'react-redux'\n\n// material-ui\nimport { Box, Typography } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// third party\nimport ReactJson from 'react-json-view'\n\n// utils\nimport { copyToClipboard } from 'utils/genericHelper'\n\n//css\nimport './OptionParamsResponse.css'\n\n// ==============================|| OPTION PARAMS RESPONSE ||============================== //\n\nconst OptionParamsResponse = ({ value, options }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const getSelectedValue = (value) => options.find((option) => option.name === value)\n\n    const getSelectedOptionInputParams = (value) => {\n        const selectedOption = options.find((option) => option.name === value)\n        if (selectedOption) {\n            return selectedOption.inputParameters || ''\n        }\n        return ''\n    }\n\n    const getSelectedOptionExampleParams = (value) => {\n        const selectedOption = options.find((option) => option.name === value)\n        if (selectedOption) {\n            return selectedOption.exampleParameters || ''\n        }\n        return ''\n    }\n\n    const getSelectedOptionExampleResponse = (value) => {\n        const selectedOption = options.find((option) => option.name === value)\n        if (selectedOption) {\n            return selectedOption.exampleResponse || ''\n        }\n        return ''\n    }\n\n    return (\n        <>\n            {getSelectedValue(value) && getSelectedOptionInputParams(value) && (\n                <Box\n                    sx={{\n                        p: 1,\n                        mt: 2,\n                        backgroundColor: theme.palette.secondary.light,\n                        borderRadius: `15px`,\n                        position: 'relative'\n                    }}\n                >\n                    <Typography sx={{ p: 1 }} variant='h6'>\n                        Parameters\n                    </Typography>\n                    <div className='params' dangerouslySetInnerHTML={{ __html: getSelectedOptionInputParams(value) }} />\n                </Box>\n            )}\n\n            {getSelectedValue(value) && getSelectedOptionExampleParams(value) && (\n                <Box\n                    sx={{\n                        p: 1,\n                        mt: 2,\n                        backgroundColor: theme.palette.secondary.light,\n                        borderRadius: `15px`,\n                        position: 'relative'\n                    }}\n                >\n                    <Typography sx={{ p: 1 }} variant='h6'>\n                        Example Parameters\n                    </Typography>\n                    <ReactJson\n                        theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                        collapsed\n                        style={{ padding: 10, borderRadius: 10 }}\n                        src={JSON.parse(getSelectedOptionExampleParams(value))}\n                        enableClipboard={(e) => copyToClipboard(e)}\n                    />\n                </Box>\n            )}\n\n            {getSelectedValue(value) && getSelectedOptionExampleResponse(value) && (\n                <Box\n                    sx={{\n                        p: 1,\n                        mt: 2,\n                        backgroundColor: theme.palette.secondary.light,\n                        borderRadius: `15px`,\n                        position: 'relative'\n                    }}\n                >\n                    <Typography sx={{ p: 1 }} variant='h6'>\n                        Example Response\n                    </Typography>\n                    <ReactJson\n                        theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                        collapsed\n                        style={{ padding: 10, borderRadius: 10 }}\n                        src={getSelectedOptionExampleResponse(value)}\n                        enableClipboard={(e) => copyToClipboard(e)}\n                    />\n                </Box>\n            )}\n        </>\n    )\n}\n\nOptionParamsResponse.propTypes = {\n    value: PropTypes.string,\n    options: PropTypes.array\n}\n\nexport default OptionParamsResponse\n"
  },
  {
    "path": "packages/ui/src/views/output/OutputResponses.js",
    "content": "import { useState, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { Box, Button, Chip, CircularProgress, Stack, Typography, IconButton } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// third party\nimport ReactJson from 'react-json-view'\nimport socketIOClient from 'socket.io-client'\n\n// project imports\nimport AnimateButton from 'ui-component/extended/AnimateButton'\nimport AttachmentDialog from 'ui-component/dialog/AttachmentDialog'\nimport HTMLDialog from 'ui-component/dialog/HTMLDialog'\nimport ExpandDataDialog from 'ui-component/dialog/ExpandDataDialog'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// API\nimport nodesApi from 'api/nodes'\nimport webhookApi from 'api/webhooks'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// icons\nimport { IconExclamationMark, IconCopy, IconArrowUpRightCircle, IconX, IconArrowsMaximize } from '@tabler/icons'\n\n// const\nimport { baseURL } from 'store/constant'\n\n// utils\nimport { copyToClipboard } from 'utils/genericHelper'\n\n// ==============================|| OUTPUT RESPONSES ||============================== //\n\nconst OutputResponses = ({ nodeId, nodeParamsType, nodeFlowData, nodes, edges, workflow, onSubmit }) => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [outputResponse, setOutputResponse] = useState([])\n    const [errorResponse, setErrorResponse] = useState(null)\n    const [nodeName, setNodeName] = useState(null)\n    const [nodeType, setNodeType] = useState(null)\n    const [nodeLabel, setNodeLabel] = useState(null)\n    const [isTestNodeBtnDisabled, disableTestNodeBtn] = useState(true)\n    const [testNodeLoading, setTestNodeLoading] = useState(null)\n    const [showHTMLDialog, setShowHTMLDialog] = useState(false)\n    const [HTMLDialogProps, setHTMLDialogProps] = useState({})\n    const [showAttachmentDialog, setShowAttachmentDialog] = useState(false)\n    const [attachmentDialogProps, setAttachmentDialogProps] = useState({})\n    const [showExpandDialog, setShowExpandDialog] = useState(false)\n    const [expandDialogProps, setExpandDialogProps] = useState({})\n    const [tunnelURL, setTunnelURL] = useState('')\n\n    const testNodeApi = useApi(nodesApi.testNode)\n    const getTunnelURLApi = useApi(webhookApi.getTunnelURL)\n\n    const onTestNodeClick = (nodeType) => {\n        /* If workflow is already deployed, stop it first to be safe.\n         *  Because it could cause throttled calls\n         */\n        if (workflow.deployed) {\n            setTestNodeLoading(false)\n            alert('Testing trigger requires stopping workflow. Please stop workflow first')\n            return\n        }\n\n        const testNodeBody = {\n            nodes,\n            edges,\n            nodeId\n        }\n\n        try {\n            setTestNodeLoading(true)\n\n            if (nodeType === 'webhook') {\n                const socket = socketIOClient(baseURL)\n\n                socket.on('connect', async () => {\n                    testNodeBody.clientId = socket.id\n                    testNodeApi.request(nodeFlowData.name, testNodeBody)\n                })\n\n                socket.on('testWebhookNodeResponse', (data) => {\n                    setOutputResponse(data)\n                    setTestNodeLoading(false)\n                    const formValues = {\n                        submit: true,\n                        needRetest: null,\n                        output: data\n                    }\n                    onSubmit(formValues, 'outputResponses')\n                    socket.disconnect()\n                })\n            } else {\n                testNodeApi.request(nodeFlowData.name, testNodeBody)\n            }\n        } catch (error) {\n            setTestNodeLoading(false)\n            setOutputResponse([])\n            setErrorResponse(error)\n            console.error(error)\n        }\n    }\n\n    const checkIfTestNodeValid = () => {\n        const paramsTypes = nodeParamsType.filter((type) => type !== 'outputResponses')\n        for (let i = 0; i < paramsTypes.length; i += 1) {\n            const paramType = paramsTypes[i]\n\n            if (!nodeFlowData[paramType] || !nodeFlowData[paramType].submit) {\n                return true\n            }\n        }\n        return false\n    }\n\n    const openAttachmentDialog = (outputResponse) => {\n        const dialogProp = {\n            title: 'Attachments',\n            executionData: outputResponse\n        }\n        setAttachmentDialogProps(dialogProp)\n        setShowAttachmentDialog(true)\n    }\n\n    const openHTMLDialog = (executionData) => {\n        const dialogProp = {\n            title: 'HTML',\n            executionData\n        }\n        setHTMLDialogProps(dialogProp)\n        setShowHTMLDialog(true)\n    }\n\n    const onExpandDialogClicked = (executionData) => {\n        const dialogProp = {\n            title: `Output Responses: ${nodeLabel} `,\n            data: executionData\n        }\n        setExpandDialogProps(dialogProp)\n        setShowExpandDialog(true)\n    }\n\n    useEffect(() => {\n        if (nodeFlowData && nodeFlowData.outputResponses && nodeFlowData.outputResponses.output) {\n            setOutputResponse(nodeFlowData.outputResponses.output)\n        } else {\n            setOutputResponse([])\n        }\n\n        disableTestNodeBtn(checkIfTestNodeValid())\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [nodeFlowData, nodeParamsType])\n\n    useEffect(() => {\n        if (nodes && nodeId) {\n            const selectedNode = nodes.find((nd) => nd.id === nodeId)\n            if (selectedNode) {\n                setNodeName(selectedNode.data.name)\n                setNodeType(selectedNode.data.type)\n                setNodeLabel(selectedNode.data.label)\n            }\n        }\n    }, [nodes, nodeId])\n\n    // Test node successful\n    useEffect(() => {\n        if (testNodeApi.data && nodeType && nodeType !== 'webhook') {\n            const testNodeData = testNodeApi.data\n            setOutputResponse(testNodeData)\n            setErrorResponse(null)\n            const formValues = {\n                submit: true,\n                needRetest: null,\n                output: testNodeData\n            }\n            onSubmit(formValues, 'outputResponses')\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [testNodeApi.data])\n\n    useEffect(() => {\n        getTunnelURLApi.request()\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    useEffect(() => {\n        if (getTunnelURLApi.data) {\n            setTunnelURL(getTunnelURLApi.data)\n        }\n    }, [getTunnelURLApi.data])\n\n    // Test node error\n    useEffect(() => {\n        if (testNodeApi.error && nodeType && nodeType !== 'webhook') {\n            let errorMessage = 'Unexpected Error.'\n\n            if (testNodeApi.error.response && testNodeApi.error.response.data) {\n                errorMessage = testNodeApi.error.response.data\n            } else if (testNodeApi.error.message) {\n                errorMessage = testNodeApi.error.message\n            }\n\n            setErrorResponse(errorMessage)\n            setOutputResponse([])\n            const formValues = {\n                submit: null,\n                needRetest: null,\n                output: []\n            }\n            onSubmit(formValues, 'outputResponses')\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [testNodeApi.error])\n\n    // Test node loading\n    useEffect(() => {\n        if (nodeType && nodeType !== 'webhook') setTestNodeLoading(testNodeApi.loading)\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [testNodeApi.loading])\n\n    return (\n        <>\n            <Box sx={{ width: 400 }}>\n                {nodeFlowData && nodeFlowData.outputResponses && nodeFlowData.outputResponses.needRetest && (\n                    <Chip sx={{ mb: 2 }} icon={<IconExclamationMark />} label='Retest the node for updated parameters' color='warning' />\n                )}\n                {nodeName && (nodeName === 'webhook' || nodeName === 'chainLinkFunctionWebhook') && (\n                    <Box sx={{ mb: 3 }}>\n                        <Typography variant='h5' sx={{ mb: 1 }}>\n                            Local URL:\n                        </Typography>\n                        <Typography\n                            variant='h5'\n                            sx={{\n                                p: 1,\n                                borderRadius: 2,\n                                backgroundColor: theme.palette.primary.light,\n                                width: 'max-content',\n                                height: 'max-content',\n                                mb: 2,\n                                mr: 2\n                            }}\n                        >\n                            {`${baseURL}/api/v1/webhook/${nodeFlowData.webhookEndpoint}`}\n                        </Typography>\n                        <Stack direction='row' spacing={2}>\n                            <Button\n                                size='small'\n                                variant='outlined'\n                                startIcon={<IconCopy />}\n                                onClick={() => navigator.clipboard.writeText(`${baseURL}/api/v1/webhook/${nodeFlowData.webhookEndpoint}`)}\n                            >\n                                Copy URL\n                            </Button>\n                            <Button\n                                size='small'\n                                variant='outlined'\n                                startIcon={<IconArrowUpRightCircle />}\n                                onClick={() => window.open(`${baseURL}/api/v1/webhook/${nodeFlowData.webhookEndpoint}`, '_blank')}\n                            >\n                                Open in New Tab\n                            </Button>\n                        </Stack>\n                        {tunnelURL && (\n                            <div>\n                                <Typography variant='h5' sx={{ mb: 1, mt: 2 }}>\n                                    Tunnel URL:\n                                </Typography>\n                                <Typography\n                                    variant='h5'\n                                    sx={{\n                                        p: 1,\n                                        borderRadius: 2,\n                                        backgroundColor: theme.palette.primary.light,\n                                        width: 'max-content',\n                                        height: 'max-content',\n                                        mb: 2,\n                                        mr: 2\n                                    }}\n                                >\n                                    {`${tunnelURL}api/v1/webhook/${nodeFlowData.webhookEndpoint}`}\n                                </Typography>\n                                <Stack direction='row' spacing={2}>\n                                    <Button\n                                        size='small'\n                                        variant='outlined'\n                                        startIcon={<IconCopy />}\n                                        onClick={() =>\n                                            navigator.clipboard.writeText(`${tunnelURL}api/v1/webhook/${nodeFlowData.webhookEndpoint}`)\n                                        }\n                                    >\n                                        Copy URL\n                                    </Button>\n                                    <Button\n                                        size='small'\n                                        variant='outlined'\n                                        startIcon={<IconArrowUpRightCircle />}\n                                        onClick={() => window.open(`${tunnelURL}api/v1/webhook/${nodeFlowData.webhookEndpoint}`, '_blank')}\n                                    >\n                                        Open in New Tab\n                                    </Button>\n                                </Stack>\n                            </div>\n                        )}\n                    </Box>\n                )}\n                {errorResponse && (\n                    <Box sx={{ mb: 2 }}>\n                        <Chip sx={{ mb: 2 }} icon={<IconX />} label='Error' color='error' />\n                        <div style={{ color: 'red' }}>{errorResponse}</div>\n                    </Box>\n                )}\n                <Box sx={{ position: 'relative' }}>\n                    <ReactJson\n                        theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}\n                        collapsed\n                        style={{ padding: 10, borderRadius: 10 }}\n                        src={outputResponse}\n                        enableClipboard={(e) => copyToClipboard(e)}\n                    />\n                    <IconButton\n                        size='small'\n                        sx={{\n                            height: 25,\n                            width: 25,\n                            position: 'absolute',\n                            top: 5,\n                            right: 5\n                        }}\n                        title='Expand Data'\n                        color='primary'\n                        onClick={() => onExpandDialogClicked(outputResponse)}\n                    >\n                        <IconArrowsMaximize />\n                    </IconButton>\n                    <div>\n                        {outputResponse.map((respObj, respObjIndex) => (\n                            <div key={respObjIndex}>\n                                {respObj.html && (\n                                    <Typography sx={{ p: 1, mt: 2 }} variant='h5'>\n                                        HTML\n                                    </Typography>\n                                )}\n                                {respObj.html && (\n                                    <div\n                                        style={{\n                                            width: '100%',\n                                            height: '100%',\n                                            maxHeight: 400,\n                                            overflow: 'auto',\n                                            backgroundColor: 'white',\n                                            borderRadius: 5\n                                        }}\n                                        dangerouslySetInnerHTML={{ __html: respObj.html }}\n                                    />\n                                )}\n                                {respObj.html && (\n                                    <StyledButton\n                                        sx={{ mt: 1 }}\n                                        size='small'\n                                        variant='contained'\n                                        onClick={() => openHTMLDialog(outputResponse)}\n                                    >\n                                        View HTML\n                                    </StyledButton>\n                                )}\n\n                                {respObj.attachments && (\n                                    <Typography sx={{ p: 1, mt: 2, pb: 0 }} variant='h5'>\n                                        Attachments\n                                    </Typography>\n                                )}\n                                {respObj.attachments &&\n                                    respObj.attachments.map((attachment, attchIndex) => (\n                                        <div key={attchIndex}>\n                                            <Typography sx={{ p: 1 }} variant='h6'>\n                                                Item {respObjIndex} |{' '}\n                                                {attachment.filename ? attachment.filename : `Attachment ${attchIndex}`}\n                                            </Typography>\n                                            <embed\n                                                src={attachment.content}\n                                                width='100%'\n                                                height='100%'\n                                                style={{ borderStyle: 'solid' }}\n                                                type={attachment.contentType}\n                                            />\n                                            <Button size='small' variant='contained' onClick={() => openAttachmentDialog(outputResponse)}>\n                                                View Attachment\n                                            </Button>\n                                        </div>\n                                    ))}\n                            </div>\n                        ))}\n                    </div>\n                </Box>\n                <Box sx={{ mt: 2, position: 'relative' }}>\n                    <AnimateButton>\n                        <StyledButton\n                            disableElevation\n                            disabled={isTestNodeBtnDisabled || testNodeLoading}\n                            fullWidth\n                            size='large'\n                            type='submit'\n                            variant='contained'\n                            color='secondary'\n                            onClick={() => onTestNodeClick(nodeType)}\n                        >\n                            Test Node\n                        </StyledButton>\n                    </AnimateButton>\n                    {testNodeLoading && (\n                        <CircularProgress\n                            size={24}\n                            sx={{\n                                color: theme.palette.secondary.main,\n                                position: 'absolute',\n                                top: '50%',\n                                left: '50%',\n                                marginTop: '-12px',\n                                marginLeft: '-12px'\n                            }}\n                        />\n                    )}\n                </Box>\n            </Box>\n            <AttachmentDialog\n                show={showAttachmentDialog}\n                dialogProps={attachmentDialogProps}\n                onCancel={() => setShowAttachmentDialog(false)}\n            ></AttachmentDialog>\n            <HTMLDialog show={showHTMLDialog} dialogProps={HTMLDialogProps} onCancel={() => setShowHTMLDialog(false)}></HTMLDialog>\n            <ExpandDataDialog\n                show={showExpandDialog}\n                dialogProps={expandDialogProps}\n                onCancel={() => setShowExpandDialog(false)}\n            ></ExpandDataDialog>\n        </>\n    )\n}\n\nOutputResponses.propTypes = {\n    nodeId: PropTypes.string,\n    nodeParamsType: PropTypes.array,\n    nodeFlowData: PropTypes.object,\n    nodes: PropTypes.array,\n    edges: PropTypes.array,\n    workflow: PropTypes.object,\n    onSubmit: PropTypes.func\n}\n\nexport default OutputResponses\n"
  },
  {
    "path": "packages/ui/src/views/settings/index.js",
    "content": "import { useState, useEffect } from 'react'\nimport PropTypes from 'prop-types'\n\n// material-ui\nimport { useTheme } from '@mui/material/styles'\nimport { Box, List, Paper, Popper } from '@mui/material'\n\n// third-party\nimport PerfectScrollbar from 'react-perfect-scrollbar'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport Transitions from 'ui-component/extended/Transitions'\nimport NavItem from 'layout/MainLayout/Sidebar/MenuList/NavItem'\n\nimport settings from 'menu-items/settings'\n\n// ==============================|| SETTINGS ||============================== //\n\nconst Settings = ({ workflow, isSettingsOpen, anchorEl, onSettingsItemClick, onUploadFile }) => {\n    const theme = useTheme()\n    const [settingsMenu, setSettingsMenu] = useState([])\n\n    const [open, setOpen] = useState(false)\n\n    useEffect(() => {\n        if (workflow && !workflow.shortId) {\n            const settingsMenu = settings.children.filter((menu) => menu.id === 'loadWorkflow')\n            setSettingsMenu(settingsMenu)\n        } else if (workflow && workflow.shortId) {\n            const settingsMenu = settings.children\n            setSettingsMenu(settingsMenu)\n        }\n    }, [workflow])\n\n    useEffect(() => {\n        setOpen(isSettingsOpen)\n    }, [isSettingsOpen])\n\n    // settings list items\n    const items = settingsMenu.map((menu) => {\n        return (\n            <NavItem\n                key={menu.id}\n                item={menu}\n                level={1}\n                navType='SETTINGS'\n                onClick={(id) => onSettingsItemClick(id)}\n                onUploadFile={onUploadFile}\n            />\n        )\n    })\n\n    return (\n        <>\n            <Popper\n                placement='bottom-end'\n                open={open}\n                anchorEl={anchorEl}\n                role={undefined}\n                transition\n                disablePortal\n                popperOptions={{\n                    modifiers: [\n                        {\n                            name: 'offset',\n                            options: {\n                                offset: [170, 20]\n                            }\n                        }\n                    ]\n                }}\n                sx={{ zIndex: 1000 }}\n            >\n                {({ TransitionProps }) => (\n                    <Transitions in={open} {...TransitionProps}>\n                        <Paper>\n                            <MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}>\n                                <PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 250px)', overflowX: 'hidden' }}>\n                                    <Box sx={{ p: 2 }}>\n                                        <List>{items}</List>\n                                    </Box>\n                                </PerfectScrollbar>\n                            </MainCard>\n                        </Paper>\n                    </Transitions>\n                )}\n            </Popper>\n        </>\n    )\n}\n\nSettings.propTypes = {\n    workflow: PropTypes.object,\n    isSettingsOpen: PropTypes.bool,\n    anchorEl: PropTypes.any,\n    onSettingsItemClick: PropTypes.func,\n    onUploadFile: PropTypes.func\n}\n\nexport default Settings\n"
  },
  {
    "path": "packages/ui/src/views/wallets/WalletDialog.js",
    "content": "import { createPortal } from 'react-dom'\nimport PropTypes from 'prop-types'\nimport { useState, useEffect } from 'react'\nimport { useDispatch } from 'react-redux'\nimport { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'\n\nimport {\n    Avatar,\n    Accordion,\n    AccordionSummary,\n    AccordionDetails,\n    Box,\n    Divider,\n    Typography,\n    Button,\n    Dialog,\n    DialogActions,\n    DialogContent,\n    DialogTitle,\n    Stack,\n    IconButton\n} from '@mui/material'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport { useTheme } from '@mui/material/styles'\n\n// third-party\nimport * as Yup from 'yup'\nimport lodash from 'lodash'\n\n// project imports\nimport InputParameters from 'views/inputs/InputParameters'\nimport CredentialInput from 'views/inputs/CredentialInput'\nimport EditVariableDialog from 'ui-component/dialog/EditVariableDialog'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// Icons\nimport { IconCheck, IconX, IconArrowUpRightCircle, IconCopy, IconKey } from '@tabler/icons'\n\n// API\nimport walletsApi from 'api/wallets'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// Const\nimport { wallet_details, networkExplorers, privateKeyField } from 'store/constant'\n\n// utils\nimport { handleCredentialParams, initializeNodeData } from 'utils/genericHelper'\nimport useNotifier from 'utils/useNotifier'\n\nconst WalletDialog = ({ show, dialogProps, onCancel, onConfirm }) => {\n    const portalElement = document.getElementById('portal')\n\n    const theme = useTheme()\n    const dispatch = useDispatch()\n\n    // ==============================|| Snackbar ||============================== //\n\n    useNotifier()\n    const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))\n    const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))\n\n    const [walletDetails, setWalletDetails] = useState(wallet_details)\n    const [walletData, setWalletData] = useState({})\n    const [walletParams, setWalletParams] = useState([])\n    const [walletValues, setWalletValues] = useState({})\n    const [walletValidation, setWalletValidation] = useState({})\n    const [walletCredential, setWalletCredential] = useState({})\n    const [expanded, setExpanded] = useState(false)\n    const [isReadyToAdd, setIsReadyToAdd] = useState(false)\n    const [isEditVariableDialogOpen, setEditVariableDialog] = useState(false)\n    const [editVariableDialogProps, setEditVariableDialogProps] = useState({})\n    const walletParamsType = ['networks', 'credentials', 'walletInfo']\n\n    const getSpecificWalletApi = useApi(walletsApi.getSpecificWallet)\n    const getWalletCredentialApi = useApi(walletsApi.getWalletCredential)\n\n    const handleAccordionChange = (expanded) => (event, isExpanded) => {\n        setExpanded(isExpanded ? expanded : false)\n    }\n\n    const reset = () => {\n        setWalletData({})\n        setWalletParams([])\n        setWalletValues({})\n        setWalletValidation({})\n        setWalletCredential({})\n        setIsReadyToAdd(false)\n        setExpanded(false)\n    }\n\n    const checkIsReadyToAdd = (walletData) => {\n        for (let i = 0; i < walletParamsType.length; i += 1) {\n            const paramType = walletParamsType[i]\n            if (!walletData[paramType] || !walletData[paramType].submit) {\n                setIsReadyToAdd(false)\n                return\n            }\n        }\n        setIsReadyToAdd(true)\n    }\n\n    const onEditVariableDialogOpen = (input, values, arrayItemBody) => {\n        const dialogProps = {\n            input,\n            values,\n            arrayItemBody,\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Save',\n            hideVariables: true\n        }\n\n        setEditVariableDialogProps(dialogProps)\n        setEditVariableDialog(true)\n    }\n\n    const addNewWallet = async (type) => {\n        const createNewWalletBody = {\n            network: walletData.networks.network,\n            name: walletData.walletInfo.name,\n            providerCredential: JSON.stringify(walletData.credentials)\n        }\n        if (type === 'IMPORT') createNewWalletBody.privateKey = walletData.walletInfo.privateKey\n        try {\n            const createResp = await walletsApi.createNewWallet(createNewWalletBody)\n            if (createResp.data) {\n                enqueueSnackbar({\n                    message: 'New wallet added',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to add new wallet: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const saveWallet = async () => {\n        const saveWalletBody = {\n            network: walletData.networks.network,\n            name: walletData.walletInfo.name,\n            providerCredential: JSON.stringify(walletData.credentials)\n        }\n        try {\n            const saveResp = await walletsApi.updateWallet(dialogProps.id, saveWalletBody)\n            if (saveResp.data) {\n                enqueueSnackbar({\n                    message: 'Wallet saved',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to save wallet: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const deleteWallet = async () => {\n        try {\n            const deleteResp = await walletsApi.deleteWallet(dialogProps.id)\n            if (deleteResp.data) {\n                enqueueSnackbar({\n                    message: 'Wallet deleted',\n                    options: {\n                        key: new Date().getTime() + Math.random(),\n                        variant: 'success',\n                        action: (key) => (\n                            <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                                <IconX />\n                            </Button>\n                        )\n                    }\n                })\n                onConfirm()\n            }\n        } catch (error) {\n            const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`\n            enqueueSnackbar({\n                message: `Failed to delete wallet: ${errorData}`,\n                options: {\n                    key: new Date().getTime() + Math.random(),\n                    variant: 'error',\n                    persist: true,\n                    action: (key) => (\n                        <Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>\n                            <IconX />\n                        </Button>\n                    )\n                }\n            })\n            onCancel()\n        }\n    }\n\n    const valueChanged = (formValues, paramsType) => {\n        const updateWalletData = {\n            ...walletData,\n            [paramsType]: formValues\n        }\n\n        const index = walletParamsType.indexOf(paramsType)\n        if (index >= 0 && index !== walletParamsType.length - 1) {\n            for (let i = index + 1; i < walletParamsType.length; i += 1) {\n                const paramType = walletParamsType[i]\n                if (updateWalletData[paramType]) {\n                    updateWalletData[paramType].submit = null\n                }\n            }\n        }\n\n        setWalletData(updateWalletData)\n    }\n\n    const paramsChanged = (formParams, paramsType) => {\n        // Because formParams options can be changed due to show hide options,\n        // To avoid that, replace with original details options\n\n        const credentialMethodParam = formParams.find((param) => param.name === 'credentialMethod')\n        const credentialMethodParamIndex = formParams.findIndex((param) => param.name === 'credentialMethod')\n\n        if (credentialMethodParam !== undefined) {\n            const originalParam = walletDetails[paramsType].find((param) => param.name === 'credentialMethod')\n            if (originalParam !== undefined) {\n                formParams[credentialMethodParamIndex]['options'] = originalParam.options\n            }\n        }\n\n        const updateWalletDetails = {\n            ...walletDetails,\n            [paramsType]: formParams\n        }\n        setWalletDetails(updateWalletDetails)\n    }\n\n    const onSubmit = async (formValues, paramsType) => {\n        const updateWalletData = {\n            ...walletData,\n            [paramsType]: formValues\n        }\n        setWalletData(updateWalletData)\n\n        const index = walletParamsType.indexOf(paramsType)\n        if (index >= 0 && index !== walletParamsType.length - 1) {\n            setExpanded(walletParamsType[index + 1])\n        } else if (index === walletParamsType.length - 1) {\n            setExpanded(false)\n        }\n\n        checkIsReadyToAdd(updateWalletData)\n    }\n\n    const showHideOptions = (displayType, options) => {\n        let returnOptions = options\n        const toBeDeleteOptions = []\n\n        for (let i = 0; i < returnOptions.length; i += 1) {\n            const option = returnOptions[i]\n            const displayOptions = option[displayType]\n\n            if (displayOptions) {\n                Object.keys(displayOptions).forEach((path) => {\n                    const comparisonValue = displayOptions[path]\n                    const groundValue = lodash.get(walletData, path, '')\n\n                    if (Array.isArray(comparisonValue)) {\n                        if (displayType === 'show' && !comparisonValue.includes(groundValue)) {\n                            toBeDeleteOptions.push(option)\n                        }\n                        if (displayType === 'hide' && comparisonValue.includes(groundValue)) {\n                            toBeDeleteOptions.push(option)\n                        }\n                    }\n                })\n            }\n        }\n\n        for (let i = 0; i < toBeDeleteOptions.length; i += 1) {\n            returnOptions = returnOptions.filter((opt) => JSON.stringify(opt) !== JSON.stringify(toBeDeleteOptions[i]))\n        }\n\n        return returnOptions\n    }\n\n    const displayOptions = (params) => {\n        let clonedParams = params\n\n        for (let i = 0; i < clonedParams.length; i += 1) {\n            const input = clonedParams[i]\n            if (input.type === 'options') {\n                input.options = showHideOptions('show', input.options)\n                input.options = showHideOptions('hide', input.options)\n            }\n        }\n\n        return clonedParams\n    }\n\n    const setYupValidation = (params) => {\n        const validationSchema = {}\n        for (let i = 0; i < params.length; i += 1) {\n            const input = params[i]\n            if (input.type === 'string' && !input.optional) {\n                validationSchema[input.name] = Yup.string().required(`${input.label} is required. Type: ${input.type}`)\n            } else if (input.type === 'number' && !input.optional) {\n                validationSchema[input.name] = Yup.number().required(`${input.label} is required. Type: ${input.type}`)\n            } else if ((input.type === 'options' || input.type === 'asyncOptions') && !input.optional) {\n                validationSchema[input.name] = Yup.string().required(`${input.label} is required. Type: ${input.type}`)\n            }\n        }\n        return validationSchema\n    }\n\n    const initializeFormValuesAndParams = (paramsType) => {\n        const initialValues = {}\n        let walletParams = displayOptions(lodash.cloneDeep(walletDetails[paramsType] || []))\n        walletParams = handleCredentialParams(walletParams, paramsType, walletDetails[paramsType], walletData)\n\n        if (dialogProps.type === 'IMPORT' && paramsType === 'walletInfo') {\n            walletParams.push(...privateKeyField)\n        }\n\n        for (let i = 0; i < walletParams.length; i += 1) {\n            const input = walletParams[i]\n\n            // Load from walletData values\n            if (paramsType in walletData && input.name in walletData[paramsType]) {\n                initialValues[input.name] = walletData[paramsType][input.name]\n\n                // Check if option value is still available from the list of options\n                if (input.type === 'options') {\n                    const optionVal = input.options.find((option) => option.name === initialValues[input.name])\n                    if (!optionVal) delete initialValues[input.name]\n                }\n            } else {\n                // Load from walletParams default values\n                initialValues[input.name] = input.default || ''\n            }\n        }\n\n        initialValues.submit = null\n\n        setWalletValues(initialValues)\n        setWalletValidation(setYupValidation(walletParams))\n        setWalletParams(walletParams)\n    }\n\n    const transformWalletResponse = (walletResponseData, walletDetails) => {\n        const walletData = {\n            networks: {},\n            credentials: {},\n            walletInfo: {}\n        }\n\n        if (walletResponseData) {\n            walletData.networks = { network: walletResponseData.network, submit: true }\n            walletData.walletInfo = { ...walletResponseData, submit: true }\n            if (walletResponseData.providerCredential) {\n                try {\n                    walletData.credentials = JSON.parse(walletResponseData.providerCredential)\n                } catch (e) {\n                    console.error(e)\n                }\n            }\n        } else {\n            walletData.networks = initializeNodeData(walletDetails.networks)\n            walletData.credentials = initializeNodeData(walletDetails.credentials)\n            walletData.walletInfo = initializeNodeData(walletDetails.walletInfo)\n        }\n        return walletData\n    }\n\n    // Get Wallet Details from API\n    useEffect(() => {\n        if (getSpecificWalletApi.data) {\n            const walletResponseData = getSpecificWalletApi.data\n            setWalletData(transformWalletResponse(walletResponseData))\n            setExpanded('networks')\n        }\n    }, [getSpecificWalletApi.data])\n\n    // Get Wallet Credential from API\n    useEffect(() => {\n        if (getWalletCredentialApi.data) {\n            const walletCredResponseData = getWalletCredentialApi.data\n            setWalletCredential(walletCredResponseData)\n        }\n    }, [getWalletCredentialApi.data])\n\n    // Initialization\n    useEffect(() => {\n        if (show && (dialogProps.type === 'ADD' || dialogProps.type === 'IMPORT')) {\n            reset()\n            setWalletData(transformWalletResponse(null, walletDetails))\n            setExpanded('networks')\n        } else if (show && dialogProps.type === 'EDIT' && dialogProps.id) {\n            reset()\n            getSpecificWalletApi.request(dialogProps.id)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [show, dialogProps])\n\n    // Initialize Parameters Initial Values & Validation\n    useEffect(() => {\n        if (walletDetails && walletData && expanded) {\n            initializeFormValuesAndParams(expanded)\n        }\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [walletDetails, walletData, expanded])\n\n    const component = show ? (\n        <Dialog open={show} onClose={onCancel} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>\n            <DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>\n                {dialogProps.title}\n            </DialogTitle>\n            <DialogContent>\n                {walletData && walletData.walletInfo && walletData.walletInfo.address && dialogProps.type === 'EDIT' && (\n                    <Box sx={{ p: 2 }}>\n                        <Typography sx={{ p: 1 }} variant='overline'>\n                            BALANCE\n                        </Typography>\n                        <Typography sx={{ p: 1, mb: 1 }} variant='h3'>\n                            {walletData.walletInfo.balance}\n                        </Typography>\n                        <Typography sx={{ p: 1 }} variant='overline'>\n                            ADDRESS\n                        </Typography>\n                        <Stack direction='row' sx={{ p: 1, mb: 1 }}>\n                            <Typography\n                                sx={{\n                                    p: 1,\n                                    borderRadius: 10,\n                                    backgroundColor: theme.palette.primary.light,\n                                    width: 'max-content',\n                                    height: 'max-content'\n                                }}\n                                variant='h5'\n                            >\n                                {walletData.walletInfo.address}\n                            </Typography>\n                            <IconButton\n                                title='Copy Address'\n                                color='primary'\n                                onClick={() => navigator.clipboard.writeText(walletData.walletInfo.address)}\n                            >\n                                <IconCopy />\n                            </IconButton>\n                            <IconButton\n                                title='Open in Block Explorer'\n                                color='primary'\n                                onClick={() =>\n                                    window.open(\n                                        `${networkExplorers[walletData.networks.network]}/address/${walletData.walletInfo.address}`,\n                                        '_blank'\n                                    )\n                                }\n                            >\n                                <IconArrowUpRightCircle />\n                            </IconButton>\n                        </Stack>\n                        {walletCredential && walletCredential.privateKey && (\n                            <>\n                                <Typography sx={{ p: 1 }} variant='overline'>\n                                    PRIVATE KEY\n                                </Typography>\n                                <Stack direction='row' sx={{ p: 1, mb: 1 }}>\n                                    <Typography\n                                        sx={{\n                                            p: 1,\n                                            borderRadius: 10,\n                                            backgroundColor: theme.palette.primary.light,\n                                            width: 'max-content',\n                                            height: 'max-content'\n                                        }}\n                                        variant='h5'\n                                    >\n                                        {walletCredential.privateKey}\n                                    </Typography>\n                                    <IconButton\n                                        title='Copy Key'\n                                        color='primary'\n                                        onClick={() => navigator.clipboard.writeText(walletCredential.privateKey)}\n                                    >\n                                        <IconCopy />\n                                    </IconButton>\n                                </Stack>\n                            </>\n                        )}\n                        {walletCredential && walletCredential.mnemonic && (\n                            <>\n                                <Typography sx={{ p: 1 }} variant='overline'>\n                                    mnemonic\n                                </Typography>\n                                <Stack direction='row' sx={{ p: 1, mb: 1 }}>\n                                    <Typography\n                                        sx={{\n                                            p: 1,\n                                            borderRadius: 10,\n                                            backgroundColor: theme.palette.primary.light,\n                                            width: 'max-content',\n                                            height: 'max-content'\n                                        }}\n                                        variant='h5'\n                                    >\n                                        {walletCredential.mnemonic}\n                                    </Typography>\n                                    <IconButton\n                                        title='Copy Mnemonic'\n                                        color='primary'\n                                        onClick={() => navigator.clipboard.writeText(walletCredential.mnemonic)}\n                                    >\n                                        <IconCopy />\n                                    </IconButton>\n                                </Stack>\n                            </>\n                        )}\n                        {!Object.keys(walletCredential).length && (\n                            <StyledButton\n                                size='small'\n                                sx={{ ml: 1 }}\n                                variant='contained'\n                                startIcon={<IconKey />}\n                                onClick={() => getWalletCredentialApi.request(dialogProps.id)}\n                            >\n                                View PrivateKey and Mnemonic\n                            </StyledButton>\n                        )}\n                    </Box>\n                )}\n\n                {/* networks */}\n                <Box sx={{ p: 2 }}>\n                    <Accordion expanded={expanded === 'networks'} onChange={handleAccordionChange('networks')}>\n                        <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls='networks-content' id='networks-header'>\n                            <Typography variant='h4'>Networks</Typography>\n                            {walletData && walletData.networks && walletData.networks.submit && (\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.smallAvatar,\n                                        borderRadius: '50%',\n                                        background: theme.palette.success.dark,\n                                        color: 'white',\n                                        ml: 2\n                                    }}\n                                >\n                                    <IconCheck />\n                                </Avatar>\n                            )}\n                        </AccordionSummary>\n                        <AccordionDetails>\n                            <InputParameters\n                                paramsType='networks'\n                                params={walletParams}\n                                initialValues={walletValues}\n                                nodeParamsValidation={walletValidation}\n                                valueChanged={valueChanged}\n                                onSubmit={onSubmit}\n                                setVariableSelectorState={() => null}\n                                onEditVariableDialogOpen={onEditVariableDialogOpen}\n                            />\n                        </AccordionDetails>\n                    </Accordion>\n                    <Divider />\n                </Box>\n\n                {/* credentials */}\n                <Box sx={{ p: 2 }}>\n                    <Accordion expanded={expanded === 'credentials'} onChange={handleAccordionChange('credentials')}>\n                        <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls='credentials-content' id='credentials-header'>\n                            <Typography variant='h4'>Credentials</Typography>\n                            {walletData && walletData.credentials && walletData.credentials.submit && (\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.smallAvatar,\n                                        borderRadius: '50%',\n                                        background: theme.palette.success.dark,\n                                        color: 'white',\n                                        ml: 2\n                                    }}\n                                >\n                                    <IconCheck />\n                                </Avatar>\n                            )}\n                        </AccordionSummary>\n                        <AccordionDetails>\n                            <CredentialInput\n                                paramsType='credentials'\n                                initialParams={walletParams}\n                                initialValues={walletValues}\n                                initialValidation={walletValidation}\n                                valueChanged={valueChanged}\n                                paramsChanged={paramsChanged}\n                                onSubmit={onSubmit}\n                            />\n                        </AccordionDetails>\n                    </Accordion>\n                    <Divider />\n                </Box>\n\n                {/* walletInfo */}\n                <Box sx={{ p: 2 }}>\n                    <Accordion expanded={expanded === 'walletInfo'} onChange={handleAccordionChange('walletInfo')}>\n                        <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls='walletInfo-content' id='walletInfo-header'>\n                            <Typography variant='h4'>Wallet Details</Typography>\n                            {walletData && walletData.walletInfo && walletData.walletInfo.submit && (\n                                <Avatar\n                                    variant='rounded'\n                                    sx={{\n                                        ...theme.typography.smallAvatar,\n                                        borderRadius: '50%',\n                                        background: theme.palette.success.dark,\n                                        color: 'white',\n                                        ml: 2\n                                    }}\n                                >\n                                    <IconCheck />\n                                </Avatar>\n                            )}\n                        </AccordionSummary>\n                        <AccordionDetails>\n                            <InputParameters\n                                paramsType='walletInfo'\n                                params={walletParams}\n                                initialValues={walletValues}\n                                nodeParamsValidation={walletValidation}\n                                valueChanged={valueChanged}\n                                onSubmit={onSubmit}\n                                setVariableSelectorState={() => null}\n                                onEditVariableDialogOpen={onEditVariableDialogOpen}\n                            />\n                        </AccordionDetails>\n                    </Accordion>\n                    <Divider />\n                </Box>\n                <EditVariableDialog\n                    key={JSON.stringify(editVariableDialogProps)}\n                    show={isEditVariableDialogOpen}\n                    dialogProps={editVariableDialogProps}\n                    onCancel={() => setEditVariableDialog(false)}\n                    onConfirm={(updateValues) => {\n                        valueChanged(updateValues, expanded)\n                        setEditVariableDialog(false)\n                    }}\n                />\n            </DialogContent>\n            <DialogActions>\n                <Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>\n                {dialogProps.type === 'EDIT' && (\n                    <StyledButton variant='contained' color='error' onClick={() => deleteWallet()}>\n                        Delete\n                    </StyledButton>\n                )}\n                <StyledButton\n                    variant='contained'\n                    disabled={!isReadyToAdd}\n                    onClick={() =>\n                        dialogProps.type === 'ADD' || dialogProps.type === 'IMPORT' ? addNewWallet(dialogProps.type) : saveWallet()\n                    }\n                >\n                    {dialogProps.confirmButtonName}\n                </StyledButton>\n            </DialogActions>\n        </Dialog>\n    ) : null\n\n    return createPortal(component, portalElement)\n}\n\nWalletDialog.propTypes = {\n    show: PropTypes.bool,\n    dialogProps: PropTypes.object,\n    onCancel: PropTypes.func,\n    onConfirm: PropTypes.func\n}\n\nexport default WalletDialog\n"
  },
  {
    "path": "packages/ui/src/views/wallets/index.js",
    "content": "import { useEffect, useState } from 'react'\nimport { useSelector } from 'react-redux'\n\n// material-ui\nimport { Grid, Box, Stack } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport ItemCard from 'ui-component/cards/ItemCard'\nimport WalletDialog from './WalletDialog'\nimport WalletEmptySVG from 'assets/images/wallet_empty.svg'\nimport { TooltipWithParser } from 'ui-component/TooltipWithParser'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// const\nimport { gridSpacing } from 'store/constant'\n\n// API\nimport walletsApi from 'api/wallets'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// ==============================|| WALLETS ||============================== //\n\nconst Wallets = () => {\n    const theme = useTheme()\n    const customization = useSelector((state) => state.customization)\n\n    const [isLoading, setLoading] = useState(true)\n    const [showDialog, setShowDialog] = useState(false)\n    const [dialogProps, setDialogProps] = useState({})\n\n    const getAllWalletsApi = useApi(walletsApi.getAllWallets)\n\n    const addNew = () => {\n        const dialogProp = {\n            title: 'Add New Wallet',\n            type: 'ADD',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Add'\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const importNew = () => {\n        const dialogProp = {\n            title: 'Import Wallet',\n            type: 'IMPORT',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'IMPORT'\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const edit = (id) => {\n        const dialogProp = {\n            title: 'Edit Wallet',\n            type: 'EDIT',\n            cancelButtonName: 'Cancel',\n            confirmButtonName: 'Save',\n            id\n        }\n        setDialogProps(dialogProp)\n        setShowDialog(true)\n    }\n\n    const onConfirm = () => {\n        setShowDialog(false)\n        getAllWalletsApi.request()\n    }\n\n    useEffect(() => {\n        getAllWalletsApi.request()\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    useEffect(() => {\n        setLoading(getAllWalletsApi.loading)\n    }, [getAllWalletsApi.loading])\n\n    return (\n        <>\n            <MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>\n                <Stack flexDirection='row'>\n                    <h1>Wallets&nbsp;</h1>\n                    <TooltipWithParser title='Wallet is used to execute transaction on blockchain. For instance: swapping token, execute smart contract function, etc. Read <a target=\"_blank\" href=https://docs.outerbridge.io/how-to-use/wallets>more</a>' />\n                    <Grid sx={{ mb: 1.25 }} container direction='row'>\n                        <Box sx={{ flexGrow: 1 }} />\n                        <Grid item>\n                            <StyledButton variant='contained' sx={{ color: 'white', mr: 2 }} onClick={addNew}>\n                                Add New\n                            </StyledButton>\n                            <StyledButton\n                                variant='contained'\n                                sx={{ color: 'white', backgroundColor: theme.palette.secondary.main }}\n                                onClick={importNew}\n                            >\n                                Import Wallet\n                            </StyledButton>\n                        </Grid>\n                    </Grid>\n                </Stack>\n                <Grid container spacing={gridSpacing}>\n                    {!isLoading &&\n                        getAllWalletsApi.data &&\n                        getAllWalletsApi.data.map((data, index) => (\n                            <Grid key={index} item lg={4} md={6} sm={6} xs={12}>\n                                <ItemCard isLoading={isLoading} onClick={() => edit(data._id)} data={data} />\n                            </Grid>\n                        ))}\n                </Grid>\n                {!isLoading && (!getAllWalletsApi.data || getAllWalletsApi.data.length === 0) && (\n                    <Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>\n                        <Box sx={{ p: 2, height: 'auto' }}>\n                            <img style={{ objectFit: 'cover', height: '30vh', width: 'auto' }} src={WalletEmptySVG} alt='WalletEmptySVG' />\n                        </Box>\n                        <div>No Wallets Yet</div>\n                    </Stack>\n                )}\n            </MainCard>\n            <WalletDialog\n                show={showDialog}\n                dialogProps={dialogProps}\n                onCancel={() => setShowDialog(false)}\n                onConfirm={onConfirm}\n            ></WalletDialog>\n        </>\n    )\n}\n\nexport default Wallets\n"
  },
  {
    "path": "packages/ui/src/views/workflows/index.js",
    "content": "import { useEffect, useState } from 'react'\nimport { useNavigate } from 'react-router-dom'\nimport { useDispatch, useSelector } from 'react-redux'\n\n// material-ui\nimport { Grid, Box, Stack, Tooltip, Switch } from '@mui/material'\nimport { useTheme, styled } from '@mui/material/styles'\n\n// project imports\nimport MainCard from 'ui-component/cards/MainCard'\nimport ItemCard from 'ui-component/cards/ItemCard'\nimport { gridSpacing } from 'store/constant'\nimport WorkflowEmptySVG from 'assets/images/workflow_empty.svg'\nimport { SET_LAYOUT } from 'store/actions'\nimport { StyledButton } from 'ui-component/StyledButton'\n\n// API\nimport workflowsApi from 'api/workflows'\n\n// Hooks\nimport useApi from 'hooks/useApi'\n\n// const\nimport { baseURL } from 'store/constant'\n\nconst MaterialUISwitch = styled(Switch)(({ theme }) => ({\n    width: 62,\n    height: 34,\n    padding: 7,\n    '& .MuiSwitch-switchBase': {\n        margin: 1,\n        padding: 0,\n        transform: 'translateX(6px)',\n        '&.Mui-checked': {\n            color: '#fff',\n            transform: 'translateX(22px)',\n            '& .MuiSwitch-thumb:before': {\n                backgroundImage: `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-switch-vertical' width='24' height='24' viewBox='0 0 24 24' stroke-width='2' stroke='%23fff' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'%3E%3C/path%3E%3Cpath d='M3 8l4 -4l4 4'%3E%3C/path%3E%3Cpath d='M7 4l0 9'%3E%3C/path%3E%3Cpath d='M13 16l4 4l4 -4'%3E%3C/path%3E%3Cpath d='M17 10l0 10'%3E%3C/path%3E%3C/svg%3E\")`\n            },\n            '& + .MuiSwitch-track': {\n                opacity: 1,\n                backgroundColor: theme.palette.mode === 'dark' ? '#8796A5' : '#aab4be'\n            }\n        }\n    },\n    '& .MuiSwitch-thumb': {\n        backgroundColor: theme.palette.mode === 'dark' ? '#003892' : '#001e3c',\n        width: 32,\n        height: 32,\n        '&:before': {\n            content: \"''\",\n            position: 'absolute',\n            width: '100%',\n            height: '100%',\n            left: 0,\n            top: 0,\n            backgroundRepeat: 'no-repeat',\n            backgroundPosition: 'center',\n            backgroundImage: `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='icon icon-tabler icon-tabler-switch-horizontal' width='24' height='24' viewBox='0 0 24 24' stroke-width='2' stroke='%23fff' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'%3E%3C/path%3E%3Cpath d='M16 3l4 4l-4 4'%3E%3C/path%3E%3Cpath d='M10 7l10 0'%3E%3C/path%3E%3Cpath d='M8 13l-4 4l4 4'%3E%3C/path%3E%3Cpath d='M4 17l9 0'%3E%3C/path%3E%3C/svg%3E\")`\n        }\n    },\n    '& .MuiSwitch-track': {\n        opacity: 1,\n        backgroundColor: theme.palette.mode === 'dark' ? '#8796A5' : '#aab4be',\n        borderRadius: 20 / 2\n    }\n}))\n\n// ==============================|| WORKFLOWS ||============================== //\n\nconst Workflows = () => {\n    const navigate = useNavigate()\n    const theme = useTheme()\n    const dispatch = useDispatch()\n    const customization = useSelector((state) => state.customization)\n\n    const [isLoading, setLoading] = useState(true)\n    const [images, setImages] = useState({})\n    const [isHorizontal, setIsHorizontal] = useState(customization.isHorizontal)\n\n    const getAllWorkflowsApi = useApi(workflowsApi.getAllWorkflows)\n\n    const addNew = () => {\n        navigate('/canvas')\n    }\n\n    const goToCanvas = (selectedWorkflow) => {\n        navigate(`/canvas/${selectedWorkflow.shortId}`)\n    }\n\n    useEffect(() => {\n        getAllWorkflowsApi.request()\n\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    useEffect(() => {\n        setLoading(getAllWorkflowsApi.loading)\n    }, [getAllWorkflowsApi.loading])\n\n    useEffect(() => {\n        if (getAllWorkflowsApi.data) {\n            try {\n                const workflows = getAllWorkflowsApi.data\n                const images = {}\n\n                for (let i = 0; i < workflows.length; i += 1) {\n                    const flowDataStr = workflows[i].flowData\n                    const flowData = JSON.parse(flowDataStr)\n                    const nodes = flowData.nodes || []\n                    images[workflows[i].shortId] = []\n\n                    for (let j = 0; j < nodes.length; j += 1) {\n                        const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}`\n                        if (!images[workflows[i].shortId].includes(imageSrc)) {\n                            images[workflows[i].shortId].push(imageSrc)\n                        }\n                    }\n                }\n                setImages(images)\n            } catch (e) {\n                console.error(e)\n            }\n        }\n    }, [getAllWorkflowsApi.data])\n\n    const handleSwapLayout = () => {\n        dispatch({ type: SET_LAYOUT, isHorizontal: !isHorizontal })\n        setIsHorizontal((isHorizontal) => !isHorizontal)\n        localStorage.setItem('isHorizontal', !isHorizontal)\n    }\n\n    return (\n        <MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>\n            <Stack flexDirection='row'>\n                <h1>Workflows</h1>\n                <Tooltip placement='right' sx={{ ml: 2, mt: 1.2 }} title='Change workflow flow top to bottom or left to right'>\n                    <MaterialUISwitch checked={isHorizontal} onChange={handleSwapLayout} />\n                </Tooltip>\n                <Grid sx={{ mb: 1.25 }} container direction='row'>\n                    <Box sx={{ flexGrow: 1 }} />\n                    <Grid item>\n                        <StyledButton variant='contained' sx={{ color: 'white' }} onClick={addNew}>\n                            Add New\n                        </StyledButton>\n                    </Grid>\n                </Grid>\n            </Stack>\n            <Grid container spacing={gridSpacing}>\n                {!isLoading &&\n                    getAllWorkflowsApi.data &&\n                    getAllWorkflowsApi.data.map((data, index) => (\n                        <Grid key={index} item lg={4} md={6} sm={6} xs={12}>\n                            <ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.shortId]} />\n                        </Grid>\n                    ))}\n            </Grid>\n            {!isLoading && (!getAllWorkflowsApi.data || getAllWorkflowsApi.data.length === 0) && (\n                <Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>\n                    <Box sx={{ p: 2, height: 'auto' }}>\n                        <img style={{ objectFit: 'cover', height: '30vh', width: 'auto' }} src={WorkflowEmptySVG} alt='WorkflowEmptySVG' />\n                    </Box>\n                    <div>No Workflows Yet</div>\n                </Stack>\n            )}\n        </MainCard>\n    )\n}\n\nexport default Workflows\n"
  },
  {
    "path": "turbo.json",
    "content": "{\n    \"$schema\": \"https://turbo.build/schema.json\",\n    \"pipeline\": {\n        \"build\": {\n            \"dependsOn\": [\"^build\"],\n            \"outputs\": [\"dist/**\"]\n        },\n        \"test\": {},\n        \"dev\": {\n            \"cache\": false\n        }\n    }\n}\n"
  }
]